Use Equihash for Proof-of-Work
The main and test networks are configured to use parameters that are currently low-memory but usable with the basic solver; they will be increased once the solver is optimised. The regtest network is configured to have extremely low memory usage for speed. Note that Bitcoin's double-hasher is used for the difficulty check. This does not match the paper, but is simpler than changing the block header serialization. Single hashing is kept for the EquiHash solver because there is no requirement on execution time there, only on memory usage.
This commit is contained in:
118
src/miner.cpp
118
src/miner.cpp
@@ -18,9 +18,12 @@
|
||||
#include "util.h"
|
||||
#include "utilmoneystr.h"
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "crypto/equihash.h"
|
||||
#include "wallet/wallet.h"
|
||||
#endif
|
||||
|
||||
#include "sodium.h"
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
@@ -338,7 +341,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
||||
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
|
||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
|
||||
pblock->nNonce = 0;
|
||||
pblock->nNonce = uint256();
|
||||
pblock->nSolution.clear();
|
||||
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
|
||||
|
||||
CValidationState state;
|
||||
@@ -374,39 +378,6 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int&
|
||||
// Internal miner
|
||||
//
|
||||
|
||||
//
|
||||
// ScanHash scans nonces looking for a hash with at least some zero bits.
|
||||
// The nonce is usually preserved between calls, but periodically or if the
|
||||
// nonce is 0xffff0000 or above, the block is rebuilt and nNonce starts over at
|
||||
// zero.
|
||||
//
|
||||
bool static ScanHash(const CBlockHeader *pblock, uint32_t& nNonce, uint256 *phash)
|
||||
{
|
||||
// Write the first 76 bytes of the block header to a double-SHA256 state.
|
||||
CHash256 hasher;
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << *pblock;
|
||||
assert(ss.size() == 80);
|
||||
hasher.Write((unsigned char*)&ss[0], 76);
|
||||
|
||||
while (true) {
|
||||
nNonce++;
|
||||
|
||||
// Write the last 4 bytes of the block header (the nonce) to a copy of
|
||||
// the double-SHA256 state, and compute the result.
|
||||
CHash256(hasher).Write((unsigned char*)&nNonce, 4).Finalize((unsigned char*)phash);
|
||||
|
||||
// Return the nonce if the hash has at least some zero bits,
|
||||
// caller will check if it has enough to reach the target
|
||||
if (((uint16_t*)phash)[15] == 0)
|
||||
return true;
|
||||
|
||||
// If nothing found after trying for a while, return -1
|
||||
if ((nNonce & 0xfff) == 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey)
|
||||
{
|
||||
CPubKey pubkey;
|
||||
@@ -426,7 +397,7 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese
|
||||
{
|
||||
LOCK(cs_main);
|
||||
if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
|
||||
return error("BitcoinMiner: generated block is stale");
|
||||
return error("ZcashMiner: generated block is stale");
|
||||
}
|
||||
|
||||
// Remove key from key pool
|
||||
@@ -441,22 +412,24 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese
|
||||
// Process this block the same as if we had received it from another node
|
||||
CValidationState state;
|
||||
if (!ProcessNewBlock(state, NULL, pblock, true, NULL))
|
||||
return error("BitcoinMiner: ProcessNewBlock, block not accepted");
|
||||
return error("ZcashMiner: ProcessNewBlock, block not accepted");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void static BitcoinMiner(CWallet *pwallet)
|
||||
{
|
||||
LogPrintf("BitcoinMiner started\n");
|
||||
LogPrintf("ZcashMiner started\n");
|
||||
SetThreadPriority(THREAD_PRIORITY_LOWEST);
|
||||
RenameThread("bitcoin-miner");
|
||||
RenameThread("zcash-miner");
|
||||
const CChainParams& chainparams = Params();
|
||||
|
||||
// Each thread has its own key and counter
|
||||
CReserveKey reservekey(pwallet);
|
||||
unsigned int nExtraNonce = 0;
|
||||
|
||||
Equihash eh {chainparams.EquihashN(), chainparams.EquihashK()};
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
if (chainparams.MiningRequiresPeers()) {
|
||||
@@ -483,13 +456,13 @@ void static BitcoinMiner(CWallet *pwallet)
|
||||
auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
|
||||
if (!pblocktemplate.get())
|
||||
{
|
||||
LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
|
||||
LogPrintf("Error in ZcashMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
|
||||
return;
|
||||
}
|
||||
CBlock *pblock = &pblocktemplate->block;
|
||||
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
|
||||
|
||||
LogPrintf("Running BitcoinMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
|
||||
LogPrintf("Running ZcashMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
|
||||
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
|
||||
|
||||
//
|
||||
@@ -497,21 +470,48 @@ void static BitcoinMiner(CWallet *pwallet)
|
||||
//
|
||||
int64_t nStart = GetTime();
|
||||
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
|
||||
uint256 hash;
|
||||
uint32_t nNonce = 0;
|
||||
while (true) {
|
||||
// Check if something found
|
||||
if (ScanHash(pblock, nNonce, &hash))
|
||||
{
|
||||
if (UintToArith256(hash) <= hashTarget)
|
||||
{
|
||||
// Found a solution
|
||||
pblock->nNonce = nNonce;
|
||||
assert(hash == pblock->GetHash());
|
||||
|
||||
// Hash state
|
||||
crypto_generichash_blake2b_state state;
|
||||
eh.InitialiseState(state);
|
||||
|
||||
// I = the block header minus nonce and solution.
|
||||
CEquihashInput I{*pblock};
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << I;
|
||||
|
||||
// H(I||...
|
||||
crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size());
|
||||
|
||||
while (true) {
|
||||
// Find valid nonce
|
||||
while (true)
|
||||
{
|
||||
// H(I||V||...
|
||||
crypto_generichash_blake2b_state curr_state;
|
||||
curr_state = state;
|
||||
crypto_generichash_blake2b_update(&curr_state,
|
||||
pblock->nNonce.begin(),
|
||||
pblock->nNonce.size());
|
||||
|
||||
// (x_1, x_2, ...) = A(I, V, n, k)
|
||||
LogPrint("pow", "Running Equihash solver with nNonce = %s\n",
|
||||
pblock->nNonce.ToString());
|
||||
std::set<std::vector<unsigned int>> solns = eh.BasicSolve(curr_state);
|
||||
LogPrint("pow", "Solutions: %d\n", solns.size());
|
||||
|
||||
// Write the solution to the hash and compute the result.
|
||||
for (auto soln : solns) {
|
||||
pblock->nSolution = soln;
|
||||
|
||||
if (UintToArith256(pblock->GetHash()) > hashTarget) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found a solution
|
||||
SetThreadPriority(THREAD_PRIORITY_NORMAL);
|
||||
LogPrintf("BitcoinMiner:\n");
|
||||
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex(), hashTarget.GetHex());
|
||||
LogPrintf("ZcashMiner:\n");
|
||||
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex());
|
||||
ProcessBlockFound(pblock, *pwallet, reservekey);
|
||||
SetThreadPriority(THREAD_PRIORITY_LOWEST);
|
||||
|
||||
@@ -519,16 +519,20 @@ void static BitcoinMiner(CWallet *pwallet)
|
||||
if (chainparams.MineBlocksOnDemand())
|
||||
throw boost::thread_interrupted();
|
||||
|
||||
break;
|
||||
goto updateblock;
|
||||
}
|
||||
pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
|
||||
if ((UintToArith256(pblock->nNonce) & 0x1) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
updateblock:
|
||||
// Check for stop or if block needs to be rebuilt
|
||||
boost::this_thread::interruption_point();
|
||||
// Regtest mode doesn't require peers
|
||||
if (vNodes.empty() && chainparams.MiningRequiresPeers())
|
||||
break;
|
||||
if (nNonce >= 0xffff0000)
|
||||
if (UintToArith256(pblock->nNonce) >= 0xffff)
|
||||
break;
|
||||
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
|
||||
break;
|
||||
@@ -547,12 +551,12 @@ void static BitcoinMiner(CWallet *pwallet)
|
||||
}
|
||||
catch (const boost::thread_interrupted&)
|
||||
{
|
||||
LogPrintf("BitcoinMiner terminated\n");
|
||||
LogPrintf("ZcashMiner terminated\n");
|
||||
throw;
|
||||
}
|
||||
catch (const std::runtime_error &e)
|
||||
{
|
||||
LogPrintf("BitcoinMiner runtime error: %s\n", e.what());
|
||||
LogPrintf("ZcashMiner runtime error: %s\n", e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user