Skip to content

Commit

Permalink
Add ability to upgrade Equihash parameters for a given network upgrade.
Browse files Browse the repository at this point in the history
Add Equihash support for (144, 5)
Add configuration option -eqparams=branchid:n:k
Add QA test to upgrade regtest parameters from (48,5) to (96, 5)

Example usage:

./zcashd -nuparams=5ba81b19:10 -eqparams=5ba81b19:96:5

This activates branch 5ba81b19 at block 10, switching over to
parameters N=96 and K=5.
  • Loading branch information
bitcartel committed Apr 9, 2018
1 parent 079d9e6 commit 02397b6
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 11 deletions.
1 change: 1 addition & 0 deletions qa/pull-tester/rpc-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ testScripts=(
'rewind_index.py'
'p2p_txexpiry_dos.py'
'p2p_node_bloom.py'
'equihash_params.py'
);
testScriptsExt=(
'getblocktemplate_longpoll.py'
Expand Down
27 changes: 27 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,3 +511,30 @@ void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivation
{
regTestParams.UpdateNetworkUpgradeParameters(idx, nActivationHeight);
}

// To help debugging, let developers change the equihash parameters for a network upgrade.
// TODO: Restrict this to regtest mode in the future.
void UpdateEquihashUpgradeParameters(Consensus::UpgradeIndex idx, unsigned int n, unsigned int k)
{
assert(idx > Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES);
EquihashUpgradeInfo[idx].N = n;
EquihashUpgradeInfo[idx].K = k;
}

// Return Equihash parameter N at a given block height.
unsigned int CChainParams::EquihashN(int nHeight) const {
unsigned int n = EquihashUpgradeInfo[CurrentEpoch(nHeight, GetConsensus())].N;
if (n == EquihashInfo::DEFAULT_PARAMS) {
n = nEquihashN;
}
return n;
}

// Return Equihash parameter K at a given block height.
unsigned int CChainParams::EquihashK(int nHeight) const {
unsigned int k = EquihashUpgradeInfo[CurrentEpoch(nHeight, GetConsensus())].K;
if (k == EquihashInfo::DEFAULT_PARAMS) {
k = nEquihashK;
}
return k;
}
9 changes: 7 additions & 2 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ class CChainParams
bool RequireStandard() const { return fRequireStandard; }
int64_t MaxTipAge() const { return nMaxTipAge; }
int64_t PruneAfterHeight() const { return nPruneAfterHeight; }
unsigned int EquihashN() const { return nEquihashN; }
unsigned int EquihashK() const { return nEquihashK; }
unsigned int EquihashN(int nHeight = 0) const;
unsigned int EquihashK(int nHeight = 0) const;
std::string CurrencyUnits() const { return strCurrencyUnits; }
/** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */
bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; }
Expand Down Expand Up @@ -139,4 +139,9 @@ bool SelectParamsFromCommandLine();
*/
void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight);

/**
* Let developers modify the equihash upgrade parameters for testing purposes.
*/
void UpdateEquihashUpgradeParameters(Consensus::UpgradeIndex idx, unsigned int n, unsigned int k);

