diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 3f3d1e237..8e48793c9 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -180,6 +180,7 @@ + @@ -1679,6 +1680,11 @@ font-size: 14pt &GPU Mining + + + Retry Unknown Parent Blocks + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 2e4478594..a60875ba6 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1752,6 +1752,11 @@ void Main::on_clearPending_triggered() refreshAll(); } +void Main::on_retryUnknown_triggered() +{ + ethereum()->retryUnkonwn(); +} + void Main::on_killBlockchain_triggered() { writeSettings(); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index a5c74eeaa..a8579ed01 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -163,6 +163,7 @@ private slots: void on_usePrivate_triggered(); void on_turboMining_triggered(); void on_jitvm_triggered(); + void on_retryUnknown_triggered(); // Debugger void on_debugCurrent_triggered(); diff --git a/eth/main.cpp b/eth/main.cpp index 24bf839aa..461494cef 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -32,11 +32,12 @@ #include #include #include +#include +#include #include #include #include #include -#include #if ETH_READLINE #include #include @@ -111,6 +112,7 @@ void help() << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl << " -B,--block-fees Set the block fee profit in the reference unit e.g. ยข (Default: 15)." << endl << " -c,--client-name Add a name to your client's version string (default: blank)." << endl + << " -C,--check-pow Check PoW credentials for validity." << endl << " -d,--db-path Load database from path (default: ~/.ethereum " << endl << " /Etherum or Library/Application Support/Ethereum)." << endl << " -D,--create-dag Create the DAG in preparation for mining on given block and exit." << endl @@ -401,6 +403,43 @@ int main(int argc, char** argv) return -1; } } + else if ((arg == "-C" || arg == "--check-pow") && i + 4 < argc) + { + string m; + try + { + BlockInfo bi; + m = boost::to_lower_copy(string(argv[++i])); + h256 powHash(m); + m = boost::to_lower_copy(string(argv[++i])); + h256 seedHash; + if (m.size() == 64 || m.size() == 66) + seedHash = h256(m); + else + seedHash = EthashAux::seedHash(stol(m)); + m = boost::to_lower_copy(string(argv[++i])); + bi.difficulty = u256(m); + auto boundary = bi.boundary(); + m = boost::to_lower_copy(string(argv[++i])); + bi.nonce = h64(m); + auto r = EthashAux::eval(seedHash, powHash, bi.nonce); + bool valid = r.value < boundary; + cout << (valid ? "VALID :-)" : "INVALID :-(") << endl; + cout << r.value << (valid ? " < " : " >= ") << boundary << endl; + cout << " where " << boundary << " = 2^256 / " << bi.difficulty << endl; + cout << " and " << r.value << " = ethash(" << powHash << ", " << bi.nonce << ")" << endl; + cout << " with seed as " << seedHash << endl; + if (valid) + cout << "(mixHash = " << r.mixHash << ")" << endl; + cout << "SHA3( light(seed) ) = " << sha3(bytesConstRef((byte const*)EthashAux::light(seedHash), EthashAux::params(seedHash).cache_size)) << endl; + exit(0); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << m << endl; + return -1; + } + } else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) { try diff --git a/libdevcore/Guards.h b/libdevcore/Guards.h index f5c64b041..4229428ce 100644 --- a/libdevcore/Guards.h +++ b/libdevcore/Guards.h @@ -38,4 +38,75 @@ using UpgradableGuard = boost::upgrade_lock; using UpgradeGuard = boost::upgrade_to_unique_lock; using WriteGuard = boost::unique_lock; +template +struct GenericGuardBool: GuardType +{ + GenericGuardBool(MutexType& _m): GuardType(_m) {} + bool b = true; +}; +template +struct GenericUnguardBool +{ + GenericUnguardBool(MutexType& _m): m(_m) { m.unlock(); } + ~GenericUnguardBool() { m.lock(); } + bool b = true; + MutexType& m; +}; +template +struct GenericUnguardSharedBool +{ + GenericUnguardSharedBool(MutexType& _m): m(_m) { m.unlock_shared(); } + ~GenericUnguardSharedBool() { m.lock_shared(); } + bool b = true; + MutexType& m; +}; + +/** @brief Simple block guard. + * The expression/block following is guarded though the given mutex. + * Usage: + * @code + * Mutex m; + * unsigned d; + * ... + * ETH_GUARDED(m) d = 1; + * ... + * ETH_GUARDED(m) { for (auto d = 10; d > 0; --d) foo(d); d = 0; } + * @endcode + * + * There are several variants of this basic mechanism for different Mutex types and Guards. + * + * There is also the UNGUARD variant which allows an unguarded expression/block to exist within a + * guarded expression. eg: + * + * @code + * Mutex m; + * int d; + * ... + * ETH_GUARDED(m) + * { + * for (auto d = 50; d > 25; --d) + * foo(d); + * ETH_UNGUARDED(m) + * bar(); + * for (; d > 0; --d) + * foo(d); + * } + * @endcode + */ + +#define ETH_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_READ_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_WRITE_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_RECURSIVE_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_UNGUARDED(MUTEX) \ + for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_READ_UNGUARDED(MUTEX) \ + for (GenericUnguardSharedBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_WRITE_UNGUARDED(MUTEX) \ + for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) + } diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index fb4c2820d..061af566e 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -67,19 +67,24 @@ h256 EthashAux::seedHash(unsigned _number) { unsigned epoch = _number / ETHASH_EPOCH_LENGTH; RecursiveGuard l(get()->x_this); - if (_number < get()->m_seedHashes.size()) - return get()->m_seedHashes[_number]; - h256 ret; - unsigned n = 0; - if (!get()->m_seedHashes.empty()) + if (epoch >= get()->m_seedHashes.size()) { - ret = get()->m_seedHashes.back(); - n = get()->m_seedHashes.size() - 1; + h256 ret; + unsigned n = 0; + if (!get()->m_seedHashes.empty()) + { + ret = get()->m_seedHashes.back(); + n = get()->m_seedHashes.size() - 1; + } + get()->m_seedHashes.resize(epoch + 1); + cdebug << "Searching for seedHash of epoch " << epoch; + for (; n <= epoch; ++n, ret = sha3(ret)) + { + get()->m_seedHashes[n] = ret; + cdebug << "Epoch" << n << "is" << ret.abridged(); + } } - cdebug << "Searching for seedHash of epoch " << epoch; - for (; n < epoch; ++n, ret = sha3(ret)) - cdebug << "Epoch" << n << "is" << ret.abridged(); - return ret; + return get()->m_seedHashes[epoch]; } ethash_params EthashAux::params(h256 const& _seedHash) @@ -93,17 +98,13 @@ ethash_params EthashAux::params(h256 const& _seedHash) catch (...) { cdebug << "Searching for seedHash " << _seedHash.abridged(); - for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h)) - { - cdebug << "Epoch" << epoch << "is" << h.abridged(); - } + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {} if (epoch == 2048) { std::ostringstream error; error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); throw std::invalid_argument(error.str()); } - get()->m_epochs[_seedHash] = epoch; } return params(epoch * ETHASH_EPOCH_LENGTH); } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index 94c2243e0..c927a012b 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -41,7 +41,7 @@ public: static ethash_params params(h256 const& _seedHash); static ethash_params params(unsigned _n); static LightType light(BlockInfo const& _header); - static LightType light(h256 const& _header); + static LightType light(h256 const& _seedHash); static bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef()); static bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef()); diff --git a/libethcore/Params.cpp b/libethcore/Params.cpp index 029f8b47a..655c8a78b 100644 --- a/libethcore/Params.cpp +++ b/libethcore/Params.cpp @@ -30,7 +30,6 @@ namespace eth //--- BEGIN: AUTOGENERATED FROM github.com/ethereum/common/params.json u256 const c_genesisDifficulty = 131072; u256 const c_maximumExtraDataSize = 1024; -u256 const c_epochDuration = 30000; u256 const c_genesisGasLimit = 3141592; u256 const c_minGasLimit = 125000; u256 const c_gasLimitBoundDivisor = 1024; diff --git a/libethcore/Params.h b/libethcore/Params.h index 46b30e2c3..b957f9737 100644 --- a/libethcore/Params.h +++ b/libethcore/Params.h @@ -37,7 +37,6 @@ extern u256 const c_minimumDifficulty; extern u256 const c_difficultyBoundDivisor; extern u256 const c_durationLimit; extern u256 const c_maximumExtraDataSize; -extern u256 const c_epochDuration; extern u256 const c_stackLimit; extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them. diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index e9dd99cd1..b76e4bed6 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -183,3 +183,15 @@ void BlockQueue::noteReadyWithoutWriteGuard(h256 _good) m_unknown.erase(r.first, r.second); } } + +void BlockQueue::retryAllUnknown() +{ + for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it) + { + m_ready.push_back(it->second.second); + auto newReady = it->second.first; + m_unknownSet.erase(newReady); + m_readySet.insert(newReady); + } + m_unknown.clear(); +} diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index f5cdf7ab5..4a503d114 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -71,6 +71,9 @@ public: /// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain). void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); } + /// Force a retry of all the blocks with unknown parents. + void retryAllUnknown(); + /// Get information on the items queued. std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index c99b13425..0660b6cee 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -436,15 +436,12 @@ void Client::syncBlockQueue() { ImportRoute ir; + cwork << "BQ ==> CHAIN ==> STATE"; { WriteGuard l(x_stateDB); - cwork << "BQ ==> CHAIN ==> STATE"; OverlayDB db = m_stateDB; - x_stateDB.unlock(); - - tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); - - x_stateDB.lock(); + ETH_WRITE_UNGUARDED(x_stateDB) + tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); if (ir.first.empty()) return; m_stateDB = db; @@ -458,7 +455,11 @@ void Client::syncTransactionQueue() cwork << "postSTATE <== TQ"; h256Set changeds; - TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); + TransactionReceipts newPendingReceipts; + + ETH_WRITE_GUARDED(x_stateDB) + newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); + if (newPendingReceipts.size()) { for (size_t i = 0; i < newPendingReceipts.size(); i++) @@ -512,8 +513,7 @@ void Client::onChainChanged(ImportRoute const& _ir) // RESTART MINING // LOCKS REALLY NEEDED? - { - ReadGuard l(x_stateDB); + ETH_WRITE_GUARDED(x_stateDB) if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { if (isMining()) @@ -522,11 +522,9 @@ void Client::onChainChanged(ImportRoute const& _ir) m_postMine = m_preMine; changeds.insert(PendingChangedFilter); - x_stateDB.unlock_shared(); - onPostStateChanged(); - x_stateDB.lock_shared(); + ETH_WRITE_UNGUARDED(x_stateDB) + onPostStateChanged(); } - } noteChanged(changeds); } @@ -534,17 +532,24 @@ void Client::onChainChanged(ImportRoute const& _ir) void Client::onPostStateChanged() { cnote << "Post state changed: Restarting mining..."; + if (isMining()) { - WriteGuard l(x_stateDB); - cdebug << "Pre:" << m_preMine.info(); - m_postMine.commitToMine(m_bc); - m_miningInfo = m_postMine.info(); - cdebug << "Pre:" << m_preMine.info(); - cdebug << "Post:" << m_postMine.info(); - cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce); + { + WriteGuard l(x_stateDB); + m_postMine.commitToMine(m_bc); + m_miningInfo = m_postMine.info(); + } + m_farm.setWork(m_miningInfo); } +} - m_farm.setWork(m_miningInfo); +void Client::startMining() +{ + if (m_turboMining) + m_farm.startGPU(); + else + m_farm.startCPU(); + onPostStateChanged(); } void Client::noteChanged(h256Set const& _filters) diff --git a/libethereum/Client.h b/libethereum/Client.h index 289df3fe0..dedb3bcf1 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -169,7 +169,7 @@ public: /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - void startMining() override { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); onPostStateChanged(); } + void startMining() override; /// Stop mining. /// NOT thread-safe void stopMining() override { m_farm.stop(); } @@ -202,6 +202,8 @@ public: void clearPending(); /// Kills the blockchain. Just for debug use. void killChain(); + /// Retries all blocks with unknown parents. + void retryUnkonwn() { m_bq.retryAllUnknown(); } protected: /// InterfaceStub methods diff --git a/libethereum/Farm.h b/libethereum/Farm.h index afca853ed..26d4b139e 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -92,11 +92,12 @@ public: WriteGuard l(x_minerWork); m_miners.clear(); m_work.reset(); + m_isMining = false; } bool isMining() const { - return !!m_work; + return m_isMining; } /** @@ -165,6 +166,7 @@ private: m_miners.push_back(std::shared_ptr(new MinerType(std::make_pair(this, i)))); m_miners.back()->setWork(m_work); } + m_isMining = true; resetTimer(); return true; } @@ -179,6 +181,8 @@ private: WorkPackage m_work; BlockInfo m_header; + std::atomic m_isMining = {false}; + mutable SharedMutex x_progress; mutable MiningProgress m_progress; std::chrono::steady_clock::time_point m_lastStart;