Auto merge of #4608 - str4d:post-heartwood-cleanups, r=nuttycom

Post-Heartwood cleanups

The C++ Equihash validator is removed; the Rust validator is now used unconditionally for the entire chain.

Changes made to enable contextually switching between the two validators have been reverted.

Also fixes a segfault that could occur on reindex during consensus-related development.
This commit is contained in:
Homu 2020-07-15 14:59:15 +00:00
commit b4ec903ac4
9 changed files with 29 additions and 154 deletions

View File

@ -16,6 +16,8 @@
#include "config/bitcoin-config.h"
#endif
#ifdef ENABLE_MINING
#include "compat/endian.h"
#include "crypto/equihash.h"
#include "util.h"
@ -324,7 +326,6 @@ std::shared_ptr<eh_trunc> TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t le
return p;
}
#ifdef ENABLE_MINING
template<unsigned int N, unsigned int K>
bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
@ -717,100 +718,41 @@ invalidsolution:
return false;
}
#endif // ENABLE_MINING
template<unsigned int N, unsigned int K>
bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln)
{
if (soln.size() != SolutionWidth) {
LogPrint("pow", "Invalid solution length: %d (expected %d)\n",
soln.size(), SolutionWidth);
return false;
}
std::vector<FullStepRow<FinalFullWidth>> X;
X.reserve(1 << K);
unsigned char tmpHash[HashOutput];
for (eh_index i : GetIndicesFromMinimal(soln, CollisionBitLength)) {
GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput);
X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * N/8),
N/8, HashLength, CollisionBitLength, i);
}
size_t hashLen = HashLength;
size_t lenIndices = sizeof(eh_index);
while (X.size() > 1) {
std::vector<FullStepRow<FinalFullWidth>> Xc;
for (int i = 0; i < X.size(); i += 2) {
if (!HasCollision(X[i], X[i+1], CollisionByteLength)) {
LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
LogPrint("pow", "X[i] = %s\n", X[i].GetHex(hashLen));
LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex(hashLen));
return false;
}
if (X[i+1].IndicesBefore(X[i], hashLen, lenIndices)) {
LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n");
return false;
}
if (!DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) {
LogPrint("pow", "Invalid solution: duplicate indices\n");
return false;
}
Xc.emplace_back(X[i], X[i+1], hashLen, lenIndices, CollisionByteLength);
}
X = Xc;
hashLen -= CollisionByteLength;
lenIndices *= 2;
}
assert(X.size() == 1);
return X[0].IsZero(hashLen);
}
// Explicit instantiations for Equihash<96,3>
template int Equihash<96,3>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<96,3>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<200,9>
template int Equihash<200,9>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<200,9>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<200,9>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<96,5>
template int Equihash<96,5>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<96,5>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<48,5>
template int Equihash<48,5>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<48,5>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
#endif // ENABLE_MINING

View File

@ -5,6 +5,7 @@
#ifndef BITCOIN_EQUIHASH_H
#define BITCOIN_EQUIHASH_H
#ifdef ENABLE_MINING
#include "crypto/sha256.h"
#include "utilstrencodings.h"
@ -183,15 +184,12 @@ public:
Equihash() { }
int InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
bool BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
bool OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
bool IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
};
#include "equihash.tcc"
@ -214,7 +212,6 @@ static Equihash<48,5> Eh48_5;
throw std::invalid_argument("Unsupported Equihash parameters"); \
}
#ifdef ENABLE_MINING
inline bool EhBasicSolve(unsigned int n, unsigned int k, const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled)
@ -264,18 +261,4 @@ inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const
}
#endif // ENABLE_MINING
#define EhIsValidSolution(n, k, base_state, soln, ret) \
if (n == 96 && k == 3) { \
ret = Eh96_3.IsValidSolution(base_state, soln); \
} else if (n == 200 && k == 9) { \
ret = Eh200_9.IsValidSolution(base_state, soln); \
} else if (n == 96 && k == 5) { \
ret = Eh96_5.IsValidSolution(base_state, soln); \
} else if (n == 48 && k == 5) { \
ret = Eh48_5.IsValidSolution(base_state, soln); \
} else { \
ret = false; \
throw std::invalid_argument("Unsupported Equihash parameters"); \
}
#endif // BITCOIN_EQUIHASH_H

View File