#endif // BITCOIN_CHAINPARAMS_H
24 changes: 24 additions & 0 deletions src/consensus/upgrades.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,30 @@ const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = {

const uint32_t SPROUT_BRANCH_ID = NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId;

/**
* General information associating epoch with equihash parameters.
* Ordered by Consensus::UpgradeIndex, to match NetworkUpgradeInfo.
* TODO: Maybe refactor and include in NetworkUpgradeInfo
* TODO: Make this const
*/
struct EquihashInfo EquihashUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = {
// BASE_SPROUT
{
/* N = */ EquihashInfo::DEFAULT_PARAMS,
/* K = */ EquihashInfo::DEFAULT_PARAMS,
},
// UPGRADE_TESTDUMMY
{
/* N = */ EquihashInfo::DEFAULT_PARAMS,
/* K = */ EquihashInfo::DEFAULT_PARAMS,
},
// UPGRADE_OVERWINTER
{
/* N = */ 144, // FIXME: This is just an example
/* K = */ 5, // FIXME: This is just an example
}
};

UpgradeState NetworkUpgradeState(
int nHeight,
const Consensus::Params& params,
Expand Down
10 changes: 10 additions & 0 deletions src/consensus/upgrades.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ extern const struct NUInfo NetworkUpgradeInfo[];
// Consensus branch id to identify pre-overwinter (Sprout) consensus rules.
extern const uint32_t SPROUT_BRANCH_ID;

struct EquihashInfo {
unsigned int N;
unsigned int K;

// Use default value as set in chainparams and genesis block
static constexpr int DEFAULT_PARAMS = 0;
};

extern struct EquihashInfo EquihashUpgradeInfo[];

/**
* Checks the state of a given network upgrade based on block height.
* Caller must check that the height is >= 0 (and handle unknown heights).
Expand Down
12 changes: 12 additions & 0 deletions src/crypto/equihash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,3 +814,15 @@ template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);

// Explicit instantiations for Equihash<144,5>
template int Equihash<144,5>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<144,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<144,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<144,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
9 changes: 9 additions & 0 deletions src/crypto/equihash.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ static Equihash<96,3> Eh96_3;
static Equihash<200,9> Eh200_9;
static Equihash<96,5> Eh96_5;
static Equihash<48,5> Eh48_5;
static Equihash<144,5> Eh144_5;

#define EhInitialiseState(n, k, base_state) \
if (n == 96 && k == 3) { \
Expand All @@ -209,6 +210,8 @@ static Equihash<48,5> Eh48_5;
Eh96_5.InitialiseState(base_state); \
} else if (n == 48 && k == 5) { \
Eh48_5.InitialiseState(base_state); \
} else if (n == 144 && k == 5) { \
Eh144_5.InitialiseState(base_state); \
} else { \
throw std::invalid_argument("Unsupported Equihash parameters"); \
}
Expand All @@ -226,6 +229,8 @@ inline bool EhBasicSolve(unsigned int n, unsigned int k, const eh_HashState& bas
return Eh96_5.BasicSolve(base_state, validBlock, cancelled);
} else if (n == 48 && k == 5) {
return Eh48_5.BasicSolve(base_state, validBlock, cancelled);
} else if (n == 144 && k == 5) {
return Eh144_5.BasicSolve(base_state, validBlock, cancelled);
} else {
throw std::invalid_argument("Unsupported Equihash parameters");
}
Expand All @@ -250,6 +255,8 @@ inline bool EhOptimisedSolve(unsigned int n, unsigned int k, const eh_HashState&
return Eh96_5.OptimisedSolve(base_state, validBlock, cancelled);
} else if (n == 48 && k == 5) {
return Eh48_5.OptimisedSolve(base_state, validBlock, cancelled);
} else if (n == 144 && k == 5) {
return Eh144_5.OptimisedSolve(base_state, validBlock, cancelled);
} else {
throw std::invalid_argument("Unsupported Equihash parameters");
}
Expand All @@ -272,6 +279,8 @@ inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const
ret = Eh96_5.IsValidSolution(base_state, soln); \
} else if (n == 48 && k == 5) { \
ret = Eh48_5.IsValidSolution(base_state, soln); \
} else if (n == 144 && k == 5) { \
ret = Eh144_5.IsValidSolution(base_state, soln); \
} else { \
throw std::invalid_argument("Unsupported Equihash parameters"); \
}
Expand Down
38 changes: 38 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", 1));
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0));
strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)");
strUsage += HelpMessageOpt("-eqparams=hexBranchId:N:K", "Use given equihash parameters for specified network upgrade");
}
string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, libevent, lock, mempool, net, partitioncheck, pow, proxy, prune, "
"rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these
Expand Down Expand Up @@ -1085,6 +1086,43 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}

if (!mapMultiArgs["-eqparams"].empty()) {
// Allow overriding equihash upgrade parameters for testing
if (Params().NetworkIDString() != "regtest") {
return InitError("Network upgrade parameters may only be overridden on regtest.");
}
const vector<string>& deployments = mapMultiArgs["-eqparams"];
for (auto i : deployments) {
std::vector<std::string> vDeploymentParams;
boost::split(vDeploymentParams, i, boost::is_any_of(":"));
if (vDeploymentParams.size() != 3) {
return InitError("Equihash upgrade parameters malformed, expecting hexBranchId:N:K");
}
int n, k;
// TODO: Restrict to support n,k parameters and cast to unsigned int
if (!ParseInt32(vDeploymentParams[1], &n)) {
return InitError(strprintf("Invalid N (%s)", vDeploymentParams[1]));
}
if (!ParseInt32(vDeploymentParams[2], &k)) {
return InitError(strprintf("Invalid K (%s)", vDeploymentParams[2]));
}
bool found = false;
// Exclude Sprout from upgrades
for (auto i = Consensus::BASE_SPROUT + 1; i < Consensus::MAX_NETWORK_UPGRADES; ++i)
{
if (vDeploymentParams[0].compare(HexInt(NetworkUpgradeInfo[i].nBranchId)) == 0) {
UpdateEquihashUpgradeParameters(Consensus::UpgradeIndex(i), n, k);
found = true;
LogPrintf("Setting equihash upgrade activation parameters for %s to n=%d, k=%d\n", vDeploymentParams[0], n, k);
break;
}
}
if (!found) {
return InitError(strprintf("Invalid equihash upgrade (%s)", vDeploymentParams[0]));
}
}
}

// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log

// Initialize libsodium
Expand Down
16 changes: 16 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3297,6 +3297,22 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta

int nHeight = pindexPrev->nHeight+1;

// If the equihash solution is set, check the size is correct for given parameters.
size_t nSolSize = block.nSolution.size();
if (nSolSize > 0) {
int n = chainParams.EquihashN(nHeight);
int k = chainParams.EquihashK(nHeight);
size_t expectedSize = (pow(2, k) * ((n/(k+1))+1)) / 8;
if (nSolSize != expectedSize){
return state.DoS(
100,
error("%s: incorrect equihash solution size %d, expected size %d for parameters (%d, %d)",
__func__, nSolSize, expectedSize, n, k),
REJECT_INVALID,
"bad-equihash-solution-size");
}
}

// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
return state.DoS(100, error("%s: incorrect proof of work", __func__),
Expand Down
18 changes: 13 additions & 5 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,12 +514,8 @@ void static BitcoinMiner()
// Each thread has its own counter
unsigned int nExtraNonce = 0;

unsigned int n = chainparams.EquihashN();
unsigned int k = chainparams.EquihashK();

std::string solver = GetArg("-equihashsolver", "default");
assert(solver == "tromp" || solver == "default");
LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k);

std::mutex m_cs;
bool cancelSolver = false;
Expand Down Expand Up @@ -554,7 +550,19 @@ void static BitcoinMiner()
// Create new block
//
unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrev = chainActive.Tip();

// Get the height of current tip
int nHeight = chainActive.Height();
if (nHeight == -1) {
LogPrintf("Error in ZcashMiner: chainActive.Height() returned -1\n");
return;
}
CBlockIndex* pindexPrev = chainActive[nHeight];

// Get equihash parameters for the next block to be mined.
unsigned int n = chainparams.EquihashN(nHeight + 1);
unsigned int k = chainparams.EquihashK(nHeight + 1);
LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k);

#ifdef ENABLE_WALLET
unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
Expand Down
22 changes: 20 additions & 2 deletions src/pow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,26 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,

bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& params)
{
unsigned int n = params.EquihashN();
unsigned int k = params.EquihashK();
// Derive n, k from the solution size as the block header does not specify parameters used.
// In the future, we could pass in the block height and call EquihashN() and EquihashK()
// to perform a contextual check against the parameters in use at a given block height.
unsigned int n, k;
size_t nSolSize = pblock->nSolution.size();
if (nSolSize == 1344) { // mainnet and testnet genesis
n = 200;
k = 9;
} else if (nSolSize == 36) { // regtest genesis
n = 48;
k = 5;
} else if (nSolSize == 100) {
n = 144;
k = 5;
} else if (nSolSize == 68) {
n = 96;
k = 5;
} else {
return error("%s: Unsupported solution size of %d", __func__, nSolSize);
}

// Hash state
crypto_generichash_blake2b_state state;
Expand Down
5 changes: 3 additions & 2 deletions src/rpcmining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,11 @@ UniValue generate(const UniValue& params, bool fHelp)
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
unsigned int n = Params().EquihashN();
unsigned int k = Params().EquihashK();
while (nHeight < nHeightEnd)
{
unsigned int n = Params().EquihashN(nHeight + 1);
unsigned int k = Params().EquihashK(nHeight + 1);

#ifdef ENABLE_WALLET
std::unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
#else
Expand Down

0 comments on commit 02397b6

Please sign in to comment.