diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 30bbe8243..8c86cc4b7 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -40,6 +40,7 @@ #endif #include #include +#include #include #include #include @@ -1369,7 +1370,7 @@ void Main::on_blocks_currentItemChanged() s << "
Difficulty: " << info.difficulty << ""; if (info.number) { - auto e = ProofOfWork::eval(info); + auto e = Ethasher::eval(info); s << "
Proof-of-Work: " << e.value << " <= " << (h256)u256((bigint(1) << 256) / info.difficulty) << " (mixhash: " << e.mixHash.abridged() << ")"; } else @@ -1393,7 +1394,7 @@ void Main::on_blocks_currentItemChanged() s << line << "Nonce: " << uncle.nonce << ""; s << line << "Hash w/o nonce: " << uncle.headerHash(WithoutNonce) << ""; s << line << "Difficulty: " << uncle.difficulty << ""; - auto e = ProofOfWork::eval(uncle); + auto e = Ethasher::eval(uncle); s << line << "Proof-of-Work: " << e.value << " <= " << (h256)u256((bigint(1) << 256) / uncle.difficulty) << " (mixhash: " << e.mixHash.abridged() << ")"; } if (info.parentHash) diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index a119d92eb..95519b57d 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -85,6 +85,7 @@ public: static h256 headerHash(bytes const& _block) { return headerHash(&_block); } static h256 headerHash(bytesConstRef _block); + static BlockInfo fromHeader(bytes const& _block) { return fromHeader(bytesConstRef(&_block)); } static BlockInfo fromHeader(bytesConstRef _block); explicit operator bool() const { return timestamp != Invalid256; } diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp new file mode 100644 index 000000000..a28895a1a --- /dev/null +++ b/libethcore/Ethasher.cpp @@ -0,0 +1,115 @@ +/* + 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 Ethasher.cpp + * @author Gav Wood + * @date 2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BlockInfo.h" +#include "Ethasher.h" +using namespace std; +using namespace chrono; +using namespace dev; +using namespace eth; + +Ethasher* dev::eth::Ethasher::s_this = nullptr; + +bytes const& Ethasher::cache(BlockInfo const& _header) +{ + RecursiveGuard l(x_this); + if (!m_caches.count(_header.seedHash)) + { + try { + boost::filesystem::create_directories(getDataDir() + "/ethashcache"); + } catch (...) {} + std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".cache"; + m_caches[_header.seedHash] = contents(memoFile); + if (m_caches[_header.seedHash].empty()) + { + ethash_params p = params((unsigned)_header.number); + m_caches[_header.seedHash].resize(p.cache_size); + ethash_prep_light(m_caches[_header.seedHash].data(), &p, _header.seedHash.data()); + writeFile(memoFile, m_caches[_header.seedHash]); + } + } + return m_caches[_header.seedHash]; +} + +bytesConstRef Ethasher::full(BlockInfo const& _header) +{ + RecursiveGuard l(x_this); + if (!m_fulls.count(_header.seedHash)) + { + if (!m_fulls.empty()) + { + delete [] m_fulls.begin()->second.data(); + m_fulls.erase(m_fulls.begin()); + } + std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".full"; + m_fulls[_header.seedHash] = contentsNew(memoFile); + if (!m_fulls[_header.seedHash]) + { + ethash_params p = params((unsigned)_header.number); + m_fulls[_header.seedHash] = bytesRef(new byte[p.full_size], p.full_size); + auto c = cache(_header); + ethash_prep_full(m_fulls[_header.seedHash].data(), &p, c.data()); + writeFile(memoFile, m_fulls[_header.seedHash]); + } + } + return m_fulls[_header.seedHash]; +} + +ethash_params Ethasher::params(BlockInfo const& _header) +{ + return params((unsigned)_header.number); +} + +ethash_params Ethasher::params(unsigned _n) +{ + ethash_params p; + p.cache_size = ethash_get_cachesize(_n); + p.full_size = ethash_get_datasize(_n); + return p; +} + +bool Ethasher::verify(BlockInfo const& _header) +{ + bigint boundary = (bigint(1) << 256) / _header.difficulty; + auto e = eval(_header, _header.nonce); + return (u256)e.value <= boundary && e.mixHash == _header.mixHash; +} + +Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) +{ + auto p = Ethasher::params(_header); + ethash_return_value r; + ethash_compute_light(&r, Ethasher::get()->cache(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; +} + diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h new file mode 100644 index 000000000..cfe0d1c82 --- /dev/null +++ b/libethcore/Ethasher.h @@ -0,0 +1,97 @@ +/* + 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 Ethasher.h + * @author Gav Wood + * @date 2014 + * + * ProofOfWork algorithm. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include // TODO: REMOVE once everything merged into this class and an opaque API can be provided. +#include "Common.h" +#include "BlockInfo.h" + +namespace dev +{ +namespace eth +{ + +class Ethasher +{ +public: + Ethasher() {} + + static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; } + + bytes const& cache(BlockInfo const& _header); + bytesConstRef full(BlockInfo const& _header); + static ethash_params params(BlockInfo const& _header); + static ethash_params params(unsigned _n); + + struct Result + { + h256 value; + h256 mixHash; + }; + + static Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } + static Result eval(BlockInfo const& _header, Nonce const& _nonce); + static bool verify(BlockInfo const& _header); + + class Miner + { + public: + Miner(BlockInfo const& _header): + m_headerHash(_header.headerHash(WithoutNonce)), + m_params(Ethasher::params(_header)), + m_datasetPointer(Ethasher::get()->full(_header).data()) + {} + + inline h256 mine(uint64_t _nonce) + { + ethash_compute_full(&m_ethashReturn, m_datasetPointer, &m_params, m_headerHash.data(), _nonce); + return h256(m_ethashReturn.result, h256::ConstructFromPointer); + } + + inline h256 lastMixHash() const + { + return h256(m_ethashReturn.mix_hash, h256::ConstructFromPointer); + } + + private: + ethash_return_value m_ethashReturn; + h256 m_headerHash; + ethash_params m_params; + void const* m_datasetPointer; + }; + +private: + static Ethasher* s_this; + RecursiveMutex x_this; + std::map m_caches; + std::map m_fulls; +}; + +} +} diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index c879df2ce..d261ccb1c 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -30,8 +30,8 @@ #include #include #include -#include #include "BlockInfo.h" +#include "Ethasher.h" #include "ProofOfWork.h" using namespace std; using namespace std::chrono; @@ -41,100 +41,14 @@ namespace dev namespace eth { -class Ethasher -{ -public: - Ethasher() {} - - static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; } - - bytes const& cache(BlockInfo const& _header) - { - RecursiveGuard l(x_this); - if (!m_caches.count(_header.seedHash)) - { - try { - boost::filesystem::create_directories(getDataDir() + "/ethashcache"); - } catch (...) {} - std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".cache"; - m_caches[_header.seedHash] = contents(memoFile); - if (m_caches[_header.seedHash].empty()) - { - ethash_params p = params((unsigned)_header.number); - m_caches[_header.seedHash].resize(p.cache_size); - ethash_prep_light(m_caches[_header.seedHash].data(), &p, _header.seedHash.data()); - writeFile(memoFile, m_caches[_header.seedHash]); - } - } - return m_caches[_header.seedHash]; - } - - byte const* full(BlockInfo const& _header) - { - RecursiveGuard l(x_this); - if (!m_fulls.count(_header.seedHash)) - { - if (!m_fulls.empty()) - { - delete [] m_fulls.begin()->second.data(); - m_fulls.erase(m_fulls.begin()); - } - std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".full"; - m_fulls[_header.seedHash] = contentsNew(memoFile); - if (!m_fulls[_header.seedHash]) - { - ethash_params p = params((unsigned)_header.number); - m_fulls[_header.seedHash] = bytesRef(new byte[p.full_size], p.full_size); - auto c = cache(_header); - ethash_prep_full(m_fulls[_header.seedHash].data(), &p, c.data()); - writeFile(memoFile, m_fulls[_header.seedHash]); - } - } - return m_fulls[_header.seedHash].data(); - } - - static ethash_params params(BlockInfo const& _header) - { - return params((unsigned)_header.number); - } - - static ethash_params params(unsigned _n) - { - ethash_params p; - p.cache_size = ethash_get_cachesize(_n); - p.full_size = ethash_get_datasize(_n); - return p; - } - -private: - static Ethasher* s_this; - RecursiveMutex x_this; - std::map m_caches; - std::map m_fulls; -}; - -Ethasher* Ethasher::s_this = nullptr; - bool Ethash::verify(BlockInfo const& _header) { - bigint boundary = (bigint(1) << 256) / _header.difficulty; - auto e = eval(_header, _header.nonce); - return (u256)e.value <= boundary && e.mixHash == _header.mixHash; -} - -Ethash::Result Ethash::eval(BlockInfo const& _header, Nonce const& _nonce) -{ - auto p = Ethasher::params(_header); - ethash_return_value r; - ethash_compute_light(&r, Ethasher::get()->cache(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); - return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; + return Ethasher::verify(_header); } std::pair Ethash::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue, bool _turbo) { - auto h = _header.headerHash(WithoutNonce); - auto p = Ethasher::params(_header); - auto d = Ethasher::get()->full(_header); + Ethasher::Miner m(_header); std::pair ret; static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()))); @@ -152,17 +66,15 @@ std::pair Ethash::mine(BlockInfo const& _header, unsign std::this_thread::sleep_for(std::chrono::milliseconds(_msTimeout * 90 / 100)); double best = 1e99; // high enough to be effectively infinity :) Proof result; - ethash_return_value ethashReturn; unsigned hashCount = 0; for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; tryNonce++, hashCount++) { - ethash_compute_full(ðashReturn, d, &p, h.data(), tryNonce); - u256 val(h256(ethashReturn.result, h256::ConstructFromPointer)); + u256 val(m.mine(tryNonce)); best = std::min(best, log2((double)val)); if (val <= boundary) { ret.first.completed = true; - result.mixHash = h256(ethashReturn.mix_hash, h256::ConstructFromPointer); + result.mixHash = m.lastMixHash(); result.nonce = u64(tryNonce); break; } diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index 7006f6f61..250ddb73d 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -49,20 +49,12 @@ struct MineInfo class Ethash { public: - // bit-compatible with ethash_return_value struct Proof { Nonce nonce; h256 mixHash; }; - struct Result - { - h256 value; - h256 mixHash; - }; - static Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } - static Result eval(BlockInfo const& _header, Nonce const& _nonce); static bool verify(BlockInfo const& _header); std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true, bool _turbo = false); static void assignResult(Proof const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } @@ -78,9 +70,7 @@ public: using Proof = Nonce; static bool verify(BlockInfo const& _header) { return (bigint)(u256)Evaluator::eval(_header.headerHash(WithoutNonce), _header.nonce) <= (bigint(1) << 256) / _header.difficulty; } - inline std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true, bool _turbo = false); - static void assignResult(Proof const& _r, BlockInfo& _header) { _header.nonce = _r; } protected: diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index b1727ae1c..ac88e7cdf 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -308,7 +308,13 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, 3), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp())); m_extrasDB->Put(m_writeOptions, toSlice(newHash, 4), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); + m_extrasDB->Put(m_writeOptions, toSlice(newHash, 4), (ldb::Slice)dev::ref(m_receipts[newHash].rlp())); m_db->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block)); + RLP blockRLP(_block); + TransactionAddress ta; + ta.blockHash = newHash; + for (ta.index = 0; ta.index < blockRLP[1].itemCount(); ++ta.index) + m_extrasDB->Put(m_writeOptions, toSlice(sha3(blockRLP[1][ta.index].data()), 5), (ldb::Slice)dev::ref(ta.rlp())); #if ETH_PARANOIA checkConsistency(); diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 6d9e3f961..cd3053c63 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -34,6 +34,7 @@ #include #include "BlockDetails.h" #include "Account.h" +#include "Transaction.h" #include "BlockQueue.h" namespace ldb = leveldb; @@ -61,6 +62,8 @@ std::map const& genesisState(); ldb::Slice toSlice(h256 _h, unsigned _sub = 0); +using TransactionHashes = h256s; + /** * @brief Implements the blockchain database. All data this gives is disk-backed. * @threadsafe @@ -108,12 +111,19 @@ public: BlockReceipts receipts(h256 _hash) const { return queryExtras(_hash, m_receipts, x_receipts, NullBlockReceipts); } BlockReceipts receipts() const { return receipts(currentHash()); } + /// Get a list of transaction hashes for a given block. Thread-safe. + TransactionHashes transactionHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } + TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); } + /// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe. bytes block(h256 _hash) const; bytes block() const { return block(currentHash()); } + /// Get a transaction from its hash. Thread-safe. + bytes transaction(h256 _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } + /// Get a block's transaction (RLP format) for the given block hash (or the most recent mined if none given) & index. Thread-safe. - bytes transaction(h256 _hash, unsigned _i) const { bytes b = block(_hash); return RLP(b)[1][_i].data().toBytes(); } + bytes transaction(h256 _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); } bytes transaction(unsigned _i) const { return transaction(currentHash(), _i); } /// Get a number for the given hash (or the most recent mined if none given). Thread-safe. @@ -185,6 +195,8 @@ private: mutable BlockLogBloomsHash m_logBlooms; mutable boost::shared_mutex x_receipts; mutable BlockReceiptsHash m_receipts; + mutable boost::shared_mutex x_transactionAddresses; + mutable TransactionAddressHash m_transactionAddresses; mutable boost::shared_mutex x_cache; mutable std::map m_cache; diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index 9a3ac9ff1..b59d165ab 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -70,13 +70,27 @@ struct BlockReceipts TransactionReceipts receipts; }; +struct TransactionAddress +{ + TransactionAddress() {} + TransactionAddress(RLP const& _rlp) { blockHash = _rlp[0].toHash(); index = _rlp[1].toInt(); } + bytes rlp() const { RLPStream s(2); s << blockHash << index; return s.out(); } + + explicit operator bool() const { return !!blockHash; } + + h256 blockHash; + unsigned index = 0; +}; + using BlockDetailsHash = std::map; using BlockLogBloomsHash = std::map; using BlockReceiptsHash = std::map; +using TransactionAddressHash = std::map; static const BlockDetails NullBlockDetails; static const BlockLogBlooms NullBlockLogBlooms; static const BlockReceipts NullBlockReceipts; +static const TransactionAddress NullTransactionAddress; } } diff --git a/test/dagger.cpp b/test/dagger.cpp index 87c49bd7d..dec753fe7 100644 --- a/test/dagger.cpp +++ b/test/dagger.cpp @@ -17,37 +17,69 @@ /** @file dagger.cpp * @author Gav Wood * @date 2014 - * ProofOfWork test functions. + * Dashimoto test functions. */ -#include -#include +#include +#include +#include "JsonSpiritHeaders.h" +#include #include +#include +#include +#include "TestHelper.h" + using namespace std; -using namespace std::chrono; using namespace dev; using namespace dev::eth; -int daggerTest() +namespace js = json_spirit; + +using dev::operator <<; + +BOOST_AUTO_TEST_SUITE(DashimotoTests) + +BOOST_AUTO_TEST_CASE(basic_test) { -#if 0 - cnote << "Testing ProofOfWork..."; - // Test dagger - { - auto s = steady_clock::now(); - cout << hex << ProofOfWork().eval((h256)(u256)1, (h256)(u256)0); - cout << " " << dec << duration_cast(steady_clock::now() - s).count() << " ms" << endl; - cout << hex << ProofOfWork().eval((h256)(u256)1, (h256)(u256)1); - cout << " " << dec << duration_cast(steady_clock::now() - s).count() << " ms" << endl; - } + string testPath = test::getTestPath(); + + testPath += "/PoWTests"; + + cnote << "Testing Secure Trie..."; + js::mValue v; + string s = asString(contents(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()) { - auto s = steady_clock::now(); - cout << hex << ProofOfWork().eval((h256)(u256)1, (h256)(u256)0); - cout << " " << dec << duration_cast(steady_clock::now() - s).count() << " ms" << endl; - cout << hex << ProofOfWork().eval((h256)(u256)1, (h256)(u256)1); - cout << " " << dec << duration_cast(steady_clock::now() - s).count() << " ms" << endl; - } + cnote << i.first; + js::mObject& o = i.second.get_obj(); + vector> ss; + BlockInfo header = BlockInfo::fromHeader(fromHex(o["header"].get_str())); + h256 headerHash(o["header_hash"].get_str()); + Nonce nonce(o["nonce"].get_str()); + BOOST_REQUIRE_EQUAL(headerHash, header.headerHash(WithoutNonce)); + BOOST_REQUIRE_EQUAL(nonce, header.nonce); + + unsigned cacheSize(o["cache_size"].get_int()); + h256 cacheHash(o["cache_hash"].get_str()); + BOOST_REQUIRE_EQUAL(Ethasher::get()->cache(header).size(), cacheSize); + BOOST_REQUIRE_EQUAL(sha3(Ethasher::get()->cache(header)), cacheHash); + +#if TEST_FULL + unsigned fullSize(o["full_size"].get_int()); + h256 fullHash(o["full_hash"].get_str()); + BOOST_REQUIRE_EQUAL(Ethasher::get()->full(header).size(), fullSize); + BOOST_REQUIRE_EQUAL(sha3(Ethasher::get()->full(header)), fullHash); #endif - return 0; + + h256 result(o["result"].get_str()); + Ethasher::Result r = Ethasher::eval(header); + BOOST_REQUIRE_EQUAL(r.value, result); + BOOST_REQUIRE_EQUAL(r.mixHash, header.mixHash); + } } +BOOST_AUTO_TEST_SUITE_END() + +