@ -1824,7 +1824,7 @@ bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHea
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, int nHeight, const Consensus::Params& consensusParams)
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
block.SetNull();
@ -1842,7 +1842,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, int nHeight, con
}
// Check the header
if (!(CheckEquihashSolution(&block, nHeight, consensusParams) &&
if (!(CheckEquihashSolution(&block, consensusParams) &&
CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)))
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
@ -1851,7 +1851,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, int nHeight, con
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), pindex->nHeight, consensusParams))
if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), consensusParams))
return false;
if (block.GetHash() != pindex->GetBlockHash())
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
@ -3979,19 +3979,13 @@ bool CheckBlockHeader(
const CChainParams& chainparams,
bool fCheckPOW)
{
auto consensusParams = chainparams.GetConsensus();
// Check block version
if (block.nVersion < MIN_BLOCK_VERSION)
return state.DoS(100, error("CheckBlockHeader(): block version too low"),
REJECT_INVALID, "version-too-low");
// Check Equihash solution is valid. The main check is in ContextualCheckBlockHeader,
// because we currently need to know the block height. That function skips the genesis
// block because it has no previous block, so we check it specifically here.
if (fCheckPOW &&
block.GetHash() == consensusParams.hashGenesisBlock &&
!CheckEquihashSolution(&block, 0, consensusParams))
// Check Equihash solution is valid
if (fCheckPOW && !CheckEquihashSolution(&block, chainparams.GetConsensus()))
return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),
REJECT_INVALID, "invalid-solution");
@ -4068,8 +4062,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state,
bool ContextualCheckBlockHeader(
const CBlockHeader& block, CValidationState& state,
const CChainParams& chainParams, CBlockIndex * const pindexPrev,
bool fCheckPOW)
const CChainParams& chainParams, CBlockIndex * const pindexPrev)
{
const Consensus::Params& consensusParams = chainParams.GetConsensus();
uint256 hash = block.GetHash();
@ -4081,11 +4074,6 @@ bool ContextualCheckBlockHeader(
int nHeight = pindexPrev->nHeight+1;
// Check Equihash solution is valid
if (fCheckPOW && !CheckEquihashSolution(&block, nHeight, consensusParams))
return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),
REJECT_INVALID, "invalid-solution");
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) {
return state.DoS(100, error("%s: incorrect proof of work", __func__),
@ -4377,7 +4365,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
auto verifier = libzcash::ProofVerifier::Disabled();
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, fCheckPOW))
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev))
return false;
if (!CheckBlock(block, state, chainparams, verifier, fCheckPOW, fCheckMerkleRoot))
return false;
@ -5173,7 +5161,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head);
while (range.first != range.second) {
std::multimap<uint256, CDiskBlockPos>::iterator it = range.first;
if (ReadBlockFromDisk(block, it->second, mapBlockIndex[head]->nHeight, chainparams.GetConsensus()))
if (ReadBlockFromDisk(block, it->second, chainparams.GetConsensus()))
{
LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(),
head.ToString());

View File

@ -436,7 +436,7 @@ bool GetTimestampIndex(unsigned int high, unsigned int low, bool fActiveOnly,
/** Functions for disk access for blocks */
bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, int nHeight, const Consensus::Params& consensusParams);
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
/** Functions for validating blocks and updating the block tree */
@ -454,8 +454,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state,
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock(). */
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state,
const CChainParams& chainparams, CBlockIndex *pindexPrev,
bool fCheckPOW = true);
const CChainParams& chainparams, CBlockIndex *pindexPrev);
bool ContextualCheckBlock(const CBlock& block, CValidationState& state,
const CChainParams& chainparams, CBlockIndex *pindexPrev);

View File

@ -14,7 +14,6 @@
#include "uint256.h"
#include <librustzcash.h>
#include "sodium.h"
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
@ -93,42 +92,22 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
return bnNew.GetCompact();
}
bool CheckEquihashSolution(const CBlockHeader *pblock, int nHeight, const Consensus::Params& params)
bool CheckEquihashSolution(const CBlockHeader *pblock, const Consensus::Params& params)
{
unsigned int n = params.nEquihashN;
unsigned int k = params.nEquihashK;
// Hash state
crypto_generichash_blake2b_state state;
EhInitialiseState(n, k, state);
// I = the block header minus nonce and solution.
CEquihashInput I{*pblock};
// I||V
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << I;
// From Heartwood activation, check with the Rust validator
if (params.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD)) {
return librustzcash_eh_isvalid(
n, k,
(unsigned char*)&ss[0], ss.size(),
pblock->nNonce.begin(), pblock->nNonce.size(),
pblock->nSolution.data(), pblock->nSolution.size());
}
// Before Heartwood activation, check with the C++ validator
ss << pblock->nNonce;
// H(I||V||...
crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size());
bool isValid;
EhIsValidSolution(n, k, state, pblock->nSolution, isValid);
if (!isValid)
return error("CheckEquihashSolution(): invalid solution");
return true;
return librustzcash_eh_isvalid(
n, k,
(unsigned char*)&ss[0], ss.size(),
pblock->nNonce.begin(), pblock->nNonce.size(),
pblock->nSolution.data(), pblock->nSolution.size());
}
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)

