From 777b298613c3c28cdd656db3f81a86657a70144c Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Fri, 5 Jun 2015 11:28:19 +0200 Subject: [PATCH 01/66] warnings fixed --- test/libp2p/peer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/libp2p/peer.cpp b/test/libp2p/peer.cpp index d46964d21..bcb6d4085 100644 --- a/test/libp2p/peer.cpp +++ b/test/libp2p/peer.cpp @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(saveNodes) unsigned const c_nodes = 6; unsigned const c_peers = c_nodes - 1; - for (int i = 0; i < c_nodes; ++i) + for (unsigned i = 0; i < c_nodes; ++i) { Host* h = new Host("Test", NetworkPreferences("127.0.0.1", 30300 + i, false)); h->setIdealPeerCount(10); @@ -95,14 +95,14 @@ BOOST_AUTO_TEST_CASE(saveNodes) for (auto const& h: hosts) host.addNode(h->id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort())); - for (int i = 0; i < c_peers * 1000 && host.peerCount() < c_peers; i += c_step) + for (unsigned i = 0; i < c_peers * 1000 && host.peerCount() < c_peers; i += c_step) this_thread::sleep_for(chrono::milliseconds(c_step)); Host& host2 = *hosts.back(); for (auto const& h: hosts) host2.addNode(h->id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort())); - for (int i = 0; i < c_peers * 1000 && host2.peerCount() < c_peers; i += c_step) + for (unsigned i = 0; i < c_peers * 1000 && host2.peerCount() < c_peers; i += c_step) this_thread::sleep_for(chrono::milliseconds(c_step)); BOOST_CHECK_EQUAL(host.peerCount(), c_peers); From a7f861a46be770d37a2048900448e7d41cadd2ab Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Fri, 5 Jun 2015 11:31:38 +0200 Subject: [PATCH 02/66] comment updated --- libdevcore/Log.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libdevcore/Log.h b/libdevcore/Log.h index f104caccd..a4011fc0e 100644 --- a/libdevcore/Log.h +++ b/libdevcore/Log.h @@ -60,6 +60,7 @@ extern std::function g_logPost; extern std::map g_logOverride; /// Temporary changes system's verbosity for specific function. Restores the old verbosity when function returns. +/// Not thread-safe, use with caution! struct VerbosityHolder { VerbosityHolder(int _temporaryValue) : oldLogVerbosity(g_logVerbosity) { g_logVerbosity = _temporaryValue; } From 5dacb60e098aeb182819968ded8e4516cd858e33 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 10 Jun 2015 04:17:31 +0200 Subject: [PATCH 03/66] FreeBSD fix for ethash byte ordering Changes from https://github.com/ethereum/cpp-ethereum/pull/1919 . It closes https://github.com/ethereum/cpp-ethereum/issues/1918 --- endian.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/endian.h b/endian.h index 0ee402d9a..6ca6cc036 100644 --- a/endian.h +++ b/endian.h @@ -32,6 +32,9 @@ #include #define ethash_swap_u32(input_) OSSwapInt32(input_) #define ethash_swap_u64(input_) OSSwapInt64(input_) +#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) +#define ethash_swap_u32(input_) bswap32(input_) +#define ethash_swap_u64(input_) bswap64(input_) #else // posix #include #define ethash_swap_u32(input_) __bswap_32(input_) From ee06872b1927b6f88e2dc8892d94f00fecbe2a7e Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Fri, 12 Jun 2015 17:37:59 +0200 Subject: [PATCH 04/66] ImportRoute class instead of pair --- libethereum/BlockChain.cpp | 14 +++++++------- libethereum/BlockChain.h | 14 +++++++++++++- libethereum/Client.cpp | 13 +++++++------ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 480c7f977..b7618e2c7 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -326,8 +326,8 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st ImportRoute r; DEV_TIMED_ABOVE(Block import, 500) r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles); - fresh += r.first; - dead += r.second; + fresh += r.liveBlocks(); + dead += r.deadBlocks(); } catch (dev::eth::UnknownParent) { @@ -364,21 +364,21 @@ pair BlockChain::attemptImport(bytes const& _block, O } catch (UnknownParent&) { - return make_pair(ImportResult::UnknownParent, make_pair(h256s(), h256s())); + return make_pair(ImportResult::UnknownParent, ImportRoute()); } catch (AlreadyHaveBlock&) { - return make_pair(ImportResult::AlreadyKnown, make_pair(h256s(), h256s())); + return make_pair(ImportResult::AlreadyKnown, ImportRoute()); } catch (FutureTime&) { - return make_pair(ImportResult::FutureTime, make_pair(h256s(), h256s())); + return make_pair(ImportResult::FutureTime, ImportRoute()); } catch (Exception& ex) { if (m_onBad) m_onBad(ex); - return make_pair(ImportResult::Malformed, make_pair(h256s(), h256s())); + return make_pair(ImportResult::Malformed, ImportRoute()); } } @@ -699,7 +699,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const& dead.push_back(h); else fresh.push_back(h); - return make_pair(fresh, dead); + return ImportRoute(dead, fresh); } void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end) diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 5f197cfee..6decf46b6 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -80,7 +80,19 @@ ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::unordered_map; using TransactionHashes = h256s; using UncleHashes = h256s; -using ImportRoute = std::pair; + +class ImportRoute +{ +public: + ImportRoute() {}; + ImportRoute(h256s const& _deadBlocks, h256s const& _liveBlocks): m_deadBlocks(_deadBlocks), m_liveBlocks(_liveBlocks) {}; + h256s const& deadBlocks() const { return m_deadBlocks; } + h256s const& liveBlocks() const { return m_liveBlocks; } + +private: + h256s m_deadBlocks; + h256s m_liveBlocks; +}; enum { ExtraDetails = 0, diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 5581d1071..9d2c9a9e6 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -613,10 +613,11 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) void Client::syncBlockQueue() { - ImportRoute ir; cwork << "BQ ==> CHAIN ==> STATE"; - tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5); - if (ir.first.empty()) + pair blocks; + tie(blocks.first, blocks.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5); + ImportRoute ir(blocks.second, blocks.first); + if (ir.liveBlocks().empty()) return; onChainChanged(ir); } @@ -658,7 +659,7 @@ void Client::syncTransactionQueue() void Client::onChainChanged(ImportRoute const& _ir) { // insert transactions that we are declaring the dead part of the chain - for (auto const& h: _ir.second) + for (auto const& h: _ir.deadBlocks()) { clog(ClientNote) << "Dead block:" << h; for (auto const& t: m_bc.transactions(h)) @@ -669,7 +670,7 @@ void Client::onChainChanged(ImportRoute const& _ir) } // remove transactions from m_tq nicely rather than relying on out of date nonce later on. - for (auto const& h: _ir.first) + for (auto const& h: _ir.liveBlocks()) { clog(ClientChat) << "Live block:" << h; for (auto const& th: m_bc.transactionHashes(h)) @@ -683,7 +684,7 @@ void Client::onChainChanged(ImportRoute const& _ir) h->noteNewBlocks(); h256Hash changeds; - for (auto const& h: _ir.first) + for (auto const& h: _ir.liveBlocks()) appendFromNewBlock(h, changeds); // RESTART MINING From e9748677df6463b3bebb304bbad609fee5f1fa01 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sun, 14 Jun 2015 06:19:29 +0200 Subject: [PATCH 05/66] ImportRoute struct instead of class --- libethereum/BlockChain.cpp | 8 ++++---- libethereum/BlockChain.h | 14 +++----------- libethereum/Client.cpp | 13 ++++++------- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index b76ec591e..83275045c 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -326,8 +326,8 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st ImportRoute r; DEV_TIMED_ABOVE(Block import, 500) r = import(block.verified, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles); - fresh += r.liveBlocks(); - dead += r.deadBlocks(); + fresh += r.liveBlocks; + dead += r.deadBlocks; } catch (dev::eth::UnknownParent) { @@ -699,8 +699,8 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const& dead.push_back(h); else fresh.push_back(h); - return ImportRoute(dead, fresh); -} + return ImportRoute{dead, fresh}; +}; void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end) { diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index d865fd025..23137c38d 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -81,17 +81,9 @@ using BlocksHash = std::unordered_map; using TransactionHashes = h256s; using UncleHashes = h256s; -class ImportRoute -{ -public: - ImportRoute() {}; - ImportRoute(h256s const& _deadBlocks, h256s const& _liveBlocks): m_deadBlocks(_deadBlocks), m_liveBlocks(_liveBlocks) {}; - h256s const& deadBlocks() const { return m_deadBlocks; } - h256s const& liveBlocks() const { return m_liveBlocks; } - -private: - h256s m_deadBlocks; - h256s m_liveBlocks; +struct ImportRoute { + h256s deadBlocks; + h256s liveBlocks; }; enum { diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index cd8cb6805..0177c6007 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -614,10 +614,9 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) void Client::syncBlockQueue() { cwork << "BQ ==> CHAIN ==> STATE"; - pair blocks; - tie(blocks.first, blocks.second, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5); - ImportRoute ir(blocks.second, blocks.first); - if (ir.liveBlocks().empty()) + ImportRoute ir; + tie(ir.liveBlocks, ir.deadBlocks, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5); + if (ir.liveBlocks.empty()) return; onChainChanged(ir); } @@ -659,7 +658,7 @@ void Client::syncTransactionQueue() void Client::onChainChanged(ImportRoute const& _ir) { // insert transactions that we are declaring the dead part of the chain - for (auto const& h: _ir.deadBlocks()) + for (auto const& h: _ir.deadBlocks) { clog(ClientNote) << "Dead block:" << h; for (auto const& t: m_bc.transactions(h)) @@ -670,7 +669,7 @@ void Client::onChainChanged(ImportRoute const& _ir) } // remove transactions from m_tq nicely rather than relying on out of date nonce later on. - for (auto const& h: _ir.liveBlocks()) + for (auto const& h: _ir.liveBlocks) { clog(ClientChat) << "Live block:" << h; for (auto const& th: m_bc.transactionHashes(h)) @@ -684,7 +683,7 @@ void Client::onChainChanged(ImportRoute const& _ir) h->noteNewBlocks(); h256Hash changeds; - for (auto const& h: _ir.liveBlocks()) + for (auto const& h: _ir.liveBlocks) appendFromNewBlock(h, changeds); // RESTART MINING From 821ba0e002a0af53c6c86c4f712d03bcb2f10a2d Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sun, 14 Jun 2015 06:32:51 +0200 Subject: [PATCH 06/66] changed Blockchain sync return tuple from to --- libethereum/BlockChain.cpp | 4 ++-- libethereum/BlockChain.h | 2 +- libethereum/Client.cpp | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 83275045c..0a3be0f1b 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -305,7 +305,7 @@ LastHashes BlockChain::lastHashes(unsigned _n) const return m_lastLastHashes; } -tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) +tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) { // _bq.tick(*this); @@ -353,7 +353,7 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st badBlocks.push_back(block.verified.info.hash()); } } - return make_tuple(fresh, dead, _bq.doneDrain(badBlocks)); + return make_tuple(ImportRoute{dead, fresh}, _bq.doneDrain(badBlocks)); } pair BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, ImportRequirements::value _ir) noexcept diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 23137c38d..e3fcf83c1 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -116,7 +116,7 @@ public: /// Sync the chain with any incoming blocks. All blocks should, if processed in order. /// @returns fresh blocks, dead blocks and true iff there are additional blocks to be processed waiting. - std::tuple sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max); + std::tuple sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max); /// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 0177c6007..70ef1e406 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -614,8 +614,9 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) void Client::syncBlockQueue() { cwork << "BQ ==> CHAIN ==> STATE"; + ImportRoute ir; - tie(ir.liveBlocks, ir.deadBlocks, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5); + tie(ir, m_syncBlockQueue) = m_bc.sync(m_bq, m_stateDB, rand() % 10 + 5); if (ir.liveBlocks.empty()) return; onChainChanged(ir); From 63fb8d89bd0b9112d6253cb817ee43c8f7a54d1a Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sun, 14 Jun 2015 06:38:12 +0200 Subject: [PATCH 07/66] merge fix --- libethereum/Client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index a3294f3c7..9ce0cb5ac 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -629,7 +629,7 @@ void Client::syncBlockQueue() m_syncAmount = max(c_syncMin, m_syncAmount * 9 / 10); else if (elapsed < c_targetDuration * 0.9 && m_syncAmount < c_syncMax) m_syncAmount = min(c_syncMax, m_syncAmount * 11 / 10 + 1); - if (ir.first.empty()) + if (ir.liveBlocks.empty()) return; onChainChanged(ir); } From aca7c7c01e4731341d71106d67b7f5e208dbc002 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sun, 14 Jun 2015 08:28:05 +0200 Subject: [PATCH 08/66] enable syncing with rude peer --- libethereum/EthereumHost.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 67a21d36a..e69bfa684 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -758,10 +758,6 @@ bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const { - // Early exit if this peer has proved unreliable. - if (_peer->isRude()) - return false; - h256 c = m_chain.currentHash(); unsigned n = m_chain.number(); u256 td = m_chain.details().totalDifficulty; From 30e47e38cb98e309870bb166b56d6cf6826c91f4 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 14 Jun 2015 04:05:53 -0400 Subject: [PATCH 09/66] Slightly more aggressive 'require peer', required-peer pinning, and option to disable discovery. --- eth/main.cpp | 17 +++++++++++++++++ libp2p/Host.cpp | 7 ++++--- libp2p/Network.h | 2 ++ libp2p/NodeTable.cpp | 12 ++++++++---- libp2p/NodeTable.h | 4 +++- libp2p/Peer.cpp | 2 ++ 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 4b23ef62a..bc143154e 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -168,6 +168,9 @@ void help() << " --port Connect to remote port (default: 30303)." << endl << " --network-id Only connect to other hosts with this network id (default:0)." << endl << " --upnp Use UPnP for NAT (default: on)." << endl + << " --no-discovery Disable Node discovery. (experimental)" << endl + << " --pin Only connect to required (trusted) peers. (experimental)" << endl +// << " --require-peers List of required (trusted) peers. (experimental)" << endl << endl; MinerCLI::streamHelp(cout); cout @@ -304,6 +307,8 @@ int main(int argc, char** argv) unsigned short remotePort = 30303; unsigned peers = 11; bool bootstrap = false; + bool disableDiscovery = false; + bool pinning = false; unsigned networkId = 0; /// Mining params @@ -592,6 +597,16 @@ int main(int argc, char** argv) } else if (arg == "-b" || arg == "--bootstrap") bootstrap = true; + else if (arg == "--no-discovery") + if (bootstrap) + { + cerr << "-b/--bootstrap cannot be used with --no-discovery." << endl; + return -1; + } + else + disableDiscovery = true; + else if (arg == "--pin") + pinning = true; else if (arg == "-f" || arg == "--force-mining") forceMining = true; else if (arg == "-i" || arg == "--interactive") @@ -684,6 +699,8 @@ int main(int argc, char** argv) StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); + netPrefs.discovery = !disableDiscovery; + netPrefs.pin = pinning; auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 55389ed1b..b0d246458 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -307,7 +307,8 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) { clog(NetP2PNote) << "p2p.host.nodeTable.events.NodeEntryDropped " << _n; RecursiveGuard l(x_sessions); - m_peers.erase(_n); + if (m_peers.count(_n) && !m_peers[_n]->required) + m_peers.erase(_n); } } @@ -628,7 +629,7 @@ void Host::run(boost::system::error_code const&) { RecursiveGuard l(x_sessions); for (auto p: m_peers) - if (p.second->shouldReconnect() && !havePeerSession(p.second->id)) + if (p.second->shouldReconnect() && !havePeerSession(p.second->id) && (!m_netPrefs.pin || p.second->required)) toConnect.push_back(p.second); } @@ -679,7 +680,7 @@ void Host::startedWorking() else clog(NetP2PNote) << "p2p.start.notice id:" << id() << "TCP Listen port is invalid or unavailable."; - shared_ptr nodeTable(new NodeTable(m_ioService, m_alias, NodeIPEndpoint(bi::address::from_string(listenAddress()), listenPort(), listenPort()))); + shared_ptr nodeTable(new NodeTable(m_ioService, m_alias, NodeIPEndpoint(bi::address::from_string(listenAddress()), listenPort(), listenPort()), m_netPrefs.discovery)); nodeTable->setEventHandler(new HostNodeTableHandler(*this)); m_nodeTable = nodeTable; restoreNetwork(&m_restoreNetwork); diff --git a/libp2p/Network.h b/libp2p/Network.h index d02ce3cbe..97c631a7c 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -51,6 +51,8 @@ struct NetworkPreferences std::string publicIPAddress; std::string listenIPAddress; unsigned short listenPort = 30303; + bool discovery = true; // Discovery is activated with network. + bool pin = false; // Only connect to trusted ("required") peers. bool traverseNAT = true; }; diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 6344dc263..491795274 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -40,14 +40,15 @@ const char* NodeTableIngress::name() { return "<connect(); - doRefreshBuckets(boost::system::error_code()); + if (!m_disabled) + { + m_socketPointer->connect(); + doRefreshBuckets(boost::system::error_code()); + } } NodeTable::~NodeTable() diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 0ec13c828..bca14e5d4 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -130,7 +130,7 @@ public: enum NodeRelation { Unknown = 0, Known }; /// Constructor requiring host for I/O, credentials, and IP Address and port to listen on. - NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint); + NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _disabled = false); ~NodeTable(); /// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable. @@ -271,6 +271,8 @@ private: boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions. + + bool m_disabled; ///< Disable discovery. }; inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) diff --git a/libp2p/Peer.cpp b/libp2p/Peer.cpp index 6f368a4b4..a8b4a993d 100644 --- a/libp2p/Peer.cpp +++ b/libp2p/Peer.cpp @@ -38,6 +38,8 @@ bool Peer::shouldReconnect() const unsigned Peer::fallbackSeconds() const { + if (required) + return 5; switch (m_lastDisconnect) { case BadProtocol: From 0a0d36382b8e1d5fdfccfeac6b997571a8833603 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 14 Jun 2015 04:50:18 -0400 Subject: [PATCH 10/66] Fix bool. --- eth/main.cpp | 8 +------- libp2p/Network.h | 2 +- libp2p/NodeTable.cpp | 4 ++-- libp2p/NodeTable.h | 2 +- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index bc143154e..e914bfc31 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -598,13 +598,7 @@ int main(int argc, char** argv) else if (arg == "-b" || arg == "--bootstrap") bootstrap = true; else if (arg == "--no-discovery") - if (bootstrap) - { - cerr << "-b/--bootstrap cannot be used with --no-discovery." << endl; - return -1; - } - else - disableDiscovery = true; + disableDiscovery = true; else if (arg == "--pin") pinning = true; else if (arg == "-f" || arg == "--force-mining") diff --git a/libp2p/Network.h b/libp2p/Network.h index 97c631a7c..e70dd89ea 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -51,9 +51,9 @@ struct NetworkPreferences std::string publicIPAddress; std::string listenIPAddress; unsigned short listenPort = 30303; + bool traverseNAT = true; bool discovery = true; // Discovery is activated with network. bool pin = false; // Only connect to trusted ("required") peers. - bool traverseNAT = true; }; /** diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 491795274..1a0b11734 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -40,7 +40,7 @@ const char* NodeTableIngress::name() { return "< Date: Sun, 14 Jun 2015 06:30:20 -0400 Subject: [PATCH 11/66] ensure required peers --- libp2p/Host.cpp | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b0d246458..596182413 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -619,26 +619,36 @@ void Host::run(boost::system::error_code const&) // updated. // disconnectLatePeers(); // todo: update peerSlotsAvailable() + + list> toConnect; + unsigned reqConn = 0; + { + RecursiveGuard l(x_sessions); + for (auto const& p: m_peers) + { + bool haveSession = havePeerSession(p.second->id); + bool required = p.second->required; + if (haveSession && required) + reqConn++; + else if (!haveSession && p.second->shouldReconnect() && (!m_netPrefs.pin || required)) + toConnect.push_back(p.second); + } + } + + for (auto p: toConnect) + if (p->required && reqConn++ < m_idealPeerCount) + connect(p); + unsigned pendingCount = 0; DEV_GUARDED(x_pendingNodeConns) pendingCount = m_pendingPeerConns.size(); - int openSlots = m_idealPeerCount - peerCount() - pendingCount; - if (openSlots > 0) + int openSlots = m_idealPeerCount - peerCount() - pendingCount + reqConn; + if (openSlots > 0 && !m_netPrefs.pin) { - list> toConnect; - { - RecursiveGuard l(x_sessions); - for (auto p: m_peers) - if (p.second->shouldReconnect() && !havePeerSession(p.second->id) && (!m_netPrefs.pin || p.second->required)) - toConnect.push_back(p.second); - } - for (auto p: toConnect) - if (openSlots--) + if (!p->required && openSlots--) connect(p); - else - break; - + m_nodeTable->discover(); } From 826858496c7847ab97ceeccf0ca4b2296df3f9f6 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 14 Jun 2015 06:42:43 -0400 Subject: [PATCH 12/66] cleanup --- libp2p/Host.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 596182413..dd1185c24 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -639,17 +639,20 @@ void Host::run(boost::system::error_code const&) if (p->required && reqConn++ < m_idealPeerCount) connect(p); - unsigned pendingCount = 0; - DEV_GUARDED(x_pendingNodeConns) - pendingCount = m_pendingPeerConns.size(); - int openSlots = m_idealPeerCount - peerCount() - pendingCount + reqConn; - if (openSlots > 0 && !m_netPrefs.pin) + if (!m_netPrefs.pin) { - for (auto p: toConnect) - if (!p->required && openSlots--) - connect(p); - - m_nodeTable->discover(); + unsigned pendingCount = 0; + DEV_GUARDED(x_pendingNodeConns) + pendingCount = m_pendingPeerConns.size(); + int openSlots = m_idealPeerCount - peerCount() - pendingCount + reqConn; + if (openSlots > 0) + { + for (auto p: toConnect) + if (!p->required && openSlots--) + connect(p); + + m_nodeTable->discover(); + } } auto runcb = [this](boost::system::error_code const& error) { run(error); }; From c592fde8cf8481d77ffd5d3a97ad851d1c0c4008 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 15 Jun 2015 03:22:55 +0200 Subject: [PATCH 13/66] style fix --- libethereum/BlockChain.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index e3fcf83c1..2d3abd922 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -81,7 +81,8 @@ using BlocksHash = std::unordered_map; using TransactionHashes = h256s; using UncleHashes = h256s; -struct ImportRoute { +struct ImportRoute +{ h256s deadBlocks; h256s liveBlocks; }; From 6ae2eb8039efc4427c0295f202c2c254a2804d3f Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 15 Jun 2015 04:39:44 +0200 Subject: [PATCH 14/66] removed extra ; --- libethereum/BlockChain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 0a3be0f1b..7386fa776 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -700,7 +700,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const& else fresh.push_back(h); return ImportRoute{dead, fresh}; -}; +} void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end) { From e923f9c4350bed0cba13286a01084fdb21cb16cc Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 15 Jun 2015 09:42:58 +0200 Subject: [PATCH 15/66] add balanceatblock and storageatblock cli interactive commands# --- eth/main.cpp | 55 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 5d70b3fe5..0c701d615 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -102,6 +102,9 @@ void interactiveHelp() << " listaccounts List the accounts on the network." << endl << " listcontracts List the contracts on the network." << endl << " balanceat
Gives the balance of the given account." << endl + << " balanceatblock
Gives the balance of the given account." << endl + << " storageat
Gives the storage of the given account." << endl + << " storageatblock
Gives the storahe of the given account at a given blocknumber." << endl << " codeat
Gives the code of the given account." << endl #endif << " setsigningkey Set the address with which to sign transactions." << endl @@ -1338,19 +1341,48 @@ int main(int argc, char** argv) cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl; } } - // TODO implement << operator for std::unorderd_map -// else if (c && cmd == "storageat") -// { -// if (iss.peek() != -1) -// { -// string stringHash; -// iss >> stringHash; + else if (c && cmd == "balanceatblock") + { + if (iss.peek() != -1) + { + string stringHash; + unsigned blocknumber; + iss >> stringHash >> blocknumber; -// Address address = h160(fromHex(stringHash)); + Address address = h160(fromHex(stringHash)); + + cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address, blocknumber)) << endl; + } + } + else if (c && cmd == "storageat") + { + if (iss.peek() != -1) + { + string stringHash; + iss >> stringHash; -// cout << "storage at " << stringHash << " is: " << c->storageAt(address) << endl; -// } -// } + Address address = h160(fromHex(stringHash)); + + cout << "storage at " << stringHash << " is: " << endl; + for (auto s: c->storageAt(address)) + cout << toHex(s.first) << " : " << toHex(s.second) << endl; + } + } + else if (c && cmd == "storageatblock") + { + if (iss.peek() != -1) + { + string stringHash; + unsigned blocknumber; + iss >> stringHash >> blocknumber; + + Address address = h160(fromHex(stringHash)); + + cout << "storage at " << stringHash << " is: " << endl; + for (auto s: c->storageAt(address, blocknumber)) + cout << "\"0x" << toHex(s.first) << "\" : \"0x" << toHex(s.second) << "\"," << endl; + } + } else if (c && cmd == "codeat") { if (iss.peek() != -1) @@ -1364,6 +1396,7 @@ int main(int argc, char** argv) } } #endif + else if (c && cmd == "send") { if (iss.peek() != -1) From 822103e6020d3cda16405bd25c9ccba879456d3e Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 15 Jun 2015 11:36:55 +0200 Subject: [PATCH 16/66] ET won't be sad anymore --- ethminer/MinerAux.h | 1 - 1 file changed, 1 deletion(-) diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index 6de3913e4..3351b90de 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -410,7 +410,6 @@ private: } catch (...) { - cout << "Error phoning home. ET is sad." << endl; } } #endif From d416e667ed2cded424d4565bc4a0ecff240356a6 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 15 Jun 2015 12:18:49 +0200 Subject: [PATCH 17/66] Fix macosx opencl warnings It seems that OpenCL macosx implementation needs a static on the function implementations if there is no corresponding declaration as can be seen by this report: https://github.com/ethereum/cpp-ethereum/issues/2172 --- libethash-cl/ethash_cl_miner_kernel.cl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libethash-cl/ethash_cl_miner_kernel.cl b/libethash-cl/ethash_cl_miner_kernel.cl index 2143435ed..8ea6df12d 100644 --- a/libethash-cl/ethash_cl_miner_kernel.cl +++ b/libethash-cl/ethash_cl_miner_kernel.cl @@ -36,7 +36,7 @@ __constant uint2 const Keccak_f1600_RC[24] = { (uint2)(0x80008008, 0x80000000), }; -void keccak_f1600_round(uint2* a, uint r, uint out_size) +static void keccak_f1600_round(uint2* a, uint r, uint out_size) { #if !__ENDIAN_LITTLE__ for (uint i = 0; i != 25; ++i) @@ -152,7 +152,7 @@ void keccak_f1600_round(uint2* a, uint r, uint out_size) #endif } -void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate) +static void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate) { for (uint i = in_size; i != 25; ++i) { @@ -194,17 +194,17 @@ void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate) #define countof(x) (sizeof(x) / sizeof(x[0])) -uint fnv(uint x, uint y) +static uint fnv(uint x, uint y) { return x * FNV_PRIME ^ y; } -uint4 fnv4(uint4 x, uint4 y) +static uint4 fnv4(uint4 x, uint4 y) { return x * FNV_PRIME ^ y; } -uint fnv_reduce(uint4 v) +static uint fnv_reduce(uint4 v) { return fnv(fnv(fnv(v.x, v.y), v.z), v.w); } @@ -227,7 +227,7 @@ typedef union uint4 uint4s[128 / sizeof(uint4)]; } hash128_t; -hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate) +static hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate) { hash64_t init; uint const init_size = countof(init.ulongs); @@ -243,7 +243,7 @@ hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate) return init; } -uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, __global hash128_t const* g_dag1, __global hash128_t const* g_dag2, __global hash128_t const* g_dag3, uint isolate) +static uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, __global hash128_t const* g_dag1, __global hash128_t const* g_dag2, __global hash128_t const* g_dag3, uint isolate) { uint4 mix = init; @@ -277,7 +277,7 @@ uint inner_loop_chunks(uint4 init, uint thread_id, __local uint* share, __global -uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate) +static uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate) { uint4 mix = init; @@ -311,7 +311,7 @@ uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash12 } -hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate) +static hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate) { ulong state[25]; @@ -330,7 +330,7 @@ hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate) return hash; } -hash32_t compute_hash_simple( +static hash32_t compute_hash_simple( __constant hash32_t const* g_header, __global hash128_t const* g_dag, ulong nonce, @@ -383,7 +383,7 @@ typedef union } compute_hash_share; -hash32_t compute_hash( +static hash32_t compute_hash( __local compute_hash_share* share, __constant hash32_t const* g_header, __global hash128_t const* g_dag, @@ -427,7 +427,7 @@ hash32_t compute_hash( } -hash32_t compute_hash_chunks( +static hash32_t compute_hash_chunks( __local compute_hash_share* share, __constant hash32_t const* g_header, __global hash128_t const* g_dag, From c1826e463412eba285157e65dcf8d9a33e38f9c2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Jun 2015 12:25:18 +0200 Subject: [PATCH 18/66] Upgrade "BadJumpDestination" to semi-recoverable "OutOfGas" exception. --- mix/MixClient.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index e2e554cb9..85f7f0a46 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -218,6 +218,8 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); case TransactionException::BlockGasLimitReached: BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); + case TransactionException::BadJumpDestination: + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Solidity exception (bad jump)")); case TransactionException::OutOfStack: BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); case TransactionException::StackUnderflow: @@ -225,7 +227,6 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c //these should not happen in mix case TransactionException::Unknown: case TransactionException::BadInstruction: - case TransactionException::BadJumpDestination: case TransactionException::InvalidSignature: case TransactionException::InvalidNonce: case TransactionException::BadRLP: From 45858c0a7838ee1b620eb4fd9210e1e0b84b6b4d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 15 Jun 2015 13:28:58 +0200 Subject: [PATCH 19/66] OpenCL in MacosX does not like static variables Should fix #2172 --- libethash-cl/ethash_cl_miner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 6c2f8269a..b160cdd94 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -429,7 +429,8 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook }; queue pending; - static uint32_t const c_zero = 0; + // this can't be a static because in MacOSX OpenCL implementation a segfault occurs when a static is passed to OpenCL functions + uint32_t const c_zero = 0; // update header constant buffer m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); From 8357930b25e5e4914d84eba507b7d94885ff5966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 15 Jun 2015 14:07:59 +0200 Subject: [PATCH 20/66] Do not increase Windows stack size. --- cmake/EthCompilerSettings.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 53535a489..b48dae8f8 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -43,8 +43,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification # warning LNK4099: pdb was not found with lib - # stack size 16MB - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:33554432") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075") # windows likes static if (NOT ETH_STATIC) From 4fd0d70c471cabe73f4ee269841d53bdff44dd5f Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Mon, 15 Jun 2015 16:14:23 +0200 Subject: [PATCH 21/66] FixedHash::bloom() refactored --- libdevcore/FixedHash.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 2d1822b2c..ca80d745c 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -158,16 +158,17 @@ public: template inline FixedHash& shiftBloom(FixedHash const& _h) { - return (*this |= _h.template bloom()); + return (*this |= _h.template bloomPart()); } template inline bool containsBloom(FixedHash const& _h) { - return contains(_h.template bloom()); + return contains(_h.template bloomPart()); } - template inline FixedHash bloom() const + template inline FixedHash bloomPart() const { + static_assert((M & (M - 1)) == 0, "M must be power-of-two"); static const unsigned c_bloomBits = M * 8; unsigned mask = c_bloomBits - 1; unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8; From 402dc016408ac87fb53118e63f95196a4319efbf Mon Sep 17 00:00:00 2001 From: Dimitry Date: Mon, 15 Jun 2015 16:31:41 +0300 Subject: [PATCH 22/66] Coverage info --- cmake/EthCompilerSettings.cmake | 7 ++++ getcoverage.sh | 33 +++++++++++++++++++ ...reCompiledContractsTransactionFiller.json} | 0 test/libethereum/state.cpp | 4 +-- 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100755 getcoverage.sh rename test/libethereum/StateTestsFiller/{stPrecompiledContractsTransactionFiller.json => stPreCompiledContractsTransactionFiller.json} (100%) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 53535a489..4009b22a6 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -64,6 +64,13 @@ if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_C set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lprofiler") endif () +if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU"))) + set(CMAKE_CXX_FLAGS "-g --coverage ${CMAKE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "-g --coverage ${CMAKE_C_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS} -lprofiler") + set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS} -lprofiler") +endif () + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) option(USE_LD_GOLD "Use GNU gold linker" ON) if (USE_LD_GOLD) diff --git a/getcoverage.sh b/getcoverage.sh new file mode 100755 index 000000000..a04ab78fe --- /dev/null +++ b/getcoverage.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +CPP_ETHEREUM_PATH=$(pwd) + +if [ ! -d "$CPP_ETHEREUM_PATH/build/test" ]; then + echo "You need to compile and build ethereum with cmake -DPROFILING option to the build dir!" + exit; +fi + +if which lcov >/dev/null; then + if which genhtml >/dev/null; then + echo Running testeth... + $($CPP_ETHEREUM_PATH/build/test/testeth) + echo Prepearing coverage info... + else + echo genhtml not found + exit; + fi +else + echo lcov not found + exit; +fi + +OUTPUT_DIR="$CPP_ETHEREUM_PATH/build/test/coverage" + +TESTETH=$CPP_ETHEREUM_PATH/build/test/CMakeFiles/testeth.dir +lcov --capture --directory $TESTETH --output-file $OUTPUT_DIR/coverage.info +genhtml $OUTPUT_DIR/coverage.info --output-directory $OUTPUT_DIR/testeth + +echo "Coverage info should be located at: $CPP_ETHEREUM_PATH/build/test/coverage/testeth" +echo "Opening index..." + +xdg-open $CPP_ETHEREUM_PATH/build/test/coverage/testeth/index.html diff --git a/test/libethereum/StateTestsFiller/stPrecompiledContractsTransactionFiller.json b/test/libethereum/StateTestsFiller/stPreCompiledContractsTransactionFiller.json similarity index 100% rename from test/libethereum/StateTestsFiller/stPrecompiledContractsTransactionFiller.json rename to test/libethereum/StateTestsFiller/stPreCompiledContractsTransactionFiller.json diff --git a/test/libethereum/state.cpp b/test/libethereum/state.cpp index 5eb3c76c3..632e7982b 100644 --- a/test/libethereum/state.cpp +++ b/test/libethereum/state.cpp @@ -129,9 +129,9 @@ BOOST_AUTO_TEST_CASE(stPreCompiledContracts) dev::test::executeTests("stPreCompiledContracts", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); } -BOOST_AUTO_TEST_CASE(stPrecompiledContractsTransaction) +BOOST_AUTO_TEST_CASE(stPreCompiledContractsTransaction) { - dev::test::executeTests("stPrecompiledContractsTransaction", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); + dev::test::executeTests("stPreCompiledContractsTransaction", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); } BOOST_AUTO_TEST_CASE(stLogTests) From 1959b140f9a628ec48fae59f195392465b0415fa Mon Sep 17 00:00:00 2001 From: Dimitry Date: Mon, 15 Jun 2015 18:27:00 +0300 Subject: [PATCH 23/66] Coverage script --- getcoverage.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/getcoverage.sh b/getcoverage.sh index a04ab78fe..4c01bad46 100755 --- a/getcoverage.sh +++ b/getcoverage.sh @@ -7,8 +7,12 @@ if [ ! -d "$CPP_ETHEREUM_PATH/build/test" ]; then exit; fi +OUTPUT_DIR="$CPP_ETHEREUM_PATH/build/test/coverage" +TESTETH=$CPP_ETHEREUM_PATH/build #/test/CMakeFiles/testeth.dir + if which lcov >/dev/null; then if which genhtml >/dev/null; then + lcov --directory $TESTETH --zerocounters echo Running testeth... $($CPP_ETHEREUM_PATH/build/test/testeth) echo Prepearing coverage info... @@ -21,13 +25,15 @@ else exit; fi -OUTPUT_DIR="$CPP_ETHEREUM_PATH/build/test/coverage" - -TESTETH=$CPP_ETHEREUM_PATH/build/test/CMakeFiles/testeth.dir -lcov --capture --directory $TESTETH --output-file $OUTPUT_DIR/coverage.info -genhtml $OUTPUT_DIR/coverage.info --output-directory $OUTPUT_DIR/testeth +echo Cleaning previous report... +rm -r $OUTPUT_DIR/testeth +rm $OUTPUT_DIR/full_coverage.info +rm $OUTPUT_DIR/testeth_coverage.info +lcov --capture --directory $TESTETH --output-file $OUTPUT_DIR/full_coverage.info +lcov --extract $OUTPUT_DIR/full_coverage.info *cpp-ethereum/* --output-file $OUTPUT_DIR/testeth_coverage.info +genhtml $OUTPUT_DIR/testeth_coverage.info --output-directory $OUTPUT_DIR/testeth echo "Coverage info should be located at: $CPP_ETHEREUM_PATH/build/test/coverage/testeth" echo "Opening index..." -xdg-open $CPP_ETHEREUM_PATH/build/test/coverage/testeth/index.html +xdg-open $CPP_ETHEREUM_PATH/build/test/coverage/testeth/index.html & From 1c2a9cd088d4e072e064a05aece206159137d471 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 15 Jun 2015 19:01:16 +0200 Subject: [PATCH 24/66] fixed syncing stalling issues --- libethereum/EthereumHost.cpp | 38 ++++++++++++++++++++++++------------ libethereum/EthereumPeer.cpp | 1 + 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index e69bfa684..b10e74f22 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -292,8 +292,8 @@ void EthereumHost::onPeerStatus(EthereumPeer* _peer) else _peer->m_expectedHashes = estimatedHashes; continueSync(_peer); + DEV_INVARIANT_CHECK; } - DEV_INVARIANT_CHECK; } unsigned EthereumHost::estimateHashes() @@ -313,6 +313,7 @@ unsigned EthereumHost::estimateHashes() void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) { RecursiveGuard l(x_sync); + DEV_INVARIANT_CHECK; if (_peer->m_syncHashNumber > 0) _peer->m_syncHashNumber += _hashes.size(); @@ -322,7 +323,6 @@ void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes) void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete) { - DEV_INVARIANT_CHECK; if (_hashes.empty()) { _peer->m_hashSub.doneFetch(); @@ -454,7 +454,8 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) unsigned unknown = 0; unsigned got = 0; unsigned repeated = 0; - h256 lastUnknown; + u256 maxDifficulty = 0; + h256 maxUnknown; for (unsigned i = 0; i < itemCount; ++i) { @@ -483,9 +484,17 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) break; case ImportResult::UnknownParent: - lastUnknown = h; + { unknown++; + BlockInfo bi; + bi.populateFromHeader(_r[i][0]); + if (bi.difficulty > maxDifficulty) + { + maxDifficulty = bi.difficulty; + maxUnknown = h; + } break; + } default:; } @@ -501,8 +510,10 @@ void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r) if (m_state == SyncState::NewBlocks && unknown > 0) { - _peer->m_latestHash = lastUnknown; - resetSyncTo(lastUnknown); + _peer->m_latestHash = maxUnknown; + _peer->m_totalDifficulty = maxDifficulty; + if (peerShouldGrabChain(_peer)) + resetSyncTo(maxUnknown); } continueSync(_peer); @@ -528,7 +539,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) { RecursiveGuard l(x_sync); DEV_INVARIANT_CHECK; - if ((isSyncing() || _peer->isConversing()) && m_state != SyncState::NewBlocks) + if ((isSyncing() || _peer->isConversing())) { clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading."; return; @@ -565,11 +576,14 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) u256 difficulty = _r[1].toInt(); if (m_syncingTotalDifficulty < difficulty) { - clog(NetMessageSummary) << "Received block with no known parent. Resyncing..."; _peer->m_latestHash = h; _peer->m_totalDifficulty = difficulty; - resetSyncTo(h);; - sync = true; + if (peerShouldGrabChain(_peer)) + { + clog(NetMessageSummary) << "Received block with no known parent. Resyncing..."; + resetSyncTo(h);; + sync = true; + } } } break; @@ -580,7 +594,7 @@ void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r) _peer->m_knownBlocks.insert(h); if (sync) - continueSync(); + continueSync(_peer); } DEV_INVARIANT_CHECK; } @@ -623,6 +637,7 @@ void EthereumHost::onPeerAborting(EthereumPeer* _peer) _peer->setRude(); continueSync(); } + DEV_INVARIANT_CHECK; } void EthereumHost::continueSync() @@ -811,6 +826,5 @@ bool EthereumHost::invariants() const return false; if (needBlocks() && (m_syncingLatestHash || !m_hashes.empty())) return false; - return true; } diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 7a30f1ad9..583932d48 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -100,6 +100,7 @@ void EthereumPeer::requestStatus() { assert(m_asking == Asking::Nothing); setAsking(Asking::State); + m_requireTransactions = true; RLPStream s; bool latest = m_peerCapabilityVersion == host()->protocolVersion(); prep(s, StatusPacket, latest ? 6 : 5) From b6ca0d7856818d4ec43e4a1e21fceb8b5d7a5838 Mon Sep 17 00:00:00 2001 From: Dimitry Date: Mon, 15 Jun 2015 20:07:11 +0300 Subject: [PATCH 25/66] Coerage Script --- getcoverage.sh | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/getcoverage.sh b/getcoverage.sh index 4c01bad46..446d584e6 100755 --- a/getcoverage.sh +++ b/getcoverage.sh @@ -2,38 +2,44 @@ CPP_ETHEREUM_PATH=$(pwd) -if [ ! -d "$CPP_ETHEREUM_PATH/build/test" ]; then - echo "You need to compile and build ethereum with cmake -DPROFILING option to the build dir!" - exit; +which $CPP_ETHEREUM_PATH/build/test/testeth >/dev/null 2>&1 +if [ $? != 0 ] +then + echo "You need to compile and build ethereum with cmake -DPROFILING option to the build dir!" + exit; fi OUTPUT_DIR="$CPP_ETHEREUM_PATH/build/test/coverage" TESTETH=$CPP_ETHEREUM_PATH/build #/test/CMakeFiles/testeth.dir if which lcov >/dev/null; then - if which genhtml >/dev/null; then + if which genhtml >/dev/null; then lcov --directory $TESTETH --zerocounters echo Running testeth... - $($CPP_ETHEREUM_PATH/build/test/testeth) + $($CPP_ETHEREUM_PATH/build/test/testeth --all) + $($CPP_ETHEREUM_PATH/build/test/testeth -t StateTests --jit --all) + $($CPP_ETHEREUM_PATH/build/test/testeth -t VMTests --jit --all) echo Prepearing coverage info... - else + else echo genhtml not found exit; - fi + fi else - echo lcov not found - exit; + echo lcov not found + exit; +fi + +if [ -d "$OUTPUT_DIR" ]; then + echo Cleaning previous report... + rm -r $OUTPUT_DIR fi -echo Cleaning previous report... -rm -r $OUTPUT_DIR/testeth -rm $OUTPUT_DIR/full_coverage.info -rm $OUTPUT_DIR/testeth_coverage.info +mkdir $OUTPUT_DIR lcov --capture --directory $TESTETH --output-file $OUTPUT_DIR/full_coverage.info lcov --extract $OUTPUT_DIR/full_coverage.info *cpp-ethereum/* --output-file $OUTPUT_DIR/testeth_coverage.info genhtml $OUTPUT_DIR/testeth_coverage.info --output-directory $OUTPUT_DIR/testeth -echo "Coverage info should be located at: $CPP_ETHEREUM_PATH/build/test/coverage/testeth" +echo "Coverage info should be located at: $OUTPUT_DIR/testeth" echo "Opening index..." -xdg-open $CPP_ETHEREUM_PATH/build/test/coverage/testeth/index.html & +xdg-open $OUTPUT_DIR/testeth/index.html & From e0c101ca3b0a850221c8f2786647c3f259899829 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Jun 2015 12:10:41 +0200 Subject: [PATCH 26/66] Copying between memory and memory. Also fixed some encoding and padding issues with older copying code. --- libsolidity/Compiler.cpp | 36 +- libsolidity/CompilerUtils.cpp | 383 +++++++++++++++++- libsolidity/CompilerUtils.h | 32 ++ libsolidity/ExpressionCompiler.cpp | 341 +++------------- libsolidity/ExpressionCompiler.h | 35 +- test/libsolidity/Assembly.cpp | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 27 +- test/libsolidity/solidityExecutionFramework.h | 6 +- 8 files changed, 521 insertions(+), 341 deletions(-) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index b55ae7d36..82e98dfff 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -30,9 +30,8 @@ #include using namespace std; - -namespace dev { -namespace solidity { +using namespace dev; +using namespace dev::solidity; /** * Simple helper class to ensure that the stack height is the same at certain places in the code. @@ -301,24 +300,18 @@ void Compiler::appendCalldataUnpacker( void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters) { - unsigned dataOffset = 0; - unsigned stackDepth = 0; - for (TypePointer const& type: _typeParameters) - stackDepth += type->getSizeOnStack(); - - for (TypePointer const& type: _typeParameters) - { - CompilerUtils(m_context).copyToStackTop(stackDepth, type->getSizeOnStack()); - ExpressionCompiler(m_context, m_optimize).appendTypeConversion(*type, *type, true); - bool const c_padToWords = true; - dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, *type, c_padToWords); - stackDepth -= type->getSizeOnStack(); - } - // note that the stack is not cleaned up here - if (dataOffset == 0) + CompilerUtils utils(m_context); + if (_typeParameters.empty()) m_context << eth::Instruction::STOP; else - m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; + { + utils.fetchFreeMemoryPointer(); + //@todo optimization: if we return a single memory array, there should be enough space before + // its data to add the needed parts and we avoid a memory copy. + utils.encodeToMemory(_typeParameters, _typeParameters); + utils.toSizeAfterFreeMemoryPointer(); + m_context << eth::Instruction::RETURN; + } } void Compiler::registerStateVariables(ContractDefinition const& _contract) @@ -634,8 +627,5 @@ void Compiler::compileExpression(Expression const& _expression, TypePointer cons ExpressionCompiler expressionCompiler(m_context, m_optimize); expressionCompiler.compile(_expression); if (_targetType) - expressionCompiler.appendTypeConversion(*_expression.getType(), *_targetType); -} - -} + CompilerUtils(m_context).convertType(*_expression.getType(), *_targetType); } diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index 7a96db928..6110fdf71 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include using namespace std; @@ -33,6 +35,7 @@ namespace solidity const unsigned CompilerUtils::dataStartOffset = 4; const size_t CompilerUtils::freeMemoryPointer = 64; +const unsigned CompilerUtils::identityContractAddress = 4; void CompilerUtils::initialiseFreeMemoryPointer() { @@ -83,8 +86,7 @@ void CompilerUtils::loadFromMemoryDynamic( if (_keepUpdatedMemoryOffset) { // update memory counter - for (unsigned i = 0; i < _type.getSizeOnStack(); ++i) - m_context << eth::swapInstruction(1 + i); + moveToStackTop(_type.getSizeOnStack()); m_context << u256(numBytes) << eth::Instruction::ADD; } } @@ -114,9 +116,74 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP; } + else if (type.location() == ReferenceType::Location::Memory) + { + // memcpy using the built-in contract + ArrayUtils(m_context).retrieveLength(type); + if (type.isDynamicallySized()) + { + // change pointer to data part + m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD; + m_context << eth::Instruction::SWAP1; + } + // stack: + // stack for call: outsize target size source value contract gas + m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4; + m_context << eth::Instruction::DUP2 << eth::Instruction::DUP5; + m_context << u256(0) << u256(identityContractAddress); + //@TODO do not use ::CALL if less than 32 bytes? + //@todo in production, we should not have to pair c_callNewAccountGas. + m_context << u256(eth::c_callGas + 10 + eth::c_callNewAccountGas) << eth::Instruction::GAS; + m_context << eth::Instruction::SUB << eth::Instruction::CALL; + m_context << eth::Instruction::POP; // ignore return value + + m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; + // stack: + + if (_padToWordBoundaries && (type.isDynamicallySized() || (type.getLength()) % 32 != 0)) + { + // stack: + m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD; + // stack: + m_context << eth::Instruction::SWAP1 << u256(31) << eth::Instruction::AND; + // stack: + eth::AssemblyItem skip = m_context.newTag(); + if (type.isDynamicallySized()) + { + m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO; + m_context.appendConditionalJumpTo(skip); + } + // round off, load from there. + // stack + m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3; + m_context << eth::Instruction::SUB; + // stack: target+length remainder + m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD; + // Now we AND it with ~(2**(8 * (32 - remainder)) - 1) + m_context << u256(1); + m_context << eth::Instruction::DUP4 << u256(32) << eth::Instruction::SUB; + // stack: ... 1 <32 - remainder> + m_context << u256(0x100) << eth::Instruction::EXP << eth::Instruction::SUB; + m_context << eth::Instruction::NOT << eth::Instruction::AND; + // stack: target+length remainder target+length-remainder + m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE; + // stack: target+length remainder target+length-remainder + m_context << u256(32) << eth::Instruction::ADD; + // stack: target+length remainder + m_context << eth::Instruction::SWAP2 << eth::Instruction::POP; + + if (type.isDynamicallySized()) + m_context << skip.tag(); + // stack + m_context << eth::Instruction::POP; + } + else + // stack: + m_context << eth::Instruction::ADD; + } else { - solAssert(type.location() == ReferenceType::Location::Storage, "Memory arrays not yet implemented."); + solAssert(type.location() == ReferenceType::Location::Storage, ""); m_context << eth::Instruction::POP; // remove offset, arrays always start new slot m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; // stack here: memory_offset storage_offset length_bytes @@ -144,6 +211,17 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound // check for loop condition << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT; m_context.appendConditionalJumpTo(loopStart); + // stack here: memory_end_offset storage_data_offset memory_offset + if (_padToWordBoundaries) + { + // memory_end_offset - start is the actual length (we want to compute the ceil of). + // memory_offset - start is its next multiple of 32, but it might be off by 32. + // so we compute: memory_end_offset += (memory_offset - memory_end_offest) & 31 + m_context << eth::Instruction::DUP3 << eth::Instruction::SWAP1 << eth::Instruction::SUB; + m_context << u256(31) << eth::Instruction::AND; + m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; + m_context << eth::Instruction::SWAP2; + } m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP; } } @@ -159,6 +237,288 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound } } +void CompilerUtils::encodeToMemory( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _padToWordBoundaries, + bool _copyDynamicDataInPlace +) +{ + // stack: ... + TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; + solAssert(targetTypes.size() == _givenTypes.size(), ""); + for (TypePointer& t: targetTypes) + t = t->mobileType()->externalType(); + + // Stack during operation: + // ... ... + // The values dyn_head_i are added during the first loop and they point to the head part + // of the ith dynamic parameter, which is filled once the dynamic parts are processed. + + // store memory start pointer + m_context << eth::Instruction::DUP1; + + unsigned argSize = CompilerUtils::getSizeOnStack(_givenTypes); + unsigned stackPos = 0; // advances through the argument values + unsigned dynPointers = 0; // number of dynamic head pointers on the stack + for (size_t i = 0; i < _givenTypes.size(); ++i) + { + TypePointer targetType = targetTypes[i]; + solAssert(!!targetType, "Externalable type expected."); + if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace) + { + // leave end_of_mem as dyn head pointer + m_context << eth::Instruction::DUP1 << u256(32) << eth::Instruction::ADD; + dynPointers++; + } + else + { + copyToStackTop(argSize - stackPos + dynPointers + 2, _givenTypes[i]->getSizeOnStack()); + if (targetType->isValueType()) + convertType(*_givenTypes[i], *targetType, true); + solAssert(!!targetType, "Externalable type expected."); + storeInMemoryDynamic(*targetType, _padToWordBoundaries); + } + stackPos += _givenTypes[i]->getSizeOnStack(); + } + + // now copy the dynamic part + // Stack: ... ... + stackPos = 0; + unsigned thisDynPointer = 0; + for (size_t i = 0; i < _givenTypes.size(); ++i) + { + TypePointer targetType = targetTypes[i]; + solAssert(!!targetType, "Externalable type expected."); + if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace) + { + solAssert(_givenTypes[i]->getCategory() == Type::Category::Array, "Unknown dynamic type."); + auto const& arrayType = dynamic_cast(*_givenTypes[i]); + // copy tail pointer (=mem_end - mem_start) to memory + m_context << eth::dupInstruction(2 + dynPointers) << eth::Instruction::DUP2; + m_context << eth::Instruction::SUB; + m_context << eth::dupInstruction(2 + dynPointers - thisDynPointer); + m_context << eth::Instruction::MSTORE; + // now copy the array + copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType.getSizeOnStack()); + // stack: ... + // copy length to memory + m_context << eth::dupInstruction(1 + arrayType.getSizeOnStack()); + if (arrayType.location() == ReferenceType::Location::CallData) + m_context << eth::Instruction::DUP2; // length is on stack + else if (arrayType.location() == ReferenceType::Location::Storage) + m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD; + else + { + solAssert(arrayType.location() == ReferenceType::Location::Memory, ""); + m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD; + } + // stack: ... + storeInMemoryDynamic(IntegerType(256), true); + // stack: ... + // copy the new memory pointer + m_context << eth::swapInstruction(arrayType.getSizeOnStack() + 1) << eth::Instruction::POP; + // stack: ... + // copy data part + storeInMemoryDynamic(arrayType, true); + // stack: ... + + thisDynPointer++; + } + stackPos += _givenTypes[i]->getSizeOnStack(); + } + + // remove unneeded stack elements (and retain memory pointer) + m_context << eth::swapInstruction(argSize + dynPointers + 1); + popStackSlots(argSize + dynPointers + 1); +} + +void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded) +{ + // For a type extension, we need to remove all higher-order bits that we might have ignored in + // previous operations. + // @todo: store in the AST whether the operand might have "dirty" higher order bits + + if (_typeOnStack == _targetType && !_cleanupNeeded) + return; + Type::Category stackTypeCategory = _typeOnStack.getCategory(); + Type::Category targetTypeCategory = _targetType.getCategory(); + + switch (stackTypeCategory) + { + case Type::Category::FixedBytes: + { + FixedBytesType const& typeOnStack = dynamic_cast(_typeOnStack); + if (targetTypeCategory == Type::Category::Integer) + { + // conversion from bytes to integer. no need to clean the high bit + // only to shift right because of opposite alignment + IntegerType const& targetIntegerType = dynamic_cast(_targetType); + m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV; + if (targetIntegerType.getNumBits() < typeOnStack.getNumBytes() * 8) + convertType(IntegerType(typeOnStack.getNumBytes() * 8), _targetType, _cleanupNeeded); + } + else + { + // clear lower-order bytes for conversion to shorter bytes - we always clean + solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested."); + FixedBytesType const& targetType = dynamic_cast(_targetType); + if (targetType.getNumBytes() < typeOnStack.getNumBytes()) + { + if (targetType.getNumBytes() == 0) + m_context << eth::Instruction::DUP1 << eth::Instruction::XOR; + else + m_context << (u256(1) << (256 - targetType.getNumBytes() * 8)) + << eth::Instruction::DUP1 << eth::Instruction::SWAP2 + << eth::Instruction::DIV << eth::Instruction::MUL; + } + } + } + break; + case Type::Category::Enum: + solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, ""); + break; + case Type::Category::Integer: + case Type::Category::Contract: + case Type::Category::IntegerConstant: + if (targetTypeCategory == Type::Category::FixedBytes) + { + solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant, + "Invalid conversion to FixedBytesType requested."); + // conversion from bytes to string. no need to clean the high bit + // only to shift left because of opposite alignment + FixedBytesType const& targetBytesType = dynamic_cast(_targetType); + if (auto typeOnStack = dynamic_cast(&_typeOnStack)) + if (targetBytesType.getNumBytes() * 8 > typeOnStack->getNumBits()) + cleanHigherOrderBits(*typeOnStack); + m_context << (u256(1) << (256 - targetBytesType.getNumBytes() * 8)) << eth::Instruction::MUL; + } + else if (targetTypeCategory == Type::Category::Enum) + // just clean + convertType(_typeOnStack, *_typeOnStack.mobileType(), true); + else + { + solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); + IntegerType addressType(0, IntegerType::Modifier::Address); + IntegerType const& targetType = targetTypeCategory == Type::Category::Integer + ? dynamic_cast(_targetType) : addressType; + if (stackTypeCategory == Type::Category::IntegerConstant) + { + IntegerConstantType const& constType = dynamic_cast(_typeOnStack); + // We know that the stack is clean, we only have to clean for a narrowing conversion + // where cleanup is forced. + if (targetType.getNumBits() < constType.getIntegerType()->getNumBits() && _cleanupNeeded) + cleanHigherOrderBits(targetType); + } + else + { + IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer + ? dynamic_cast(_typeOnStack) : addressType; + // Widening: clean up according to source type width + // Non-widening and force: clean up according to target type bits + if (targetType.getNumBits() > typeOnStack.getNumBits()) + cleanHigherOrderBits(typeOnStack); + else if (_cleanupNeeded) + cleanHigherOrderBits(targetType); + } + } + break; + case Type::Category::Array: + { + solAssert(targetTypeCategory == stackTypeCategory, ""); + ArrayType const& typeOnStack = dynamic_cast(_typeOnStack); + ArrayType const& targetType = dynamic_cast(_targetType); + switch (targetType.location()) + { + case ReferenceType::Location::Storage: + // Other cases are done explicitly in LValue::storeValue, and only possible by assignment. + solAssert( + targetType.isPointer() && + typeOnStack.location() == ReferenceType::Location::Storage, + "Invalid conversion to storage type." + ); + break; + case ReferenceType::Location::Memory: + { + // Copy the array to a free position in memory, unless it is already in memory. + if (typeOnStack.location() != ReferenceType::Location::Memory) + { + // stack: (variably sized) + unsigned stackSize = typeOnStack.getSizeOnStack(); + fetchFreeMemoryPointer(); + moveIntoStack(stackSize); + // stack: (variably sized) + if (targetType.isDynamicallySized()) + { + bool fromStorage = (typeOnStack.location() == ReferenceType::Location::Storage); + // store length + if (fromStorage) + { + stackSize--; + // remove storage offset, as requested by ArrayUtils::retrieveLength + m_context << eth::Instruction::POP; + } + ArrayUtils(m_context).retrieveLength(typeOnStack); + // Stack: + m_context << eth::dupInstruction(2 + stackSize) << eth::Instruction::MSTORE; + m_context << eth::dupInstruction(1 + stackSize) << u256(0x20); + m_context << eth::Instruction::ADD; + moveIntoStack(stackSize); + if (fromStorage) + { + m_context << u256(0); + stackSize++; + } + } + else + { + m_context << eth::dupInstruction(1 + stackSize); + moveIntoStack(stackSize); + } + // Stack: + // Store data part. + storeInMemoryDynamic(typeOnStack); + // Stack + storeFreeMemoryPointer(); + } + else if (typeOnStack.location() == ReferenceType::Location::CallData) + { + // Stack: + //@todo + solAssert(false, "Not yet implemented."); + } + // nothing to do for memory to memory + break; + } + default: + solAssert(false, "Invalid type conversion requested."); + } + break; + } + case Type::Category::Struct: + { + //@todo we can probably use some of the code for arrays here. + solAssert(targetTypeCategory == stackTypeCategory, ""); + auto& targetType = dynamic_cast(_targetType); + auto& stackType = dynamic_cast(_typeOnStack); + solAssert( + targetType.location() == ReferenceType::Location::Storage && + stackType.location() == ReferenceType::Location::Storage, + "Non-storage structs not yet implemented." + ); + solAssert( + targetType.isPointer(), + "Type conversion to non-pointer struct requested." + ); + break; + } + default: + // All other types should not be convertible to non-equal types. + solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + break; + } +} + void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable)); @@ -189,6 +549,13 @@ void CompilerUtils::moveToStackTop(unsigned _stackDepth) m_context << eth::swapInstruction(1 + i); } +void CompilerUtils::moveIntoStack(unsigned _stackDepth) +{ + solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables."); + for (unsigned i = _stackDepth; i > 0; --i) + m_context << eth::swapInstruction(i); +} + void CompilerUtils::popStackElement(Type const& _type) { popStackSlots(_type.getSizeOnStack()); @@ -238,6 +605,16 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda return numBytes; } +void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack) +{ + if (_typeOnStack.getNumBits() == 256) + return; + else if (_typeOnStack.isSigned()) + m_context << u256(_typeOnStack.getNumBits() / 8 - 1) << eth::Instruction::SIGNEXTEND; + else + m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND; +} + unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const { unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries); diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h index 27c46ba11..32dc93a2c 100644 --- a/libsolidity/CompilerUtils.h +++ b/libsolidity/CompilerUtils.h @@ -81,6 +81,30 @@ public: /// Stack post: (memory_offset+length) void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true); + /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given + /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes. + /// Removes the values from the stack and leaves the updated memory pointer. + /// Stack pre: ... + /// Stack post: + /// Does not touch the memory-free pointer. + /// @param _padToWordBoundaries if false, all values are concatenated without padding. + /// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length) + /// together with fixed-length data. + /// @note the locations of target reference types are ignored, because it will always be + /// memory. + void encodeToMemory( + TypePointers const& _givenTypes = {}, + TypePointers const& _targetTypes = {}, + bool _padToWordBoundaries = true, + bool _copyDynamicDataInPlace = false + ); + + /// Appends code for an implicit or explicit type conversion. For now this comprises only erasing + /// higher-order bits (@see appendHighBitCleanup) when widening integer. + /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be + /// necessary. + void convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); + /// Moves the value that is at the top of the stack to a stack variable. void moveToStackVariable(VariableDeclaration const& _variable); /// Copies an item that occupies @a _itemSize stack slots from a stack depth of @a _stackDepth @@ -88,6 +112,8 @@ public: void copyToStackTop(unsigned _stackDepth, unsigned _itemSize); /// Moves a single stack element (with _stackDepth items on top of it) to the top of the stack. void moveToStackTop(unsigned _stackDepth); + /// Moves a single stack element past @a _stackDepth other stack elements + void moveIntoStack(unsigned _stackDepth); /// Removes the current value from the top of the stack. void popStackElement(Type const& _type); /// Removes element from the top of the stack _amount times. @@ -110,6 +136,12 @@ public: static const size_t freeMemoryPointer; private: + /// Address of the precompiled identity contract. + static const unsigned identityContractAddress; + + //// Appends code that cleans higher-order bits for integer types. + void cleanHigherOrderBits(IntegerType const& _typeOnStack); + /// Prepares the given type for storing in memory by shifting it if necessary. unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const; /// Loads type from memory assuming memory offset is on stack top. diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 811ee60ec..d5ffd35b4 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -51,7 +51,7 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c solAssert(!!_varDecl.getValue()->getType(), "Type information not available."); CompilerContext::LocationSetter locationSetter(m_context, _varDecl); _varDecl.getValue()->accept(*this); - appendTypeConversion(*_varDecl.getValue()->getType(), *_varDecl.getType(), true); + utils().convertType(*_varDecl.getValue()->getType(), *_varDecl.getType(), true); StorageItem(m_context, _varDecl).storeValue(*_varDecl.getType(), _varDecl.getLocation(), true); } @@ -77,10 +77,10 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& // pop offset m_context << eth::Instruction::POP; // move storage offset to memory. - CompilerUtils(m_context).storeInMemory(32); + utils().storeInMemory(32); // move key to memory. - CompilerUtils(m_context).copyToStackTop(paramTypes.size() - i, 1); - CompilerUtils(m_context).storeInMemory(0); + utils().copyToStackTop(paramTypes.size() - i, 1); + utils().storeInMemory(0); m_context << u256(64) << u256(0) << eth::Instruction::SHA3; // push offset m_context << u256(0); @@ -90,7 +90,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& { // pop offset m_context << eth::Instruction::POP; - CompilerUtils(m_context).copyToStackTop(paramTypes.size() - i + 1, 1); + utils().copyToStackTop(paramTypes.size() - i + 1, 1); ArrayUtils(m_context).accessIndex(*arrayType); returnType = arrayType->getBaseType(); } @@ -105,7 +105,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& m_context << eth::swapInstruction(paramTypes.size()); m_context << eth::Instruction::POP; m_context << eth::swapInstruction(paramTypes.size()); - CompilerUtils(m_context).popStackSlots(paramTypes.size() - 1); + utils().popStackSlots(paramTypes.size() - 1); } unsigned retSizeOnStack = 0; solAssert(accessorType.getReturnParameterTypes().size() >= 1, ""); @@ -142,128 +142,14 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); } -void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded) -{ - // For a type extension, we need to remove all higher-order bits that we might have ignored in - // previous operations. - // @todo: store in the AST whether the operand might have "dirty" higher order bits - - if (_typeOnStack == _targetType && !_cleanupNeeded) - return; - Type::Category stackTypeCategory = _typeOnStack.getCategory(); - Type::Category targetTypeCategory = _targetType.getCategory(); - - switch (stackTypeCategory) - { - case Type::Category::FixedBytes: - { - FixedBytesType const& typeOnStack = dynamic_cast(_typeOnStack); - if (targetTypeCategory == Type::Category::Integer) - { - // conversion from bytes to integer. no need to clean the high bit - // only to shift right because of opposite alignment - IntegerType const& targetIntegerType = dynamic_cast(_targetType); - m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV; - if (targetIntegerType.getNumBits() < typeOnStack.getNumBytes() * 8) - appendTypeConversion(IntegerType(typeOnStack.getNumBytes() * 8), _targetType, _cleanupNeeded); - } - else - { - // clear lower-order bytes for conversion to shorter bytes - we always clean - solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested."); - FixedBytesType const& targetType = dynamic_cast(_targetType); - if (targetType.getNumBytes() < typeOnStack.getNumBytes()) - { - if (targetType.getNumBytes() == 0) - m_context << eth::Instruction::DUP1 << eth::Instruction::XOR; - else - m_context << (u256(1) << (256 - targetType.getNumBytes() * 8)) - << eth::Instruction::DUP1 << eth::Instruction::SWAP2 - << eth::Instruction::DIV << eth::Instruction::MUL; - } - } - } - break; - case Type::Category::Enum: - solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, ""); - break; - case Type::Category::Integer: - case Type::Category::Contract: - case Type::Category::IntegerConstant: - if (targetTypeCategory == Type::Category::FixedBytes) - { - solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant, - "Invalid conversion to FixedBytesType requested."); - // conversion from bytes to string. no need to clean the high bit - // only to shift left because of opposite alignment - FixedBytesType const& targetBytesType = dynamic_cast(_targetType); - if (auto typeOnStack = dynamic_cast(&_typeOnStack)) - if (targetBytesType.getNumBytes() * 8 > typeOnStack->getNumBits()) - appendHighBitsCleanup(*typeOnStack); - m_context << (u256(1) << (256 - targetBytesType.getNumBytes() * 8)) << eth::Instruction::MUL; - } - else if (targetTypeCategory == Type::Category::Enum) - // just clean - appendTypeConversion(_typeOnStack, *_typeOnStack.mobileType(), true); - else - { - solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); - IntegerType addressType(0, IntegerType::Modifier::Address); - IntegerType const& targetType = targetTypeCategory == Type::Category::Integer - ? dynamic_cast(_targetType) : addressType; - if (stackTypeCategory == Type::Category::IntegerConstant) - { - IntegerConstantType const& constType = dynamic_cast(_typeOnStack); - // We know that the stack is clean, we only have to clean for a narrowing conversion - // where cleanup is forced. - if (targetType.getNumBits() < constType.getIntegerType()->getNumBits() && _cleanupNeeded) - appendHighBitsCleanup(targetType); - } - else - { - IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer - ? dynamic_cast(_typeOnStack) : addressType; - // Widening: clean up according to source type width - // Non-widening and force: clean up according to target type bits - if (targetType.getNumBits() > typeOnStack.getNumBits()) - appendHighBitsCleanup(typeOnStack); - else if (_cleanupNeeded) - appendHighBitsCleanup(targetType); - } - } - break; - case Type::Category::Array: - //@TODO - break; - case Type::Category::Struct: - { - solAssert(targetTypeCategory == stackTypeCategory, ""); - auto& targetType = dynamic_cast(_targetType); - auto& stackType = dynamic_cast(_typeOnStack); - solAssert( - targetType.location() == ReferenceType::Location::Storage && - stackType.location() == ReferenceType::Location::Storage, - "Non-storage structs not yet implemented." - ); - solAssert( - targetType.isPointer(), - "Type conversion to non-pointer struct requested." - ); - break; - } - default: - // All other types should not be convertible to non-equal types. - solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); - break; - } -} - bool ExpressionCompiler::visit(Assignment const& _assignment) { CompilerContext::LocationSetter locationSetter(m_context, _assignment); _assignment.getRightHandSide().accept(*this); if (_assignment.getType()->isValueType()) - appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); + utils().convertType(*_assignment.getRightHandSide().getType(), *_assignment.getType()); + // We need this conversion mostly in the case of compound assignments. For non-value types + // the conversion is done in LValue::storeValue. _assignment.getLeftHandSide().accept(*this); solAssert(!!m_currentLValue, "LValue not retrieved."); @@ -275,8 +161,8 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) unsigned itemSize = _assignment.getType()->getSizeOnStack(); if (lvalueSize > 0) { - CompilerUtils(m_context).copyToStackTop(lvalueSize + itemSize, itemSize); - CompilerUtils(m_context).copyToStackTop(itemSize + lvalueSize, lvalueSize); + utils().copyToStackTop(lvalueSize + itemSize, itemSize); + utils().copyToStackTop(itemSize + lvalueSize, lvalueSize); // value lvalue_ref value lvalue_ref } m_currentLValue->retrieveValue(_assignment.getLocation(), true); @@ -391,16 +277,16 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) if (swap) { leftExpression.accept(*this); - appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); + utils().convertType(*leftExpression.getType(), commonType, cleanupNeeded); rightExpression.accept(*this); - appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); + utils().convertType(*rightExpression.getType(), commonType, cleanupNeeded); } else { rightExpression.accept(*this); - appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); + utils().convertType(*rightExpression.getType(), commonType, cleanupNeeded); leftExpression.accept(*this); - appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); + utils().convertType(*leftExpression.getType(), commonType, cleanupNeeded); } if (Token::isCompareOp(c_op)) appendCompareOperatorCode(c_op, commonType); @@ -423,7 +309,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(_functionCall.getNames().empty(), ""); Expression const& firstArgument = *_functionCall.getArguments().front(); firstArgument.accept(*this); - appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); + utils().convertType(*firstArgument.getType(), *_functionCall.getType()); } else { @@ -461,7 +347,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) for (unsigned i = 0; i < arguments.size(); ++i) { arguments[i]->accept(*this); - appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i]); + utils().convertType(*arguments[i]->getType(), *function.getParameterTypes()[i]); } _functionCall.getExpression().accept(*this); @@ -475,7 +361,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // @todo for now, the return value of a function is its first return value, so remove // all others for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) - CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]); + utils().popStackElement(*function.getReturnParameterTypes()[i]); break; } case Location::External: @@ -500,7 +386,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) *function.getReturnParameterTypes().front()).getContractDefinition(); // copy the contract's code into memory bytes const& bytecode = m_context.getCompiledContract(contract); - CompilerUtils(m_context).fetchFreeMemoryPointer(); + utils().fetchFreeMemoryPointer(); m_context << u256(bytecode.size()) << eth::Instruction::DUP1; //@todo could be done by actually appending the Assembly, but then we probably need to compile // multiple times. Will revisit once external fuctions are inlined. @@ -508,10 +394,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY; m_context << eth::Instruction::ADD; - encodeToMemory(argumentTypes, function.getParameterTypes()); + utils().encodeToMemory(argumentTypes, function.getParameterTypes()); // now on stack: memory_end_ptr // need: size, offset, endowment - CompilerUtils(m_context).toSizeAfterFreeMemoryPointer(); + utils().toSizeAfterFreeMemoryPointer(); if (function.valueSet()) m_context << eth::dupInstruction(3); else @@ -527,7 +413,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _functionCall.getExpression().accept(*this); arguments.front()->accept(*this); - appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true); + utils().convertType(*arguments.front()->getType(), IntegerType(256), true); // Note that function is not the original function, but the ".gas" function. // Its values of gasSet and valueSet is equal to the original function's though. unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0); @@ -550,7 +436,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _functionCall.getExpression().accept(*this); m_context << u256(0); // do not send gas (there still is the stipend) arguments.front()->accept(*this); - appendTypeConversion(*arguments.front()->getType(), + utils().convertType(*arguments.front()->getType(), *function.getParameterTypes().front(), true); appendExternalFunctionCall( FunctionType( @@ -568,7 +454,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) break; case Location::Suicide: arguments.front()->accept(*this); - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); + utils().convertType(*arguments.front()->getType(), *function.getParameterTypes().front(), true); m_context << eth::Instruction::SUICIDE; break; case Location::SHA3: @@ -579,9 +465,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arg->accept(*this); argumentTypes.push_back(arg->getType()); } - CompilerUtils(m_context).fetchFreeMemoryPointer(); - encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true); - CompilerUtils(m_context).toSizeAfterFreeMemoryPointer(); + utils().fetchFreeMemoryPointer(); + utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true); + utils().toSizeAfterFreeMemoryPointer(); m_context << eth::Instruction::SHA3; break; } @@ -595,16 +481,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) for (unsigned arg = logNumber; arg > 0; --arg) { arguments[arg]->accept(*this); - appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true); + utils().convertType(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true); } arguments.front()->accept(*this); - CompilerUtils(m_context).fetchFreeMemoryPointer(); - encodeToMemory( + utils().fetchFreeMemoryPointer(); + utils().encodeToMemory( {arguments.front()->getType()}, {function.getParameterTypes().front()}, false, true); - CompilerUtils(m_context).toSizeAfterFreeMemoryPointer(); + utils().toSizeAfterFreeMemoryPointer(); m_context << eth::logInstruction(logNumber); break; } @@ -619,7 +505,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { ++numIndexed; arguments[arg - 1]->accept(*this); - appendTypeConversion( + utils().convertType( *arguments[arg - 1]->getType(), *function.getParameterTypes()[arg - 1], true @@ -642,17 +528,17 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) nonIndexedArgTypes.push_back(arguments[arg]->getType()); nonIndexedParamTypes.push_back(function.getParameterTypes()[arg]); } - CompilerUtils(m_context).fetchFreeMemoryPointer(); - encodeToMemory(nonIndexedArgTypes, nonIndexedParamTypes); + utils().fetchFreeMemoryPointer(); + utils().encodeToMemory(nonIndexedArgTypes, nonIndexedParamTypes); // need: topic1 ... topicn memsize memstart - CompilerUtils(m_context).toSizeAfterFreeMemoryPointer(); + utils().toSizeAfterFreeMemoryPointer(); m_context << eth::logInstruction(numIndexed); break; } case Location::BlockHash: { arguments[0]->accept(*this); - appendTypeConversion(*arguments[0]->getType(), *function.getParameterTypes()[0], true); + utils().convertType(*arguments[0]->getType(), *function.getParameterTypes()[0], true); m_context << eth::Instruction::BLOCKHASH; break; } @@ -713,7 +599,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) identifier = FunctionType(*function).externalIdentifier(); else solAssert(false, "Contract member is neither variable nor function."); - appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::Address), true); + utils().convertType(type, IntegerType(0, IntegerType::Modifier::Address), true); m_context << identifier; } else @@ -726,12 +612,12 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case Type::Category::Integer: if (member == "balance") { - appendTypeConversion(*_memberAccess.getExpression().getType(), + utils().convertType(*_memberAccess.getExpression().getType(), IntegerType(0, IntegerType::Modifier::Address), true); m_context << eth::Instruction::BALANCE; } else if ((set{"send", "call", "callcode"}).count(member)) - appendTypeConversion(*_memberAccess.getExpression().getType(), + utils().convertType(*_memberAccess.getExpression().getType(), IntegerType(0, IntegerType::Modifier::Address), true); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); @@ -809,7 +695,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) auto const& type = dynamic_cast(*_memberAccess.getExpression().getType()); if (!type.isDynamicallySized()) { - CompilerUtils(m_context).popStackElement(type); + utils().popStackElement(type); m_context << type.getLength(); } else @@ -850,7 +736,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression()); m_context << eth::Instruction::SWAP1; solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); - appendTypeMoveToMemory(IntegerType(256)); + utils().storeInMemoryDynamic(IntegerType(256)); m_context << u256(0) << eth::Instruction::SHA3; m_context << u256(0); setLValueToStorageItem(_indexAccess); @@ -1071,16 +957,6 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator) } } -void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack) -{ - if (_typeOnStack.getNumBits() == 256) - return; - else if (_typeOnStack.isSigned()) - m_context << u256(_typeOnStack.getNumBits() / 8 - 1) << eth::Instruction::SIGNEXTEND; - else - m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND; -} - void ExpressionCompiler::appendExternalFunctionCall( FunctionType const& _functionType, vector> const& _arguments @@ -1127,7 +1003,7 @@ void ExpressionCompiler::appendExternalFunctionCall( // If we have a BareCall or BareCallCode and the first type has exactly 4 bytes, use it as // function identifier. _arguments.front()->accept(*this); - appendTypeConversion( + utils().convertType( *_arguments.front()->getType(), IntegerType(8 * CompilerUtils::dataStartOffset), true @@ -1144,16 +1020,16 @@ void ExpressionCompiler::appendExternalFunctionCall( } // Copy function identifier to memory. - CompilerUtils(m_context).fetchFreeMemoryPointer(); + utils().fetchFreeMemoryPointer(); if (!_functionType.isBareCall() || manualFunctionId) { m_context << eth::dupInstruction(2 + gasValueSize + CompilerUtils::getSizeOnStack(argumentTypes)); - appendTypeMoveToMemory(IntegerType(8 * CompilerUtils::dataStartOffset), false); + utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false); } // If the function takes arbitrary parameters, copy dynamic length data in place. // Move argumenst to memory, will not update the free memory pointer (but will update the memory // pointer on the stack). - encodeToMemory( + utils().encodeToMemory( argumentTypes, _functionType.getParameterTypes(), _functionType.padArguments(), @@ -1171,7 +1047,7 @@ void ExpressionCompiler::appendExternalFunctionCall( // Output data will replace input data. // put on stack: m_context << u256(retSize); - CompilerUtils(m_context).fetchFreeMemoryPointer(); + utils().fetchFreeMemoryPointer(); m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SUB; m_context << eth::Instruction::DUP2; @@ -1212,7 +1088,7 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context.appendConditionalJumpTo(m_context.errorTag()); } - CompilerUtils(m_context).popStackSlots(remainsSize); + utils().popStackSlots(remainsSize); if (returnSuccessCondition) { @@ -1221,118 +1097,16 @@ void ExpressionCompiler::appendExternalFunctionCall( else if (funKind == FunctionKind::RIPEMD160) { // fix: built-in contract returns right-aligned data - CompilerUtils(m_context).fetchFreeMemoryPointer(); - CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(160), false, true, false); - appendTypeConversion(IntegerType(160), FixedBytesType(20)); + utils().fetchFreeMemoryPointer(); + utils().loadFromMemoryDynamic(IntegerType(160), false, true, false); + utils().convertType(IntegerType(160), FixedBytesType(20)); } else if (firstReturnType) { //@todo manually update free memory pointer if we accept returning memory-stored objects - CompilerUtils(m_context).fetchFreeMemoryPointer(); - CompilerUtils(m_context).loadFromMemoryDynamic(*firstReturnType, false, true, false); - } -} - -void ExpressionCompiler::encodeToMemory( - TypePointers const& _givenTypes, - TypePointers const& _targetTypes, - bool _padToWordBoundaries, - bool _copyDynamicDataInPlace -) -{ - // stack: ... - TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; - solAssert(targetTypes.size() == _givenTypes.size(), ""); - for (TypePointer& t: targetTypes) - t = t->mobileType()->externalType(); - - // Stack during operation: - // ... ... - // The values dyn_head_i are added during the first loop and they point to the head part - // of the ith dynamic parameter, which is filled once the dynamic parts are processed. - - // store memory start pointer - m_context << eth::Instruction::DUP1; - - unsigned argSize = CompilerUtils::getSizeOnStack(_givenTypes); - unsigned stackPos = 0; // advances through the argument values - unsigned dynPointers = 0; // number of dynamic head pointers on the stack - for (size_t i = 0; i < _givenTypes.size(); ++i) - { - TypePointer targetType = targetTypes[i]; - solAssert(!!targetType, "Externalable type expected."); - if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace) - { - // leave end_of_mem as dyn head pointer - m_context << eth::Instruction::DUP1 << u256(32) << eth::Instruction::ADD; - dynPointers++; - } - else - { - CompilerUtils(m_context).copyToStackTop( - argSize - stackPos + dynPointers + 2, - _givenTypes[i]->getSizeOnStack() - ); - if (targetType->isValueType()) - appendTypeConversion(*_givenTypes[i], *targetType, true); - solAssert(!!targetType, "Externalable type expected."); - appendTypeMoveToMemory(*targetType, _padToWordBoundaries); - } - stackPos += _givenTypes[i]->getSizeOnStack(); + utils().fetchFreeMemoryPointer(); + utils().loadFromMemoryDynamic(*firstReturnType, false, true, false); } - - // now copy the dynamic part - // Stack: ... ... - stackPos = 0; - unsigned thisDynPointer = 0; - for (size_t i = 0; i < _givenTypes.size(); ++i) - { - TypePointer targetType = targetTypes[i]; - solAssert(!!targetType, "Externalable type expected."); - if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace) - { - solAssert(_givenTypes[i]->getCategory() == Type::Category::Array, "Unknown dynamic type."); - auto const& arrayType = dynamic_cast(*_givenTypes[i]); - // copy tail pointer (=mem_end - mem_start) to memory - m_context << eth::dupInstruction(2 + dynPointers) << eth::Instruction::DUP2; - m_context << eth::Instruction::SUB; - m_context << eth::dupInstruction(2 + dynPointers - thisDynPointer); - m_context << eth::Instruction::MSTORE; - // now copy the array - CompilerUtils(m_context).copyToStackTop( - argSize - stackPos + dynPointers + 2, - arrayType.getSizeOnStack() - ); - // copy length to memory - m_context << eth::dupInstruction(1 + arrayType.getSizeOnStack()); - if (arrayType.location() == ReferenceType::Location::CallData) - m_context << eth::Instruction::DUP2; // length is on stack - else if (arrayType.location() == ReferenceType::Location::Storage) - m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD; - else - { - solAssert(arrayType.location() == ReferenceType::Location::Memory, ""); - m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD; - } - appendTypeMoveToMemory(IntegerType(256), true); - // copy the new memory pointer - m_context << eth::swapInstruction(arrayType.getSizeOnStack() + 1) << eth::Instruction::POP; - // copy data part - appendTypeMoveToMemory(arrayType, true); - - thisDynPointer++; - } - stackPos += _givenTypes[i]->getSizeOnStack(); - } - - // remove unneeded stack elements (and retain memory pointer) - m_context << eth::swapInstruction(argSize + dynPointers + 1); - CompilerUtils(m_context).popStackSlots(argSize + dynPointers + 1); -} - -void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries) -{ - CompilerUtils(m_context).storeInMemoryDynamic(_type, _padToWordBoundaries); } void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression) @@ -1340,11 +1114,11 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, _expression.accept(*this); if (_expectedType.isValueType()) { - appendTypeConversion(*_expression.getType(), _expectedType, true); - appendTypeMoveToMemory(_expectedType); + utils().convertType(*_expression.getType(), _expectedType, true); + utils().storeInMemoryDynamic(_expectedType); } else - appendTypeMoveToMemory(*_expression.getType()->mobileType()); + utils().storeInMemoryDynamic(*_expression.getType()->mobileType()); } void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) @@ -1364,5 +1138,10 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) setLValue(_expression, *_expression.getType()); } +CompilerUtils ExpressionCompiler::utils() +{ + return CompilerUtils(m_context); +} + } } diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 90994dfdb..642560c64 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -26,9 +26,9 @@ #include #include #include -#include #include #include +#include namespace dev { namespace eth @@ -39,6 +39,7 @@ namespace solidity { // forward declarations class CompilerContext; +class CompilerUtils; class Type; class IntegerType; class ArrayType; @@ -66,12 +67,6 @@ public: /// Appends code for a State Variable accessor function void appendStateVariableAccessor(VariableDeclaration const& _varDecl); - /// Appends an implicit or explicit type conversion. For now this comprises only erasing - /// higher-order bits (@see appendHighBitCleanup) when widening integer. - /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be - /// necessary. - void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); - private: virtual bool visit(Assignment const& _assignment) override; virtual bool visit(UnaryOperation const& _unaryOperation) override; @@ -94,33 +89,11 @@ private: void appendShiftOperatorCode(Token::Value _operator); /// @} - //// Appends code that cleans higher-order bits for integer types. - void appendHighBitsCleanup(IntegerType const& _typeOnStack); - /// Appends code to call a function of the given type with the given arguments. void appendExternalFunctionCall( FunctionType const& _functionType, std::vector> const& _arguments ); - /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given - /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes. - /// Removes the values from the stack and leaves the updated memory pointer. - /// Stack pre: ... - /// Stack post: - /// Does not touch the memory-free pointer. - /// @param _padToWordBoundaries if false, all values are concatenated without padding. - /// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length) - /// together with fixed-length data. - void encodeToMemory( - TypePointers const& _givenTypes = {}, - TypePointers const& _targetTypes = {}, - bool _padToWordBoundaries = true, - bool _copyDynamicDataInPlace = false - ); - /// Appends code that moves a stack element of the given type to memory. The memory offset is - /// expected below the stack element and is updated by this call. - /// For arrays, this only copies the data part. - void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true); /// Appends code that evaluates a single expression and moves the result to memory. The memory offset is /// expected to be on the stack and is updated by this call. void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression); @@ -137,9 +110,13 @@ private: template void setLValue(Expression const& _expression, _Arguments const&... _arguments); + /// @returns the CompilerUtils object containing the current context. + CompilerUtils utils(); + bool m_optimize; CompilerContext& m_context; std::unique_ptr m_currentLValue; + }; template diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index fd4bbcf6d..8d316a977 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(location_test) AssemblyItems items = compileContract(sourceCode); vector locations = vector(17, SourceLocation(2, 75, n)) + - vector(14, SourceLocation(20, 72, n)) + + vector(26, SourceLocation(20, 72, n)) + vector{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector(4, SourceLocation(58, 67, n)) + vector(3, SourceLocation(20, 72, n)); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f12abd48e..f4d875e77 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2420,7 +2420,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3) + asBytes("ABC")); + BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3, string("ABC"))); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); } @@ -4232,6 +4232,31 @@ BOOST_AUTO_TEST_CASE(reusing_memory) BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(dev::sha3(dev::toBigEndian(u256(0x34))))); } +BOOST_AUTO_TEST_CASE(return_string) +{ + char const* sourceCode = R"( + contract Main { + string public s; + function set(string _s) external { + s = _s; + } + function get1() returns (string r) { + return s; + } +// function get2() returns (string r) { +// r = s; +// } + } + )"; + compileAndRun(sourceCode, 0, "Main"); + string s("Julia"); + bytes args = encodeArgs(u256(0x20), u256(s.length()), s); + BOOST_REQUIRE(callContractFunction("set(string)", asString(args)) == encodeArgs()); + BOOST_CHECK(callContractFunction("get1()") == args); +// BOOST_CHECK(callContractFunction("get2()") == args); +// BOOST_CHECK(callContractFunction("s()") == args); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/solidityExecutionFramework.h b/test/libsolidity/solidityExecutionFramework.h index 44590b1c8..4ba229815 100644 --- a/test/libsolidity/solidityExecutionFramework.h +++ b/test/libsolidity/solidityExecutionFramework.h @@ -174,11 +174,11 @@ protected: BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas)); } - BOOST_REQUIRE(executive.go()); + BOOST_REQUIRE(executive.go(/* DEBUG eth::Executive::simpleTrace() */)); m_state.noteSending(m_sender); executive.finalize(); - m_gasUsed = executive.gasUsed(); - m_output = std::move(res.output); // FIXME: Looks like Framework needs ExecutiveResult embedded + m_gasUsed = res.gasUsed; + m_output = std::move(res.output); m_logs = executive.logs(); } From be45d62b6144c5ee8f0061bb5e0065ca93cbf33e Mon Sep 17 00:00:00 2001 From: Dimitry Date: Mon, 15 Jun 2015 21:08:22 +0300 Subject: [PATCH 27/66] Code Coverage: bash syntax --- getcoverage.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/getcoverage.sh b/getcoverage.sh index 446d584e6..ac50bf94b 100755 --- a/getcoverage.sh +++ b/getcoverage.sh @@ -16,9 +16,9 @@ if which lcov >/dev/null; then if which genhtml >/dev/null; then lcov --directory $TESTETH --zerocounters echo Running testeth... - $($CPP_ETHEREUM_PATH/build/test/testeth --all) - $($CPP_ETHEREUM_PATH/build/test/testeth -t StateTests --jit --all) - $($CPP_ETHEREUM_PATH/build/test/testeth -t VMTests --jit --all) + $CPP_ETHEREUM_PATH/build/test/testeth -t --all + $CPP_ETHEREUM_PATH/build/test/testeth -t StateTests --jit --all + $CPP_ETHEREUM_PATH/build/test/testeth -t VMTests --jit --all echo Prepearing coverage info... else echo genhtml not found From 5c2018d1bc9611fbbce7660b6923313c4c21ed0f Mon Sep 17 00:00:00 2001 From: gluk256 Date: Mon, 15 Jun 2015 20:19:52 +0200 Subject: [PATCH 28/66] Update FixedHash.h --- libdevcore/FixedHash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index ca80d745c..922a6ec30 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -168,7 +168,7 @@ public: template inline FixedHash bloomPart() const { - static_assert((M & (M - 1)) == 0, "M must be power-of-two"); + static_assert((M & (M - 1)) == 0, "M must be power-of-two"); static const unsigned c_bloomBits = M * 8; unsigned mask = c_bloomBits - 1; unsigned bloomBytes = (dev::toLog2(c_bloomBits) + 7) / 8; From 2c37204a69d8d73c5f561d327f2781264fafe9ab Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 11 Jun 2015 17:35:50 +0200 Subject: [PATCH 29/66] KeyManager refactoring to increase readability. --- alethzero/MainWin.cpp | 46 ++++--- alethzero/MainWin.h | 2 +- alethzero/OurWebThreeStubServer.cpp | 2 +- alethzero/Transact.cpp | 7 +- eth/main.cpp | 8 +- ethkey/KeyAux.h | 8 +- libdevcore/CommonData.h | 9 +- libethcore/KeyManager.cpp | 169 +++++++++++++++----------- libethcore/KeyManager.h | 66 ++++++---- libweb3jsonrpc/AccountHolder.cpp | 2 +- libweb3jsonrpc/AccountHolder.h | 1 - libweb3jsonrpc/WebThreeStubServer.cpp | 12 +- 12 files changed, 194 insertions(+), 138 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 848020cbc..297ad0eda 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -235,7 +235,7 @@ Main::Main(QWidget *parent) : // ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true); // QWebEngineInspector* inspector = new QWebEngineInspector(); // inspector->setPage(page); - setBeneficiary(*m_keyManager.accounts().begin()); + setBeneficiary(m_keyManager.accounts().front()); ethereum()->setDefault(LatestBlock); @@ -430,9 +430,9 @@ void Main::installBalancesWatch() // TODO: Update for new currencies reg. for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i) altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); - for (auto const& i: m_keyManager.accounts()) + for (auto const& address: m_keyManager.accounts()) for (auto c: altCoins) - tf.address(c).topic(0, h256(i, h256::AlignRight)); + tf.address(c).topic(0, h256(address, h256::AlignRight)); uninstallWatch(m_balancesFilter); m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); }); @@ -501,7 +501,7 @@ void Main::load(QString _s) void Main::on_newTransaction_triggered() { - m_transact->setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB); + m_transact->setEnvironment(m_keyManager.accountsHash(), ethereum(), &m_natSpecDB); m_transact->show(); } @@ -735,18 +735,17 @@ void Main::writeSettings() s.setValue("windowState", saveState()); } -Secret Main::retrieveSecret(Address const& _a) const +Secret Main::retrieveSecret(Address const& _address) const { - auto info = m_keyManager.accountDetails()[_a]; while (true) { - Secret s = m_keyManager.secret(_a, [&](){ + Secret s = m_keyManager.secret(_address, [&](){ QDialog d; Ui_GetPassword gp; gp.setupUi(&d); d.setWindowTitle("Unlock Account"); - gp.label->setText(QString("Enter the password for the account %2 (%1).").arg(QString::fromStdString(_a.abridged())).arg(QString::fromStdString(info.first))); - gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(info.second)); + gp.label->setText(QString("Enter the password for the account %2 (%1).").arg(QString::fromStdString(_address.abridged())).arg(QString::fromStdString(m_keyManager.accountName(_address)))); + gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(m_keyManager.passwordHint(_address))); return d.exec() == QDialog::Accepted ? gp.entry->text().toStdString() : string(); }); if (s || QMessageBox::warning(nullptr, "Unlock Account", "The password you gave is incorrect for this key.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) @@ -770,7 +769,7 @@ void Main::readSettings(bool _skipGeometry) for (unsigned i = 0; i < b.size() / sizeof(Secret); ++i) { memcpy(&k, b.data() + i * sizeof(Secret), sizeof(Secret)); - if (!m_keyManager.accounts().count(KeyPair(k).address())) + if (!m_keyManager.hasAccount(KeyPair(k).address())) m_keyManager.import(k, "Imported (UNSAFE) key."); } } @@ -858,7 +857,7 @@ void Main::on_importKey_triggered() if (b.size() == 32) { auto k = KeyPair(h256(b)); - if (!m_keyManager.accounts().count(k.address())) + if (!m_keyManager.hasAccount(k.address())) { QString s = QInputDialog::getText(this, "Import Account Key", "Enter this account's name"); if (QMessageBox::question(this, "Additional Security?", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) @@ -939,7 +938,7 @@ void Main::on_claimPresale_triggered() } cnote << k.address(); - if (!m_keyManager.accounts().count(k.address())) + if (!m_keyManager.hasAccount(k.address())) ethereum()->submitTransaction(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_beneficiary, {}, c_txGas, gasPrice()); else QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); @@ -1110,13 +1109,13 @@ void Main::refreshBalances() // cdebug << n << addr << denom << sha3(h256(n).asBytes()); altCoins[addr] = make_tuple(fromRaw(n), 0, denom); }*/ - for (pair> const& i: m_keyManager.accountDetails()) + for (auto const& address: m_keyManager.accounts()) { - u256 b = ethereum()->balanceAt(i.first); - QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(i.first))).arg((unsigned)ethereum()->countAt(i.first)).arg(QString::fromStdString(i.second.first)), ui->ourAccounts); - li->setData(Qt::UserRole, QByteArray((char const*)i.first.data(), Address::size)); + u256 b = ethereum()->balanceAt(address); + QListWidgetItem* li = new QListWidgetItem(QString("%4 %2: %1 [%3]").arg(formatBalance(b).c_str()).arg(QString::fromStdString(render(address))).arg((unsigned)ethereum()->countAt(address)).arg(QString::fromStdString(m_keyManager.accountName(address))), ui->ourAccounts); + li->setData(Qt::UserRole, QByteArray((char const*)address.data(), Address::size)); li->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - li->setCheckState(m_beneficiary == i.first ? Qt::Checked : Qt::Unchecked); + li->setCheckState(m_beneficiary == address ? Qt::Checked : Qt::Unchecked); totalBalance += b; // for (auto& c: altCoins) @@ -2094,9 +2093,8 @@ void Main::on_killAccount_triggered() { auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); Address h((byte const*)hba.data(), Address::ConstructFromPointer); - auto k = m_keyManager.accountDetails()[h]; - QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + k.first + "?!"), - QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it.\r\nIt, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" + QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + m_keyManager.accountName(h) + "?!"), + QString::fromStdString("Account " + m_keyManager.accountName(h) + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it.\r\nIt, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" "Are you sure you want to continue? \r\n If so, type 'YES' to confirm."), QLineEdit::Normal, "NO"); if (s != "YES") @@ -2104,10 +2102,10 @@ void Main::on_killAccount_triggered() m_keyManager.kill(h); if (m_keyManager.accounts().empty()) m_keyManager.import(Secret::random(), "Default account"); - m_beneficiary = *m_keyManager.accounts().begin(); + m_beneficiary = m_keyManager.accounts().front(); keysChanged(); if (m_beneficiary == h) - setBeneficiary(*m_keyManager.accounts().begin()); + setBeneficiary(m_keyManager.accounts().front()); } } @@ -2128,7 +2126,7 @@ void Main::on_reencryptKey_triggered() return; try { auto pw = [&](){ - auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.hint(a)), QLineEdit::Password, QString()).toStdString(); + auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.passwordHint(a)), QLineEdit::Password, QString()).toStdString(); if (p.empty()) throw PasswordUnknown(); return p; @@ -2151,7 +2149,7 @@ void Main::on_reencryptAll_triggered() try { for (Address const& a: m_keyManager.accounts()) while (!m_keyManager.recode(a, SemanticPassword::Existing, [&](){ - auto p = QInputDialog::getText(nullptr, "Re-Encrypt Key", QString("Enter the original password for key %1.\nHint: %2").arg(QString::fromStdString(pretty(a))).arg(QString::fromStdString(m_keyManager.hint(a))), QLineEdit::Password, QString()).toStdString(); + auto p = QInputDialog::getText(nullptr, "Re-Encrypt Key", QString("Enter the original password for key %1.\nHint: %2").arg(QString::fromStdString(pretty(a))).arg(QString::fromStdString(m_keyManager.passwordHint(a))), QLineEdit::Password, QString()).toStdString(); if (p.empty()) throw PasswordUnknown(); return p; diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index efff89d2b..f8a6fa6c7 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -96,7 +96,7 @@ public: dev::eth::KeyManager& keyManager() override { return m_keyManager; } bool doConfirm(); - dev::Secret retrieveSecret(dev::Address const& _a) const override; + dev::Secret retrieveSecret(dev::Address const& _address) const override; public slots: void load(QString _file); diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index e18cb55d5..aaeffa16b 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -136,7 +136,7 @@ void OurAccountHolder::doValidations() AddressHash OurAccountHolder::realAccounts() const { - return m_main->keyManager().accounts(); + return m_main->keyManager().accountsHash(); } bool OurAccountHolder::validateTransaction(TransactionSkeleton const& _t, bool _toProxy) diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index fd466e475..7a26f56f2 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -77,11 +77,10 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e auto old = ui->from->currentIndex(); ui->from->clear(); - for (auto const& i: m_accounts) + for (auto const& address: m_accounts) { - auto d = m_context->keyManager().accountDetails()[i]; - u256 b = ethereum()->balanceAt(i, PendingBlock); - QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(i))).arg(QString::fromStdString(d.first)); + u256 b = ethereum()->balanceAt(address, PendingBlock); + QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(address))).arg(QString::fromStdString(m_context->keyManager().accountName(address))); ui->from->addItem(s); } if (old > -1 && old < ui->from->count()) diff --git a/eth/main.cpp b/eth/main.cpp index 382858ae7..a7d9d7c00 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -678,7 +678,7 @@ int main(int argc, char** argv) return ret; }; auto getAccountPassword = [&](Address const& a){ - return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); + return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): "); }; StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); @@ -1122,10 +1122,10 @@ int main(int argc, char** argv) { cout << "Accounts:" << endl; u256 total = 0; - for (auto const& i: keyManager.accountDetails()) + for (auto const& address: keyManager.accounts()) { - auto b = c->balanceAt(i.first); - cout << ((i.first == signingKey) ? "SIGNING " : " ") << ((i.first == beneficiary) ? "COINBASE " : " ") << i.second.first << " (" << i.first << "): " << formatBalance(b) << " = " << b << " wei" << endl; + auto b = c->balanceAt(address); + cout << ((address == signingKey) ? "SIGNING " : " ") << ((address == beneficiary) ? "COINBASE " : " ") << keyManager.accountName(address) << " (" << address << "): " << formatBalance(b) << " = " << b << " wei" << endl; total += b; } cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl; diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h index d2ec13b2a..b7b10ce21 100644 --- a/ethkey/KeyAux.h +++ b/ethkey/KeyAux.h @@ -44,7 +44,7 @@ class BadArgument: public Exception {}; string getAccountPassword(KeyManager& keyManager, Address const& a) { - return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); + return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): "); } string createPassword(std::string const& _prompt) @@ -359,20 +359,18 @@ public: nonIcap.push_back(u); else { - std::pair info = wallet.accountDetails()[a]; cout << toUUID(u) << " " << a.abridged(); cout << " " << ICAP(a).encoded(); - cout << " " << info.first << endl; + cout << " " << wallet.accountName(a) << endl; } else bare.push_back(u); for (auto const& u: nonIcap) if (Address a = wallet.address(u)) { - std::pair info = wallet.accountDetails()[a]; cout << toUUID(u) << " " << a.abridged(); cout << " (Not ICAP) "; - cout << " " << info.first << endl; + cout << " " << wallet.accountName(a) << endl; } for (auto const& u: bare) cout << toUUID(u) << " (Bare)" << endl; diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index ddc00e09f..780e8f6f5 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -258,7 +259,7 @@ template std::set& operator+=(std::set& _a, U const& _b return _a; } -/// Insert the contents of a container into an unordered_st +/// Insert the contents of a container into an unordered_set template std::unordered_set& operator+=(std::unordered_set& _a, U const& _b) { for (auto const& i: _b) @@ -280,6 +281,12 @@ template std::set operator+(std::set _a, U const& _b) return _a += _b; } +/// Insert the contents of a container into an unordered_set +template std::unordered_set operator+(std::unordered_set _a, U const& _b) +{ + return _a += _b; +} + /// Concatenate the contents of a container onto a vector template std::vector operator+(std::vector _a, U const& _b) { diff --git a/libethcore/KeyManager.cpp b/libethcore/KeyManager.cpp index 4430a588e..602c60b4a 100644 --- a/libethcore/KeyManager.cpp +++ b/libethcore/KeyManager.cpp @@ -31,7 +31,7 @@ using namespace dev; using namespace eth; namespace fs = boost::filesystem; -KeyManager::KeyManager(std::string const& _keysFile, std::string const& _secretsPath): +KeyManager::KeyManager(string const& _keysFile, string const& _secretsPath): m_keysFile(_keysFile), m_store(_secretsPath) {} @@ -43,13 +43,13 @@ bool KeyManager::exists() const return !contents(m_keysFile + ".salt").empty() && !contents(m_keysFile).empty(); } -void KeyManager::create(std::string const& _pass) +void KeyManager::create(string const& _pass) { - m_password = asString(h256::random().asBytes()); + m_defaultPasswordDeprecated = asString(h256::random().asBytes()); write(_pass, m_keysFile); } -bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass, KDF _kdf) +bool KeyManager::recode(Address const& _address, string const& _newPass, string const& _hint, function const& _pass, KDF _kdf) { noteHint(_newPass, _hint); h128 u = uuid(_address); @@ -61,10 +61,10 @@ bool KeyManager::recode(Address const& _address, std::string const& _newPass, st return true; } -bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass, KDF _kdf) +bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, function const& _pass, KDF _kdf) { h128 u = uuid(_address); - std::string p; + string p; if (_newPass == SemanticPassword::Existing) p = getPassword(u, _pass); else if (_newPass == SemanticPassword::Master) @@ -75,41 +75,47 @@ bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std: return recode(_address, p, string(), _pass, _kdf); } -bool KeyManager::load(std::string const& _pass) +bool KeyManager::load(string const& _pass) { - try { + try + { bytes salt = contents(m_keysFile + ".salt"); bytes encKeys = contents(m_keysFile); - m_key = h128(pbkdf2(_pass, salt, 262144, 16)); - bytes bs = decryptSymNoAuth(m_key, h128(), &encKeys); + m_keysFileKey = h128(pbkdf2(_pass, salt, 262144, 16)); + bytes bs = decryptSymNoAuth(m_keysFileKey, h128(), &encKeys); RLP s(bs); - unsigned version = (unsigned)s[0]; + unsigned version = unsigned(s[0]); if (version == 1) { for (auto const& i: s[1]) { - m_keyInfo[m_addrLookup[(Address)i[0]] = (h128)i[1]] = KeyInfo((h256)i[2], (std::string)i[3]); -// cdebug << toString((Address)i[0]) << toString((h128)i[1]) << toString((h256)i[2]) << (std::string)i[3]; + h128 uuid(i[1]); + Address addr(i[0]); + m_addrLookup[addr] = uuid; + m_keyInfo[uuid] = KeyInfo(h256(i[2]), string(i[3])); +// cdebug << toString(addr) << toString(uuid) << toString((h256)i[2]) << (string)i[3]; } for (auto const& i: s[2]) - m_passwordInfo[(h256)i[0]] = (std::string)i[1]; - m_password = (string)s[3]; + m_passwordHint[h256(i[0])] = string(i[1]); + m_defaultPasswordDeprecated = string(s[3]); } // cdebug << hashPassword(m_password) << toHex(m_password); - m_cachedPasswords[hashPassword(m_password)] = m_password; + cachePassword(m_defaultPasswordDeprecated); // cdebug << hashPassword(asString(m_key.ref())) << m_key.hex(); - m_cachedPasswords[hashPassword(asString(m_key.ref()))] = asString(m_key.ref()); + cachePassword(asString(m_keysFileKey.ref())); // cdebug << hashPassword(_pass) << _pass; - m_cachedPasswords[m_master = hashPassword(_pass)] = _pass; + m_master = hashPassword(_pass); + cachePassword(_pass); return true; } - catch (...) { + catch (...) + { return false; } } -Secret KeyManager::secret(Address const& _address, function const& _pass) const +Secret KeyManager::secret(Address const& _address, function const& _pass) const { auto it = m_addrLookup.find(_address); if (it == m_addrLookup.end()) @@ -117,12 +123,12 @@ Secret KeyManager::secret(Address const& _address, function const return secret(it->second, _pass); } -Secret KeyManager::secret(h128 const& _uuid, function const& _pass) const +Secret KeyManager::secret(h128 const& _uuid, function const& _pass) const { return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); })); } -std::string KeyManager::getPassword(h128 const& _uuid, function const& _pass) const +string KeyManager::getPassword(h128 const& _uuid, function const& _pass) const { auto kit = m_keyInfo.find(_uuid); h256 ph; @@ -131,19 +137,19 @@ std::string KeyManager::getPassword(h128 const& _uuid, function c return getPassword(ph, _pass); } -std::string KeyManager::getPassword(h256 const& _passHash, function const& _pass) const +string KeyManager::getPassword(h256 const& _passHash, function const& _pass) const { auto it = m_cachedPasswords.find(_passHash); if (it != m_cachedPasswords.end()) return it->second; - for (unsigned i = 0; i< 10; ++i) + for (unsigned i = 0; i < 10; ++i) { - std::string p = _pass(); + string p = _pass(); if (p.empty()) break; - if (hashPassword(p) == _passHash || _passHash == UnknownPassword) + if (_passHash == UnknownPassword || hashPassword(p) == _passHash) { - m_cachedPasswords[hashPassword(p)] = p; + cachePassword(p); return p; } } @@ -166,20 +172,20 @@ Address KeyManager::address(h128 const& _uuid) const return Address(); } -h128 KeyManager::import(Secret const& _s, string const& _info, std::string const& _pass, string const& _passInfo) +h128 KeyManager::import(Secret const& _s, string const& _accountName, string const& _pass, string const& _passwordHint) { Address addr = KeyPair(_s).address(); auto passHash = hashPassword(_pass); - m_cachedPasswords[passHash] = _pass; - m_passwordInfo[passHash] = _passInfo; + cachePassword(_pass); + m_passwordHint[passHash] = _passwordHint; auto uuid = m_store.importSecret(_s.asBytes(), _pass); - m_keyInfo[uuid] = KeyInfo{passHash, _info}; + m_keyInfo[uuid] = KeyInfo{passHash, _accountName}; m_addrLookup[addr] = uuid; write(m_keysFile); return uuid; } -void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo) +void KeyManager::importExisting(h128 const& _uuid, string const& _info, string const& _pass, string const& _passwordHint) { bytes key = m_store.secret(_uuid, [&](){ return _pass; }); if (key.empty()) @@ -187,17 +193,17 @@ void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, std Address a = KeyPair(Secret(key)).address(); auto passHash = hashPassword(_pass); if (!m_cachedPasswords.count(passHash)) - m_cachedPasswords[passHash] = _pass; - importExisting(_uuid, _info, a, passHash, _passInfo); + cachePassword(_pass); + importExisting(_uuid, _info, a, passHash, _passwordHint); } -void KeyManager::importExisting(h128 const& _uuid, std::string const& _info, Address const& _address, h256 const& _passHash, std::string const& _passInfo) +void KeyManager::importExisting(h128 const& _uuid, string const& _accountName, Address const& _address, h256 const& _passHash, string const& _passwordHint) { - if (!m_passwordInfo.count(_passHash)) - m_passwordInfo[_passHash] = _passInfo; + if (!m_passwordHint.count(_passHash)) + m_passwordHint[_passHash] = _passwordHint; m_addrLookup[_address] = _uuid; m_keyInfo[_uuid].passHash = _passHash; - m_keyInfo[_uuid].info = _info; + m_keyInfo[_uuid].accountName = _accountName; write(m_keysFile); } @@ -209,67 +215,92 @@ void KeyManager::kill(Address const& _a) m_store.kill(id); } -AddressHash KeyManager::accounts() const +Addresses KeyManager::accounts() const { - AddressHash ret; + Addresses ret; + ret.reserve(m_addrLookup.size()); for (auto const& i: m_addrLookup) if (m_keyInfo.count(i.second) > 0) - ret.insert(i.first); + ret.push_back(i.first); return ret; } -std::unordered_map> KeyManager::accountDetails() const +bool KeyManager::hasAccount(const Address& _address) const { - std::unordered_map> ret; - for (auto const& i: m_addrLookup) - if (m_keyInfo.count(i.second) > 0) - ret[i.first] = make_pair(m_keyInfo.count(i.second) ? m_keyInfo.at(i.second).info : "", m_keyInfo.count(i.second) && m_passwordInfo.count(m_keyInfo.at(i.second).passHash) ? m_passwordInfo.at(m_keyInfo.at(i.second).passHash) : ""); - return ret; + return m_addrLookup.count(_address) && m_keyInfo.count(m_addrLookup.at(_address)); +} + +string const& KeyManager::accountName(Address const& _address) const +{ + try + { + return m_keyInfo.at(m_addrLookup.at(_address)).accountName; + } + catch (...) + { + return EmptyString; + } +} + +string const& KeyManager::passwordHint(Address const& _address) const +{ + try + { + return m_passwordHint.at(m_keyInfo.at(m_addrLookup.at(_address)).passHash); + } + catch (...) + { + return EmptyString; + } } -h256 KeyManager::hashPassword(std::string const& _pass) const +h256 KeyManager::hashPassword(string const& _pass) const { // TODO SECURITY: store this a bit more securely; Scrypt perhaps? - return h256(pbkdf2(_pass, asBytes(m_password), 262144, 32)); + return h256(pbkdf2(_pass, asBytes(m_defaultPasswordDeprecated), 262144, 32)); +} + +void KeyManager::cachePassword(string const& _password) const +{ + m_cachedPasswords[hashPassword(_password)] = _password; } -bool KeyManager::write(std::string const& _keysFile) const +bool KeyManager::write(string const& _keysFile) const { - if (!m_key) + if (!m_keysFileKey) return false; - write(m_key, _keysFile); + write(m_keysFileKey, _keysFile); return true; } -void KeyManager::write(std::string const& _pass, std::string const& _keysFile) const +void KeyManager::write(string const& _pass, string const& _keysFile) const { bytes salt = h256::random().asBytes(); writeFile(_keysFile + ".salt", salt); auto key = h128(pbkdf2(_pass, salt, 262144, 16)); - m_cachedPasswords[hashPassword(_pass)] = _pass; + cachePassword(_pass); m_master = hashPassword(_pass); write(key, _keysFile); } -void KeyManager::write(h128 const& _key, std::string const& _keysFile) const +void KeyManager::write(h128 const& _key, string const& _keysFile) const { RLPStream s(4); - s << 1; - s.appendList(m_addrLookup.size()); - for (auto const& i: m_addrLookup) - if (m_keyInfo.count(i.second)) - { - auto ki = m_keyInfo.at(i.second); - s.appendList(4) << i.first << i.second << ki.passHash << ki.info; - } - s.appendList(m_passwordInfo.size()); - for (auto const& i: m_passwordInfo) + s << 1; // version + s.appendList(accounts().size()); + for (auto const& address: accounts()) + { + h128 id = uuid(address); + auto const& ki = m_keyInfo.at(id); + s.appendList(4) << address << id << ki.passHash << ki.accountName; + } + s.appendList(m_passwordHint.size()); + for (auto const& i: m_passwordHint) s.appendList(2) << i.first << i.second; - s.append(m_password); + s.append(m_defaultPasswordDeprecated); writeFile(_keysFile, encryptSymNoAuth(_key, h128(), &s.out())); - m_key = _key; - m_cachedPasswords[hashPassword(defaultPassword())] = defaultPassword(); - + m_keysFileKey = _key; + cachePassword(defaultPassword()); } diff --git a/libethcore/KeyManager.h b/libethcore/KeyManager.h index 62263c3c5..d9bb6457c 100644 --- a/libethcore/KeyManager.h +++ b/libethcore/KeyManager.h @@ -23,8 +23,9 @@ #include #include -#include #include +#include +#include namespace dev { @@ -35,14 +36,15 @@ class PasswordUnknown: public Exception {}; struct KeyInfo { KeyInfo() = default; - KeyInfo(h256 const& _passHash, std::string const& _info): passHash(_passHash), info(_info) {} + KeyInfo(h256 const& _passHash, std::string const& _accountName): passHash(_passHash), accountName(_accountName) {} - h256 passHash; ///< Hash of the password or h256() if unknown. - std::string info; ///< Name of the key, or JSON key info if begins with '{'. + h256 passHash; ///< Hash of the password or h256() / UnknownPassword if unknown. + std::string accountName; ///< Name of the key, or JSON key info if begins with '{'. }; -static const h256 UnknownPassword; -static const auto DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); }; +static h256 const UnknownPassword; +/// Password query function that never returns a password. +static auto const DontKnowThrow = [](){ throw PasswordUnknown(); return std::string(); }; enum class SemanticPassword { @@ -53,12 +55,15 @@ enum class SemanticPassword // TODO: This one is specifically for Ethereum, but we can make it generic in due course. // TODO: hidden-partition style key-store. /** - * @brief High-level manager of keys for Ethereum. + * @brief High-level manager of password-encrypted keys for Ethereum. * Usage: * * Call exists() to check whether there is already a database. If so, get the master password from * the user and call load() with it. If not, get a new master password from the user (get them to type * it twice and keep some hint around!) and call create() with it. + * + * Uses a "key file" (and a corresponding .salt file) that contains encrypted information about the keys and + * a directory called "secrets path" that contains a file for each key. */ class KeyManager { @@ -75,25 +80,37 @@ public: void save(std::string const& _pass) const { write(_pass, m_keysFile); } void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; } - void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordInfo[hashPassword(_pass)] = _hint; } + void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordHint[hashPassword(_pass)] = _hint; } bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); } - AddressHash accounts() const; - std::unordered_map> accountDetails() const; - std::string const& hint(Address const& _a) const { try { return m_passwordInfo.at(m_keyInfo.at(m_addrLookup.at(_a)).passHash); } catch (...) { return EmptyString; } } - + /// @returns the list of account addresses. + Addresses accounts() const; + /// @returns a hashset of all account addresses. + AddressHash accountsHash() const { return AddressHash() + accounts(); } + bool hasAccount(Address const& _address) const; + /// @returns the human-readable name or json-encoded info of the account for the given address. + std::string const& accountName(Address const& _address) const; + /// @returns the password hint for the account for the given address; + std::string const& passwordHint(Address const& _address) const; + + /// @returns the uuid of the key for the address @a _a or the empty hash on error. h128 uuid(Address const& _a) const; + /// @returns the address corresponding to the key with uuid @a _uuid or the zero address on error. Address address(h128 const& _uuid) const; - h128 import(Secret const& _s, std::string const& _info, std::string const& _pass, std::string const& _passInfo); - h128 import(Secret const& _s, std::string const& _info) { return import(_s, _info, defaultPassword(), std::string()); } + h128 import(Secret const& _s, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint); + h128 import(Secret const& _s, std::string const& _accountName) { return import(_s, _accountName, defaultPassword(), std::string()); } SecretStore& store() { return m_store; } - void importExisting(h128 const& _uuid, std::string const& _info, std::string const& _pass, std::string const& _passInfo); - void importExisting(h128 const& _uuid, std::string const& _info) { importExisting(_uuid, _info, defaultPassword(), std::string()); } - void importExisting(h128 const& _uuid, std::string const& _info, Address const& _addr, h256 const& _passHash = h256(), std::string const& _passInfo = std::string()); + void importExisting(h128 const& _uuid, std::string const& _accountName, std::string const& _pass, std::string const& _passwordHint); + void importExisting(h128 const& _uuid, std::string const& _accountName) { importExisting(_uuid, _accountName, defaultPassword(), std::string()); } + void importExisting(h128 const& _uuid, std::string const& _accountName, Address const& _addr, h256 const& _passHash = h256(), std::string const& _passwordHint = std::string()); + /// @returns the secret key associated with an address provided the password query + /// function @a _pass or the zero-secret key on error. Secret secret(Address const& _address, std::function const& _pass = DontKnowThrow) const; + /// @returns the secret key associated with the uuid of a key provided the password query + /// function @a _pass or the zero-secret key on error. Secret secret(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; bool recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); @@ -110,6 +127,9 @@ private: std::string defaultPassword(std::function const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); } h256 hashPassword(std::string const& _pass) const; + /// Stores the password by its hash in the password cache. + void cachePassword(std::string const& _password) const; + // Only use if previously loaded ok. // @returns false if wasn't previously loaded ok. bool write() const { return write(m_keysFile); } @@ -118,11 +138,15 @@ private: void write(h128 const& _key, std::string const& _keysFile) const; // Ethereum keys. + + /// Mapping address -> key uuid. std::unordered_map m_addrLookup; + /// Mapping key uuid -> key info. std::unordered_map m_keyInfo; - std::unordered_map m_passwordInfo; + /// Mapping password hash -> password hint. + std::unordered_map m_passwordHint; - // Passwords that we're storing. + // Passwords that we're storing. Mapping password hash -> password. mutable std::unordered_map m_cachedPasswords; // DEPRECATED. @@ -130,10 +154,10 @@ private: // Now the default password is based off the key of the keys file directly, so this is redundant // except for the fact that people have existing keys stored with it. Leave for now until/unless // we have an upgrade strategy. - std::string m_password; + std::string m_defaultPasswordDeprecated; mutable std::string m_keysFile; - mutable h128 m_key; + mutable h128 m_keysFileKey; mutable h256 m_master; SecretStore m_store; }; diff --git a/libweb3jsonrpc/AccountHolder.cpp b/libweb3jsonrpc/AccountHolder.cpp index abd0a1adf..3250eae6b 100644 --- a/libweb3jsonrpc/AccountHolder.cpp +++ b/libweb3jsonrpc/AccountHolder.cpp @@ -103,7 +103,7 @@ void AccountHolder::clearQueue(int _id) AddressHash SimpleAccountHolder::realAccounts() const { - return m_keyManager.accounts(); + return m_keyManager.accountsHash(); } void SimpleAccountHolder::authenticate(dev::eth::TransactionSkeleton const& _t) diff --git a/libweb3jsonrpc/AccountHolder.h b/libweb3jsonrpc/AccountHolder.h index 10b036226..559f8509a 100644 --- a/libweb3jsonrpc/AccountHolder.h +++ b/libweb3jsonrpc/AccountHolder.h @@ -48,7 +48,6 @@ class AccountHolder public: explicit AccountHolder(std::function const& _client): m_client(_client) {} - // easiest to return keyManager.addresses(); virtual AddressHash realAccounts() const = 0; // use m_web3's submitTransaction // or use AccountHolder::queueTransaction(_t) to accept diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index adef51033..f0e532c5c 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -157,19 +157,19 @@ Json::Value WebThreeStubServer::admin_eth_allAccounts(std::string const& _sessio u256 total = 0; u256 pendingtotal = 0; Address beneficiary; - for (auto const& i: m_keyMan.accountDetails()) + for (auto const& address: m_keyMan.accounts()) { - auto pending = m_web3.ethereum()->balanceAt(i.first, PendingBlock); - auto latest = m_web3.ethereum()->balanceAt(i.first, LatestBlock); + auto pending = m_web3.ethereum()->balanceAt(address, PendingBlock); + auto latest = m_web3.ethereum()->balanceAt(address, LatestBlock); Json::Value a; - if (i.first == beneficiary) + if (address == beneficiary) a["beneficiary"] = true; - a["address"] = toJS(i.first); + a["address"] = toJS(address); a["balance"] = toJS(latest); a["nicebalance"] = formatBalance(latest); a["pending"] = toJS(pending); a["nicepending"] = formatBalance(pending); - ret["accounts"][i.second.first] = a; + ret["accounts"][m_keyMan.accountName(address)] = a; total += latest; pendingtotal += pending; } From b381bc6f489b76b9986161f14fea0948a67b8769 Mon Sep 17 00:00:00 2001 From: Dimitry Date: Tue, 16 Jun 2015 01:26:11 +0300 Subject: [PATCH 30/66] Script Coverage --- getcoverage.sh | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/getcoverage.sh b/getcoverage.sh index ac50bf94b..8d34d733c 100755 --- a/getcoverage.sh +++ b/getcoverage.sh @@ -10,35 +10,37 @@ then fi OUTPUT_DIR="$CPP_ETHEREUM_PATH/build/test/coverage" -TESTETH=$CPP_ETHEREUM_PATH/build #/test/CMakeFiles/testeth.dir +TESTETH=$CPP_ETHEREUM_PATH/build if which lcov >/dev/null; then if which genhtml >/dev/null; then - lcov --directory $TESTETH --zerocounters - echo Running testeth... - $CPP_ETHEREUM_PATH/build/test/testeth -t --all - $CPP_ETHEREUM_PATH/build/test/testeth -t StateTests --jit --all - $CPP_ETHEREUM_PATH/build/test/testeth -t VMTests --jit --all - echo Prepearing coverage info... + echo Cleaning previous report... + if [ -d "$OUTPUT_DIR" ]; then + rm -r $OUTPUT_DIR + fi + mkdir $OUTPUT_DIR + lcov --directory $TESTETH --zerocounters + lcov --capture --initial --directory $TESTETH --output-file $OUTPUT_DIR/coverage_base.info + + echo Running testeth... + $CPP_ETHEREUM_PATH/build/test/testeth --all + $CPP_ETHEREUM_PATH/build/test/testeth -t StateTests --jit --all + $CPP_ETHEREUM_PATH/build/test/testeth -t VMTests --jit --all + + echo Prepearing coverage info... + lcov --capture --directory $TESTETH --output-file $OUTPUT_DIR/coverage_test.info + lcov --add-tracefile $OUTPUT_DIR/coverage_base.info --add-tracefile $OUTPUT_DIR/coverage_test.info --output-file $OUTPUT_DIR/coverage_all.info + lcov --extract $OUTPUT_DIR/coverage_all.info *cpp-ethereum/* --output-file $OUTPUT_DIR/coverage_export.info + genhtml $OUTPUT_DIR/coverage_export.info --output-directory $OUTPUT_DIR/testeth else - echo genhtml not found - exit; + echo genhtml not found + exit; fi else echo lcov not found exit; fi -if [ -d "$OUTPUT_DIR" ]; then - echo Cleaning previous report... - rm -r $OUTPUT_DIR -fi - -mkdir $OUTPUT_DIR -lcov --capture --directory $TESTETH --output-file $OUTPUT_DIR/full_coverage.info -lcov --extract $OUTPUT_DIR/full_coverage.info *cpp-ethereum/* --output-file $OUTPUT_DIR/testeth_coverage.info -genhtml $OUTPUT_DIR/testeth_coverage.info --output-directory $OUTPUT_DIR/testeth - echo "Coverage info should be located at: $OUTPUT_DIR/testeth" echo "Opening index..." From 623a24ff9903f132ac218f1dd97f8659041f44b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 16 Jun 2015 10:38:10 +0200 Subject: [PATCH 31/66] Set code hash in FakeExtVM. --- test/libevm/vm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/libevm/vm.cpp b/test/libevm/vm.cpp index 5bbab2e1c..ea9339f05 100644 --- a/test/libevm/vm.cpp +++ b/test/libevm/vm.cpp @@ -323,6 +323,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) fev.thisTxCode = get<3>(fev.addresses.at(fev.myAddress)); fev.code = fev.thisTxCode; } + fev.codeHash = sha3(fev.code); bytes output; bool vmExceptionOccured = false; From bab25a2bd736f8e348fce72eb671e8285e526771 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 16 Jun 2015 11:28:35 +0200 Subject: [PATCH 32/66] Style. --- libsolidity/CompilerUtils.cpp | 36 ++++++++++++++++-------------- libsolidity/ExpressionCompiler.cpp | 20 ++++++++++++----- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index 6110fdf71..349877a29 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -110,11 +110,11 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound if (type.location() == ReferenceType::Location::CallData) { // stack: target source_offset source_len - m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5 + m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5; // stack: target source_offset source_len source_len source_offset target - << eth::Instruction::CALLDATACOPY - << eth::Instruction::DUP3 << eth::Instruction::ADD - << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP; + m_context << eth::Instruction::CALLDATACOPY; + m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; + m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP; } else if (type.location() == ReferenceType::Location::Memory) { @@ -200,16 +200,16 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound // stack here: memory_end_offset storage_data_offset memory_offset eth::AssemblyItem loopStart = m_context.newTag(); - m_context << loopStart - // load and store - << eth::Instruction::DUP2 << eth::Instruction::SLOAD - << eth::Instruction::DUP2 << eth::Instruction::MSTORE - // increment storage_data_offset by 1 - << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD - // increment memory offset by 32 - << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD - // check for loop condition - << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT; + m_context << loopStart; + // load and store + m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD; + m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE; + // increment storage_data_offset by 1 + m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD; + // increment memory offset by 32 + m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD; + // check for loop condition + m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT; m_context.appendConditionalJumpTo(loopStart); // stack here: memory_end_offset storage_data_offset memory_offset if (_padToWordBoundaries) @@ -368,9 +368,11 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp if (targetType.getNumBytes() == 0) m_context << eth::Instruction::DUP1 << eth::Instruction::XOR; else - m_context << (u256(1) << (256 - targetType.getNumBytes() * 8)) - << eth::Instruction::DUP1 << eth::Instruction::SWAP2 - << eth::Instruction::DIV << eth::Instruction::MUL; + { + m_context << (u256(1) << (256 - targetType.getNumBytes() * 8)); + m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2; + m_context << eth::Instruction::DIV << eth::Instruction::MUL; + } } } } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index d5ffd35b4..12274c7ab 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -436,8 +436,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _functionCall.getExpression().accept(*this); m_context << u256(0); // do not send gas (there still is the stipend) arguments.front()->accept(*this); - utils().convertType(*arguments.front()->getType(), - *function.getParameterTypes().front(), true); + utils().convertType( + *arguments.front()->getType(), + *function.getParameterTypes().front(), true + ); appendExternalFunctionCall( FunctionType( TypePointers{}, @@ -612,13 +614,19 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case Type::Category::Integer: if (member == "balance") { - utils().convertType(*_memberAccess.getExpression().getType(), - IntegerType(0, IntegerType::Modifier::Address), true); + utils().convertType( + *_memberAccess.getExpression().getType(), + IntegerType(0, IntegerType::Modifier::Address), + true + ); m_context << eth::Instruction::BALANCE; } else if ((set{"send", "call", "callcode"}).count(member)) - utils().convertType(*_memberAccess.getExpression().getType(), - IntegerType(0, IntegerType::Modifier::Address), true); + utils().convertType( + *_memberAccess.getExpression().getType(), + IntegerType(0, IntegerType::Modifier::Address), + true + ); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); break; From e23d6f68d7d8f334ddd03852568e9a5b6b71f1ef Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 16 Jun 2015 11:33:57 +0200 Subject: [PATCH 33/66] add ability to have invalid Pow in BlockChaintests --- test/libethereum/blockchain.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/test/libethereum/blockchain.cpp b/test/libethereum/blockchain.cpp index 2c4a0b498..50e10bb8a 100644 --- a/test/libethereum/blockchain.cpp +++ b/test/libethereum/blockchain.cpp @@ -581,6 +581,12 @@ mArray importUncles(mObject const& _blObj, vector& _vBiUncles, vector } updatePoW(uncleBlockFromFields); + if (overwrite == "nonce" || overwrite == "mixHash") + { + uncleBlockFromFields.nonce = overwrite == "nonce" ? Nonce(uncleHeaderObj["nonce"].get_str()) : uncleBlockFromFields.nonce; + uncleBlockFromFields.mixHash = overwrite == "mixHash" ? h256(uncleHeaderObj["mixHash"].get_str()) : uncleBlockFromFields.mixHash; + } + writeBlockHeaderToJson(uncleHeaderObj, uncleBlockFromFields); aUncleList.push_back(uncleHeaderObj); @@ -677,16 +683,18 @@ void overwriteBlockHeader(BlockInfo& _header, mObject& _blObj) tmp.timestamp = toInt(ho["timestamp"]); if (ho.count("extraData")) tmp.extraData = importByteArray(ho["extraData"].get_str()); - if (ho.count("mixHash")) - tmp.mixHash = h256(ho["mixHash"].get_str()); - tmp.noteDirty(); // find new valid nonce if (tmp != _header) - { mine(tmp); - _header = tmp; - } + + if (ho.count("mixHash")) + tmp.mixHash = h256(ho["mixHash"].get_str()); + if (ho.count("nonce")) + tmp.nonce = Nonce(ho["nonce"].get_str()); + + tmp.noteDirty(); + _header = tmp; } else { From a6bb3d4302f694a6821743d24b0a480c2556115a Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 16 Jun 2015 11:34:31 +0200 Subject: [PATCH 34/66] add invalid PoW in blockheader tests --- .../bcInvalidHeaderTestFiller.json | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/test/libethereum/BlockTestsFiller/bcInvalidHeaderTestFiller.json b/test/libethereum/BlockTestsFiller/bcInvalidHeaderTestFiller.json index a800f86bf..83e541da1 100644 --- a/test/libethereum/BlockTestsFiller/bcInvalidHeaderTestFiller.json +++ b/test/libethereum/BlockTestsFiller/bcInvalidHeaderTestFiller.json @@ -176,6 +176,124 @@ ] }, + "wrongMixHash" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "100" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000000000", + "nonce" : "0", + "code" : "", + "storage": {} + }, + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "100", + "nonce" : "0", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG1 0 32 0) }", + "storage": {} + } + }, + "blocks" : [ + { + "blockHeader" : { + "mixHash" : "0xbad81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" + }, + "transactions" : [ + { + "data" : "", + "gasLimit" : "50000", + "gasPrice" : "10", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "5000" + } + ], + "uncleHeaders" : [ + ] + } + ] + }, + + "wrongNonce" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "100" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000000000", + "nonce" : "0", + "code" : "", + "storage": {} + }, + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "100", + "nonce" : "0", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG1 0 32 0) }", + "storage": {} + } + }, + "blocks" : [ + { + "blockHeader" : { + "nonce" : "0x0102030405060708" + }, + "transactions" : [ + { + "data" : "", + "gasLimit" : "50000", + "gasPrice" : "10", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "5000" + } + ], + "uncleHeaders" : [ + ] + } + ] + }, + "wrongDifficulty" : { "genesisBlockHeader" : { "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", From a3823da2babeb618217d9edc4022c270161953a8 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 16 Jun 2015 11:35:03 +0200 Subject: [PATCH 35/66] add invalid PoW in uncle blockheader tests --- .../bcUncleHeaderValiditiyFiller.json | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) diff --git a/test/libethereum/BlockTestsFiller/bcUncleHeaderValiditiyFiller.json b/test/libethereum/BlockTestsFiller/bcUncleHeaderValiditiyFiller.json index ee36e230d..498486fdf 100644 --- a/test/libethereum/BlockTestsFiller/bcUncleHeaderValiditiyFiller.json +++ b/test/libethereum/BlockTestsFiller/bcUncleHeaderValiditiyFiller.json @@ -418,6 +418,216 @@ ] }, + "wrongMixHash" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "231072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "30" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "3" + }, + "acde5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1312500000000000000" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + { + "overwriteAndRedoPoW" : "mixHash", + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b", + "difficulty" : "230847", + "extraData" : "0x", + "gasLimit" : "3141593", + "gasUsed" : "150000", + "hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04", + "mixHash" : "bad7f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770", + "nonce" : "18a524c1790fa83b", + "number" : "2", + "parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae", + "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd", + "timestamp" : "142813170", + "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + } + ] + }, + + "nonceWrong" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "231072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "30" + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "nonce" : "3" + }, + "acde5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1312500000000000000" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + { + "overwriteAndRedoPoW" : "nonce", + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "acde5374fce5edbc8e2a8697c15331677e6ebf0b", + "difficulty" : "230847", + "extraData" : "0x", + "gasLimit" : "3141593", + "gasUsed" : "150000", + "hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04", + "mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770", + "nonce" : "bad524c1790fa83b", + "number" : "2", + "parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae", + "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd", + "timestamp" : "142813170", + "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + } + ] + }, + "gasLimitTooHigh" : { "genesisBlockHeader" : { "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", From 4bb4a8aa609e952db39cc833963b5958582900b3 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Jun 2015 12:05:15 +0200 Subject: [PATCH 36/66] Add cmake command for OpenCL kernel code The OpenCL kernel gets parsed and copied into a byte array accessible by a specific header during the cmake configuration step. We are now adding a special command "make clbin2h" which would generate this header byte array on demand --- libethash-cl/CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libethash-cl/CMakeLists.txt b/libethash-cl/CMakeLists.txt index fdc2dad07..6533bf794 100644 --- a/libethash-cl/CMakeLists.txt +++ b/libethash-cl/CMakeLists.txt @@ -5,6 +5,16 @@ bin2h(SOURCE_FILE ethash_cl_miner_kernel.cl VARIABLE_NAME ethash_cl_miner_kernel HEADER_FILE ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h) +# Add a custom command so that the user can regenerate the header file containing the kernel +# code's bytearray by running "make clbin2h" + add_custom_target(clbin2h) + add_custom_command(TARGET clbin2h + PRE_BUILD + COMMAND ${CMAKE_COMMAND} -DBIN2H_SOURCE_FILE="${CMAKE_CURRENT_SOURCE_DIR}/ethash_cl_miner_kernel.cl" + -DBIN2H_VARIABLE_NAME=ethash_cl_miner_kernel + -DBIN2H_HEADER_FILE="${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h" + -P "${CMAKE_CURRENT_SOURCE_DIR}/bin2h.cmake") + aux_source_directory(. SRC_LIST) file(GLOB HEADERS "*.h") From 1bc547dc480f573d9d78b3700697143b4dc42113 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 16 Jun 2015 12:10:48 +0200 Subject: [PATCH 37/66] check nonce --- libethereum/BlockChain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index bd6996a45..195f65f1d 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -1072,7 +1072,7 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, function Date: Tue, 16 Jun 2015 13:29:16 +0200 Subject: [PATCH 38/66] quick_difficulty_check difficulty argument clarification The argument passed into ethash_quick_check_difficulty is not the actual difficulty but it's 2^256 divided by the difficulty. --- internal.c | 4 ++-- internal.h | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/internal.c b/internal.c index b28a59e43..338aa5ecd 100644 --- a/internal.c +++ b/internal.c @@ -284,13 +284,13 @@ bool ethash_quick_check_difficulty( ethash_h256_t const* header_hash, uint64_t const nonce, ethash_h256_t const* mix_hash, - ethash_h256_t const* difficulty + ethash_h256_t const* boundary ) { ethash_h256_t return_hash; ethash_quick_hash(&return_hash, header_hash, nonce, mix_hash); - return ethash_check_difficulty(&return_hash, difficulty); + return ethash_check_difficulty(&return_hash, boundary); } ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed) diff --git a/internal.h b/internal.h index 4e2b695ac..26c395ad6 100644 --- a/internal.h +++ b/internal.h @@ -46,27 +46,36 @@ static inline void ethash_h256_reset(ethash_h256_t* hash) memset(hash, 0, 32); } -// Returns if hash is less than or equal to difficulty +// Returns if hash is less than or equal to boundary (2^256/difficulty) static inline bool ethash_check_difficulty( ethash_h256_t const* hash, - ethash_h256_t const* difficulty + ethash_h256_t const* boundary ) { - // Difficulty is big endian + // Boundary is big endian for (int i = 0; i < 32; i++) { - if (ethash_h256_get(hash, i) == ethash_h256_get(difficulty, i)) { + if (ethash_h256_get(hash, i) == ethash_h256_get(boundary, i)) { continue; } - return ethash_h256_get(hash, i) < ethash_h256_get(difficulty, i); + return ethash_h256_get(hash, i) < ethash_h256_get(boundary, i); } return true; } +/** + * Difficulty quick check for POW preverification + * + * @param header_hash The hash of the header + * @param nonce The block's nonce + * @param mix_hash The mix digest hash + * @param boundary The boundary is defined as (2^256 / difficulty) + * @return true for succesful pre-verification and false otherwise + */ bool ethash_quick_check_difficulty( ethash_h256_t const* header_hash, uint64_t const nonce, ethash_h256_t const* mix_hash, - ethash_h256_t const* difficulty + ethash_h256_t const* boundary ); struct ethash_light { From e486ce32f16999a842af42627dcec2c076fc1820 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Tue, 16 Jun 2015 13:33:36 +0200 Subject: [PATCH 39/66] add diff is zero test --- .../bcInvalidHeaderTestFiller.json | 59 +++++++++++++++++++ test/libethereum/blockchain.cpp | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/test/libethereum/BlockTestsFiller/bcInvalidHeaderTestFiller.json b/test/libethereum/BlockTestsFiller/bcInvalidHeaderTestFiller.json index 83e541da1..3084db0d7 100644 --- a/test/libethereum/BlockTestsFiller/bcInvalidHeaderTestFiller.json +++ b/test/libethereum/BlockTestsFiller/bcInvalidHeaderTestFiller.json @@ -353,6 +353,65 @@ ] }, + "DifficultyIsZero" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "expect" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "100" + } + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000000000", + "nonce" : "0", + "code" : "", + "storage": {} + }, + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "100", + "nonce" : "0", + "code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG1 0 32 0) }", + "storage": {} + } + }, + "blocks" : [ + { + "blockHeader" : { + "difficulty" : "0" + }, + "transactions" : [ + { + "data" : "", + "gasLimit" : "50000", + "gasPrice" : "10", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "5000" + } + ], + "uncleHeaders" : [ + ] + } + ] + }, + "DifferentExtraData1025" : { "genesisBlockHeader" : { "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", diff --git a/test/libethereum/blockchain.cpp b/test/libethereum/blockchain.cpp index 50e10bb8a..2dc30c6ba 100644 --- a/test/libethereum/blockchain.cpp +++ b/test/libethereum/blockchain.cpp @@ -685,7 +685,7 @@ void overwriteBlockHeader(BlockInfo& _header, mObject& _blObj) tmp.extraData = importByteArray(ho["extraData"].get_str()); // find new valid nonce - if (tmp != _header) + if (tmp != _header && tmp.difficulty) mine(tmp); if (ho.count("mixHash")) From be2ed88d2f6bc265906fee8a4f9589d92bb7d686 Mon Sep 17 00:00:00 2001 From: Dimitry Date: Tue, 16 Jun 2015 15:25:29 +0300 Subject: [PATCH 40/66] Coverage script --- getcoverage.sh | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/getcoverage.sh b/getcoverage.sh index 8d34d733c..196629170 100755 --- a/getcoverage.sh +++ b/getcoverage.sh @@ -1,17 +1,33 @@ #!/bin/bash CPP_ETHEREUM_PATH=$(pwd) - -which $CPP_ETHEREUM_PATH/build/test/testeth >/dev/null 2>&1 +BUILD_DIR=$CPP_ETHEREUM_PATH/build +TEST_MODE="" + +for i in "$@" +do +case $i in + -builddir) + shift + ((i++)) + BUILD_DIR=${!i} + shift + ;; + --all) + TEST_MODE="--all" + shift + ;; +esac +done + +which $BUILD_DIR/test/testeth >/dev/null 2>&1 if [ $? != 0 ] then echo "You need to compile and build ethereum with cmake -DPROFILING option to the build dir!" exit; fi -OUTPUT_DIR="$CPP_ETHEREUM_PATH/build/test/coverage" -TESTETH=$CPP_ETHEREUM_PATH/build - +OUTPUT_DIR=$BUILD_DIR/test/coverage if which lcov >/dev/null; then if which genhtml >/dev/null; then echo Cleaning previous report... @@ -19,16 +35,16 @@ if which lcov >/dev/null; then rm -r $OUTPUT_DIR fi mkdir $OUTPUT_DIR - lcov --directory $TESTETH --zerocounters - lcov --capture --initial --directory $TESTETH --output-file $OUTPUT_DIR/coverage_base.info + lcov --directory $BUILD_DIR --zerocounters + lcov --capture --initial --directory $BUILD_DIR --output-file $OUTPUT_DIR/coverage_base.info echo Running testeth... - $CPP_ETHEREUM_PATH/build/test/testeth --all - $CPP_ETHEREUM_PATH/build/test/testeth -t StateTests --jit --all - $CPP_ETHEREUM_PATH/build/test/testeth -t VMTests --jit --all + $CPP_ETHEREUM_PATH/build/test/testeth $TEST_MODE + $CPP_ETHEREUM_PATH/build/test/testeth -t StateTests --jit $TEST_MODE + $CPP_ETHEREUM_PATH/build/test/testeth -t VMTests --jit $TEST_MODE echo Prepearing coverage info... - lcov --capture --directory $TESTETH --output-file $OUTPUT_DIR/coverage_test.info + lcov --capture --directory $BUILD_DIR --output-file $OUTPUT_DIR/coverage_test.info lcov --add-tracefile $OUTPUT_DIR/coverage_base.info --add-tracefile $OUTPUT_DIR/coverage_test.info --output-file $OUTPUT_DIR/coverage_all.info lcov --extract $OUTPUT_DIR/coverage_all.info *cpp-ethereum/* --output-file $OUTPUT_DIR/coverage_export.info genhtml $OUTPUT_DIR/coverage_export.info --output-directory $OUTPUT_DIR/testeth From 93003a12242d56d8025785d1c213151e9e755e62 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 16 Jun 2015 14:58:03 +0200 Subject: [PATCH 41/66] Some changes in libdevcore. --- ethkey/KeyAux.h | 12 +++---- libdevcore/Common.h | 21 ++++++----- libdevcore/CommonData.cpp | 4 +-- libdevcore/CommonData.h | 5 --- libdevcore/CommonIO.cpp | 58 +++++++++---------------------- libdevcore/CommonIO.h | 6 +++- liblll/CodeFragment.cpp | 2 +- libtestutils/Common.cpp | 2 +- lllc/main.cpp | 2 +- solc/CommandLineInterface.cpp | 2 +- test/TestHelper.cpp | 2 +- test/libdevcore/rlp.cpp | 2 +- test/libdevcrypto/SecretStore.cpp | 2 +- test/libdevcrypto/hexPrefix.cpp | 2 +- test/libdevcrypto/trie.cpp | 6 ++-- test/libethcore/dagger.cpp | 2 +- test/libethereum/genesis.cpp | 2 +- 17 files changed, 55 insertions(+), 77 deletions(-) diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h index d2ec13b2a..e4310df30 100644 --- a/ethkey/KeyAux.h +++ b/ethkey/KeyAux.h @@ -221,26 +221,26 @@ public: break; } case OperationMode::ImportBare: - for (string const& i: m_inputs) + for (string const& input: m_inputs) { h128 u; bytes b; - b = fromHex(i); + b = fromHex(input); if (b.size() != 32) { - std::string s = contentsString(i); + std::string s = contentsString(input); b = fromHex(s); if (b.size() != 32) - u = store.importKey(i); + u = store.importKey(input); } if (!u && b.size() == 32) u = store.importSecret(b, lockPassword(toAddress(Secret(b)).abridged())); if (!u) { - cerr << "Cannot import " << i << " not a file or secret." << endl; + cerr << "Cannot import " << input << " not a file or secret." << endl; continue; } - cout << "Successfully imported " << i << " as " << toUUID(u); + cout << "Successfully imported " << input << " as " << toUUID(u); } break; case OperationMode::InspectBare: diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 1ee83c794..c6ed25223 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -113,25 +113,27 @@ static const u256 Invalid256 = ~(u256)0; static const bytes NullBytes; static const std::map EmptyMapU256U256; +/// Interprets @a _u as a two's complement signed number and returns the resulting s256. inline s256 u2s(u256 _u) { - static const bigint c_end = (bigint)1 << 256; - static const u256 c_send = (u256)1 << 255; - if (_u < c_send) - return (s256)_u; - else - return (s256)-(c_end - _u); + static const bigint c_end = bigint(1) << 256; + if (boost::multiprecision::bit_test(_u, 255)) + return s256(-(c_end - _u)); + else + return s256(_u); } +/// @returns the two's complement signed representation of the signed number _u. inline u256 s2u(s256 _u) { - static const bigint c_end = (bigint)1 << 256; + static const bigint c_end = bigint(1) << 256; if (_u >= 0) - return (u256)_u; + return u256(_u); else - return (u256)(c_end + _u); + return u256(c_end + _u); } +/// @returns the smallest n >= 0 such that (1 << n) >= _x inline unsigned int toLog2(u256 _x) { unsigned ret; @@ -139,6 +141,7 @@ inline unsigned int toLog2(u256 _x) return ret; } +/// @returns the absolute distance between _a and _b. template inline N diff(N const& _a, N const& _b) { diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index 2d6333f26..ef178965f 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -93,7 +93,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) if (h != -1) ret.push_back(h); else if (_throw == WhenError::Throw) - throw BadHexCharacter(); + BOOST_THROW_EXCEPTION(BadHexCharacter()); else return bytes(); } @@ -104,7 +104,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) if (h != -1 && l != -1) ret.push_back((byte)(h * 16 + l)); else if (_throw == WhenError::Throw) - throw BadHexCharacter(); + BOOST_THROW_EXCEPTION(BadHexCharacter()); else return bytes(); } diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index ddc00e09f..f4d3cf65d 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -68,11 +68,6 @@ int fromHex(char _i, WhenError _throw); /// If _throw = ThrowType::DontThrow, it replaces bad hex characters with 0's, otherwise it will throw an exception. bytes fromHex(std::string const& _s, WhenError _throw = WhenError::DontThrow); -#if 0 -std::string toBase58(bytesConstRef _data); -bytes fromBase58(std::string const& _s); -#endif - /// Converts byte array to a string containing the same (binary) data. Unless /// the byte array happens to contain ASCII data, this won't be printable. inline std::string asString(bytes const& _b) diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 9538ca55f..03b22bfc5 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -64,59 +64,35 @@ string dev::memDump(bytes const& _bytes, unsigned _width, bool _html) return ret.str(); } -// Don't forget to delete[] later. -bytesRef dev::contentsNew(std::string const& _file, bytesRef _dest) +template +inline _T contentsGeneric(std::string const& _file) { + _T ret; + size_t const c_elementSize = sizeof(typename _T::value_type); std::ifstream is(_file, std::ifstream::binary); if (!is) - return bytesRef(); + return ret; + // get length of file: - is.seekg (0, is.end); + is.seekg(0, is.end); streamoff length = is.tellg(); - if (length == 0) // return early, MSVC does not like reading 0 bytes - return bytesRef(); - if (!_dest.empty() && _dest.size() != (unsigned)length) - return bytesRef(); - is.seekg (0, is.beg); - bytesRef ret = _dest.empty() ? bytesRef(new byte[length], length) : _dest; - is.read((char*)ret.data(), length); - is.close(); + if (length == 0) + return ret; // do not read empty file (MSVC does not like it) + is.seekg(0, is.beg); + + ret.resize((length + c_elementSize - 1) / c_elementSize); + is.read(const_cast(reinterpret_cast(ret.data())), length); return ret; } -bytes dev::contents(std::string const& _file) +bytes dev::contents(string const& _file) { - std::ifstream is(_file, std::ifstream::binary); - if (!is) - return bytes(); - // get length of file: - is.seekg (0, is.end); - streamoff length = is.tellg(); - if (length == 0) // return early, MSVC does not like reading 0 bytes - return bytes(); - is.seekg (0, is.beg); - bytes ret(length); - is.read((char*)ret.data(), length); - is.close(); - return ret; + return contentsGeneric(_file); } -string dev::contentsString(std::string const& _file) +string dev::contentsString(string const& _file) { - std::ifstream is(_file, std::ifstream::binary); - if (!is) - return string(); - // get length of file: - is.seekg (0, is.end); - streamoff length = is.tellg(); - if (length == 0) // return early, MSVC does not like reading 0 bytes - return string(); - is.seekg (0, is.beg); - string ret; - ret.resize(length); - is.read((char*)ret.data(), length); - is.close(); - return ret; + return contentsGeneric(_file); } void dev::writeFile(std::string const& _file, bytesConstRef _data) diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 46a8b80bc..c8aa830ff 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -42,10 +42,14 @@ namespace dev { +/// Requests the user to enter a password on the console. std::string getPassword(std::string const& _prompt); -/// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes. +/// Retrieve and returns the contents of the given file. +/// If the file doesn't exist or isn't readable, returns an empty container / bytes. bytes contents(std::string const& _file); +/// Retrieve and returns the contents of the given file as a std::string. +/// If the file doesn't exist or isn't readable, returns an empty container / bytes. std::string contentsString(std::string const& _file); /// Retrieve and returns the allocated contents of the given file; if @_dest is given, don't allocate, use it directly. /// If the file doesn't exist or isn't readable, returns bytesRef(). Don't forget to delete [] the returned value's data when finished. diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index 1e7766434..b50e316d3 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -196,7 +196,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { if (_t.size() != 2) error(); - m_asm.append(CodeFragment::compile(asString(contents(firstAsString())), _s).m_asm); + m_asm.append(CodeFragment::compile(contentsString(firstAsString()), _s).m_asm); } else if (us == "SET") { diff --git a/libtestutils/Common.cpp b/libtestutils/Common.cpp index cff21d464..8f23685c1 100644 --- a/libtestutils/Common.cpp +++ b/libtestutils/Common.cpp @@ -59,7 +59,7 @@ Json::Value dev::test::loadJsonFromFile(std::string const& _path) { Json::Reader reader; Json::Value result; - string s = asString(dev::contents(_path)); + string s = dev::contentsString(_path); if (!s.length()) ctest << "Contents of " + _path + " is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"; else diff --git a/lllc/main.cpp b/lllc/main.cpp index 1a44ee950..e1a61f49a 100644 --- a/lllc/main.cpp +++ b/lllc/main.cpp @@ -95,7 +95,7 @@ int main(int argc, char** argv) } } else - src = asString(contents(infile)); + src = contentsString(infile); vector errors; if (src.empty()) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e65c602ab..ec2a90033 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -401,7 +401,7 @@ bool CommandLineInterface::processInput() continue; } - m_sourceCodes[infile] = asString(dev::contents(infile)); + m_sourceCodes[infile] = dev::contentsString(infile); } m_compiler.reset(new CompilerStack(m_args["add-std"].as())); diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 733ccb6d0..bca0528ff 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -598,7 +598,7 @@ void userDefinedTest(std::function doTests) { cnote << "Testing user defined test: " << filename; json_spirit::mValue v; - string s = asString(contents(filename)); + string s = contentsString(filename); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. "); json_spirit::read_string(s, v); json_spirit::mObject oSingleTest; diff --git a/test/libdevcore/rlp.cpp b/test/libdevcore/rlp.cpp index 41362d569..86780cfa6 100644 --- a/test/libdevcore/rlp.cpp +++ b/test/libdevcore/rlp.cpp @@ -67,7 +67,7 @@ namespace dev string testPath = getTestPath(); testPath += "/BasicTests"; - string s = asString(contents(testPath + "/rlptest.json")); + string s = contentsString(testPath + "/rlptest.json"); BOOST_REQUIRE_MESSAGE( s.length() > 0, "Contents of 'rlptest.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); diff --git a/test/libdevcrypto/SecretStore.cpp b/test/libdevcrypto/SecretStore.cpp index 1f927db5d..43c201edc 100644 --- a/test/libdevcrypto/SecretStore.cpp +++ b/test/libdevcrypto/SecretStore.cpp @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(basic_tests) cnote << "Testing Key Store..."; js::mValue v; - string s = asString(contents(testPath + "/basic_tests.json")); + string s = contentsString(testPath + "/basic_tests.json"); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'KeyStoreTests/basic_tests.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); for (auto& i: v.get_obj()) diff --git a/test/libdevcrypto/hexPrefix.cpp b/test/libdevcrypto/hexPrefix.cpp index 53e0d3dbd..4d89ec594 100644 --- a/test/libdevcrypto/hexPrefix.cpp +++ b/test/libdevcrypto/hexPrefix.cpp @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(hexPrefix_test) cnote << "Testing Hex-Prefix-Encode..."; js::mValue v; - string s = asString(contents(testPath + "/hexencodetest.json")); + string s = contentsString(testPath + "/hexencodetest.json"); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content from 'hexencodetest.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); for (auto& i: v.get_obj()) diff --git a/test/libdevcrypto/trie.cpp b/test/libdevcrypto/trie.cpp index b5d8662dc..daa3cc181 100644 --- a/test/libdevcrypto/trie.cpp +++ b/test/libdevcrypto/trie.cpp @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(hex_encoded_securetrie_test) cnote << "Testing Secure Trie..."; js::mValue v; - string s = asString(contents(testPath + "/hex_encoded_securetrie_test.json")); + string s = contentsString(testPath + "/hex_encoded_securetrie_test.json"); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'hex_encoded_securetrie_test.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); for (auto& i: v.get_obj()) @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(trie_test_anyorder) cnote << "Testing Trie..."; js::mValue v; - string s = asString(contents(testPath + "/trieanyorder.json")); + string s = contentsString(testPath + "/trieanyorder.json"); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trieanyorder.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); for (auto& i: v.get_obj()) @@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(trie_tests_ordered) cnote << "Testing Trie..."; js::mValue v; - string s = asString(contents(testPath + "/trietest.json")); + string s = contentsString(testPath + "/trietest.json"); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trietest.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); diff --git a/test/libethcore/dagger.cpp b/test/libethcore/dagger.cpp index 59770373d..1743882e5 100644 --- a/test/libethcore/dagger.cpp +++ b/test/libethcore/dagger.cpp @@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(basic_test) cnote << "Testing Proof of Work..."; js::mValue v; - string s = asString(contents(testPath + "/ethash_tests.json")); + string s = contentsString(testPath + "/ethash_tests.json"); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'ethash_tests.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); for (auto& i: v.get_obj()) diff --git a/test/libethereum/genesis.cpp b/test/libethereum/genesis.cpp index 4633a0617..02ff4517a 100644 --- a/test/libethereum/genesis.cpp +++ b/test/libethereum/genesis.cpp @@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(genesis_tests) cnote << "Testing Genesis block..."; js::mValue v; - string s = asString(contents(testPath + "/genesishashestest.json")); + string s = contentsString(testPath + "/genesishashestest.json"); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'genesishashestest.json' is empty. Have you cloned the 'tests' repo branch develop?"); js::read_string(s, v); From c925b2936b428f0354bae9ccf1dc88bdb57f15c1 Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Thu, 11 Jun 2015 10:23:04 +0200 Subject: [PATCH 42/66] Bloom Filter introduced --- libwhisper/BloomFilter.cpp | 71 +++++++++++++++++ libwhisper/BloomFilter.h | 73 ++++++++++++++++++ test/libwhisper/bloomFilter.cpp | 133 ++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 libwhisper/BloomFilter.cpp create mode 100644 libwhisper/BloomFilter.h create mode 100644 test/libwhisper/bloomFilter.cpp diff --git a/libwhisper/BloomFilter.cpp b/libwhisper/BloomFilter.cpp new file mode 100644 index 000000000..5631688ae --- /dev/null +++ b/libwhisper/BloomFilter.cpp @@ -0,0 +1,71 @@ +/* +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 BloomFilter.cpp +* @author Vladislav Gluhovsky +* @date June 2015 +*/ + +#include "BloomFilter.h" + +using namespace std; +using namespace dev; +using namespace dev::shh; + +bool BloomFilter::matches(AbridgedTopic const& _t) const +{ + static unsigned const c_PerfectMatch = ~unsigned(0); + unsigned topic = AbridgedTopic::Arith(_t).convert_to(); + unsigned matchingBits = m_filter & topic; + matchingBits |= ~topic; + return (c_PerfectMatch == matchingBits); +} + +void SharedBloomFilter::add(AbridgedTopic const& _t) +{ + unsigned const topic = AbridgedTopic::Arith(_t).convert_to(); + m_filter |= topic; + + unsigned nextBit = 1; + for (unsigned i = 0; i < ArrSize; ++i, nextBit <<= 1) + if (topic & nextBit) + if (m_refCounter[i] != numeric_limits::max()) + m_refCounter[i]++; + //else: overflow + + // in order to encounter overflow, you have to set 65536 filters simultaneously. + // we assume, it will never happen. +} + +void SharedBloomFilter::remove(AbridgedTopic const& _t) +{ + unsigned const topic = AbridgedTopic::Arith(_t).convert_to(); + + unsigned nextBit = 1; + for (unsigned i = 0; i < ArrSize; ++i, nextBit <<= 1) + if (topic & nextBit) + { + if (m_refCounter[i]) + m_refCounter[i]--; + + if (!m_refCounter[i]) + m_filter &= ~nextBit; + } +} + + + + diff --git a/libwhisper/BloomFilter.h b/libwhisper/BloomFilter.h new file mode 100644 index 000000000..22242ac77 --- /dev/null +++ b/libwhisper/BloomFilter.h @@ -0,0 +1,73 @@ +/* +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 BloomFilter.h +* @author Vladislav Gluhovsky +* @date June 2015 +*/ + +#pragma once + +#include "Common.h" + +namespace dev +{ +namespace shh +{ + +class BloomFilter +{ +public: + virtual ~BloomFilter() {} + BloomFilter(): m_filter(0) {} + BloomFilter(unsigned _i): m_filter(_i) {} + BloomFilter(AbridgedTopic const& _t): m_filter(AbridgedTopic::Arith(_t).convert_to()) {} + + bool matches(AbridgedTopic const& _t) const; + virtual void add(AbridgedTopic const& _t) { m_filter |= AbridgedTopic::Arith(_t).convert_to(); } + virtual void remove(AbridgedTopic const& ) {} // not implemented in this class, use derived class instead. + +protected: + unsigned m_filter; +}; + +class SharedBloomFilter: public BloomFilter +{ +public: + virtual ~SharedBloomFilter() {} + SharedBloomFilter() { init(); } + SharedBloomFilter(unsigned _i): BloomFilter(_i) { init(); } + SharedBloomFilter(AbridgedTopic const& _t): BloomFilter(_t) { init(); } + + void add(AbridgedTopic const& _t) override; + void remove(AbridgedTopic const& _t) override; + +protected: + void init() { for (unsigned i = 0; i < ArrSize; ++i) m_refCounter[i] = 0; } + +private: + enum { ArrSize = 8 * AbridgedTopic::size }; + unsigned short m_refCounter[ArrSize]; +}; + +} +} + + + + + + diff --git a/test/libwhisper/bloomFilter.cpp b/test/libwhisper/bloomFilter.cpp new file mode 100644 index 000000000..0a299d53c --- /dev/null +++ b/test/libwhisper/bloomFilter.cpp @@ -0,0 +1,133 @@ +/* +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 whisperMessage.cpp +* @author Vladislav Gluhovsky +* @date June 2015 +*/ + +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::shh; + +BOOST_AUTO_TEST_SUITE(bloomFilter) + +BOOST_AUTO_TEST_CASE(match) +{ + VerbosityHolder setTemporaryLevel(10); + cnote << "Testing Bloom Filter matching..."; + + SharedBloomFilter f; + unsigned b00000001 = 0x01; + unsigned b00010000 = 0x10; + unsigned b00011000 = 0x18; + unsigned b00110000 = 0x30; + unsigned b00110010 = 0x32; + unsigned b00111000 = 0x38; + unsigned b00000110 = 0x06; + unsigned b00110110 = 0x36; + unsigned b00110111 = 0x37; + + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); + f.add(AbridgedTopic(b00000001)); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00010000))); + f.add(AbridgedTopic(b00010000)); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); + f.add(AbridgedTopic(b00011000)); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110000))); + f.add(AbridgedTopic(b00110000)); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00111000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110010))); + f.add(AbridgedTopic(b00110010)); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000110))); + f.add(AbridgedTopic(b00000110)); + + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110111))); + + f.remove(AbridgedTopic(b00000001)); + f.remove(AbridgedTopic(b00000001)); + f.remove(AbridgedTopic(b00000001)); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00011000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110010))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00111000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00000110))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); + + f.remove(AbridgedTopic(b00010000)); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00011000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110010))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00111000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00000110))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); + + f.remove(AbridgedTopic(b00111000)); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110010))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00111000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00000110))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); + + f.add(AbridgedTopic(b00000001)); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00000001))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110010))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00111000))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00000110))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00110111))); + + f.remove(AbridgedTopic(b00110111)); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); + BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110010))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00111000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000110))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110110))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); + + f.remove(AbridgedTopic(b00110111)); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00010000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110010))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00111000))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000110))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110110))); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 013ba9966af6cec4f0dd13a9e8982d8c1ad2134c Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Fri, 12 Jun 2015 16:31:25 +0200 Subject: [PATCH 43/66] Bloom Filter, first version --- libwhisper/BloomFilter.cpp | 7 ++++--- libwhisper/BloomFilter.h | 7 ++++++- libwhisper/Common.cpp | 22 +++++++++++++++++++++- libwhisper/Common.h | 15 +++------------ libwhisper/WhisperHost.cpp | 4 ++++ libwhisper/WhisperHost.h | 4 +++- test/libwhisper/bloomFilter.cpp | 8 ++++++++ 7 files changed, 49 insertions(+), 18 deletions(-) diff --git a/libwhisper/BloomFilter.cpp b/libwhisper/BloomFilter.cpp index 5631688ae..4e1631025 100644 --- a/libwhisper/BloomFilter.cpp +++ b/libwhisper/BloomFilter.cpp @@ -27,11 +27,11 @@ using namespace dev::shh; bool BloomFilter::matches(AbridgedTopic const& _t) const { - static unsigned const c_PerfectMatch = ~unsigned(0); + static unsigned const c_match = ~unsigned(0); unsigned topic = AbridgedTopic::Arith(_t).convert_to(); unsigned matchingBits = m_filter & topic; matchingBits |= ~topic; - return (c_PerfectMatch == matchingBits); + return (c_match == matchingBits); } void SharedBloomFilter::add(AbridgedTopic const& _t) @@ -46,7 +46,8 @@ void SharedBloomFilter::add(AbridgedTopic const& _t) m_refCounter[i]++; //else: overflow - // in order to encounter overflow, you have to set 65536 filters simultaneously. + // in order to encounter overflow, you have to set at least 65536 filters simultaneously. + // even then, the problem will only arise after at least 65536 filters will be be removed. // we assume, it will never happen. } diff --git a/libwhisper/BloomFilter.h b/libwhisper/BloomFilter.h index 22242ac77..d663c994e 100644 --- a/libwhisper/BloomFilter.h +++ b/libwhisper/BloomFilter.h @@ -36,9 +36,14 @@ public: BloomFilter(unsigned _i): m_filter(_i) {} BloomFilter(AbridgedTopic const& _t): m_filter(AbridgedTopic::Arith(_t).convert_to()) {} + unsigned getUnsigned() const { return m_filter; } + AbridgedTopic getAbridgedTopic() const { return AbridgedTopic(m_filter); } + bool matches(AbridgedTopic const& _t) const; + virtual void add(Topic const& _t) { add(abridge(_t)); } + virtual void add(Topics const& _topics) { for (Topic t : _topics) add(abridge(t)); } virtual void add(AbridgedTopic const& _t) { m_filter |= AbridgedTopic::Arith(_t).convert_to(); } - virtual void remove(AbridgedTopic const& ) {} // not implemented in this class, use derived class instead. + virtual void remove(AbridgedTopic const&) {} // not implemented in this class, use derived class instead. protected: unsigned m_filter; diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index 526072842..92fd997c5 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -20,9 +20,9 @@ */ #include "Common.h" - #include #include "Message.h" + using namespace std; using namespace dev; using namespace dev::p2p; @@ -84,6 +84,26 @@ bool TopicFilter::matches(Envelope const& _e) const return false; } +TopicFilter::TopicFilter(RLP const& _r) +{ + for (RLP i: _r) + { + m_topicMasks.push_back(TopicMask()); + for (RLP j: i) + m_topicMasks.back().push_back(j.toPair, FixedHash<4>>()); + } +} + +AbridgedTopic TopicFilter::exportBloomFilter() const +{ + AbridgedTopic ret; + for (TopicMask const& t: m_topicMasks) + for (auto i: t) + ret |= i.first; + + return ret; +} + TopicMask BuildTopicMask::toTopicMask() const { TopicMask ret; diff --git a/libwhisper/Common.h b/libwhisper/Common.h index b575166b4..eb624893a 100644 --- a/libwhisper/Common.h +++ b/libwhisper/Common.h @@ -48,7 +48,6 @@ using h256Set = dev::h256Set; class WhisperHost; class WhisperPeer; class Whisper; - class Envelope; enum WhisperPacket @@ -91,7 +90,7 @@ protected: h256s m_parts; }; -using TopicMask = std::vector>; +using TopicMask = std::vector>; // where pair::first is the actual abridged topic hash, pair::second is a constant (probably redundunt) using TopicMasks = std::vector; class TopicFilter @@ -101,20 +100,12 @@ public: TopicFilter(Topics const& _m) { m_topicMasks.push_back(TopicMask()); for (auto const& h: _m) m_topicMasks.back().push_back(std::make_pair(abridge(h), h ? ~AbridgedTopic() : AbridgedTopic())); } TopicFilter(TopicMask const& _m): m_topicMasks(1, _m) {} TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {} - TopicFilter(RLP const& _r)//: m_topicMasks(_r.toVector>()) - { - for (RLP i: _r) - { - m_topicMasks.push_back(TopicMask()); - for (RLP j: i) - m_topicMasks.back().push_back(j.toPair, FixedHash<4>>()); - } - } + TopicFilter(RLP const& _r); void streamRLP(RLPStream& _s) const { _s << m_topicMasks; } h256 sha3() const; - bool matches(Envelope const& _m) const; + AbridgedTopic exportBloomFilter() const; private: TopicMasks m_topicMasks; diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 366bb92e4..962abd829 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -113,6 +113,7 @@ unsigned WhisperHost::installWatch(shh::Topics const& _t) if (!m_filters.count(h)) m_filters.insert(make_pair(h, f)); + m_bloom.add(f.filter.exportBloomFilter()); return installWatchOnId(h); } @@ -151,8 +152,11 @@ void WhisperHost::uninstallWatch(unsigned _i) auto fit = m_filters.find(id); if (fit != m_filters.end()) + { + m_bloom.remove(fit->second.filter.exportBloomFilter()); if (!--fit->second.refCount) m_filters.erase(fit); + } } void WhisperHost::doWork() diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index 87563d9e8..f66036f89 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -34,6 +34,7 @@ #include "Common.h" #include "WhisperPeer.h" #include "Interface.h" +#include "BloomFilter.h" namespace dev { @@ -60,7 +61,7 @@ public: virtual void uninstallWatch(unsigned _watchId) override; virtual h256s peekWatch(unsigned _watchId) const override { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } } virtual h256s checkWatch(unsigned _watchId) override { cleanup(); dev::Guard l(m_filterLock); h256s ret; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes.clear(); } catch (...) {} return ret; } - virtual h256s watchMessages(unsigned _watchId) override; + virtual h256s watchMessages(unsigned _watchId) override; /// returns IDs of messages, which match specific watch criteria virtual Envelope envelope(h256 _m) const override { try { dev::ReadGuard l(x_messages); return m_messages.at(_m); } catch (...) { return Envelope(); } } @@ -86,6 +87,7 @@ private: mutable dev::Mutex m_filterLock; std::map m_filters; std::map m_watches; + SharedBloomFilter m_bloom; }; } diff --git a/test/libwhisper/bloomFilter.cpp b/test/libwhisper/bloomFilter.cpp index 0a299d53c..f58bf8503 100644 --- a/test/libwhisper/bloomFilter.cpp +++ b/test/libwhisper/bloomFilter.cpp @@ -44,6 +44,14 @@ BOOST_AUTO_TEST_CASE(match) unsigned b00110110 = 0x36; unsigned b00110111 = 0x37; + AbridgedTopic x(b00111000); + SharedBloomFilter f1(b00111000); + SharedBloomFilter f2(x); + BOOST_REQUIRE_EQUAL(x, f1.getAbridgedTopic()); + BOOST_REQUIRE_EQUAL(x, f2.getAbridgedTopic()); + BOOST_REQUIRE_EQUAL(b00111000, f1.getUnsigned()); + BOOST_REQUIRE_EQUAL(b00111000, f2.getUnsigned()); + BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); f.add(AbridgedTopic(b00000001)); BOOST_REQUIRE(!f.matches(AbridgedTopic(b00010000))); From f7605136578e5fe8ac7b671f1cc41dfcfa2a898a Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Fri, 12 Jun 2015 16:55:41 +0200 Subject: [PATCH 44/66] a minor refactoring --- test/libwhisper/whisperTopic.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/test/libwhisper/whisperTopic.cpp b/test/libwhisper/whisperTopic.cpp index ba487a92e..b5f5cd7d3 100644 --- a/test/libwhisper/whisperTopic.cpp +++ b/test/libwhisper/whisperTopic.cpp @@ -41,8 +41,7 @@ BOOST_FIXTURE_TEST_SUITE(whisper, P2PFixture) BOOST_AUTO_TEST_CASE(topic) { cnote << "Testing Whisper..."; - auto oldLogVerbosity = g_logVerbosity; - g_logVerbosity = 0; + VerbosityHolder setTemporaryLevel(0); Host host1("Test", NetworkPreferences("127.0.0.1", 30303, false)); host1.setIdealPeerCount(1); @@ -99,16 +98,13 @@ BOOST_AUTO_TEST_CASE(topic) } listener.join(); - g_logVerbosity = oldLogVerbosity; - BOOST_REQUIRE_EQUAL(result, 1 + 9 + 25 + 49 + 81); } BOOST_AUTO_TEST_CASE(forwarding) { cnote << "Testing Whisper forwarding..."; - auto oldLogVerbosity = g_logVerbosity; - g_logVerbosity = 0; + VerbosityHolder setTemporaryLevel(0); // Host must be configured not to share peers. Host host1("Listner", NetworkPreferences("127.0.0.1", 30303, false)); @@ -202,16 +198,13 @@ BOOST_AUTO_TEST_CASE(forwarding) listener.join(); done = true; forwarder.join(); - g_logVerbosity = oldLogVerbosity; - BOOST_REQUIRE_EQUAL(result, 1); } BOOST_AUTO_TEST_CASE(asyncforwarding) { cnote << "Testing Whisper async forwarding..."; - auto oldLogVerbosity = g_logVerbosity; - g_logVerbosity = 2; + VerbosityHolder setTemporaryLevel(2); unsigned result = 0; bool done = false; @@ -294,8 +287,6 @@ BOOST_AUTO_TEST_CASE(asyncforwarding) done = true; forwarder.join(); - g_logVerbosity = oldLogVerbosity; - BOOST_REQUIRE_EQUAL(result, 1); } From 255f81e2cc3a71aa78b7c2f11688a158f6fa3bbf Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Tue, 16 Jun 2015 15:30:20 +0200 Subject: [PATCH 45/66] Bloom Filter initial version reviewed --- libwhisper/BloomFilter.cpp | 49 +++---- libwhisper/BloomFilter.h | 48 ++---- libwhisper/WhisperHost.h | 2 +- test/libwhisper/bloomFilter.cpp | 249 +++++++++++++++++++------------- 4 files changed, 184 insertions(+), 164 deletions(-) diff --git a/libwhisper/BloomFilter.cpp b/libwhisper/BloomFilter.cpp index 4e1631025..1b6ea7f74 100644 --- a/libwhisper/BloomFilter.cpp +++ b/libwhisper/BloomFilter.cpp @@ -25,48 +25,45 @@ using namespace std; using namespace dev; using namespace dev::shh; -bool BloomFilter::matches(AbridgedTopic const& _t) const -{ - static unsigned const c_match = ~unsigned(0); - unsigned topic = AbridgedTopic::Arith(_t).convert_to(); - unsigned matchingBits = m_filter & topic; - matchingBits |= ~topic; - return (c_match == matchingBits); -} +static unsigned const c_mask[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; -void SharedBloomFilter::add(AbridgedTopic const& _t) +void TopicBloomFilter::add(AbridgedTopic const& _h) { - unsigned const topic = AbridgedTopic::Arith(_t).convert_to(); - m_filter |= topic; - - unsigned nextBit = 1; - for (unsigned i = 0; i < ArrSize; ++i, nextBit <<= 1) - if (topic & nextBit) - if (m_refCounter[i] != numeric_limits::max()) + *this |= _h; + unsigned count = 0; + for (unsigned i = 0; i < CounterSize; ++i) + if (isBitSet(_h, i)) + { + if (m_refCounter[i] != numeric_limits::max()) m_refCounter[i]++; //else: overflow - // in order to encounter overflow, you have to set at least 65536 filters simultaneously. - // even then, the problem will only arise after at least 65536 filters will be be removed. - // we assume, it will never happen. + // in order to encounter overflow, you have to set at least 65536 filters simultaneously. + // even then, the problem will only arise after at least 65536 filters will be be removed. + // we assume, it will never happen. + } } -void SharedBloomFilter::remove(AbridgedTopic const& _t) +void TopicBloomFilter::remove(AbridgedTopic const& _h) { - unsigned const topic = AbridgedTopic::Arith(_t).convert_to(); - - unsigned nextBit = 1; - for (unsigned i = 0; i < ArrSize; ++i, nextBit <<= 1) - if (topic & nextBit) + unsigned count = 0; + for (unsigned i = 0; i < CounterSize; ++i) + if (isBitSet(_h, i)) { if (m_refCounter[i]) m_refCounter[i]--; if (!m_refCounter[i]) - m_filter &= ~nextBit; + (*this)[i/8] &= ~c_mask[i%8]; } } +bool TopicBloomFilter::isBitSet(AbridgedTopic const& _h, unsigned _index) +{ + unsigned iByte = _index / 8; + unsigned iBit = _index % 8; + return (_h[iByte] & c_mask[iBit]) != 0; +} diff --git a/libwhisper/BloomFilter.h b/libwhisper/BloomFilter.h index d663c994e..89529146a 100644 --- a/libwhisper/BloomFilter.h +++ b/libwhisper/BloomFilter.h @@ -28,44 +28,26 @@ namespace dev namespace shh { -class BloomFilter +class TopicBloomFilter: public AbridgedTopic { public: - virtual ~BloomFilter() {} - BloomFilter(): m_filter(0) {} - BloomFilter(unsigned _i): m_filter(_i) {} - BloomFilter(AbridgedTopic const& _t): m_filter(AbridgedTopic::Arith(_t).convert_to()) {} - - unsigned getUnsigned() const { return m_filter; } - AbridgedTopic getAbridgedTopic() const { return AbridgedTopic(m_filter); } - - bool matches(AbridgedTopic const& _t) const; - virtual void add(Topic const& _t) { add(abridge(_t)); } - virtual void add(Topics const& _topics) { for (Topic t : _topics) add(abridge(t)); } - virtual void add(AbridgedTopic const& _t) { m_filter |= AbridgedTopic::Arith(_t).convert_to(); } - virtual void remove(AbridgedTopic const&) {} // not implemented in this class, use derived class instead. - -protected: - unsigned m_filter; -}; - -class SharedBloomFilter: public BloomFilter -{ -public: - virtual ~SharedBloomFilter() {} - SharedBloomFilter() { init(); } - SharedBloomFilter(unsigned _i): BloomFilter(_i) { init(); } - SharedBloomFilter(AbridgedTopic const& _t): BloomFilter(_t) { init(); } - - void add(AbridgedTopic const& _t) override; - void remove(AbridgedTopic const& _t) override; + TopicBloomFilter() { init(); } + TopicBloomFilter(AbridgedTopic const& _h): AbridgedTopic(_h) { init(); } -protected: - void init() { for (unsigned i = 0; i < ArrSize; ++i) m_refCounter[i] = 0; } + void addBloom(AbridgedTopic const& _h) { add(_h.template bloom()); } + void removeBloom(AbridgedTopic const& _h) { remove(_h.template bloom()); } + void add(AbridgedTopic const& _h); + void remove(AbridgedTopic const& _h); + bool containsBloom(AbridgedTopic const& _h) const { return contains(_h.template bloom()); } + enum { BitsPerBloom = 3 }; + private: - enum { ArrSize = 8 * AbridgedTopic::size }; - unsigned short m_refCounter[ArrSize]; + void init() { for (unsigned i = 0; i < CounterSize; ++i) m_refCounter[i] = 0; } + static bool isBitSet(AbridgedTopic const& _h, unsigned _index); + + enum { CounterSize = 8 * AbridgedTopic::size }; + std::array m_refCounter; }; } diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index f66036f89..46c41f3a2 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -87,7 +87,7 @@ private: mutable dev::Mutex m_filterLock; std::map m_filters; std::map m_watches; - SharedBloomFilter m_bloom; + TopicBloomFilter m_bloom; }; } diff --git a/test/libwhisper/bloomFilter.cpp b/test/libwhisper/bloomFilter.cpp index f58bf8503..d7a4c76ef 100644 --- a/test/libwhisper/bloomFilter.cpp +++ b/test/libwhisper/bloomFilter.cpp @@ -20,122 +20,163 @@ along with cpp-ethereum. If not, see . */ #include +#include #include using namespace std; using namespace dev; using namespace dev::shh; +void testAddNonExisting(TopicBloomFilter& _f, AbridgedTopic const& _h) +{ + BOOST_REQUIRE(!_f.contains(_h)); + _f.add(_h); + BOOST_REQUIRE(_f.contains(_h)); +} + +void testRemoveExisting(TopicBloomFilter& _f, AbridgedTopic const& _h) +{ + BOOST_REQUIRE(_f.contains(_h)); + _f.remove(_h); + BOOST_REQUIRE(!_f.contains(_h)); +} + +void testAddNonExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h) +{ + BOOST_REQUIRE(!_f.containsBloom(_h)); + _f.addBloom(_h); + BOOST_REQUIRE(_f.containsBloom(_h)); +} + +void testRemoveExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h) +{ + BOOST_REQUIRE(_f.containsBloom(_h)); + _f.removeBloom(_h); + BOOST_REQUIRE(!_f.containsBloom(_h)); +} + BOOST_AUTO_TEST_SUITE(bloomFilter) -BOOST_AUTO_TEST_CASE(match) +BOOST_AUTO_TEST_CASE(bloomFilterRandom) { VerbosityHolder setTemporaryLevel(10); cnote << "Testing Bloom Filter matching..."; - SharedBloomFilter f; - unsigned b00000001 = 0x01; - unsigned b00010000 = 0x10; - unsigned b00011000 = 0x18; - unsigned b00110000 = 0x30; - unsigned b00110010 = 0x32; - unsigned b00111000 = 0x38; - unsigned b00000110 = 0x06; - unsigned b00110110 = 0x36; - unsigned b00110111 = 0x37; - - AbridgedTopic x(b00111000); - SharedBloomFilter f1(b00111000); - SharedBloomFilter f2(x); - BOOST_REQUIRE_EQUAL(x, f1.getAbridgedTopic()); - BOOST_REQUIRE_EQUAL(x, f2.getAbridgedTopic()); - BOOST_REQUIRE_EQUAL(b00111000, f1.getUnsigned()); - BOOST_REQUIRE_EQUAL(b00111000, f2.getUnsigned()); - - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); - f.add(AbridgedTopic(b00000001)); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00010000))); - f.add(AbridgedTopic(b00010000)); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); - f.add(AbridgedTopic(b00011000)); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110000))); - f.add(AbridgedTopic(b00110000)); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00111000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110010))); - f.add(AbridgedTopic(b00110010)); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000110))); - f.add(AbridgedTopic(b00000110)); - - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110111))); - - f.remove(AbridgedTopic(b00000001)); - f.remove(AbridgedTopic(b00000001)); - f.remove(AbridgedTopic(b00000001)); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00011000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110010))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00111000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00000110))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); - - f.remove(AbridgedTopic(b00010000)); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00011000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110010))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00111000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00000110))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); - - f.remove(AbridgedTopic(b00111000)); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110010))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00111000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00000110))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); - - f.add(AbridgedTopic(b00000001)); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00000001))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110010))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00111000))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00000110))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110110))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00110111))); - - f.remove(AbridgedTopic(b00110111)); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); - BOOST_REQUIRE(f.matches(AbridgedTopic(b00010000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110010))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00111000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000110))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110110))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); - - f.remove(AbridgedTopic(b00110111)); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000001))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00010000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00011000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110010))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00111000))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00000110))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110110))); - BOOST_REQUIRE(!f.matches(AbridgedTopic(b00110111))); + TopicBloomFilter f; + vector vec; + Topic x(0xDEADBEEF); + int const c_rounds = 4; + + for (int i = 0; i < c_rounds; ++i, x = sha3(x)) + vec.push_back(abridge(x)); + + for (int i = 0; i < c_rounds; ++i) + testAddNonExisting(f, vec[i]); + + for (int i = 0; i < c_rounds; ++i) + testRemoveExisting(f, vec[i]); + + for (int i = 0; i < c_rounds; ++i) + testAddNonExistingBloom(f, vec[i]); + + for (int i = 0; i < c_rounds; ++i) + testRemoveExistingBloom(f, vec[i]); +} + +BOOST_AUTO_TEST_CASE(bloomFilterRaw) +{ + VerbosityHolder setTemporaryLevel(10); + cnote << "Testing Raw Bloom matching..."; + + TopicBloomFilter f; + + AbridgedTopic b00000001(0x01); + AbridgedTopic b00010000(0x10); + AbridgedTopic b00011000(0x18); + AbridgedTopic b00110000(0x30); + AbridgedTopic b00110010(0x32); + AbridgedTopic b00111000(0x38); + AbridgedTopic b00000110(0x06); + AbridgedTopic b00110110(0x36); + AbridgedTopic b00110111(0x37); + + testAddNonExisting(f, b00000001); + testAddNonExisting(f, b00010000); + testAddNonExisting(f, b00011000); + testAddNonExisting(f, b00110000); + BOOST_REQUIRE(f.contains(b00111000)); + testAddNonExisting(f, b00110010); + testAddNonExisting(f, b00000110); + BOOST_REQUIRE(f.contains(b00110110)); + BOOST_REQUIRE(f.contains(b00110111)); + + f.remove(b00000001); + f.remove(b00000001); + f.remove(b00000001); + BOOST_REQUIRE(!f.contains(b00000001)); + BOOST_REQUIRE(f.contains(b00010000)); + BOOST_REQUIRE(f.contains(b00011000)); + BOOST_REQUIRE(f.contains(b00110000)); + BOOST_REQUIRE(f.contains(b00110010)); + BOOST_REQUIRE(f.contains(b00111000)); + BOOST_REQUIRE(f.contains(b00000110)); + BOOST_REQUIRE(f.contains(b00110110)); + BOOST_REQUIRE(!f.contains(b00110111)); + + f.remove(b00010000); + BOOST_REQUIRE(!f.contains(b00000001)); + BOOST_REQUIRE(f.contains(b00010000)); + BOOST_REQUIRE(f.contains(b00011000)); + BOOST_REQUIRE(f.contains(b00110000)); + BOOST_REQUIRE(f.contains(b00110010)); + BOOST_REQUIRE(f.contains(b00111000)); + BOOST_REQUIRE(f.contains(b00000110)); + BOOST_REQUIRE(f.contains(b00110110)); + BOOST_REQUIRE(!f.contains(b00110111)); + + f.remove(b00111000); + BOOST_REQUIRE(!f.contains(b00000001)); + BOOST_REQUIRE(f.contains(b00010000)); + BOOST_REQUIRE(!f.contains(b00011000)); + BOOST_REQUIRE(f.contains(b00110000)); + BOOST_REQUIRE(f.contains(b00110010)); + BOOST_REQUIRE(!f.contains(b00111000)); + BOOST_REQUIRE(f.contains(b00000110)); + BOOST_REQUIRE(f.contains(b00110110)); + BOOST_REQUIRE(!f.contains(b00110111)); + + f.add(b00000001); + BOOST_REQUIRE(f.contains(b00000001)); + BOOST_REQUIRE(f.contains(b00010000)); + BOOST_REQUIRE(!f.contains(b00011000)); + BOOST_REQUIRE(f.contains(b00110000)); + BOOST_REQUIRE(f.contains(b00110010)); + BOOST_REQUIRE(!f.contains(b00111000)); + BOOST_REQUIRE(f.contains(b00000110)); + BOOST_REQUIRE(f.contains(b00110110)); + BOOST_REQUIRE(f.contains(b00110111)); + + f.remove(b00110111); + BOOST_REQUIRE(!f.contains(b00000001)); + BOOST_REQUIRE(f.contains(b00010000)); + BOOST_REQUIRE(!f.contains(b00011000)); + BOOST_REQUIRE(!f.contains(b00110000)); + BOOST_REQUIRE(!f.contains(b00110010)); + BOOST_REQUIRE(!f.contains(b00111000)); + BOOST_REQUIRE(!f.contains(b00000110)); + BOOST_REQUIRE(!f.contains(b00110110)); + BOOST_REQUIRE(!f.contains(b00110111)); + + f.remove(b00110111); + BOOST_REQUIRE(!f.contains(b00000001)); + BOOST_REQUIRE(!f.contains(b00010000)); + BOOST_REQUIRE(!f.contains(b00011000)); + BOOST_REQUIRE(!f.contains(b00110000)); + BOOST_REQUIRE(!f.contains(b00110010)); + BOOST_REQUIRE(!f.contains(b00111000)); + BOOST_REQUIRE(!f.contains(b00000110)); + BOOST_REQUIRE(!f.contains(b00110110)); + BOOST_REQUIRE(!f.contains(b00110111)); } BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 4c854e8e9750847cc39c717cf9df8b4a7d79c861 Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Tue, 16 Jun 2015 15:54:07 +0200 Subject: [PATCH 46/66] rebased on the latest develop --- libwhisper/BloomFilter.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libwhisper/BloomFilter.h b/libwhisper/BloomFilter.h index 89529146a..188e69f59 100644 --- a/libwhisper/BloomFilter.h +++ b/libwhisper/BloomFilter.h @@ -34,11 +34,11 @@ public: TopicBloomFilter() { init(); } TopicBloomFilter(AbridgedTopic const& _h): AbridgedTopic(_h) { init(); } - void addBloom(AbridgedTopic const& _h) { add(_h.template bloom()); } - void removeBloom(AbridgedTopic const& _h) { remove(_h.template bloom()); } + void addBloom(AbridgedTopic const& _h) { add(_h.template bloomPart()); } + void removeBloom(AbridgedTopic const& _h) { remove(_h.template bloomPart()); } void add(AbridgedTopic const& _h); void remove(AbridgedTopic const& _h); - bool containsBloom(AbridgedTopic const& _h) const { return contains(_h.template bloom()); } + bool containsBloom(AbridgedTopic const& _h) const { return contains(_h.template bloomPart()); } enum { BitsPerBloom = 3 }; From 5848faeb49112d15b963ccab0132ad1c9b24aa30 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 16 Jun 2015 16:08:40 +0200 Subject: [PATCH 47/66] Some documentation and checks for vector_ref. --- libdevcore/vector_ref.h | 25 ++++++++++++++++++++++--- libevmasm/Assembly.cpp | 10 ---------- libevmasm/AssemblyItem.cpp | 7 ------- libevmasm/AssemblyItem.h | 9 ++++++--- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index b04d449b3..98c6adb21 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -9,6 +9,9 @@ namespace dev { +/** + * A modifiable reference to an existing object or vector in memory. + */ template class vector_ref { @@ -17,34 +20,50 @@ public: using element_type = _T; using mutable_value_type = typename std::conditional::value, typename std::remove_const<_T>::type, _T>::type; + static_assert(std::is_pod::value, "vector_ref can only be used with PODs due to its low-level treatment of data."); + vector_ref(): m_data(nullptr), m_count(0) {} + /// Creates a new vector_ref to point to @a _count elements starting at @a _data. vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} + /// Creates a new vector_ref pointing to the data part of a string (given as pointer). vector_ref(typename std::conditional::value, std::string const*, std::string*>::type _data): m_data(reinterpret_cast<_T*>(_data->data())), m_count(_data->size() / sizeof(_T)) {} + /// Creates a new vector_ref pointing to the data part of a vector (given as pointer). 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)) {} + /// Creates a new vector_ref pointing to the data part of a string (given as reference). + vector_ref(typename std::conditional::value, std::string const&, std::string&>::type _data): m_data(reinterpret_cast<_T*>(_data.data())), m_count(_data.size() / sizeof(_T)) {} #ifdef STORAGE_LEVELDB_INCLUDE_DB_H_ vector_ref(leveldb::Slice const& _s): m_data(reinterpret_cast<_T*>(_s.data())), m_count(_s.size() / sizeof(_T)) {} #endif explicit operator bool() const { return m_data && m_count; } - bool contentsEqual(std::vector const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); } + bool contentsEqual(std::vector const& _c) const { if (!m_data || m_count == 0) return _c.empty(); else return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count * sizeof(_T)); } std::vector toVector() const { return std::vector(m_data, m_data + m_count); } std::vector toBytes() const { return std::vector(reinterpret_cast(m_data), reinterpret_cast(m_data) + m_count * sizeof(_T)); } std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); } + template explicit operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>(reinterpret_cast<_T2*>(m_data), m_count * sizeof(_T) / sizeof(_T2)); } operator vector_ref<_T const>() const { return vector_ref<_T const>(m_data, m_count); } _T* data() const { return m_data; } + /// @returns the number of elements referenced (not necessarily number of bytes). size_t count() const { return m_count; } + /// @returns the number of elements referenced (not necessarily number of bytes). size_t size() const { return m_count; } bool empty() const { return !m_count; } - vector_ref<_T> next() const { return vector_ref<_T>(m_data + m_count, m_count); } + /// @returns a new vector_ref pointing at the next chunk of @a size() elements. + vector_ref<_T> next() const { if (!m_data) return *this; else return vector_ref<_T>(m_data + m_count, m_count); } + /// @returns a new vector_ref which is a shifted and shortened view of the original data. + /// If this goes out of bounds in any way, returns an empty vector_ref. + /// If @a _count is ~size_t(0), extends the view to the end of the data. vector_ref<_T> cropped(size_t _begin, size_t _count) const { if (m_data && _begin + _count <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } + /// @returns a new vector_ref which is a shifted view of the original data (not going beyond it). vector_ref<_T> cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref<_T>(m_data + _begin, m_count - _begin); else return vector_ref<_T>(); } void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; } void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } template bool overlapsWith(vector_ref _t) const { void const* f1 = data(); void const* t1 = data() + size(); void const* f2 = _t.data(); void const* t2 = _t.data() + _t.size(); return f1 < t2 && t1 > f2; } + /// Copies the contents of this vector_ref to the contents of @a _t, up to the max size of @a _t. void copyTo(vector_ref::type> _t) const { if (overlapsWith(_t)) memmove(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); else memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } + /// Copies the contents of this vector_ref to the contents of @a _t, and zeros further trailing elements in @a _t. void populate(vector_ref::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); } _T* begin() { return m_data; } diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 3557fc0ee..34ee05966 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -292,16 +292,6 @@ void Assembly::injectStart(AssemblyItem const& _i) m_items.insert(m_items.begin(), _i); } -inline bool matches(AssemblyItemsConstRef _a, AssemblyItemsConstRef _b) -{ - if (_a.size() != _b.size()) - return false; - for (unsigned i = 0; i < _a.size(); ++i) - if (!_a[i].match(_b[i])) - return false; - return true; -} - struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; }; #define copt dev::LogOutputStream() diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index a4485a144..a0c5e19a6 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -126,10 +126,3 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) } return _out; } - -ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) -{ - for (AssemblyItem const& i: _i) - _out << i; - return _out; -} diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 9eca0a7d1..3fa9bb203 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -98,11 +98,14 @@ private: }; using AssemblyItems = std::vector; -using AssemblyItemsConstRef = vector_ref; std::ostream& operator<<(std::ostream& _out, AssemblyItem const& _item); -std::ostream& operator<<(std::ostream& _out, AssemblyItemsConstRef _i); -inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _i) { return operator<<(_out, AssemblyItemsConstRef(&_i)); } +inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _items) +{ + for (AssemblyItem const& item: _items) + _out << item; + return _out; +} } } From b046146d48e80d7a0e678060d696403f05d15a9a Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Tue, 16 Jun 2015 16:17:29 +0200 Subject: [PATCH 48/66] unused variable removed --- libwhisper/BloomFilter.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libwhisper/BloomFilter.cpp b/libwhisper/BloomFilter.cpp index 1b6ea7f74..e02b6e1d0 100644 --- a/libwhisper/BloomFilter.cpp +++ b/libwhisper/BloomFilter.cpp @@ -30,7 +30,6 @@ static unsigned const c_mask[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; void TopicBloomFilter::add(AbridgedTopic const& _h) { *this |= _h; - unsigned count = 0; for (unsigned i = 0; i < CounterSize; ++i) if (isBitSet(_h, i)) { @@ -46,7 +45,6 @@ void TopicBloomFilter::add(AbridgedTopic const& _h) void TopicBloomFilter::remove(AbridgedTopic const& _h) { - unsigned count = 0; for (unsigned i = 0; i < CounterSize; ++i) if (isBitSet(_h, i)) { From c449a648b4174f9aa8955ffcdfb939e83bd6d35e Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Tue, 16 Jun 2015 23:17:59 +0200 Subject: [PATCH 49/66] naming conventions changed --- libwhisper/BloomFilter.cpp | 4 ++-- libwhisper/BloomFilter.h | 12 +++++++----- libwhisper/Common.cpp | 5 +++-- libwhisper/Common.h | 2 +- libwhisper/WhisperHost.cpp | 4 ++-- test/libwhisper/bloomFilter.cpp | 28 ++++++++++++++-------------- 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/libwhisper/BloomFilter.cpp b/libwhisper/BloomFilter.cpp index e02b6e1d0..c189ee912 100644 --- a/libwhisper/BloomFilter.cpp +++ b/libwhisper/BloomFilter.cpp @@ -27,7 +27,7 @@ using namespace dev::shh; static unsigned const c_mask[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; -void TopicBloomFilter::add(AbridgedTopic const& _h) +void TopicBloomFilter::addRaw(AbridgedTopic const& _h) { *this |= _h; for (unsigned i = 0; i < CounterSize; ++i) @@ -43,7 +43,7 @@ void TopicBloomFilter::add(AbridgedTopic const& _h) } } -void TopicBloomFilter::remove(AbridgedTopic const& _h) +void TopicBloomFilter::removeRaw(AbridgedTopic const& _h) { for (unsigned i = 0; i < CounterSize; ++i) if (isBitSet(_h, i)) diff --git a/libwhisper/BloomFilter.h b/libwhisper/BloomFilter.h index 188e69f59..0ca460805 100644 --- a/libwhisper/BloomFilter.h +++ b/libwhisper/BloomFilter.h @@ -34,19 +34,21 @@ public: TopicBloomFilter() { init(); } TopicBloomFilter(AbridgedTopic const& _h): AbridgedTopic(_h) { init(); } - void addBloom(AbridgedTopic const& _h) { add(_h.template bloomPart()); } - void removeBloom(AbridgedTopic const& _h) { remove(_h.template bloomPart()); } - void add(AbridgedTopic const& _h); - void remove(AbridgedTopic const& _h); + void addBloom(AbridgedTopic const& _h) { addRaw(_h.template bloomPart()); } + void removeBloom(AbridgedTopic const& _h) { removeRaw(_h.template bloomPart()); } bool containsBloom(AbridgedTopic const& _h) const { return contains(_h.template bloomPart()); } + void addRaw(AbridgedTopic const& _h); + void removeRaw(AbridgedTopic const& _h); + bool containsRaw(AbridgedTopic const& _h) const { return contains(_h); } + enum { BitsPerBloom = 3 }; private: void init() { for (unsigned i = 0; i < CounterSize; ++i) m_refCounter[i] = 0; } static bool isBitSet(AbridgedTopic const& _h, unsigned _index); - enum { CounterSize = 8 * AbridgedTopic::size }; + enum { CounterSize = 8 * TopicBloomFilter::size }; std::array m_refCounter; }; diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index 92fd997c5..5dc267fcf 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -22,6 +22,7 @@ #include "Common.h" #include #include "Message.h" +#include "BloomFilter.h" using namespace std; using namespace dev; @@ -94,12 +95,12 @@ TopicFilter::TopicFilter(RLP const& _r) } } -AbridgedTopic TopicFilter::exportBloomFilter() const +AbridgedTopic TopicFilter::exportBloom() const { AbridgedTopic ret; for (TopicMask const& t: m_topicMasks) for (auto i: t) - ret |= i.first; + ret |= i.first.template bloomPart(); return ret; } diff --git a/libwhisper/Common.h b/libwhisper/Common.h index eb624893a..07c5b3317 100644 --- a/libwhisper/Common.h +++ b/libwhisper/Common.h @@ -105,7 +105,7 @@ public: void streamRLP(RLPStream& _s) const { _s << m_topicMasks; } h256 sha3() const; bool matches(Envelope const& _m) const; - AbridgedTopic exportBloomFilter() const; + AbridgedTopic exportBloom() const; private: TopicMasks m_topicMasks; diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 962abd829..d6759df6f 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -113,7 +113,7 @@ unsigned WhisperHost::installWatch(shh::Topics const& _t) if (!m_filters.count(h)) m_filters.insert(make_pair(h, f)); - m_bloom.add(f.filter.exportBloomFilter()); + m_bloom.addRaw(f.filter.exportBloom()); return installWatchOnId(h); } @@ -153,7 +153,7 @@ void WhisperHost::uninstallWatch(unsigned _i) auto fit = m_filters.find(id); if (fit != m_filters.end()) { - m_bloom.remove(fit->second.filter.exportBloomFilter()); + m_bloom.removeRaw(fit->second.filter.exportBloom()); if (!--fit->second.refCount) m_filters.erase(fit); } diff --git a/test/libwhisper/bloomFilter.cpp b/test/libwhisper/bloomFilter.cpp index d7a4c76ef..adf76c429 100644 --- a/test/libwhisper/bloomFilter.cpp +++ b/test/libwhisper/bloomFilter.cpp @@ -29,16 +29,16 @@ using namespace dev::shh; void testAddNonExisting(TopicBloomFilter& _f, AbridgedTopic const& _h) { - BOOST_REQUIRE(!_f.contains(_h)); - _f.add(_h); - BOOST_REQUIRE(_f.contains(_h)); + BOOST_REQUIRE(!_f.containsRaw(_h)); + _f.addRaw(_h); + BOOST_REQUIRE(_f.containsRaw(_h)); } void testRemoveExisting(TopicBloomFilter& _f, AbridgedTopic const& _h) { - BOOST_REQUIRE(_f.contains(_h)); - _f.remove(_h); - BOOST_REQUIRE(!_f.contains(_h)); + BOOST_REQUIRE(_f.containsRaw(_h)); + _f.removeRaw(_h); + BOOST_REQUIRE(!_f.containsRaw(_h)); } void testAddNonExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h) @@ -110,9 +110,9 @@ BOOST_AUTO_TEST_CASE(bloomFilterRaw) BOOST_REQUIRE(f.contains(b00110110)); BOOST_REQUIRE(f.contains(b00110111)); - f.remove(b00000001); - f.remove(b00000001); - f.remove(b00000001); + f.removeRaw(b00000001); + f.removeRaw(b00000001); + f.removeRaw(b00000001); BOOST_REQUIRE(!f.contains(b00000001)); BOOST_REQUIRE(f.contains(b00010000)); BOOST_REQUIRE(f.contains(b00011000)); @@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(bloomFilterRaw) BOOST_REQUIRE(f.contains(b00110110)); BOOST_REQUIRE(!f.contains(b00110111)); - f.remove(b00010000); + f.removeRaw(b00010000); BOOST_REQUIRE(!f.contains(b00000001)); BOOST_REQUIRE(f.contains(b00010000)); BOOST_REQUIRE(f.contains(b00011000)); @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(bloomFilterRaw) BOOST_REQUIRE(f.contains(b00110110)); BOOST_REQUIRE(!f.contains(b00110111)); - f.remove(b00111000); + f.removeRaw(b00111000); BOOST_REQUIRE(!f.contains(b00000001)); BOOST_REQUIRE(f.contains(b00010000)); BOOST_REQUIRE(!f.contains(b00011000)); @@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(bloomFilterRaw) BOOST_REQUIRE(f.contains(b00110110)); BOOST_REQUIRE(!f.contains(b00110111)); - f.add(b00000001); + f.addRaw(b00000001); BOOST_REQUIRE(f.contains(b00000001)); BOOST_REQUIRE(f.contains(b00010000)); BOOST_REQUIRE(!f.contains(b00011000)); @@ -156,7 +156,7 @@ BOOST_AUTO_TEST_CASE(bloomFilterRaw) BOOST_REQUIRE(f.contains(b00110110)); BOOST_REQUIRE(f.contains(b00110111)); - f.remove(b00110111); + f.removeRaw(b00110111); BOOST_REQUIRE(!f.contains(b00000001)); BOOST_REQUIRE(f.contains(b00010000)); BOOST_REQUIRE(!f.contains(b00011000)); @@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(bloomFilterRaw) BOOST_REQUIRE(!f.contains(b00110110)); BOOST_REQUIRE(!f.contains(b00110111)); - f.remove(b00110111); + f.removeRaw(b00110111); BOOST_REQUIRE(!f.contains(b00000001)); BOOST_REQUIRE(!f.contains(b00010000)); BOOST_REQUIRE(!f.contains(b00011000)); From 07b811ebfa47c680a8b15e8631985e65c8e9e3b3 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Wed, 17 Jun 2015 11:45:36 +0200 Subject: [PATCH 50/66] basic AES tests --- libdevcrypto/AES.h | 2 + test/libdevcrypto/AES.cpp | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 test/libdevcrypto/AES.cpp diff --git a/libdevcrypto/AES.h b/libdevcrypto/AES.h index 32d1880dc..6aaed6fad 100644 --- a/libdevcrypto/AES.h +++ b/libdevcrypto/AES.h @@ -75,6 +75,8 @@ public: /// Adjust mac interval. Next mac will be xored with value. void adjustInterval(unsigned _interval) { m_macInterval = _interval; } + + unsigned getMacInterval() { return m_macInterval;} private: AuthenticatedStream(AuthenticatedStream const&) = delete; diff --git a/test/libdevcrypto/AES.cpp b/test/libdevcrypto/AES.cpp new file mode 100644 index 000000000..5742b882e --- /dev/null +++ b/test/libdevcrypto/AES.cpp @@ -0,0 +1,92 @@ +/* + 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 MemTrie.h + * @author Christoph Jentzsch + * @date 2015 + */ + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; + +BOOST_AUTO_TEST_SUITE(AES) + +BOOST_AUTO_TEST_CASE(AesDecrypt) +{ + cout << "AesDecrypt" << endl; + bytes seed = fromHex("2dbaead416c20cfd00c2fc9f1788ff9f965a2000799c96a624767cb0e1e90d2d7191efdd92349226742fdc73d1d87e2d597536c4641098b9a89836c94f58a2ab4c525c27c4cb848b3e22ea245b2bc5c8c7beaa900b0c479253fc96fce7ffc621"); + KeyPair kp(sha3(aesDecrypt(&seed, "test"))); + BOOST_CHECK(Address("07746f871de684297923f933279555dda418f8a2") == kp.address()); +} + +BOOST_AUTO_TEST_CASE(AesDecryptWrongSeed) +{ + cout << "AesDecryptWrongSeed" << endl; + bytes seed = fromHex("badaead416c20cfd00c2fc9f1788ff9f965a2000799c96a624767cb0e1e90d2d7191efdd92349226742fdc73d1d87e2d597536c4641098b9a89836c94f58a2ab4c525c27c4cb848b3e22ea245b2bc5c8c7beaa900b0c479253fc96fce7ffc621"); + KeyPair kp(sha3(aesDecrypt(&seed, "test"))); + BOOST_CHECK(Address("07746f871de684297923f933279555dda418f8a2") != kp.address()); +} + +BOOST_AUTO_TEST_CASE(AesDecryptWrongPassword) +{ + cout << "AesDecryptWrongPassword" << endl; + bytes seed = fromHex("2dbaead416c20cfd00c2fc9f1788ff9f965a2000799c96a624767cb0e1e90d2d7191efdd92349226742fdc73d1d87e2d597536c4641098b9a89836c94f58a2ab4c525c27c4cb848b3e22ea245b2bc5c8c7beaa900b0c479253fc96fce7ffc621"); + KeyPair kp(sha3(aesDecrypt(&seed, "badtest"))); + BOOST_CHECK(Address("07746f871de684297923f933279555dda418f8a2") != kp.address()); +} + +BOOST_AUTO_TEST_CASE(AesDecryptFailInvalidSeed) +{ + cout << "AesDecryptFailInvalidSeed" << endl; + bytes seed = fromHex("xdbaead416c20cfd00c2fc9f1788ff9f965a2000799c96a624767cb0e1e90d2d7191efdd92349226742fdc73d1d87e2d597536c4641098b9a89836c94f58a2ab4c525c27c4cb848b3e22ea245b2bc5c8c7beaa900b0c479253fc96fce7ffc621"); + BOOST_CHECK(bytes() == aesDecrypt(&seed, "test")); +} + +BOOST_AUTO_TEST_CASE(AesDecryptFailInvalidSeedSize) +{ + cout << "AesDecryptFailInvalidSeedSize" << endl; + bytes seed = fromHex("000102030405060708090a0b0c0d0e0f"); + BOOST_CHECK(bytes() == aesDecrypt(&seed, "test")); +} + +BOOST_AUTO_TEST_CASE(AesDecryptFailInvalidSeed2) +{ + cout << "AesDecryptFailInvalidSeed2" << endl; + bytes seed = fromHex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"); + BOOST_CHECK(bytes() == aesDecrypt(&seed, "test")); +} +BOOST_AUTO_TEST_CASE(AuthenticatedStreamConstructor) +{ + cout << "AuthenticatedStreamConstructor" << endl; + + Secret const sec("test"); + crypto::aes::AuthenticatedStream as(crypto::aes::Encrypt, sec, 0); + BOOST_CHECK(as.getMacInterval() == 0); + as.adjustInterval(1); + BOOST_CHECK(as.getMacInterval() == 1); + crypto::aes::AuthenticatedStream as_mac(crypto::aes::Encrypt, h128(), h128(), 42); + BOOST_CHECK(as_mac.getMacInterval() == 42); +} + +BOOST_AUTO_TEST_SUITE_END() + From d420a1c025389fee00559ac5573758451f79fa74 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Wed, 17 Jun 2015 11:48:35 +0200 Subject: [PATCH 51/66] style --- test/libethereum/blockchain.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/libethereum/blockchain.cpp b/test/libethereum/blockchain.cpp index 2dc30c6ba..de28d30ff 100644 --- a/test/libethereum/blockchain.cpp +++ b/test/libethereum/blockchain.cpp @@ -581,11 +581,9 @@ mArray importUncles(mObject const& _blObj, vector& _vBiUncles, vector } updatePoW(uncleBlockFromFields); - if (overwrite == "nonce" || overwrite == "mixHash") - { - uncleBlockFromFields.nonce = overwrite == "nonce" ? Nonce(uncleHeaderObj["nonce"].get_str()) : uncleBlockFromFields.nonce; - uncleBlockFromFields.mixHash = overwrite == "mixHash" ? h256(uncleHeaderObj["mixHash"].get_str()) : uncleBlockFromFields.mixHash; - } + + uncleBlockFromFields.nonce = overwrite == "nonce" ? Nonce(uncleHeaderObj["nonce"].get_str()) : uncleBlockFromFields.nonce; + uncleBlockFromFields.mixHash = overwrite == "mixHash" ? h256(uncleHeaderObj["mixHash"].get_str()) : uncleBlockFromFields.mixHash; writeBlockHeaderToJson(uncleHeaderObj, uncleBlockFromFields); From 83cc17f240c02eab9bf4cbf4436e6ab356c11693 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Wed, 17 Jun 2015 12:22:04 +0200 Subject: [PATCH 52/66] update file name in description --- test/libdevcrypto/AES.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libdevcrypto/AES.cpp b/test/libdevcrypto/AES.cpp index 5742b882e..071f1509c 100644 --- a/test/libdevcrypto/AES.cpp +++ b/test/libdevcrypto/AES.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file MemTrie.h +/** @file AES.cpp * @author Christoph Jentzsch * @date 2015 */ From 8198665eca2d338c7484b5cfeef9e976fc830b28 Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Wed, 17 Jun 2015 13:10:10 +0200 Subject: [PATCH 53/66] minor improvements --- libwhisper/BloomFilter.cpp | 9 +++------ libwhisper/Common.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/libwhisper/BloomFilter.cpp b/libwhisper/BloomFilter.cpp index c189ee912..cff69c664 100644 --- a/libwhisper/BloomFilter.cpp +++ b/libwhisper/BloomFilter.cpp @@ -35,11 +35,8 @@ void TopicBloomFilter::addRaw(AbridgedTopic const& _h) { if (m_refCounter[i] != numeric_limits::max()) m_refCounter[i]++; - //else: overflow - - // in order to encounter overflow, you have to set at least 65536 filters simultaneously. - // even then, the problem will only arise after at least 65536 filters will be be removed. - // we assume, it will never happen. + else + BOOST_THROW_EXCEPTION(Overflow()); } } @@ -52,7 +49,7 @@ void TopicBloomFilter::removeRaw(AbridgedTopic const& _h) m_refCounter[i]--; if (!m_refCounter[i]) - (*this)[i/8] &= ~c_mask[i%8]; + (*this)[i / 8] &= ~c_mask[i % 8]; } } diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index 5dc267fcf..7f5b8ed06 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -38,7 +38,7 @@ AbridgedTopics dev::shh::abridge(Topics const& _topics) { AbridgedTopics ret; ret.reserve(_topics.size()); - for (auto const& t : _topics) + for (auto const& t: _topics) ret.push_back(abridge(t)); return ret; } @@ -87,10 +87,10 @@ bool TopicFilter::matches(Envelope const& _e) const TopicFilter::TopicFilter(RLP const& _r) { - for (RLP i: _r) + for (RLP const& i: _r) { m_topicMasks.push_back(TopicMask()); - for (RLP j: i) + for (RLP const& j: i) m_topicMasks.back().push_back(j.toPair, FixedHash<4>>()); } } @@ -99,7 +99,7 @@ AbridgedTopic TopicFilter::exportBloom() const { AbridgedTopic ret; for (TopicMask const& t: m_topicMasks) - for (auto i: t) + for (auto const& i: t) ret |= i.first.template bloomPart(); return ret; From 3264250b5de65b0db8a041826bbd8e38a11bb1a9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 17 Jun 2015 14:35:55 +0200 Subject: [PATCH 54/66] Removed spaces between code and comment. --- libethcore/KeyManager.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libethcore/KeyManager.h b/libethcore/KeyManager.h index d9bb6457c..a2b5a4e07 100644 --- a/libethcore/KeyManager.h +++ b/libethcore/KeyManager.h @@ -38,8 +38,10 @@ struct KeyInfo KeyInfo() = default; KeyInfo(h256 const& _passHash, std::string const& _accountName): passHash(_passHash), accountName(_accountName) {} - h256 passHash; ///< Hash of the password or h256() / UnknownPassword if unknown. - std::string accountName; ///< Name of the key, or JSON key info if begins with '{'. + /// Hash of the password or h256() / UnknownPassword if unknown. + h256 passHash; + /// Name of the key, or JSON key info if begins with '{'. + std::string accountName; }; static h256 const UnknownPassword; From 06770736bd662c26bb04abe0d5a0b5e14d0a44d4 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Wed, 17 Jun 2015 15:32:27 +0200 Subject: [PATCH 55/66] style 2 --- test/libethereum/blockchain.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/libethereum/blockchain.cpp b/test/libethereum/blockchain.cpp index de28d30ff..4dc1993a9 100644 --- a/test/libethereum/blockchain.cpp +++ b/test/libethereum/blockchain.cpp @@ -582,8 +582,11 @@ mArray importUncles(mObject const& _blObj, vector& _vBiUncles, vector updatePoW(uncleBlockFromFields); - uncleBlockFromFields.nonce = overwrite == "nonce" ? Nonce(uncleHeaderObj["nonce"].get_str()) : uncleBlockFromFields.nonce; - uncleBlockFromFields.mixHash = overwrite == "mixHash" ? h256(uncleHeaderObj["mixHash"].get_str()) : uncleBlockFromFields.mixHash; + if (overwrite == "nonce") + uncleBlockFromFields.nonce = Nonce(uncleHeaderObj["nonce"].get_str()); + + if (overwrite == "mixHash") + uncleBlockFromFields.mixHash = h256(uncleHeaderObj["mixHash"].get_str()); writeBlockHeaderToJson(uncleHeaderObj, uncleBlockFromFields); From 3660c4433695436b26118d78aee750952e1bf2fb Mon Sep 17 00:00:00 2001 From: Dimitry Date: Wed, 3 Jun 2015 17:21:29 +0300 Subject: [PATCH 56/66] FuzzTests: Boost Macro --- test/TestHelper.h | 16 +++ test/fuzzTesting/CMakeLists.txt | 5 + test/fuzzTesting/createRandomTest.cpp | 143 ++++++++++++++++++++++++++ test/libethereum/transaction.cpp | 43 ++++---- 4 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 test/fuzzTesting/createRandomTest.cpp diff --git a/test/TestHelper.h b/test/TestHelper.h index 8f0c73bf3..eaeed619a 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -31,6 +31,19 @@ #include #include +#define DONTUSE_BOOST_MACROS +#ifdef DONTUSE_BOOST_MACROS + #define TBOOST_THROW_EXCEPTION(arg) throw; + #define TBOOST_REQUIRE(arg) if(arg == false) throw; + #define TBOOST_CHECK_MESSAGE(arg1, arg2) if(arg1 == false) throw; + #define TBOOST_WARN_MESSAGE(arg1, arg2) throw; +#else + #define TBOOST_THROW_EXCEPTION(arg) BOOST_THROW_EXCEPTION(arg) + #define TBOOST_REQUIRE(arg) BOOST_REQUIRE(arg) + #define TBOOST_CHECK_MESSAGE(arg1, arg2) BOOST_CHECK_MESSAGE(arg1, arg2) + #define TBOOST_WARN_MESSAGE(arg1, arg2) BOOST_WARN_MESSAGE(arg1, arg2) +#endif + namespace dev { namespace eth @@ -163,6 +176,9 @@ eth::LastHashes lastHashes(u256 _currentBlockNumber); json_spirit::mObject fillJsonWithState(eth::State _state); json_spirit::mObject fillJsonWithTransaction(eth::Transaction _txn); +//Fill Test Functions +void doTransactionTests(json_spirit::mValue& _v, bool _fillin); + template void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) { diff --git a/test/fuzzTesting/CMakeLists.txt b/test/fuzzTesting/CMakeLists.txt index 371d6504d..4024b7956 100644 --- a/test/fuzzTesting/CMakeLists.txt +++ b/test/fuzzTesting/CMakeLists.txt @@ -8,6 +8,7 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) +add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp") add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp") add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp" ) @@ -32,3 +33,7 @@ target_link_libraries(checkRandomStateTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES target_link_libraries(checkRandomStateTest ethereum) target_link_libraries(checkRandomStateTest ethcore) target_link_libraries(checkRandomStateTest testutils) +target_link_libraries(createRandomTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +target_link_libraries(createRandomTest ethereum) +target_link_libraries(createRandomTest ethcore) +target_link_libraries(createRandomTest testutils) diff --git a/test/fuzzTesting/createRandomTest.cpp b/test/fuzzTesting/createRandomTest.cpp new file mode 100644 index 000000000..92fd21b76 --- /dev/null +++ b/test/fuzzTesting/createRandomTest.cpp @@ -0,0 +1,143 @@ +/* + 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 createRandomTest.cpp + * @author Dimitry Khokhlov + * @date 2015 + */ + +#include +#include + +#include +#include + + +extern std::string const c_testExampleTransactionTest; +std::vector getTypes(); +void parseTestWithTypes(std::string& test); +void randomTransactionTest(); +void randomBlockChainTest(); + +int main(int argc, char *argv[]) +{ + std::string testSuite; + for (auto i = 0; i < argc; ++i) + { + auto arg = std::string{argv[i]}; + dev::test::Options& options = const_cast(dev::test::Options::get()); + if (arg == "--fulloutput") + options.fulloutput = true; + if (arg == "-t" && i + 1 < argc) + { + testSuite = argv[i + 1]; + if (testSuite != "BlockChainTests" && testSuite != "TransactionTests") + testSuite = ""; + } + } + + if (testSuite == "") + std::cout << "Error! Test suite not supported! (Usage -t TestSuite)"; + else + if (testSuite == "BlockChainTests") + randomBlockChainTest(); + else + if (testSuite == "TransactionTests") + randomTransactionTest(); + + return 0; +} + +void randomTransactionTest() +{ + std::string newTest = c_testExampleTransactionTest; + parseTestWithTypes(newTest); + json_spirit::mValue v; + json_spirit::read_string(newTest, v); + dev::test::doTransactionTests(v, true); + std::cout << json_spirit::write_string(v, true); +} + +void randomBlockChainTest() +{ + +} + +/// Parse Test string replacing keywords to fuzzed values +void parseTestWithTypes(std::string& test) +{ + std::vector types = getTypes(); + for (unsigned i = 0; i < types.size(); i++) + { + std::size_t pos = test.find(types.at(i)); + while (pos != std::string::npos) + { + if (types.at(i) == "[CODE]") + test.replace(pos, 6, "0x"+dev::test::RandomCode::generate(10)); + else + if (types.at(i) == "[HEX]") + test.replace(pos, 5, dev::test::RandomCode::randomUniIntHex()); + else + if (types.at(i) == "[HASH20]") + test.replace(pos, 8, dev::test::RandomCode::rndByteSequence(20)); + else + if (types.at(i) == "[0xHASH32]") + test.replace(pos, 10, "0x" + dev::test::RandomCode::rndByteSequence(32)); + else + if (types.at(i) == "[V]") + { + int random = dev::test::RandomCode::randomUniInt() % 100; + if (random < 30) + test.replace(pos, 3, "28"); + else + if (random < 60) + test.replace(pos, 3, "29"); + else + test.replace(pos, 3, "0x" + dev::test::RandomCode::rndByteSequence(1)); + } + + pos = test.find(types.at(i)); + } + } +} + +std::vector getTypes() +{ + return {"[CODE]", "[HEX]", "[HASH20]", "[0xHASH32]", "[V]"}; +} + + +std::string const c_testExampleTransactionTest = R"( +{ +"TransactionTest" : { + "transaction" : + { + "data" : "[CODE]", + "gasLimit" : "[HEX]", + "gasPrice" : "[HEX]", + "nonce" : "[HEX]", + "to" : "[HASH20]", + "value" : "[HEX]", + "v" : "[V]", + "r" : "[0xHASH32]", + "s" : "[0xHASH32]" + } + } +} +)"; + + + diff --git a/test/libethereum/transaction.cpp b/test/libethereum/transaction.cpp index 017e51ded..ad20331b3 100644 --- a/test/libethereum/transaction.cpp +++ b/test/libethereum/transaction.cpp @@ -21,6 +21,7 @@ */ #include "../TestHelper.h" +#include "test/fuzzTesting/fuzzHelper.h" using namespace std; using namespace json_spirit; @@ -38,7 +39,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) if (_fillin) { - BOOST_REQUIRE(o.count("transaction") > 0); + TBOOST_REQUIRE((o.count("transaction") > 0)); mObject tObj = o["transaction"].get_obj(); //Construct Rlp of the given transaction @@ -49,7 +50,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) { Transaction txFromFields(rlpStream.out(), CheckTransaction::Everything); if (!txFromFields.signature().isValid()) - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); + TBOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); o["sender"] = toString(txFromFields.sender()); o["transaction"] = ImportTest::makeAllFieldsHex(tObj); @@ -63,9 +64,9 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) { bool expectInValid = (o["expect"].get_str() == "invalid"); if (Options::get().checkState) - BOOST_CHECK_MESSAGE(expectInValid, "Check state: Transaction '" << i.first << "' is expected to be valid!"); + {TBOOST_CHECK_MESSAGE(expectInValid, "Check state: Transaction '" << i.first << "' is expected to be valid!");} else - BOOST_WARN_MESSAGE(expectInValid, "Check state: Transaction '" << i.first << "' is expected to be valid!"); + {TBOOST_WARN_MESSAGE(expectInValid, "Check state: Transaction '" << i.first << "' is expected to be valid!");} o.erase(o.find("expect")); } @@ -76,16 +77,16 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) { bool expectValid = (o["expect"].get_str() == "valid"); if (Options::get().checkState) - BOOST_CHECK_MESSAGE(expectValid, "Check state: Transaction '" << i.first << "' is expected to be invalid!"); + {TBOOST_CHECK_MESSAGE(expectValid, "Check state: Transaction '" << i.first << "' is expected to be invalid!");} else - BOOST_WARN_MESSAGE(expectValid, "Check state: Transaction '" << i.first << "' is expected to be invalid!"); + {TBOOST_WARN_MESSAGE(expectValid, "Check state: Transaction '" << i.first << "' is expected to be invalid!");} o.erase(o.find("expect")); } } else { - BOOST_REQUIRE(o.count("rlp") > 0); + TBOOST_REQUIRE((o.count("rlp") > 0)); Transaction txFromRlp; try { @@ -93,39 +94,39 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) RLP rlp(stream); txFromRlp = Transaction(rlp.data(), CheckTransaction::Everything); if (!txFromRlp.signature().isValid()) - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); + TBOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); } catch(Exception const& _e) { cnote << i.first; cnote << "Transaction Exception: " << diagnostic_information(_e); - BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transaction object should not be defined because the RLP is invalid!"); + TBOOST_CHECK_MESSAGE((o.count("transaction") == 0), "A transaction object should not be defined because the RLP is invalid!"); continue; } catch(...) { - BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transaction object should not be defined because the RLP is invalid!"); + TBOOST_CHECK_MESSAGE((o.count("transaction") == 0), "A transaction object should not be defined because the RLP is invalid!"); continue; } - BOOST_REQUIRE(o.count("transaction") > 0); + TBOOST_REQUIRE((o.count("transaction") > 0)); mObject tObj = o["transaction"].get_obj(); Transaction txFromFields(createRLPStreamFromTransactionFields(tObj).out(), CheckTransaction::Everything); //Check the fields restored from RLP to original fields - BOOST_CHECK_MESSAGE(txFromFields.data() == txFromRlp.data(), "Data in given RLP not matching the Transaction data!"); - BOOST_CHECK_MESSAGE(txFromFields.value() == txFromRlp.value(), "Value in given RLP not matching the Transaction value!"); - BOOST_CHECK_MESSAGE(txFromFields.gasPrice() == txFromRlp.gasPrice(), "GasPrice in given RLP not matching the Transaction gasPrice!"); - BOOST_CHECK_MESSAGE(txFromFields.gas() == txFromRlp.gas(),"Gas in given RLP not matching the Transaction gas!"); - BOOST_CHECK_MESSAGE(txFromFields.nonce() == txFromRlp.nonce(),"Nonce in given RLP not matching the Transaction nonce!"); - BOOST_CHECK_MESSAGE(txFromFields.receiveAddress() == txFromRlp.receiveAddress(), "Receive address in given RLP not matching the Transaction 'to' address!"); - BOOST_CHECK_MESSAGE(txFromFields.sender() == txFromRlp.sender(), "Transaction sender address in given RLP not matching the Transaction 'vrs' signature!"); - BOOST_CHECK_MESSAGE(txFromFields == txFromRlp, "However, txFromFields != txFromRlp!"); - BOOST_REQUIRE (o.count("sender") > 0); + TBOOST_CHECK_MESSAGE((txFromFields.data() == txFromRlp.data()), "Data in given RLP not matching the Transaction data!"); + TBOOST_CHECK_MESSAGE((txFromFields.value() == txFromRlp.value()), "Value in given RLP not matching the Transaction value!"); + TBOOST_CHECK_MESSAGE((txFromFields.gasPrice() == txFromRlp.gasPrice()), "GasPrice in given RLP not matching the Transaction gasPrice!"); + TBOOST_CHECK_MESSAGE((txFromFields.gas() == txFromRlp.gas()),"Gas in given RLP not matching the Transaction gas!"); + TBOOST_CHECK_MESSAGE((txFromFields.nonce() == txFromRlp.nonce()),"Nonce in given RLP not matching the Transaction nonce!"); + TBOOST_CHECK_MESSAGE((txFromFields.receiveAddress() == txFromRlp.receiveAddress()), "Receive address in given RLP not matching the Transaction 'to' address!"); + TBOOST_CHECK_MESSAGE((txFromFields.sender() == txFromRlp.sender()), "Transaction sender address in given RLP not matching the Transaction 'vrs' signature!"); + TBOOST_CHECK_MESSAGE((txFromFields == txFromRlp), "However, txFromFields != txFromRlp!"); + TBOOST_REQUIRE ((o.count("sender") > 0)); Address addressReaded = Address(o["sender"].get_str()); - BOOST_CHECK_MESSAGE(txFromFields.sender() == addressReaded || txFromRlp.sender() == addressReaded, "Signature address of sender does not match given sender address!"); + TBOOST_CHECK_MESSAGE((txFromFields.sender() == addressReaded || txFromRlp.sender() == addressReaded), "Signature address of sender does not match given sender address!"); } }//for }//doTransactionTests From ec0e65d339f9f7d219a6b29b5c6a72c2ab6d922a Mon Sep 17 00:00:00 2001 From: Dimitry Date: Wed, 3 Jun 2015 19:36:41 +0300 Subject: [PATCH 57/66] Random code: Exceptions and outut --- test/TestHelper.h | 8 ++++---- test/fuzzTesting/createRandomTest.cpp | 25 +++++++++++++++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/test/TestHelper.h b/test/TestHelper.h index eaeed619a..649d4adf1 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -33,10 +33,10 @@ #define DONTUSE_BOOST_MACROS #ifdef DONTUSE_BOOST_MACROS - #define TBOOST_THROW_EXCEPTION(arg) throw; - #define TBOOST_REQUIRE(arg) if(arg == false) throw; - #define TBOOST_CHECK_MESSAGE(arg1, arg2) if(arg1 == false) throw; - #define TBOOST_WARN_MESSAGE(arg1, arg2) throw; + #define TBOOST_THROW_EXCEPTION(arg) throw dev::Exception(); + #define TBOOST_REQUIRE(arg) if(arg == false) throw dev::Exception(); + #define TBOOST_CHECK_MESSAGE(arg1, arg2) if(arg1 == false) throw dev::Exception(); + #define TBOOST_WARN_MESSAGE(arg1, arg2) throw dev::Exception(); #else #define TBOOST_THROW_EXCEPTION(arg) BOOST_THROW_EXCEPTION(arg) #define TBOOST_REQUIRE(arg) BOOST_REQUIRE(arg) diff --git a/test/fuzzTesting/createRandomTest.cpp b/test/fuzzTesting/createRandomTest.cpp index 92fd21b76..2177902d6 100644 --- a/test/fuzzTesting/createRandomTest.cpp +++ b/test/fuzzTesting/createRandomTest.cpp @@ -63,11 +63,28 @@ int main(int argc, char *argv[]) void randomTransactionTest() { - std::string newTest = c_testExampleTransactionTest; - parseTestWithTypes(newTest); + //redirect all output to the stream + std::ostringstream strCout; + std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); + std::cout.rdbuf( strCout.rdbuf() ); + std::cerr.rdbuf( strCout.rdbuf() ); + json_spirit::mValue v; - json_spirit::read_string(newTest, v); - dev::test::doTransactionTests(v, true); + try + { + std::string newTest = c_testExampleTransactionTest; + parseTestWithTypes(newTest); + json_spirit::read_string(newTest, v); + dev::test::doTransactionTests(v, true); + } + catch(...) + { + std::cerr << "Test fill exception!"; + } + + //restroe output + std::cout.rdbuf(oldCoutStreamBuf); + std::cerr.rdbuf(oldCoutStreamBuf); std::cout << json_spirit::write_string(v, true); } From b72edc27d039445b5ea2603b6ff8d94d4aa4d495 Mon Sep 17 00:00:00 2001 From: Dimitry Date: Fri, 5 Jun 2015 15:35:21 +0300 Subject: [PATCH 58/66] createRandomTest: State test --- test/TestHelper.cpp | 14 +-- test/TestHelper.h | 1 + test/fuzzTesting/CMakeLists.txt | 2 +- test/fuzzTesting/createRandomTest.cpp | 119 ++++++++++++++++++++------ test/fuzzTesting/fuzzHelper.cpp | 7 +- test/fuzzTesting/fuzzHelper.h | 6 +- test/libethereum/state.cpp | 14 +-- 7 files changed, 118 insertions(+), 45 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index bca0528ff..0e43d278b 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -178,7 +178,7 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state, stateOptio { stateOptions.m_bHasBalance = true; if (bigint(o["balance"].get_str()) >= c_max256plus1) - BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'balance' is equal or greater than 2**256") ); + TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'balance' is equal or greater than 2**256") ); balance = toInt(o["balance"]); } @@ -186,7 +186,7 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state, stateOptio { stateOptions.m_bHasNonce = true; if (bigint(o["nonce"].get_str()) >= c_max256plus1) - BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'nonce' is equal or greater than 2**256") ); + TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'nonce' is equal or greater than 2**256") ); nonce = toInt(o["nonce"]); } @@ -230,7 +230,7 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state) { //check that every parameter was declared in state object if (!stateOptionMap.second.isAllSet()) - BOOST_THROW_EXCEPTION(MissingFields() << errinfo_comment("Import State: Missing state fields!")); + TBOOST_THROW_EXCEPTION(MissingFields() << errinfo_comment("Import State: Missing state fields!")); } } @@ -246,13 +246,13 @@ void ImportTest::importTransaction(json_spirit::mObject& _o) assert(_o.count("data") > 0); if (bigint(_o["nonce"].get_str()) >= c_max256plus1) - BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'nonce' is equal or greater than 2**256") ); + TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'nonce' is equal or greater than 2**256") ); if (bigint(_o["gasPrice"].get_str()) >= c_max256plus1) - BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'gasPrice' is equal or greater than 2**256") ); + TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'gasPrice' is equal or greater than 2**256") ); if (bigint(_o["gasLimit"].get_str()) >= c_max256plus1) - BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'gasLimit' is equal or greater than 2**256") ); + TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'gasLimit' is equal or greater than 2**256") ); if (bigint(_o["value"].get_str()) >= c_max256plus1) - BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'value' is equal or greater than 2**256") ); + TBOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("Transaction 'value' is equal or greater than 2**256") ); m_transaction = _o["to"].get_str().empty() ? Transaction(toInt(_o["value"]), toInt(_o["gasPrice"]), toInt(_o["gasLimit"]), importData(_o), toInt(_o["nonce"]), Secret(_o["secretKey"].get_str())) : diff --git a/test/TestHelper.h b/test/TestHelper.h index 649d4adf1..df74c2dc3 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -178,6 +178,7 @@ json_spirit::mObject fillJsonWithTransaction(eth::Transaction _txn); //Fill Test Functions void doTransactionTests(json_spirit::mValue& _v, bool _fillin); +void doStateTests(json_spirit::mValue& v, bool _fillin); template void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) diff --git a/test/fuzzTesting/CMakeLists.txt b/test/fuzzTesting/CMakeLists.txt index 4024b7956..f25a2b31e 100644 --- a/test/fuzzTesting/CMakeLists.txt +++ b/test/fuzzTesting/CMakeLists.txt @@ -8,7 +8,7 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) -add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp") +add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp" "../libethereum/state.cpp") add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp") add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp" ) diff --git a/test/fuzzTesting/createRandomTest.cpp b/test/fuzzTesting/createRandomTest.cpp index 2177902d6..373207f44 100644 --- a/test/fuzzTesting/createRandomTest.cpp +++ b/test/fuzzTesting/createRandomTest.cpp @@ -25,12 +25,16 @@ #include #include - +//String Variables +extern std::string const c_testExampleStateTest; extern std::string const c_testExampleTransactionTest; + +//Main Test functinos +void fillRandomTest(std::function doTests, std::string const& testString); + +//Helper Functions std::vector getTypes(); void parseTestWithTypes(std::string& test); -void randomTransactionTest(); -void randomBlockChainTest(); int main(int argc, char *argv[]) { @@ -44,7 +48,7 @@ int main(int argc, char *argv[]) if (arg == "-t" && i + 1 < argc) { testSuite = argv[i + 1]; - if (testSuite != "BlockChainTests" && testSuite != "TransactionTests") + if (testSuite != "BlockChainTests" && testSuite != "TransactionTests" && testSuite != "StateTests") testSuite = ""; } } @@ -53,15 +57,18 @@ int main(int argc, char *argv[]) std::cout << "Error! Test suite not supported! (Usage -t TestSuite)"; else if (testSuite == "BlockChainTests") - randomBlockChainTest(); + fillRandomTest(dev::test::doTransactionTests, c_testExampleTransactionTest); else if (testSuite == "TransactionTests") - randomTransactionTest(); + fillRandomTest(dev::test::doTransactionTests, c_testExampleTransactionTest); + else + if (testSuite == "StateTests") + fillRandomTest(dev::test::doStateTests, c_testExampleStateTest); return 0; } -void randomTransactionTest() +void fillRandomTest(std::function doTests, std::string const& testString) { //redirect all output to the stream std::ostringstream strCout; @@ -72,10 +79,10 @@ void randomTransactionTest() json_spirit::mValue v; try { - std::string newTest = c_testExampleTransactionTest; + std::string newTest = testString; parseTestWithTypes(newTest); json_spirit::read_string(newTest, v); - dev::test::doTransactionTests(v, true); + doTests(v, true); } catch(...) { @@ -88,55 +95,67 @@ void randomTransactionTest() std::cout << json_spirit::write_string(v, true); } -void randomBlockChainTest() -{ - -} - /// Parse Test string replacing keywords to fuzzed values -void parseTestWithTypes(std::string& test) +void parseTestWithTypes(std::string& _test) { + dev::test::RandomCodeOptions options; + options.setWeight(dev::eth::Instruction::STOP, 10); //default 50 + options.setWeight(dev::eth::Instruction::SSTORE, 70); + options.setWeight(dev::eth::Instruction::CALL, 75); + options.addAddress(dev::Address("0xffffffffffffffffffffffffffffffffffffffff")); + options.addAddress(dev::Address("0x1000000000000000000000000000000000000000")); + options.addAddress(dev::Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87")); + options.addAddress(dev::Address("0x945304eb96065b2a98b57a48a06ae28d285a71b5")); + options.addAddress(dev::Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")); + options.addAddress(dev::Address("0x0000000000000000000000000000000000000001")); + options.addAddress(dev::Address("0x0000000000000000000000000000000000000002")); + options.addAddress(dev::Address("0x0000000000000000000000000000000000000003")); + options.addAddress(dev::Address("0x0000000000000000000000000000000000000004")); + options.smartCodeProbability = 35; + std::vector types = getTypes(); for (unsigned i = 0; i < types.size(); i++) { - std::size_t pos = test.find(types.at(i)); + std::size_t pos = _test.find(types.at(i)); while (pos != std::string::npos) { if (types.at(i) == "[CODE]") - test.replace(pos, 6, "0x"+dev::test::RandomCode::generate(10)); + _test.replace(pos, 6, "0x"+dev::test::RandomCode::generate(10, options)); else if (types.at(i) == "[HEX]") - test.replace(pos, 5, dev::test::RandomCode::randomUniIntHex()); + _test.replace(pos, 5, dev::test::RandomCode::randomUniIntHex()); else if (types.at(i) == "[HASH20]") - test.replace(pos, 8, dev::test::RandomCode::rndByteSequence(20)); + _test.replace(pos, 8, dev::test::RandomCode::rndByteSequence(20)); else if (types.at(i) == "[0xHASH32]") - test.replace(pos, 10, "0x" + dev::test::RandomCode::rndByteSequence(32)); + _test.replace(pos, 10, "0x" + dev::test::RandomCode::rndByteSequence(32)); + else + if (types.at(i) == "[HASH32]") + _test.replace(pos, 8, dev::test::RandomCode::rndByteSequence(32)); else if (types.at(i) == "[V]") { int random = dev::test::RandomCode::randomUniInt() % 100; if (random < 30) - test.replace(pos, 3, "28"); + _test.replace(pos, 3, "28"); else if (random < 60) - test.replace(pos, 3, "29"); + _test.replace(pos, 3, "29"); else - test.replace(pos, 3, "0x" + dev::test::RandomCode::rndByteSequence(1)); + _test.replace(pos, 3, "0x" + dev::test::RandomCode::rndByteSequence(1)); } - pos = test.find(types.at(i)); + pos = _test.find(types.at(i)); } } } std::vector getTypes() { - return {"[CODE]", "[HEX]", "[HASH20]", "[0xHASH32]", "[V]"}; + return {"[CODE]", "[HEX]", "[HASH20]", "[HASH32]", "[0xHASH32]", "[V]"}; } - std::string const c_testExampleTransactionTest = R"( { "TransactionTest" : { @@ -156,5 +175,49 @@ std::string const c_testExampleTransactionTest = R"( } )"; - - +std::string const c_testExampleStateTest = R"( +{ + "randomStatetest" : { + "env" : { + "currentCoinbase" : "[HASH20]", + "currentDifficulty" : "[HEX]", + "currentGasLimit" : "[HEX]", + "currentNumber" : "[HEX]", + "currentTimestamp" : "[HEX]", + "previousHash" : "[HASH32]" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "[HEX]", + "code" : "[CODE]", + "nonce" : "[V]", + "storage" : { + } + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "[HEX]", + "code" : "[CODE]", + "nonce" : "[V]", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "[HEX]", + "code" : "0x", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : { + "data" : "[CODE]", + "gasLimit" : "[HEX]", + "gasPrice" : "[V]", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "[HEX]" + } + } +} +)"; diff --git a/test/fuzzTesting/fuzzHelper.cpp b/test/fuzzTesting/fuzzHelper.cpp index 3b6cf19c9..1f09ca9fa 100644 --- a/test/fuzzTesting/fuzzHelper.cpp +++ b/test/fuzzTesting/fuzzHelper.cpp @@ -35,10 +35,12 @@ boost::random::mt19937 RandomCode::gen; boostIntDistrib RandomCode::opCodeDist = boostIntDistrib (0, 255); boostIntDistrib RandomCode::opLengDist = boostIntDistrib (1, 32); boostIntDistrib RandomCode::uniIntDist = boostIntDistrib (0, 0x7fffffff); +boostUint64Distrib RandomCode::uInt64Dist = boostUint64Distrib (0, std::numeric_limits::max()); boostIntGenerator RandomCode::randOpCodeGen = boostIntGenerator(gen, opCodeDist); boostIntGenerator RandomCode::randOpLengGen = boostIntGenerator(gen, opLengDist); boostIntGenerator RandomCode::randUniIntGen = boostIntGenerator(gen, uniIntDist); +boostUInt64Generator RandomCode::randUInt64Gen = boostUInt64Generator(gen, uInt64Dist); std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType) { @@ -92,7 +94,10 @@ std::string RandomCode::generate(int _maxOpNumber, RandomCodeOptions _options) std::string RandomCode::randomUniIntHex() { refreshSeed(); - return "0x" + toCompactHex((int)randUniIntGen()); + int rand = randUniIntGen() % 100; + if (rand < 50) + return "0x" + toCompactHex((u256)randUniIntGen()); + return "0x" + toCompactHex((u256)randUInt64Gen()); } int RandomCode::randomUniInt() diff --git a/test/fuzzTesting/fuzzHelper.h b/test/fuzzTesting/fuzzHelper.h index 371e40fbf..6a46bbeaf 100644 --- a/test/fuzzTesting/fuzzHelper.h +++ b/test/fuzzTesting/fuzzHelper.h @@ -37,9 +37,11 @@ namespace test typedef boost::random::uniform_int_distribution<> boostIntDistrib; typedef boost::random::discrete_distribution<> boostDescreteDistrib; +typedef boost::uniform_int boostUint64Distrib; typedef boost::random::variate_generator boostIntGenerator; typedef boost::random::variate_generator boostWeightGenerator; +typedef boost::random::variate_generator boostUInt64Generator; struct RandomCodeOptions { @@ -73,7 +75,7 @@ public: /// Generate random byte string of a given length static std::string rndByteSequence(int _length = 1, SizeStrictness _sizeType = SizeStrictness::Strict); - /// Generate random uniForm Int with reasonable value 0..0x7fffffff + /// Generate random int64 static std::string randomUniIntHex(); static int randomUniInt(); @@ -87,10 +89,12 @@ private: static boostIntDistrib opCodeDist; ///< 0..255 opcodes static boostIntDistrib opLengDist; ///< 1..32 byte string static boostIntDistrib uniIntDist; ///< 0..0x7fffffff + static boostUint64Distrib uInt64Dist; ///< 0..2**64 static boostIntGenerator randUniIntGen; ///< Generate random UniformInt from uniIntDist static boostIntGenerator randOpCodeGen; ///< Generate random value from opCodeDist static boostIntGenerator randOpLengGen; ///< Generate random length from opLengDist + static boostUInt64Generator randUInt64Gen; ///< Generate random uInt64 }; } diff --git a/test/libethereum/state.cpp b/test/libethereum/state.cpp index 632e7982b..492bfb746 100644 --- a/test/libethereum/state.cpp +++ b/test/libethereum/state.cpp @@ -51,9 +51,9 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) } std::cout << " " << i.first << std::endl; - BOOST_REQUIRE(o.count("env") > 0); - BOOST_REQUIRE(o.count("pre") > 0); - BOOST_REQUIRE(o.count("transaction") > 0); + TBOOST_REQUIRE((o.count("env") > 0)); + TBOOST_REQUIRE((o.count("pre") > 0)); + TBOOST_REQUIRE((o.count("transaction") > 0)); ImportTest importer(o, _fillin); @@ -80,13 +80,13 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) #if ETH_FATDB importer.exportTest(output, theState); #else - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("You can not fill tests when FATDB is switched off")); + TBOOST_THROW_EXCEPTION(Exception() << errinfo_comment("You can not fill tests when FATDB is switched off")); #endif } else { - BOOST_REQUIRE(o.count("post") > 0); - BOOST_REQUIRE(o.count("out") > 0); + TBOOST_REQUIRE((o.count("post") > 0)); + TBOOST_REQUIRE((o.count("out") > 0)); // check output checkOutput(output, o); @@ -101,7 +101,7 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) auto resultAddrs = theState.addresses(); checkAddresses(expectedAddrs, resultAddrs); #endif - BOOST_CHECK_MESSAGE(theState.rootHash() == h256(o["postStateRoot"].get_str()), "wrong post state root"); + TBOOST_CHECK_MESSAGE((theState.rootHash() == h256(o["postStateRoot"].get_str())), "wrong post state root"); } } } From 76a72769f408df1cb3c10a37b3fc200191d19d11 Mon Sep 17 00:00:00 2001 From: Dimitry Date: Fri, 5 Jun 2015 21:43:41 +0300 Subject: [PATCH 59/66] FuzzTesting: VMTests + TransactionTests support --- test/TestHelper.cpp | 33 ++++--- test/TestHelper.h | 13 ++- test/fuzzTesting/CMakeLists.txt | 3 +- test/fuzzTesting/createRandomTest.cpp | 126 ++++++++++++++++++++++++-- test/libevm/vm.cpp | 26 +++--- 5 files changed, 162 insertions(+), 39 deletions(-) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 0e43d278b..743b16273 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -285,9 +285,9 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta #define CHECK(a,b) \ { \ if (_throw == WhenError::Throw) \ - BOOST_CHECK_MESSAGE(a,b); \ + {TBOOST_CHECK_MESSAGE(a,b);}\ else \ - BOOST_WARN_MESSAGE(a,b); \ + {TBOOST_WARN_MESSAGE(a,b);} \ } for (auto const& a: _stateExpect.addresses()) @@ -304,35 +304,35 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta } catch(std::out_of_range const&) { - BOOST_ERROR("expectedStateOptions map does not match expectedState in checkExpectedState!"); + TBOOST_ERROR("expectedStateOptions map does not match expectedState in checkExpectedState!"); break; } } if (addressOptions.m_bHasBalance) - CHECK(_stateExpect.balance(a.first) == _statePost.balance(a.first), + CHECK((_stateExpect.balance(a.first) == _statePost.balance(a.first)), "Check State: " << a.first << ": incorrect balance " << _statePost.balance(a.first) << ", expected " << _stateExpect.balance(a.first)); if (addressOptions.m_bHasNonce) - CHECK(_stateExpect.transactionsFrom(a.first) == _statePost.transactionsFrom(a.first), + CHECK((_stateExpect.transactionsFrom(a.first) == _statePost.transactionsFrom(a.first)), "Check State: " << a.first << ": incorrect nonce " << _statePost.transactionsFrom(a.first) << ", expected " << _stateExpect.transactionsFrom(a.first)); if (addressOptions.m_bHasStorage) { unordered_map stateStorage = _statePost.storage(a.first); for (auto const& s: _stateExpect.storage(a.first)) - CHECK(stateStorage[s.first] == s.second, + CHECK((stateStorage[s.first] == s.second), "Check State: " << a.first << ": incorrect storage [" << s.first << "] = " << toHex(stateStorage[s.first]) << ", expected [" << s.first << "] = " << toHex(s.second)); //Check for unexpected storage values stateStorage = _stateExpect.storage(a.first); for (auto const& s: _statePost.storage(a.first)) - CHECK(stateStorage[s.first] == s.second, + CHECK((stateStorage[s.first] == s.second), "Check State: " << a.first << ": incorrect storage [" << s.first << "] = " << toHex(s.second) << ", expected [" << s.first << "] = " << toHex(stateStorage[s.first])); } if (addressOptions.m_bHasCode) - CHECK(_stateExpect.code(a.first) == _statePost.code(a.first), + CHECK((_stateExpect.code(a.first) == _statePost.code(a.first)), "Check State: " << a.first << ": incorrect code '" << toHex(_statePost.code(a.first)) << "', expected '" << toHex(_stateExpect.code(a.first)) << "'"); } } @@ -518,18 +518,17 @@ void checkOutput(bytes const& _output, json_spirit::mObject& _o) int j = 0; if (_o["out"].get_str().find("#") == 0) - BOOST_CHECK((u256)_output.size() == toInt(_o["out"].get_str().substr(1))); - + {TBOOST_CHECK(((u256)_output.size() == toInt(_o["out"].get_str().substr(1))));} else if (_o["out"].type() == json_spirit::array_type) for (auto const& d: _o["out"].get_array()) { - BOOST_CHECK_MESSAGE(_output[j] == toInt(d), "Output byte [" << j << "] different!"); + TBOOST_CHECK_MESSAGE((_output[j] == toInt(d)), "Output byte [" << j << "] different!"); ++j; } else if (_o["out"].get_str().find("0x") == 0) - BOOST_CHECK(_output == fromHex(_o["out"].get_str().substr(2))); + {TBOOST_CHECK((_output == fromHex(_o["out"].get_str().substr(2))));} else - BOOST_CHECK(_output == fromHex(_o["out"].get_str())); + TBOOST_CHECK((_output == fromHex(_o["out"].get_str()))); } void checkStorage(map _expectedStore, map _resultStore, Address _expectedAddr) @@ -557,13 +556,13 @@ void checkStorage(map _expectedStore, map _resultStore, void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs) { - BOOST_REQUIRE_EQUAL(_resultLogs.size(), _expectedLogs.size()); + TBOOST_REQUIRE_EQUAL(_resultLogs.size(), _expectedLogs.size()); for (size_t i = 0; i < _resultLogs.size(); ++i) { - BOOST_CHECK_EQUAL(_resultLogs[i].address, _expectedLogs[i].address); - BOOST_CHECK_EQUAL(_resultLogs[i].topics, _expectedLogs[i].topics); - BOOST_CHECK(_resultLogs[i].data == _expectedLogs[i].data); + TBOOST_CHECK_EQUAL(_resultLogs[i].address, _expectedLogs[i].address); + TBOOST_CHECK_EQUAL(_resultLogs[i].topics, _expectedLogs[i].topics); + TBOOST_CHECK((_resultLogs[i].data == _expectedLogs[i].data)); } } diff --git a/test/TestHelper.h b/test/TestHelper.h index df74c2dc3..b1ad923a7 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -35,13 +35,21 @@ #ifdef DONTUSE_BOOST_MACROS #define TBOOST_THROW_EXCEPTION(arg) throw dev::Exception(); #define TBOOST_REQUIRE(arg) if(arg == false) throw dev::Exception(); + #define TBOOST_REQUIRE_EQUAL(arg1, arg2) if(arg1 != arg2) throw dev::Exception(); + #define TBOOST_CHECK_EQUAL(arg1, arg2) if(arg1 != arg2) throw dev::Exception(); + #define TBOOST_CHECK(arg) if(arg == false) throw dev::Exception(); #define TBOOST_CHECK_MESSAGE(arg1, arg2) if(arg1 == false) throw dev::Exception(); #define TBOOST_WARN_MESSAGE(arg1, arg2) throw dev::Exception(); + #define TBOOST_ERROR(arg) throw dev::Exception(); #else #define TBOOST_THROW_EXCEPTION(arg) BOOST_THROW_EXCEPTION(arg) #define TBOOST_REQUIRE(arg) BOOST_REQUIRE(arg) + #define TBOOST_REQUIRE_EQUAL(arg1, arg2) BOOST_REQUIRE_EQUAL(arg1, arg2) + #define TBOOST_CHECK(arg) BOOOST_CHECK(arg) + #define TBOOST_CHECK_EQUAL(arg1, arg2) BOOST_CHECK_EQUAL(arg1, arg2) #define TBOOST_CHECK_MESSAGE(arg1, arg2) BOOST_CHECK_MESSAGE(arg1, arg2) #define TBOOST_WARN_MESSAGE(arg1, arg2) BOOST_WARN_MESSAGE(arg1, arg2) + #define TBOOST_ERROR(arg) BOOST_ERROR(arg) #endif namespace dev @@ -179,6 +187,7 @@ json_spirit::mObject fillJsonWithTransaction(eth::Transaction _txn); //Fill Test Functions void doTransactionTests(json_spirit::mValue& _v, bool _fillin); void doStateTests(json_spirit::mValue& v, bool _fillin); +void doVMTests(json_spirit::mValue& v, bool _fillin); template void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) @@ -188,9 +197,9 @@ void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) auto& resultAddr = resultPair.first; auto expectedAddrIt = _expectedAddrs.find(resultAddr); if (expectedAddrIt == _expectedAddrs.end()) - BOOST_ERROR("Missing result address " << resultAddr); + TBOOST_ERROR("Missing result address " << resultAddr); } - BOOST_CHECK(_expectedAddrs == _resultAddrs); + TBOOST_CHECK((_expectedAddrs == _resultAddrs)); } class Options diff --git a/test/fuzzTesting/CMakeLists.txt b/test/fuzzTesting/CMakeLists.txt index f25a2b31e..9cdc7eb60 100644 --- a/test/fuzzTesting/CMakeLists.txt +++ b/test/fuzzTesting/CMakeLists.txt @@ -8,7 +8,8 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) -add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp" "../libethereum/state.cpp") +add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp" "../libethereum/state.cpp" "../libevm/vm.cpp") + add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp") add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp" ) diff --git a/test/fuzzTesting/createRandomTest.cpp b/test/fuzzTesting/createRandomTest.cpp index 373207f44..a42ead3ef 100644 --- a/test/fuzzTesting/createRandomTest.cpp +++ b/test/fuzzTesting/createRandomTest.cpp @@ -19,18 +19,23 @@ * @date 2015 */ +///This file require #define DONTUSE_BOOST_MACROS compile flag to run! + #include #include #include #include +#include //String Variables extern std::string const c_testExampleStateTest; extern std::string const c_testExampleTransactionTest; +extern std::string const c_testExampleVMTest; //Main Test functinos void fillRandomTest(std::function doTests, std::string const& testString); +int checkRandomTest(std::function doTests, json_spirit::mValue& value); //Helper Functions std::vector getTypes(); @@ -39,35 +44,111 @@ void parseTestWithTypes(std::string& test); int main(int argc, char *argv[]) { std::string testSuite; + json_spirit::mValue testValue; + bool checktest = false; for (auto i = 0; i < argc; ++i) { auto arg = std::string{argv[i]}; dev::test::Options& options = const_cast(dev::test::Options::get()); if (arg == "--fulloutput") options.fulloutput = true; + else if (arg == "-t" && i + 1 < argc) { testSuite = argv[i + 1]; - if (testSuite != "BlockChainTests" && testSuite != "TransactionTests" && testSuite != "StateTests") + if (testSuite != "BlockChainTests" && testSuite != "TransactionTests" && testSuite != "StateTests" && testSuite != "VMTests") testSuite = ""; } + else + if (arg == "-checktest" && i + 1 < argc) + { + std::string s; + for (int j = i+1; j < argc; ++j) + s += argv[j]; + if (asserts(s.length() > 0)) + { + std::cout << "Error! Content of argument is empty! (Usage -checktest textstream) \n"; + return 1; + } + read_string(s, testValue); + checktest = true; + } } if (testSuite == "") + { std::cout << "Error! Test suite not supported! (Usage -t TestSuite)"; + return 1; + } else if (testSuite == "BlockChainTests") - fillRandomTest(dev::test::doTransactionTests, c_testExampleTransactionTest); + { + if (checktest) + return checkRandomTest(dev::test::doTransactionTests, testValue); + else + fillRandomTest(dev::test::doTransactionTests, c_testExampleTransactionTest); + } else if (testSuite == "TransactionTests") - fillRandomTest(dev::test::doTransactionTests, c_testExampleTransactionTest); + { + if (checktest) + return checkRandomTest(dev::test::doTransactionTests, testValue); + else + fillRandomTest(dev::test::doTransactionTests, c_testExampleTransactionTest); + } else if (testSuite == "StateTests") - fillRandomTest(dev::test::doStateTests, c_testExampleStateTest); + { + if (checktest) + return checkRandomTest(dev::test::doStateTests, testValue); + else + fillRandomTest(dev::test::doStateTests, c_testExampleStateTest); + } + else + if (testSuite == "VMTests") + { + if (checktest) + { + dev::eth::VMFactory::setKind(dev::eth::VMKind::JIT); + return checkRandomTest(dev::test::doVMTests, testValue); + } + else + fillRandomTest(dev::test::doVMTests, c_testExampleVMTest); + } return 0; } +int checkRandomTest(std::function doTests, json_spirit::mValue& value) +{ + bool ret = 0; + try + { + //redirect all output to the stream + std::ostringstream strCout; + std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); + std::cout.rdbuf( strCout.rdbuf() ); + std::cerr.rdbuf( strCout.rdbuf() ); + + doTests(value, false); + + //restroe output + std::cout.rdbuf(oldCoutStreamBuf); + std::cerr.rdbuf(oldCoutStreamBuf); + } + catch (dev::Exception const& _e) + { + std::cout << "Failed test with Exception: " << diagnostic_information(_e) << std::endl; + ret = 1; + } + catch (std::exception const& _e) + { + std::cout << "Failed test with Exception: " << _e.what() << std::endl; + ret = 1; + } + return ret; +} + void fillRandomTest(std::function doTests, std::string const& testString) { //redirect all output to the stream @@ -107,6 +188,7 @@ void parseTestWithTypes(std::string& _test) options.addAddress(dev::Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87")); options.addAddress(dev::Address("0x945304eb96065b2a98b57a48a06ae28d285a71b5")); options.addAddress(dev::Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")); + options.addAddress(dev::Address("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")); options.addAddress(dev::Address("0x0000000000000000000000000000000000000001")); options.addAddress(dev::Address("0x0000000000000000000000000000000000000002")); options.addAddress(dev::Address("0x0000000000000000000000000000000000000003")); @@ -138,10 +220,10 @@ void parseTestWithTypes(std::string& _test) { int random = dev::test::RandomCode::randomUniInt() % 100; if (random < 30) - _test.replace(pos, 3, "28"); + _test.replace(pos, 3, "0x1c"); else if (random < 60) - _test.replace(pos, 3, "29"); + _test.replace(pos, 3, "0x1d"); else _test.replace(pos, 3, "0x" + dev::test::RandomCode::rndByteSequence(1)); } @@ -221,3 +303,35 @@ std::string const c_testExampleStateTest = R"( } } )"; + +std::string const c_testExampleVMTest = R"( +{ + "randomVMTest": { + "env" : { + "previousHash" : "[HASH32]", + "currentNumber" : "[HEX]", + "currentGasLimit" : "[HEX]", + "currentDifficulty" : "[HEX]", + "currentTimestamp" : "[HEX]", + "currentCoinbase" : "[HASH20]" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "[HEX]", + "nonce" : "[HEX]", + "code" : "[CODE]", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "[HASH20]", + "caller" : "[HASH20]", + "value" : "[HEX]", + "data" : "[CODE]", + "gasPrice" : "[V]", + "gas" : "[HEX]" + } + } +} +)"; diff --git a/test/libevm/vm.cpp b/test/libevm/vm.cpp index ea9339f05..25ed8113b 100644 --- a/test/libevm/vm.cpp +++ b/test/libevm/vm.cpp @@ -306,9 +306,9 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) } std::cout << " " << i.first << "\n"; - BOOST_REQUIRE(o.count("env") > 0); - BOOST_REQUIRE(o.count("pre") > 0); - BOOST_REQUIRE(o.count("exec") > 0); + TBOOST_REQUIRE((o.count("env") > 0)); + TBOOST_REQUIRE((o.count("pre") > 0)); + TBOOST_REQUIRE((o.count("exec") > 0)); FakeExtVM fev; fev.importEnv(o["env"].get_obj()); @@ -344,12 +344,12 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) catch (Exception const& _e) { cnote << "VM did throw an exception: " << diagnostic_information(_e); - BOOST_ERROR("Failed VM Test with Exception: " << _e.what()); + TBOOST_ERROR("Failed VM Test with Exception: " << _e.what()); } catch (std::exception const& _e) { cnote << "VM did throw an exception: " << _e.what(); - BOOST_ERROR("Failed VM Test with Exception: " << _e.what()); + TBOOST_ERROR("Failed VM Test with Exception: " << _e.what()); } // delete null entries in storage for the sake of comparison @@ -410,13 +410,13 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) { if (o.count("post") > 0) // No exceptions expected { - BOOST_CHECK(!vmExceptionOccured); + TBOOST_CHECK(!vmExceptionOccured); - BOOST_REQUIRE(o.count("post") > 0); - BOOST_REQUIRE(o.count("callcreates") > 0); - BOOST_REQUIRE(o.count("out") > 0); - BOOST_REQUIRE(o.count("gas") > 0); - BOOST_REQUIRE(o.count("logs") > 0); + TBOOST_REQUIRE((o.count("post") > 0)); + TBOOST_REQUIRE((o.count("callcreates") > 0)); + TBOOST_REQUIRE((o.count("out") > 0)); + TBOOST_REQUIRE((o.count("gas") > 0)); + TBOOST_REQUIRE((o.count("logs") > 0)); dev::test::FakeExtVM test; test.importState(o["post"].get_obj()); @@ -425,7 +425,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) checkOutput(output, o); - BOOST_CHECK_EQUAL(toInt(o["gas"]), fev.gas); + TBOOST_CHECK_EQUAL(toInt(o["gas"]), fev.gas); State postState, expectState; mObject mPostState = fev.exportState(); @@ -435,7 +435,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) checkAddresses, bytes> > >(test.addresses, fev.addresses); - checkCallCreates(test.callcreates, fev.callcreates); + checkCallCreates(fev.callcreates, test.callcreates); checkLog(fev.sub.logs, test.sub.logs); } From 5a0f9900677742447e448ab4c7f8112425e262b8 Mon Sep 17 00:00:00 2001 From: Dimitry Date: Tue, 9 Jun 2015 18:52:18 +0300 Subject: [PATCH 60/66] Fuzz Tests update --- test/TestHelper.h | 2 +- test/fuzzTesting/fuzzHelper.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/TestHelper.h b/test/TestHelper.h index b1ad923a7..c97d4c017 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -45,7 +45,7 @@ #define TBOOST_THROW_EXCEPTION(arg) BOOST_THROW_EXCEPTION(arg) #define TBOOST_REQUIRE(arg) BOOST_REQUIRE(arg) #define TBOOST_REQUIRE_EQUAL(arg1, arg2) BOOST_REQUIRE_EQUAL(arg1, arg2) - #define TBOOST_CHECK(arg) BOOOST_CHECK(arg) + #define TBOOST_CHECK(arg) BOOST_CHECK(arg) #define TBOOST_CHECK_EQUAL(arg1, arg2) BOOST_CHECK_EQUAL(arg1, arg2) #define TBOOST_CHECK_MESSAGE(arg1, arg2) BOOST_CHECK_MESSAGE(arg1, arg2) #define TBOOST_WARN_MESSAGE(arg1, arg2) BOOST_WARN_MESSAGE(arg1, arg2) diff --git a/test/fuzzTesting/fuzzHelper.cpp b/test/fuzzTesting/fuzzHelper.cpp index 1f09ca9fa..60d93caf0 100644 --- a/test/fuzzTesting/fuzzHelper.cpp +++ b/test/fuzzTesting/fuzzHelper.cpp @@ -50,7 +50,7 @@ std::string RandomCode::rndByteSequence(int _length, SizeStrictness _sizeType) for (auto i = 0; i < _length; i++) { uint8_t byte = randOpCodeGen(); - hash += toCompactHex(byte); + hash += toCompactHex(byte, HexPrefix::DontAdd, 1); } return hash; } From 7db380d341e3edfa6db009362659f9a10f0f51eb Mon Sep 17 00:00:00 2001 From: Dimitry Date: Wed, 17 Jun 2015 17:01:25 +0300 Subject: [PATCH 61/66] FuzzTests: blocktests (before merge) --- test/TestHelper.h | 1 + test/fuzzTesting/CMakeLists.txt | 2 +- test/fuzzTesting/createRandomTest.cpp | 93 +++++++++++++++---- test/fuzzTesting/fuzzHelper.cpp | 8 +- test/fuzzTesting/fuzzHelper.h | 2 +- test/libethereum/blockchain.cpp | 128 +++++++++++++------------- 6 files changed, 149 insertions(+), 85 deletions(-) diff --git a/test/TestHelper.h b/test/TestHelper.h index c97d4c017..42f85a82c 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -188,6 +188,7 @@ json_spirit::mObject fillJsonWithTransaction(eth::Transaction _txn); void doTransactionTests(json_spirit::mValue& _v, bool _fillin); void doStateTests(json_spirit::mValue& v, bool _fillin); void doVMTests(json_spirit::mValue& v, bool _fillin); +void doBlockchainTests(json_spirit::mValue& _v, bool _fillin); template void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) diff --git a/test/fuzzTesting/CMakeLists.txt b/test/fuzzTesting/CMakeLists.txt index 9cdc7eb60..9bd2b5540 100644 --- a/test/fuzzTesting/CMakeLists.txt +++ b/test/fuzzTesting/CMakeLists.txt @@ -8,7 +8,7 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) -add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp" "../libethereum/state.cpp" "../libevm/vm.cpp") +add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp" "../libethereum/state.cpp" "../libevm/vm.cpp" "../libethereum/blockchain.cpp") add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp") add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp") diff --git a/test/fuzzTesting/createRandomTest.cpp b/test/fuzzTesting/createRandomTest.cpp index a42ead3ef..34f90533f 100644 --- a/test/fuzzTesting/createRandomTest.cpp +++ b/test/fuzzTesting/createRandomTest.cpp @@ -27,11 +27,13 @@ #include #include #include +#include //String Variables extern std::string const c_testExampleStateTest; extern std::string const c_testExampleTransactionTest; extern std::string const c_testExampleVMTest; +extern std::string const c_testExampleBlockchainTest; //Main Test functinos void fillRandomTest(std::function doTests, std::string const& testString); @@ -44,7 +46,7 @@ void parseTestWithTypes(std::string& test); int main(int argc, char *argv[]) { std::string testSuite; - json_spirit::mValue testValue; + json_spirit::mValue testmValue; bool checktest = false; for (auto i = 0; i < argc; ++i) { @@ -70,7 +72,7 @@ int main(int argc, char *argv[]) std::cout << "Error! Content of argument is empty! (Usage -checktest textstream) \n"; return 1; } - read_string(s, testValue); + read_string(s, testmValue); checktest = true; } } @@ -84,15 +86,15 @@ int main(int argc, char *argv[]) if (testSuite == "BlockChainTests") { if (checktest) - return checkRandomTest(dev::test::doTransactionTests, testValue); + return checkRandomTest(dev::test::doBlockchainTests, testmValue); else - fillRandomTest(dev::test::doTransactionTests, c_testExampleTransactionTest); + fillRandomTest(dev::test::doBlockchainTests, c_testExampleBlockchainTest); } else if (testSuite == "TransactionTests") { if (checktest) - return checkRandomTest(dev::test::doTransactionTests, testValue); + return checkRandomTest(dev::test::doTransactionTests, testmValue); else fillRandomTest(dev::test::doTransactionTests, c_testExampleTransactionTest); } @@ -100,7 +102,7 @@ int main(int argc, char *argv[]) if (testSuite == "StateTests") { if (checktest) - return checkRandomTest(dev::test::doStateTests, testValue); + return checkRandomTest(dev::test::doStateTests, testmValue); else fillRandomTest(dev::test::doStateTests, c_testExampleStateTest); } @@ -110,7 +112,7 @@ int main(int argc, char *argv[]) if (checktest) { dev::eth::VMFactory::setKind(dev::eth::VMKind::JIT); - return checkRandomTest(dev::test::doVMTests, testValue); + return checkRandomTest(dev::test::doVMTests, testmValue); } else fillRandomTest(dev::test::doVMTests, c_testExampleVMTest); @@ -152,16 +154,17 @@ int checkRandomTest(std::function doTests, jso void fillRandomTest(std::function doTests, std::string const& testString) { //redirect all output to the stream - std::ostringstream strCout; - std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); - std::cout.rdbuf( strCout.rdbuf() ); - std::cerr.rdbuf( strCout.rdbuf() ); + //std::ostringstream strCout; + //std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); + //std::cout.rdbuf( strCout.rdbuf() ); + //std::cerr.rdbuf( strCout.rdbuf() ); json_spirit::mValue v; try { std::string newTest = testString; parseTestWithTypes(newTest); + std::cout << newTest; json_spirit::read_string(newTest, v); doTests(v, true); } @@ -171,8 +174,8 @@ void fillRandomTest(std::function doTests, std } //restroe output - std::cout.rdbuf(oldCoutStreamBuf); - std::cerr.rdbuf(oldCoutStreamBuf); + //std::cout.rdbuf(oldCoutStreamBuf); + //std::cerr.rdbuf(oldCoutStreamBuf); std::cout << json_spirit::write_string(v, true); } @@ -207,6 +210,9 @@ void parseTestWithTypes(std::string& _test) if (types.at(i) == "[HEX]") _test.replace(pos, 5, dev::test::RandomCode::randomUniIntHex()); else + if (types.at(i) == "[GASLIMIT]") + _test.replace(pos, 10, dev::test::RandomCode::randomUniIntHex(dev::u256("3000000000"))); + else if (types.at(i) == "[HASH20]") _test.replace(pos, 8, dev::test::RandomCode::rndByteSequence(20)); else @@ -235,7 +241,7 @@ void parseTestWithTypes(std::string& _test) std::vector getTypes() { - return {"[CODE]", "[HEX]", "[HASH20]", "[HASH32]", "[0xHASH32]", "[V]"}; + return {"[CODE]", "[HEX]", "[HASH20]", "[HASH32]", "[0xHASH32]", "[V]", "[GASLIMIT]"}; } std::string const c_testExampleTransactionTest = R"( @@ -263,7 +269,7 @@ std::string const c_testExampleStateTest = R"( "env" : { "currentCoinbase" : "[HASH20]", "currentDifficulty" : "[HEX]", - "currentGasLimit" : "[HEX]", + "currentGasLimit" : "[GASLIMIT]", "currentNumber" : "[HEX]", "currentTimestamp" : "[HEX]", "previousHash" : "[HASH32]" @@ -310,7 +316,7 @@ std::string const c_testExampleVMTest = R"( "env" : { "previousHash" : "[HASH32]", "currentNumber" : "[HEX]", - "currentGasLimit" : "[HEX]", + "currentGasLimit" : "[GASLIMIT]", "currentDifficulty" : "[HEX]", "currentTimestamp" : "[HEX]", "currentCoinbase" : "[HASH20]" @@ -335,3 +341,58 @@ std::string const c_testExampleVMTest = R"( } } )"; + +std::string const c_testExampleBlockchainTest = R"( +{ + "blockTest" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "[HASH20]", + "difficulty" : "131072", + "extraData" : "[CODE]", + "gasLimit" : "3141592", + "gasUsed" : "0", + "mixHash" : "[0xHASH32]", + "nonce" : "0x0102030405060708", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "[0xHASH32]", + "stateRoot" : "[0xHASH32]", + "timestamp" : "[HEX]", + "transactionsTrie" : "[0xHASH32]", + "uncleHash" : "[0xHASH32]" + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "[HEX]", + "nonce" : "0", + "code" : "", + "storage": {} + }, + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "[HEX]", + "nonce" : "[HEX]", + "code" : "[CODE]", + "storage": {} + } + }, + "blocks" : [ + { + "transactions" : [ + { + "data" : "[CODE]", + "gasLimit" : "[HEX]", + "gasPrice" : "[V]", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "[V]" + } + ], + "uncleHeaders" : [ + ] + } + ] + } +} +)"; diff --git a/test/fuzzTesting/fuzzHelper.cpp b/test/fuzzTesting/fuzzHelper.cpp index 60d93caf0..8e03bd767 100644 --- a/test/fuzzTesting/fuzzHelper.cpp +++ b/test/fuzzTesting/fuzzHelper.cpp @@ -91,13 +91,15 @@ std::string RandomCode::generate(int _maxOpNumber, RandomCodeOptions _options) return code; } -std::string RandomCode::randomUniIntHex() +std::string RandomCode::randomUniIntHex(u256 _maxVal) { + if (_maxVal == 0) + _maxVal = std::numeric_limits::max(); refreshSeed(); int rand = randUniIntGen() % 100; if (rand < 50) - return "0x" + toCompactHex((u256)randUniIntGen()); - return "0x" + toCompactHex((u256)randUInt64Gen()); + return "0x" + toCompactHex((u256)randUniIntGen() % _maxVal); + return "0x" + toCompactHex((u256)randUInt64Gen() % _maxVal); } int RandomCode::randomUniInt() diff --git a/test/fuzzTesting/fuzzHelper.h b/test/fuzzTesting/fuzzHelper.h index 6a46bbeaf..309037e3d 100644 --- a/test/fuzzTesting/fuzzHelper.h +++ b/test/fuzzTesting/fuzzHelper.h @@ -76,7 +76,7 @@ public: static std::string rndByteSequence(int _length = 1, SizeStrictness _sizeType = SizeStrictness::Strict); /// Generate random int64 - static std::string randomUniIntHex(); + static std::string randomUniIntHex(u256 _maxVal = 0); static int randomUniInt(); private: diff --git a/test/libethereum/blockchain.cpp b/test/libethereum/blockchain.cpp index 2c4a0b498..88c39442b 100644 --- a/test/libethereum/blockchain.cpp +++ b/test/libethereum/blockchain.cpp @@ -59,10 +59,10 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) } cerr << i.first << endl; - BOOST_REQUIRE(o.count("genesisBlockHeader")); + TBOOST_REQUIRE(o.count("genesisBlockHeader")); BlockInfo biGenesisBlock = constructBlock(o["genesisBlockHeader"].get_obj()); - BOOST_REQUIRE(o.count("pre")); + TBOOST_REQUIRE(o.count("pre")); ImportTest importer(o["pre"].get_obj()); TransientDirectory td_stateDB_tmp; State trueState(OverlayDB(State::openDB(td_stateDB_tmp.path())), BaseState::Empty, biGenesisBlock.coinbaseAddress); @@ -77,7 +77,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) if (_fillin) biGenesisBlock.stateRoot = trueState.rootHash(); else - BOOST_CHECK_MESSAGE(biGenesisBlock.stateRoot == trueState.rootHash(), "root hash does not match"); + TBOOST_CHECK_MESSAGE((biGenesisBlock.stateRoot == trueState.rootHash()), "root hash does not match"); if (_fillin) { @@ -99,7 +99,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) if (_fillin) { - BOOST_REQUIRE(o.count("blocks")); + TBOOST_REQUIRE(o.count("blocks")); mArray blArray; blockSet genesis; @@ -145,7 +145,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) // get txs TransactionQueue txs; ZeroGasPricer gp; - BOOST_REQUIRE(blObj.count("transactions")); + TBOOST_REQUIRE(blObj.count("transactions")); for (auto const& txObj: blObj["transactions"].get_array()) { mObject tx = txObj.get_obj(); @@ -337,29 +337,29 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) catch (Exception const& _e) { cnote << "state sync or block import did throw an exception: " << diagnostic_information(_e); - BOOST_CHECK(blObj.count("blockHeader") == 0); - BOOST_CHECK(blObj.count("transactions") == 0); - BOOST_CHECK(blObj.count("uncleHeaders") == 0); + TBOOST_CHECK((blObj.count("blockHeader") == 0)); + TBOOST_CHECK((blObj.count("transactions") == 0)); + TBOOST_CHECK((blObj.count("uncleHeaders") == 0)); continue; } catch (std::exception const& _e) { cnote << "state sync or block import did throw an exception: " << _e.what(); - BOOST_CHECK(blObj.count("blockHeader") == 0); - BOOST_CHECK(blObj.count("transactions") == 0); - BOOST_CHECK(blObj.count("uncleHeaders") == 0); + TBOOST_CHECK((blObj.count("blockHeader") == 0)); + TBOOST_CHECK((blObj.count("transactions") == 0)); + TBOOST_CHECK((blObj.count("uncleHeaders") == 0)); continue; } catch (...) { cnote << "state sync or block import did throw an exception\n"; - BOOST_CHECK(blObj.count("blockHeader") == 0); - BOOST_CHECK(blObj.count("transactions") == 0); - BOOST_CHECK(blObj.count("uncleHeaders") == 0); + TBOOST_CHECK((blObj.count("blockHeader") == 0)); + TBOOST_CHECK((blObj.count("transactions") == 0)); + TBOOST_CHECK((blObj.count("uncleHeaders") == 0)); continue; } - BOOST_REQUIRE(blObj.count("blockHeader")); + TBOOST_REQUIRE(blObj.count("blockHeader")); mObject tObj = blObj["blockHeader"].get_obj(); BlockInfo blockHeaderFromFields; @@ -372,24 +372,24 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) if (importedAndBest) { //Check the fields restored from RLP to original fields - BOOST_CHECK_MESSAGE(blockHeaderFromFields.headerHash(WithNonce) == blockFromRlp.headerHash(WithNonce), "hash in given RLP not matching the block hash!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.parentHash == blockFromRlp.parentHash, "parentHash in given RLP not matching the block parentHash!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.sha3Uncles == blockFromRlp.sha3Uncles, "sha3Uncles in given RLP not matching the block sha3Uncles!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.coinbaseAddress == blockFromRlp.coinbaseAddress,"coinbaseAddress in given RLP not matching the block coinbaseAddress!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.stateRoot == blockFromRlp.stateRoot, "stateRoot in given RLP not matching the block stateRoot!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.transactionsRoot == blockFromRlp.transactionsRoot, "transactionsRoot in given RLP not matching the block transactionsRoot!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.receiptsRoot == blockFromRlp.receiptsRoot, "receiptsRoot in given RLP not matching the block receiptsRoot!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.logBloom == blockFromRlp.logBloom, "logBloom in given RLP not matching the block logBloom!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.difficulty == blockFromRlp.difficulty, "difficulty in given RLP not matching the block difficulty!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.number == blockFromRlp.number, "number in given RLP not matching the block number!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.gasLimit == blockFromRlp.gasLimit,"gasLimit in given RLP not matching the block gasLimit!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.gasUsed == blockFromRlp.gasUsed, "gasUsed in given RLP not matching the block gasUsed!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.timestamp == blockFromRlp.timestamp, "timestamp in given RLP not matching the block timestamp!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.extraData == blockFromRlp.extraData, "extraData in given RLP not matching the block extraData!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.mixHash == blockFromRlp.mixHash, "mixHash in given RLP not matching the block mixHash!"); - BOOST_CHECK_MESSAGE(blockHeaderFromFields.nonce == blockFromRlp.nonce, "nonce in given RLP not matching the block nonce!"); - - BOOST_CHECK_MESSAGE(blockHeaderFromFields == blockFromRlp, "However, blockHeaderFromFields != blockFromRlp!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.headerHash(WithNonce) == blockFromRlp.headerHash(WithNonce)), "hash in given RLP not matching the block hash!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.parentHash == blockFromRlp.parentHash), "parentHash in given RLP not matching the block parentHash!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.sha3Uncles == blockFromRlp.sha3Uncles), "sha3Uncles in given RLP not matching the block sha3Uncles!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.coinbaseAddress == blockFromRlp.coinbaseAddress),"coinbaseAddress in given RLP not matching the block coinbaseAddress!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.stateRoot == blockFromRlp.stateRoot), "stateRoot in given RLP not matching the block stateRoot!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.transactionsRoot == blockFromRlp.transactionsRoot), "transactionsRoot in given RLP not matching the block transactionsRoot!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.receiptsRoot == blockFromRlp.receiptsRoot), "receiptsRoot in given RLP not matching the block receiptsRoot!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.logBloom == blockFromRlp.logBloom), "logBloom in given RLP not matching the block logBloom!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.difficulty == blockFromRlp.difficulty), "difficulty in given RLP not matching the block difficulty!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.number == blockFromRlp.number), "number in given RLP not matching the block number!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.gasLimit == blockFromRlp.gasLimit),"gasLimit in given RLP not matching the block gasLimit!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.gasUsed == blockFromRlp.gasUsed), "gasUsed in given RLP not matching the block gasUsed!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.timestamp == blockFromRlp.timestamp), "timestamp in given RLP not matching the block timestamp!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.extraData == blockFromRlp.extraData), "extraData in given RLP not matching the block extraData!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.mixHash == blockFromRlp.mixHash), "mixHash in given RLP not matching the block mixHash!"); + TBOOST_CHECK_MESSAGE((blockHeaderFromFields.nonce == blockFromRlp.nonce), "nonce in given RLP not matching the block nonce!"); + + TBOOST_CHECK_MESSAGE((blockHeaderFromFields == blockFromRlp), "However, blockHeaderFromFields != blockFromRlp!"); //Check transaction list @@ -399,15 +399,15 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) { mObject tx = txObj.get_obj(); - BOOST_REQUIRE(tx.count("nonce")); - BOOST_REQUIRE(tx.count("gasPrice")); - BOOST_REQUIRE(tx.count("gasLimit")); - BOOST_REQUIRE(tx.count("to")); - BOOST_REQUIRE(tx.count("value")); - BOOST_REQUIRE(tx.count("v")); - BOOST_REQUIRE(tx.count("r")); - BOOST_REQUIRE(tx.count("s")); - BOOST_REQUIRE(tx.count("data")); + TBOOST_REQUIRE(tx.count("nonce")); + TBOOST_REQUIRE(tx.count("gasPrice")); + TBOOST_REQUIRE(tx.count("gasLimit")); + TBOOST_REQUIRE(tx.count("to")); + TBOOST_REQUIRE(tx.count("value")); + TBOOST_REQUIRE(tx.count("v")); + TBOOST_REQUIRE(tx.count("r")); + TBOOST_REQUIRE(tx.count("s")); + TBOOST_REQUIRE(tx.count("data")); try { @@ -416,7 +416,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) } catch (Exception const& _e) { - BOOST_ERROR("Failed transaction constructor with Exception: " << diagnostic_information(_e)); + TBOOST_ERROR("Failed transaction constructor with Exception: " << diagnostic_information(_e)); } catch (exception const& _e) { @@ -432,22 +432,22 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) txsFromRlp.push_back(tx); } - BOOST_CHECK_MESSAGE(txsFromRlp.size() == txsFromField.size(), "transaction list size does not match"); + TBOOST_CHECK_MESSAGE((txsFromRlp.size() == txsFromField.size()), "transaction list size does not match"); for (size_t i = 0; i < txsFromField.size(); ++i) { - BOOST_CHECK_MESSAGE(txsFromField[i].data() == txsFromRlp[i].data(), "transaction data in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].gas() == txsFromRlp[i].gas(), "transaction gasLimit in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].gasPrice() == txsFromRlp[i].gasPrice(), "transaction gasPrice in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].nonce() == txsFromRlp[i].nonce(), "transaction nonce in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].signature().r == txsFromRlp[i].signature().r, "transaction r in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].signature().s == txsFromRlp[i].signature().s, "transaction s in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].signature().v == txsFromRlp[i].signature().v, "transaction v in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].receiveAddress() == txsFromRlp[i].receiveAddress(), "transaction receiveAddress in rlp and in field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].value() == txsFromRlp[i].value(), "transaction receiveAddress in rlp and in field do not match"); - - BOOST_CHECK_MESSAGE(txsFromField[i] == txsFromRlp[i], "transactions from rlp and transaction from field do not match"); - BOOST_CHECK_MESSAGE(txsFromField[i].rlp() == txsFromRlp[i].rlp(), "transactions rlp do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].data() == txsFromRlp[i].data()), "transaction data in rlp and in field do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].gas() == txsFromRlp[i].gas()), "transaction gasLimit in rlp and in field do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].gasPrice() == txsFromRlp[i].gasPrice()), "transaction gasPrice in rlp and in field do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].nonce() == txsFromRlp[i].nonce()), "transaction nonce in rlp and in field do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].signature().r == txsFromRlp[i].signature().r), "transaction r in rlp and in field do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].signature().s == txsFromRlp[i].signature().s), "transaction s in rlp and in field do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].signature().v == txsFromRlp[i].signature().v), "transaction v in rlp and in field do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].receiveAddress() == txsFromRlp[i].receiveAddress()), "transaction receiveAddress in rlp and in field do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].value() == txsFromRlp[i].value()), "transaction receiveAddress in rlp and in field do not match"); + + TBOOST_CHECK_MESSAGE((txsFromField[i] == txsFromRlp[i]), "transactions from rlp and transaction from field do not match"); + TBOOST_CHECK_MESSAGE((txsFromField[i].rlp() == txsFromRlp[i].rlp()), "transactions rlp do not match"); } // check uncle list @@ -458,7 +458,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) for (auto const& uBlHeaderObj: blObj["uncleHeaders"].get_array()) { mObject uBlH = uBlHeaderObj.get_obj(); - BOOST_REQUIRE(uBlH.size() == 16); + TBOOST_REQUIRE((uBlH.size() == 16)); bytes uncleRLP = createBlockRLPFromFields(uBlH); const RLP c_uRLP(uncleRLP); BlockInfo uncleBlockHeader; @@ -468,7 +468,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) } catch(...) { - BOOST_ERROR("invalid uncle header"); + TBOOST_ERROR("invalid uncle header"); } uBlHsFromField.push_back(uncleBlockHeader); } @@ -482,15 +482,15 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) uBlHsFromRlp.push_back(uBl); } - BOOST_REQUIRE_EQUAL(uBlHsFromField.size(), uBlHsFromRlp.size()); + TBOOST_REQUIRE_EQUAL(uBlHsFromField.size(), uBlHsFromRlp.size()); for (size_t i = 0; i < uBlHsFromField.size(); ++i) - BOOST_CHECK_MESSAGE(uBlHsFromField[i] == uBlHsFromRlp[i], "block header in rlp and in field do not match"); + TBOOST_CHECK_MESSAGE((uBlHsFromField[i] == uBlHsFromRlp[i]), "block header in rlp and in field do not match"); }//importedAndBest }//all blocks - BOOST_REQUIRE(o.count("lastblockhash") > 0); - BOOST_CHECK_MESSAGE(toString(trueBc.info().hash()) == o["lastblockhash"].get_str(), + TBOOST_REQUIRE((o.count("lastblockhash") > 0)); + TBOOST_CHECK_MESSAGE((toString(trueBc.info().hash()) == o["lastblockhash"].get_str()), "Boost check: " + i.first + " lastblockhash does not match " + toString(trueBc.info().hash()) + " expected: " + o["lastblockhash"].get_str()); } } @@ -713,11 +713,11 @@ BlockInfo constructBlock(mObject& _o) } catch (std::exception const& _e) { - BOOST_ERROR("Failed block population with Exception: " << _e.what()); + TBOOST_ERROR("Failed block population with Exception: " << _e.what()); } catch(...) { - BOOST_ERROR("block population did throw an unknown exception\n"); + TBOOST_ERROR("block population did throw an unknown exception\n"); } return ret; } From e617bfb0f7b2854acb00ae90afb3f1a223502210 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 17 Jun 2015 17:15:30 +0200 Subject: [PATCH 62/66] Corrected typo about max peer count. --- libp2p/Host.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index a0d8e1297..feb116c4a 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -391,7 +391,7 @@ void Host::runAcceptor() { if (peerCount() > 9 * m_idealPeerCount) { - clog(NetConnect) << "Dropping incoming connect due to maximum peer count (2 * ideal peer count): " << socket->remoteEndpoint(); + clog(NetConnect) << "Dropping incoming connect due to maximum peer count (9 * ideal peer count): " << socket->remoteEndpoint(); socket->close(); if (ec.value() < 1) runAcceptor(); From 2b16073d5b11a004309003cc5dcb12238c5f5fd4 Mon Sep 17 00:00:00 2001 From: Dimitry Date: Wed, 17 Jun 2015 18:31:14 +0300 Subject: [PATCH 63/66] FuzzTests: simple block test + NOBOOST flag --- CMakeLists.txt | 5 +++++ test/TestHelper.h | 3 +-- test/fuzzTesting/createRandomTest.cpp | 23 +++++++++++------------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 729f95ed6..904badcf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ option(FATDB "Build with ability to list entries in the Trie. Doubles DB size, s option(USENPM "Use npm to recompile ethereum.js if it was changed" OFF) option(PROFILING "Build in support for profiling" OFF) + set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).") option(MINER "Build the CLI miner component" ON) option(ETHKEY "Build the CLI key manager component" ON) @@ -40,6 +41,7 @@ option(TOOLS "Build the tools components" ON) option(NCURSES "Build the NCurses components" OFF) option(GUI "Build GUI components (AlethZero, Mix)" ON) option(TESTS "Build the tests." ON) +option(NOBOOST "No use of boost macros in test functions" OFF) option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF) option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF) option(JSCONSOLE "Build in javascript console" OFF) @@ -82,6 +84,7 @@ function(configureProject) add_definitions(-DETH_CURL) endif() + add_definitions(-DNOBOOST) add_definitions(-DETH_TRUE) endfunction() @@ -195,6 +198,7 @@ eth_format_option(PROFILING) eth_format_option(SOLIDITY) eth_format_option(GUI) eth_format_option(TESTS) +eth_format_option(NOBOOST) eth_format_option(TOOLS) eth_format_option(ETHASHCL) eth_format_option(JSCONSOLE) @@ -316,6 +320,7 @@ message("-- SERPENT Build Serpent language components ${SERPENT} message("-- GUI Build GUI components ${GUI}") message("-- NCURSES Build NCurses components ${NCURSES}") message("-- TESTS Build tests ${TESTS}") +message("-- NOBOOST No BOOST macros in test functions ${NOBOOST}") message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}") message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}") message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}") diff --git a/test/TestHelper.h b/test/TestHelper.h index 42f85a82c..df33c00d8 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -31,8 +31,7 @@ #include #include -#define DONTUSE_BOOST_MACROS -#ifdef DONTUSE_BOOST_MACROS +#ifdef NOBOOST #define TBOOST_THROW_EXCEPTION(arg) throw dev::Exception(); #define TBOOST_REQUIRE(arg) if(arg == false) throw dev::Exception(); #define TBOOST_REQUIRE_EQUAL(arg1, arg2) if(arg1 != arg2) throw dev::Exception(); diff --git a/test/fuzzTesting/createRandomTest.cpp b/test/fuzzTesting/createRandomTest.cpp index 34f90533f..e21f22eea 100644 --- a/test/fuzzTesting/createRandomTest.cpp +++ b/test/fuzzTesting/createRandomTest.cpp @@ -154,17 +154,16 @@ int checkRandomTest(std::function doTests, jso void fillRandomTest(std::function doTests, std::string const& testString) { //redirect all output to the stream - //std::ostringstream strCout; - //std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); - //std::cout.rdbuf( strCout.rdbuf() ); - //std::cerr.rdbuf( strCout.rdbuf() ); + std::ostringstream strCout; + std::streambuf* oldCoutStreamBuf = std::cout.rdbuf(); + std::cout.rdbuf( strCout.rdbuf() ); + std::cerr.rdbuf( strCout.rdbuf() ); json_spirit::mValue v; try { std::string newTest = testString; parseTestWithTypes(newTest); - std::cout << newTest; json_spirit::read_string(newTest, v); doTests(v, true); } @@ -174,8 +173,8 @@ void fillRandomTest(std::function doTests, std } //restroe output - //std::cout.rdbuf(oldCoutStreamBuf); - //std::cerr.rdbuf(oldCoutStreamBuf); + std::cout.rdbuf(oldCoutStreamBuf); + std::cerr.rdbuf(oldCoutStreamBuf); std::cout << json_spirit::write_string(v, true); } @@ -246,7 +245,7 @@ std::vector getTypes() std::string const c_testExampleTransactionTest = R"( { -"TransactionTest" : { + "randomTransactionTest" : { "transaction" : { "data" : "[CODE]", @@ -344,7 +343,7 @@ std::string const c_testExampleVMTest = R"( std::string const c_testExampleBlockchainTest = R"( { - "blockTest" : { + "randomBlockTest" : { "genesisBlockHeader" : { "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "coinbase" : "[HASH20]", @@ -359,8 +358,8 @@ std::string const c_testExampleBlockchainTest = R"( "receiptTrie" : "[0xHASH32]", "stateRoot" : "[0xHASH32]", "timestamp" : "[HEX]", - "transactionsTrie" : "[0xHASH32]", - "uncleHash" : "[0xHASH32]" + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" }, "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -371,7 +370,7 @@ std::string const c_testExampleBlockchainTest = R"( }, "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "balance" : "[HEX]", - "nonce" : "[HEX]", + "nonce" : "0", "code" : "[CODE]", "storage": {} } From 5390c358b725a457a54e8c77c714d7f9d159906f Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 17 Jun 2015 17:57:37 +0200 Subject: [PATCH 64/66] OpenCL bin2h script correction - The script to turn the source into a bytearray header is no longer a function but is instead the body of a script so that it's callable as an external cmake command - Spaces -> Tabs in the touched cmake files --- libethash-cl/CMakeLists.txt | 27 +++++----- libethash-cl/bin2h.cmake | 100 +++++++++++++++++------------------- 2 files changed, 62 insertions(+), 65 deletions(-) diff --git a/libethash-cl/CMakeLists.txt b/libethash-cl/CMakeLists.txt index 6533bf794..6da254cfb 100644 --- a/libethash-cl/CMakeLists.txt +++ b/libethash-cl/CMakeLists.txt @@ -1,22 +1,23 @@ set(EXECUTABLE ethash-cl) -include(bin2h.cmake) -bin2h(SOURCE_FILE ethash_cl_miner_kernel.cl - VARIABLE_NAME ethash_cl_miner_kernel - HEADER_FILE ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h) - -# Add a custom command so that the user can regenerate the header file containing the kernel -# code's bytearray by running "make clbin2h" - add_custom_target(clbin2h) - add_custom_command(TARGET clbin2h - PRE_BUILD - COMMAND ${CMAKE_COMMAND} -DBIN2H_SOURCE_FILE="${CMAKE_CURRENT_SOURCE_DIR}/ethash_cl_miner_kernel.cl" +# A custom command and target to turn the OpenCL kernel into a byte array header +# The normal build depends on it properly and if the kernel file is changed, then +# a rebuild of libethash-cl should be triggered +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h + COMMAND ${CMAKE_COMMAND} ARGS + -DBIN2H_SOURCE_FILE="${CMAKE_CURRENT_SOURCE_DIR}/ethash_cl_miner_kernel.cl" -DBIN2H_VARIABLE_NAME=ethash_cl_miner_kernel -DBIN2H_HEADER_FILE="${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h" - -P "${CMAKE_CURRENT_SOURCE_DIR}/bin2h.cmake") + -P "${CMAKE_CURRENT_SOURCE_DIR}/bin2h.cmake" + COMMENT "Generating OpenCL Kernel Byte Array" + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ethash_cl_miner_kernel.cl +) +add_custom_target(clbin2h DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h ${CMAKE_CURRENT_SOURCE_DIR}/ethash_cl_miner_kernel.cl) aux_source_directory(. SRC_LIST) -file(GLOB HEADERS "*.h") +file(GLOB OUR_HEADERS "*.h") +set(HEADERS ${OUR_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${OpenCL_INCLUDE_DIRS}) diff --git a/libethash-cl/bin2h.cmake b/libethash-cl/bin2h.cmake index 90ca9cc5b..27ab4eefa 100644 --- a/libethash-cl/bin2h.cmake +++ b/libethash-cl/bin2h.cmake @@ -6,31 +6,31 @@ include(CMakeParseArguments) # VARIABLE - The name of the CMake variable holding the string. # AT_COLUMN - The column position at which string will be wrapped. function(WRAP_STRING) - set(oneValueArgs VARIABLE AT_COLUMN) - cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN}) + set(oneValueArgs VARIABLE AT_COLUMN) + cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN}) - string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength) - math(EXPR offset "0") + string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength) + math(EXPR offset "0") - while(stringLength GREATER 0) + while(stringLength GREATER 0) - if(stringLength GREATER ${WRAP_STRING_AT_COLUMN}) - math(EXPR length "${WRAP_STRING_AT_COLUMN}") - else() - math(EXPR length "${stringLength}") - endif() + if(stringLength GREATER ${WRAP_STRING_AT_COLUMN}) + math(EXPR length "${WRAP_STRING_AT_COLUMN}") + else() + math(EXPR length "${stringLength}") + endif() - string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line) - set(lines "${lines}\n${line}") + string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line) + set(lines "${lines}\n${line}") - math(EXPR stringLength "${stringLength} - ${length}") - math(EXPR offset "${offset} + ${length}") - endwhile() + math(EXPR stringLength "${stringLength} - ${length}") + math(EXPR offset "${offset} + ${length}") + endwhile() - set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE) + set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE) endfunction() -# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file +# Script to embed contents of a file as byte array in C/C++ header file(.h). The header file # will contain a byte array and integer variable holding the size of the array. # Parameters # SOURCE_FILE - The path of source file whose contents will be embedded in the header file. @@ -42,45 +42,41 @@ endfunction() # useful if the source file is a text file and we want to use the file contents # as string. But the size variable holds size of the byte array without this # null byte. -# Usage: -# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG") -function(BIN2H) - set(options APPEND NULL_TERMINATE) - set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE) - cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN}) +set(options APPEND NULL_TERMINATE) +set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE) +# cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN}) - # reads source file contents as hex string - file(READ ${BIN2H_SOURCE_FILE} hexString HEX) - string(LENGTH ${hexString} hexStringLength) +# reads source file contents as hex string +file(READ ${BIN2H_SOURCE_FILE} hexString HEX) +string(LENGTH ${hexString} hexStringLength) - # appends null byte if asked - if(BIN2H_NULL_TERMINATE) - set(hexString "${hexString}00") - endif() +# appends null byte if asked +if(BIN2H_NULL_TERMINATE) + set(hexString "${hexString}00") +endif() - # wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line) - wrap_string(VARIABLE hexString AT_COLUMN 32) - math(EXPR arraySize "${hexStringLength} / 2") +# wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line) +wrap_string(VARIABLE hexString AT_COLUMN 32) +math(EXPR arraySize "${hexStringLength} / 2") - # adds '0x' prefix and comma suffix before and after every byte respectively - string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString}) - # removes trailing comma - string(REGEX REPLACE ", $" "" arrayValues ${arrayValues}) +# adds '0x' prefix and comma suffix before and after every byte respectively +string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString}) +# removes trailing comma +string(REGEX REPLACE ", $" "" arrayValues ${arrayValues}) - # converts the variable name into proper C identifier - IF (${CMAKE_VERSION} GREATER 2.8.10) # fix for legacy cmake - string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) - ENDIF() - string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) +# converts the variable name into proper C identifier +IF (${CMAKE_VERSION} GREATER 2.8.10) # fix for legacy cmake + string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) +ENDIF() +string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) - # declares byte array and the length variables - set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };") - set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};") +# declares byte array and the length variables +set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };") +set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};") - set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n") - if(BIN2H_APPEND) - file(APPEND ${BIN2H_HEADER_FILE} "${declarations}") - else() - file(WRITE ${BIN2H_HEADER_FILE} "${declarations}") - endif() -endfunction() +set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n") +if(BIN2H_APPEND) + file(APPEND ${BIN2H_HEADER_FILE} "${declarations}") +else() + file(WRITE ${BIN2H_HEADER_FILE} "${declarations}") +endif() From faafba12fa4d8696e57760b9367ac80d3d23e94f Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 17 Jun 2015 18:51:29 +0200 Subject: [PATCH 65/66] Storage array reference test. --- test/libsolidity/SolidityEndToEndTest.cpp | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f4d875e77..66a8f8820 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -4257,6 +4257,59 @@ BOOST_AUTO_TEST_CASE(return_string) // BOOST_CHECK(callContractFunction("s()") == args); } +BOOST_AUTO_TEST_CASE(storage_array_ref) +{ + char const* sourceCode = R"( + contract BinarySearch { + /// Finds the position of _value in the sorted list _data. + /// Note that "internal" is important here, because storage references only work for internal or private functions + function find(uint[] storage _data, uint _value) internal returns (uint o_position) { + return find(_data, 0, _data.length, _value); + } + function find(uint[] storage _data, uint _begin, uint _len, uint _value) private returns (uint o_position) { + if (_len == 0 || (_len == 1 && _data[_begin] != _value)) + return uint(-1); // failure + uint halfLen = _len / 2; + uint v = _data[_begin + halfLen]; + if (_value < v) + return find(_data, _begin, halfLen, _value); + else if (_value > v) + return find(_data, _begin + halfLen + 1, halfLen - 1, _value); + else + return _begin + halfLen; + } + } + + contract Store is BinarySearch { + uint[] data; + function add(uint v) { + data.length++; + data[data.length - 1] = v; + } + function find(uint v) returns (uint) { + return find(data, v); + } + } + )"; + compileAndRun(sourceCode, 0, "Store"); + BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1))); + BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("add(uint256)", u256(11)) == encodeArgs()); + BOOST_CHECK(callContractFunction("add(uint256)", u256(17)) == encodeArgs()); + BOOST_CHECK(callContractFunction("add(uint256)", u256(27)) == encodeArgs()); + BOOST_CHECK(callContractFunction("add(uint256)", u256(31)) == encodeArgs()); + BOOST_CHECK(callContractFunction("add(uint256)", u256(32)) == encodeArgs()); + BOOST_CHECK(callContractFunction("add(uint256)", u256(66)) == encodeArgs()); + BOOST_CHECK(callContractFunction("add(uint256)", u256(177)) == encodeArgs()); + BOOST_CHECK(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("find(uint256)", u256(27)) == encodeArgs(u256(3))); + BOOST_CHECK(callContractFunction("find(uint256)", u256(32)) == encodeArgs(u256(5))); + BOOST_CHECK(callContractFunction("find(uint256)", u256(176)) == encodeArgs(u256(-1))); + BOOST_CHECK(callContractFunction("find(uint256)", u256(0)) == encodeArgs(u256(-1))); + BOOST_CHECK(callContractFunction("find(uint256)", u256(400)) == encodeArgs(u256(-1))); +} + BOOST_AUTO_TEST_SUITE_END() } From 4e48ba1d4a1bf801626cac5e7e03672fae05079b Mon Sep 17 00:00:00 2001 From: Vlad Gluhovsky Date: Thu, 18 Jun 2015 00:44:20 +0200 Subject: [PATCH 66/66] false positive test introduced --- test/libwhisper/bloomFilter.cpp | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/libwhisper/bloomFilter.cpp b/test/libwhisper/bloomFilter.cpp index adf76c429..799c4caf6 100644 --- a/test/libwhisper/bloomFilter.cpp +++ b/test/libwhisper/bloomFilter.cpp @@ -55,8 +55,66 @@ void testRemoveExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h) BOOST_REQUIRE(!_f.containsBloom(_h)); } +int calculateExpected(TopicBloomFilter const& f, int const n) +{ + int const m = f.size * 8; // number of bits in the bloom + int const k = f.BitsPerBloom; // number of hash functions (e.g. bits set to 1 in every bloom) + + double singleBitSet = 1.0 / m; // probability of any bit being set after inserting a single bit + double singleBitNotSet = (1.0 - singleBitSet); + + double singleNot = 1; // single bit not set after inserting N elements in the bloom filter + for (int i = 0; i < k * n; ++i) + singleNot *= singleBitNotSet; + + double single = 1.0 - singleNot; // probability of a single bit being set after inserting N elements in the bloom filter + + double kBitsSet = 1; // probability of K bits being set after inserting N elements in the bloom filter + for (int i = 0; i < k; ++i) + kBitsSet *= single; + + return static_cast(kBitsSet * 100 + 0.5); // in percents, rounded up +} + +void testFalsePositiveRate(TopicBloomFilter const& f, int const inserted, Topic& x) +{ + int const c_sampleSize = 1000; + int falsePositive = 0; + + for (int i = 0; i < c_sampleSize; ++i) + { + x = sha3(x); + AbridgedTopic a(x); + if (f.containsBloom(a)) + ++falsePositive; + } + + falsePositive /= (c_sampleSize / 100); // in percents + int expected = calculateExpected(f, inserted); + int allowed = expected + (expected / 5); // allow deviations ~20% + + //cnote << "Inserted: " << inserted << ", False Positive Rate: " << falsePositive << ", Expected: " << expected; + BOOST_REQUIRE(falsePositive <= allowed); +} + BOOST_AUTO_TEST_SUITE(bloomFilter) +BOOST_AUTO_TEST_CASE(falsePositiveRate) +{ + VerbosityHolder setTemporaryLevel(10); + cnote << "Testing Bloom Filter False Positive Rate..."; + + TopicBloomFilter f; + Topic x(0xABCDEF); // deterministic pseudorandom value + + for (int i = 1; i < 21; ++i) + { + x = sha3(x); + f.addBloom(AbridgedTopic(x)); + testFalsePositiveRate(f, i, x); + } +} + BOOST_AUTO_TEST_CASE(bloomFilterRandom) { VerbosityHolder setTemporaryLevel(10);