View File

@ -23,7 +23,7 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
int nextHeight);
/** Check whether the Equihash solution in a block header is valid */
bool CheckEquihashSolution(const CBlockHeader *pblock, int nHeight, const Consensus::Params&);
bool CheckEquihashSolution(const CBlockHeader *pblock, const Consensus::Params&);
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);

View File

@ -91,22 +91,13 @@ void TestEquihashValidator(unsigned int n, unsigned int k, const std::string &I,
size_t cBitLen { n/(k+1) };
auto minimal = GetMinimalFromIndices(soln, cBitLen);
// First test the C++ validator
crypto_generichash_blake2b_state state;
EhInitialiseState(n, k, state);
uint256 V = ArithToUint256(nonce);
crypto_generichash_blake2b_update(&state, (unsigned char*)&I[0], I.size());
crypto_generichash_blake2b_update(&state, V.begin(), V.size());
BOOST_TEST_MESSAGE("Running validator: n = " << n << ", k = " << k << ", I = " << I << ", V = " << V.GetHex() << ", expected = " << expected << ", soln =");
std::stringstream strm;
PrintSolution(strm, soln);
BOOST_TEST_MESSAGE(strm.str());
bool isValid;
EhIsValidSolution(n, k, state, minimal, isValid);
BOOST_CHECK(isValid == expected);
// The Rust validator should have the exact same result
isValid = librustzcash_eh_isvalid(
bool isValid = librustzcash_eh_isvalid(
n, k,
(unsigned char*)&I[0], I.size(),
V.begin(), V.size(),
@ -216,12 +207,8 @@ BOOST_AUTO_TEST_CASE(validator_allbitsmatter) {
// Initialize the state according to one of the test vectors above.
unsigned int n = 96;
unsigned int k = 5;
crypto_generichash_blake2b_state state;
EhInitialiseState(n, k, state);
uint256 V = ArithToUint256(1);
std::string I = "Equihash is an asymmetric PoW based on the Generalised Birthday problem.";
crypto_generichash_blake2b_update(&state, (unsigned char*)&I[0], I.size());
crypto_generichash_blake2b_update(&state, V.begin(), V.size());
// Encode the correct solution.
std::vector<uint32_t> soln = {2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568};
@ -229,9 +216,6 @@ BOOST_AUTO_TEST_CASE(validator_allbitsmatter) {
std::vector<unsigned char> sol_char = GetMinimalFromIndices(soln, cBitLen);
// Prove that the solution is valid.
bool isValid;
EhIsValidSolution(n, k, state, sol_char, isValid);
BOOST_CHECK(isValid == true);
BOOST_CHECK(librustzcash_eh_isvalid(
n, k,
(unsigned char*)&I[0], I.size(),
@ -242,8 +226,6 @@ BOOST_AUTO_TEST_CASE(validator_allbitsmatter) {
for (size_t i = 0; i < sol_char.size() * 8; i++) {
std::vector<unsigned char> mutated = sol_char;
mutated.at(i/8) ^= (1 << (i % 8));
EhIsValidSolution(n, k, state, mutated, isValid);
BOOST_CHECK(isValid == false);
BOOST_CHECK(!librustzcash_eh_isvalid(
n, k,
(unsigned char*)&I[0], I.size(),

View File

@ -247,10 +247,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
solns.insert(sol_char);
}
bool ret;
for (auto soln : solns) {
EhIsValidSolution(n, k, curr_state, soln, ret);
if (!ret) continue;
if (!librustzcash_eh_isvalid(
n, k,
(unsigned char*)&ss[0], ss.size(),
pblock->nNonce.begin(), pblock->nNonce.size(),
soln.data(), soln.size())) continue;
pblock->nSolution = soln;
CValidationState state;

View File

@ -204,7 +204,7 @@ double benchmark_verify_equihash()
CBlockHeader genesis_header = genesis.GetBlockHeader();
struct timeval tv_start;
timer_start(tv_start);
assert(CheckEquihashSolution(&genesis_header, 1, params.GetConsensus()));
CheckEquihashSolution(&genesis_header, params.GetConsensus());
return timer_stop(tv_start);
}