#include "amount.h"
#include "chainparams.h"
-#include "cc/CoinbaseGuard.h"
+#include "cc/StakeGuard.h"
#include "importcoin.h"
#include "consensus/consensus.h"
#include "consensus/upgrades.h"
#include "wallet/wallet.h"
#endif
+#include "zcash/Address.hpp"
+#include "transaction_builder.h"
+
#include "sodium.h"
#include <boost/thread.hpp>
#endif
#include <mutex>
+#include "pbaas/pbaas.h"
+#include "pbaas/notarization.h"
+
using namespace std;
//////////////////////////////////////////////////////////////////////////////
void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
- pblock->nTime = 1 + std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+ pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+
+ // Updating time can change work required on testnet:
+ if (consensusParams.nPowAllowMinDifficultyBlocksAfterHeight != boost::none) {
+ pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
+ }
}
#include "komodo_defs.h"
extern CCriticalSection cs_metrics;
extern int32_t KOMODO_MININGTHREADS,KOMODO_LONGESTCHAIN,ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAIN_INIT,KOMODO_INITDONE,KOMODO_ON_DEMAND,KOMODO_INITDONE,KOMODO_PASSPORT_INITDONE;
extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_STAKED;
+extern bool VERUS_MINTBLOCKS;
extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[];
extern const char *ASSETCHAINS_ALGORITHMS[];
extern int32_t VERUS_MIN_STAKEAGE, ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA, ASSETCHAINS_LWMAPOS, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[];
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
+extern uint160 ASSETCHAINS_CHAINID;
+extern uint160 VERUS_CHAINID;
+extern std::string VERUS_CHAINNAME;
+extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK;
+extern string PBAAS_HOST, PBAAS_USERPASS;
+extern int32_t PBAAS_PORT;
extern std::string NOTARY_PUBKEY,ASSETCHAINS_OVERRIDE_PUBKEY;
void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len);
uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params();
//fprintf(stderr,"create new block\n");
- // Create new block
+ // Create new block
if ( gpucount < 0 )
gpucount = KOMODO_MAXGPUCOUNT;
std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
return NULL;
}
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
- // -regtest only: allow overriding block.nVersion with
+
+ // set version according to the current tip height, add solution if it is
+ // VerusHash
+ if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH)
+ {
+ pblock->nSolution.resize(Eh200_9.SolutionWidth);
+ }
+ else
+ {
+ pblock->nSolution.clear();
+ }
+ pblock->SetVersionByHeight(chainActive.LastTip()->GetHeight() + 1);
+
+ // -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (Params().MineBlocksOnDemand())
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
// Collect memory pool transactions into the block
CAmount nFees = 0;
+
+ // we will attempt to spend any cheats we see
+ CTransaction cheatTx;
+ boost::optional<CTransaction> cheatSpend;
+ uint256 cbHash;
+
CBlockIndex* pindexPrev = 0;
{
LOCK2(cs_main, mempool.cs);
pindexPrev = chainActive.LastTip();
const int nHeight = pindexPrev->GetHeight() + 1;
- uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus());
+ const Consensus::Params &consensusParams = chainparams.GetConsensus();
+ uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, consensusParams);
+ bool sapling = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_SAPLING);
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
uint32_t proposedTime = GetAdjustedTime();
// This vector will be sorted into a priority queue:
vector<TxPriority> vecPriority;
- vecPriority.reserve(mempool.mapTx.size());
+ vecPriority.reserve(mempool.mapTx.size() + 1);
+
+ // check if we should add cheat transaction
+ CBlockIndex *ppast;
+ CTransaction cb;
+ int cheatHeight = nHeight - COINBASE_MATURITY < 1 ? 1 : nHeight - COINBASE_MATURITY;
+ if (cheatCatcher &&
+ sapling && chainActive.Height() > 100 &&
+ (ppast = chainActive[cheatHeight]) &&
+ ppast->IsVerusPOSBlock() &&
+ cheatList.IsHeightOrGreaterInList(cheatHeight))
+ {
+ // get the block and see if there is a cheat candidate for the stake tx
+ CBlock b;
+ if (!(fHavePruned && !(ppast->nStatus & BLOCK_HAVE_DATA) && ppast->nTx > 0) && ReadBlockFromDisk(b, ppast, 1))
+ {
+ CTransaction &stakeTx = b.vtx[b.vtx.size() - 1];
+
+ if (cheatList.IsCheatInList(stakeTx, &cheatTx))
+ {
+ // make and sign the cheat transaction to spend the coinbase to our address
+ CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
+
+ uint32_t voutNum;
+ // get the first vout with value
+ for (voutNum = 0; voutNum < b.vtx[0].vout.size(); voutNum++)
+ {
+ if (b.vtx[0].vout[voutNum].nValue > 0)
+ break;
+ }
+
+ // send to the same pub key as the destination of this block reward
+ if (MakeCheatEvidence(mtx, b.vtx[0], voutNum, cheatTx))
+ {
+ extern CWallet *pwalletMain;
+ LOCK(pwalletMain->cs_wallet);
+ TransactionBuilder tb = TransactionBuilder(consensusParams, nHeight);
+ cb = b.vtx[0];
+ cbHash = cb.GetHash();
+
+ bool hasInput = false;
+ for (uint32_t i = 0; i < cb.vout.size(); i++)
+ {
+ // add the spends with the cheat
+ if (cb.vout[i].nValue > 0)
+ {
+ tb.AddTransparentInput(COutPoint(cbHash,i), cb.vout[0].scriptPubKey, cb.vout[0].nValue);
+ hasInput = true;
+ }
+ }
+
+ if (hasInput)
+ {
+ // this is a send from a t-address to a sapling address, which we don't have an ovk for.
+ // Instead, generate a common one from the HD seed. This ensures the data is
+ // recoverable, at least for us, while keeping it logically separate from the ZIP 32
+ // Sapling key hierarchy, which the user might not be using.
+ uint256 ovk;
+ HDSeed seed;
+ if (pwalletMain->GetHDSeed(seed)) {
+ ovk = ovkForShieldingFromTaddr(seed);
+
+ // send everything to Sapling address
+ tb.SendChangeTo(cheatCatcher.value(), ovk);
+
+ tb.AddOpRet(mtx.vout[mtx.vout.size() - 1].scriptPubKey);
+
+ cheatSpend = tb.Build();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (cheatSpend)
+ {
+ cheatTx = cheatSpend.value();
+ std::list<CTransaction> removed;
+ mempool.removeConflicts(cheatTx, removed);
+ printf("Found cheating stake! Adding cheat spend for %.8f at block #%d, coinbase tx\n%s\n",
+ (double)cb.GetValueOut() / (double)COIN, nHeight, cheatSpend.value().vin[0].prevout.hash.GetHex().c_str());
+
+ // add to mem pool and relay
+ if (myAddtomempool(cheatTx))
+ {
+ RelayTransaction(cheatTx);
+ }
+ }
+
+ // if we are a PBaaS chain, first make sure we don't start prematurely, and if
+ // we should make an earned notarization, make it and set index to non-zero value
+ int32_t pbaasNotarizationTx = 0;
+ int64_t pbaasTransparentIn = 0;
+ int32_t pbaasCoinbaseInstantSpendOut = 0;
+ if (!IsVerusActive())
+ {
+ // if we don't have a connected root PBaaS chain, we can't properly check
+ // and notarize the start block, so we have to pass and wait
+ if (nHeight != 1 || (ConnectedChains.IsVerusPBaaSAvailable() && ConnectedChains.notaryChainHeight >= PBAAS_STARTBLOCK))
+ {
+ // if we have access to our parent daemon
+ // create a notarization, if we would qualify, and add it to the mempool and block
+ CMutableTransaction newNotarizationTx;
+ CTransaction prevTx, crossTx;
+ ChainMerkleMountainView mmv = chainActive.GetMMV();
+ uint256 mmrRoot = mmv.GetRoot();
+ if (CreateEarnedNotarization(newNotarizationTx, prevTx, crossTx, nHeight, mmrRoot))
+ {
+ // we have a valid, earned notarization transaction. we still need to verify:
+ // 1. it has enough input for its outputs. it can be returned with less than enough,
+ // and if so, take it as instant-spend from the coinbase. instant-spend and notarization
+ // threads on a PBaaS chain can only be used for notarization, and can never convert to
+ // available supply
+
+ // Fetch previous transactions (inputs):
+ for (const CTxIn& txin : newNotarizationTx.vin)
+ {
+ const uint256& prevHash = txin.prevout.hash;
+ const CCoins *pcoins = view.AccessCoins(prevHash); // this is certainly allowed to fail
+ pbaasTransparentIn += pcoins && (pcoins->vout.size() > txin.prevout.n) ? pcoins->vout[txin.prevout.n].nValue : 0;
+ }
+ pblock->vtx.push_back(CTransaction(newNotarizationTx));
+ pblocktemplate->vTxFees.push_back(0);
+ pblocktemplate->vTxSigOps.push_back(-1); // updated at end
+ pbaasNotarizationTx = pblock->vtx.size() - 1;
+ }
+ else if (nHeight == 1)
+ {
+ // failed to notarize at block 1
+ return NULL;
+ }
+ }
+ else
+ {
+ // can't mine block 1 unless we have a connection to Verus and can notarize
+ return NULL;
+ }
+ }
+
+ // now add transactions from the mem pool
for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
mi != mempool.mapTx.end(); ++mi)
{
int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
? nMedianTimePast
: pblock->GetBlockTime();
-
+
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight))
{
//fprintf(stderr,"coinbase.%d finaltx.%d expired.%d\n",tx.IsCoinBase(),IsFinalTx(tx, nHeight, nLockTimeCutoff),IsExpiredTx(tx, nHeight));
continue;
}
+
if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,nHeight,(uint32_t)pblock->nTime,0) < 0 )
{
//fprintf(stderr,"CreateNewBlock: komodo_validate_interest failure nHeight.%d nTime.%u vs locktime.%u\n",nHeight,(uint32_t)pblock->nTime,(uint32_t)tx.nLockTime);
continue;
}
+
COrphan* porphan = NULL;
double dPriority = 0;
CAmount nTotalIn = 0;
else
vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx())));
}
-
+
// Collect transactions into block
uint64_t nBlockSize = 1000;
uint64_t nBlockTx = 0;
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
int32_t stakeHeight = chainActive.Height() + 1;
- bool extendedStake = (Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight >= stakeHeight);
//LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x\n", nBlockSize,blocktime,pblock->nBits);
if ( ASSETCHAINS_SYMBOL[0] != 0 && isStake )
// after Sapling, stake transactions have a fee, but it is recovered in the reward
// this ensures that a rebroadcast goes through quickly to begin staking again
- txfees = extendedStake ? DEFAULT_STAKE_TXFEE : 0;
+ txfees = sapling ? DEFAULT_STAKE_TXFEE : 0;
pblock->vtx.push_back(txStaked);
pblocktemplate->vTxFees.push_back(txfees);
}
// Create coinbase tx
- CMutableTransaction txNew = CreateNewContextualCMutableTransaction(chainparams.GetConsensus(), nHeight);
+ CMutableTransaction txNew = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
- txNew.vout[0].nValue = GetBlockSubsidy(nHeight,chainparams.GetConsensus()) + nFees;
+ txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees;
- // once we get to Sapling, enable CC CoinbaseGuard for stake transactions
- if (isStake && extendedStake)
+ // once we get to Sapling, enable CC StakeGuard for stake transactions
+ if (isStake && sapling)
{
// if there is a specific destination, use it
CTransaction stakeTx = pblock->vtx[pblock->vtx.size() - 1];
CStakeParams p;
- if (ValidateStakeTransaction(stakeTx, p))
+ if (ValidateStakeTransaction(stakeTx, p, false))
{
if (!p.pk.IsValid() || !MakeGuardedOutput(txNew.vout[0].nValue, p.pk, stakeTx, txNew.vout[0]))
{
//printf("autocreate commision vout\n");
}
+ // if we need an instant out to be a source of funds for the notarization transaction, make it here
+ if (pbaasNotarizationTx)
+ {
+ extern CWallet *pwalletMain;
+ LOCK(pwalletMain->cs_wallet);
+
+ // now add an input from the coinbase to the notarization
+ CMutableTransaction mntx(pblock->vtx[pbaasNotarizationTx]);
+ CTransaction ntx = pblock->vtx[pbaasNotarizationTx];
+
+ int64_t pbaasTxValueOut = ntx.GetValueOut();
+ int numOutputs = ntx.vout.size() - (ntx.vout[ntx.vout.size() - 1].scriptPubKey.IsOpReturn() ? 1 : 0);
+ if (pbaasNotarizationTx != 0 && pbaasTxValueOut > pbaasTransparentIn)
+ {
+ int64_t needed = pbaasTxValueOut - pbaasTransparentIn;
+ if (needed > PBAAS_MINNOTARIZATIONOUTPUT * numOutputs)
+ {
+ fprintf(stderr,"CreateNewBlock: too much output from earned notarization transaction\n");
+ return NULL;
+ }
+
+ auto coinbaseOutIt = txNew.vout.end();
+ pbaasCoinbaseInstantSpendOut = txNew.vout.size();
+ if ((coinbaseOutIt - 1)->scriptPubKey.IsOpReturn())
+ {
+ coinbaseOutIt -= 1;
+ pbaasCoinbaseInstantSpendOut -= 1;
+ }
+
+ CCcontract_info CC;
+ CCcontract_info *cp;
+ vector<CTxDestination> vKeys;
+
+ // make the earned notarization output
+ cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
+ // need to be able to send this to EVAL_PBAASDEFINITION address as a destination, locked by the default pubkey
+ CPubKey pk = CPubKey(std::vector<unsigned char>(CC.CChexstr, CC.CChexstr + strlen(CC.CChexstr)));
+
+ vKeys.push_back(CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(VERUS_CHAINID, EVAL_EARNEDNOTARIZATION))));
+
+ // output duplicate notarization as coinbase output for instant spend to notarization
+ // these coins will never join the supply pool, so they do not need to be considered as
+ // part of the total value out sum of this coinbase
+ CPBaaSNotarization pbn(ntx);
+ txNew.vout.insert(coinbaseOutIt, MakeCC1of1Vout(EVAL_EARNEDNOTARIZATION, PBAAS_MINNOTARIZATIONOUTPUT, pk, vKeys, pbn));
+ mntx.vin.push_back(CTxIn(txNew.GetHash(), pbaasCoinbaseInstantSpendOut));
+ }
+
+ // we need to sign mntx and put it back
+ int nIn = 0;
+ ntx = CTransaction(mntx);
+
+ for (int i = 0; i < ntx.vin.size(); i++)
+ {
+ bool signSuccess;
+ const CCoins *coins = view.AccessCoins(ntx.vin[i].prevout.hash);
+ const CScript& scriptPubKey = coins->vout[ntx.vin[i].prevout.n].scriptPubKey;
+ SignatureData sigdata;
+
+ signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &ntx, i, coins->vout[ntx.vin[i].prevout.n].nValue, SIGHASH_ALL), scriptPubKey, sigdata, consensusBranchId);
+
+ if (!signSuccess)
+ {
+ fprintf(stderr,"CreateNewBlock: failure to sign earned notarization\n");
+ return NULL;
+ } else {
+ UpdateTransaction(mntx, i, sigdata);
+ }
+ }
+ pblock->vtx[pbaasNotarizationTx] = mntx;
+ pblocktemplate->vTxSigOps[pbaasNotarizationTx] = GetLegacySigOpCount(mntx);
+ }
+
pblock->vtx[0] = txNew;
pblocktemplate->vTxFees[0] = -nFees;
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
}
- pblock->nSolution.clear();
+
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
+
if ( ASSETCHAINS_SYMBOL[0] == 0 && IS_KOMODO_NOTARY != 0 && My_notaryid >= 0 )
{
uint32_t r;
}
else
{
- if (!reservekey.GetReservedKey(pubkey))
+ if (!isStake)
{
- return NULL;
+ if (!reservekey.GetReservedKey(pubkey))
+ {
+ return NULL;
+ }
+ scriptPubKey.resize(35);
+ ptr = (uint8_t *)pubkey.begin();
+ scriptPubKey[0] = 33;
+ for (i=0; i<33; i++)
+ scriptPubKey[i+1] = ptr[i];
+ scriptPubKey[34] = OP_CHECKSIG;
+ //scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
}
- scriptPubKey.resize(35);
- ptr = (uint8_t *)pubkey.begin();
- scriptPubKey[0] = 33;
- for (i=0; i<33; i++)
- scriptPubKey[i+1] = ptr[i];
- scriptPubKey[34] = OP_CHECKSIG;
- //scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
}
return CreateNewBlock(scriptPubKey, gpucount, isStake);
}
// Found a solution
{
- //LOCK(cs_main);
if (pblock->hashPrevBlock != chainActive.LastTip()->GetBlockHash())
{
uint256 hash; int32_t i;
extern int32_t KOMODO_LASTMINED,KOMODO_INSYNC;
int32_t roundrobin_delay;
arith_uint256 HASHTarget,HASHTarget_POW;
+int32_t komodo_longestchain();
// wait for peers to connect
-int32_t waitForPeers(const CChainParams &chainparams)
+void waitForPeers(const CChainParams &chainparams)
{
if (chainparams.MiningRequiresPeers())
{
bool fvNodesEmpty;
{
+ boost::this_thread::interruption_point();
LOCK(cs_vNodes);
fvNodesEmpty = vNodes.empty();
}
- if (fvNodesEmpty || !IsInSync())
+ int longestchain = komodo_longestchain();
+ int lastlongest = 0;
+ if (fvNodesEmpty || IsNotInSync() || (longestchain != 0 && longestchain > chainActive.LastTip()->GetHeight()))
{
+ int loops = 0, blockDiff = 0, newDiff = 0;
+
do {
if (fvNodesEmpty)
- MilliSleep(1000 + rand() % 4000);
{
+ MilliSleep(1000 + rand() % 4000);
+ boost::this_thread::interruption_point();
LOCK(cs_vNodes);
fvNodesEmpty = vNodes.empty();
+ loops = 0;
+ blockDiff = 0;
+ lastlongest = 0;
}
- } while (fvNodesEmpty || !IsInSync());
+ else if ((newDiff = IsNotInSync()) > 0)
+ {
+ if (blockDiff != newDiff)
+ {
+ blockDiff = newDiff;
+ }
+ else
+ {
+ if (++loops <= 5)
+ {
+ MilliSleep(1000);
+ }
+ else break;
+ }
+ lastlongest = 0;
+ }
+ else if (!fvNodesEmpty && !IsNotInSync() && longestchain > chainActive.LastTip()->GetHeight())
+ {
+ // the only thing may be that we are seeing a long chain that we'll never get
+ // don't wait forever
+ if (lastlongest == 0)
+ {
+ MilliSleep(3000);
+ lastlongest = longestchain;
+ }
+ }
+ } while (fvNodesEmpty || IsNotInSync());
MilliSleep(100 + rand() % 400);
}
}
return(0);
}
+/*
+ * When called, this checks to see if Verus daemon is running and available. If so, it calls to get the latest
+ * notarization data and all information necessary to make a notarization transaction for the current chain, or the
+ * Verus chain.
+ */
+void static UpdateNotarizationData()
+{
+
+}
+
/*
* A separate thread to stake, while the miner threads mine.
*/
RenameThread("verus-staker");
const CChainParams& chainparams = Params();
+ auto consensusParams = chainparams.GetConsensus();
// Each thread has its own key
CReserveKey reservekey(pwallet);
// Each thread has its own counter
unsigned int nExtraNonce = 0;
- std::vector<unsigned char> solnPlaceholder = std::vector<unsigned char>();
- solnPlaceholder.resize(Eh200_9.SolutionWidth);
+
uint8_t *script; uint64_t total,checktoshis; int32_t i,j;
while ( (ASSETCHAIN_INIT == 0 || KOMODO_INITDONE == 0) ) //chainActive.Tip()->GetHeight() != 235300 &&
} while (pindexPrev != pindexCur);
try {
+ static int32_t lastStakingHeight = 0;
+
while (true)
{
waitForPeers(chainparams);
CBlockIndex* pindexPrev = chainActive.LastTip();
- printf("Staking height %d for %s\n", pindexPrev->GetHeight() + 1, ASSETCHAINS_SYMBOL);
// Create new block
unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
+
if ( Mining_height != pindexPrev->GetHeight()+1 )
{
Mining_height = pindexPrev->GetHeight()+1;
Mining_start = (uint32_t)time(NULL);
}
+ if ( Mining_height != lastStakingHeight )
+ {
+ printf("Staking height %d for %s\n", Mining_height, ASSETCHAINS_SYMBOL);
+ lastStakingHeight = Mining_height;
+ }
+
// Check for stop or if block needs to be rebuilt
boost::this_thread::interruption_point();
{
// wait to try another staking block until after the tip moves again
while ( chainActive.LastTip() == pindexPrev )
- sleep(1);
+ MilliSleep(100);
continue;
}
//
int64_t nStart = GetTime();
- // take up the necessary space for alignment
- pblock->nSolution = solnPlaceholder;
-
// we don't use this, but IncrementExtraNonce is the function that builds the merkle tree
unsigned int nExtraNonce = 0;
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
+ // update PBaaS header
+ if (CConstVerusSolutionVector::activationHeight.ActiveVersion(Mining_height) == CActivationHeight::SOLUTION_VERUSV3)
+ {
+ uint256 mmvRoot;
+ {
+ LOCK(cs_main);
+ // set the PBaaS header
+ ChainMerkleMountainView mmv = chainActive.GetMMV();
+ mmvRoot = mmv.GetRoot();
+ }
+ pblock->AddUpdatePBaaSHeader(mmvRoot);
+ }
+
if (vNodes.empty() && chainparams.MiningRequiresPeers())
{
if ( Mining_height > ASSETCHAINS_MINHEIGHT )
uint256 hashTarget = ArithToUint256(arith_uint256().SetCompact(pblock->nBits));
- pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
+ pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
- UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
+ UpdateTime(pblock, consensusParams, pindexPrev);
ProcessBlockFound(pblock, *pwallet, reservekey);
arith_uint256 post;
post.SetCompact(pblock->GetVerusPOSTarget());
pindexPrev = get_chainactive(Mining_height - 100);
- printf(" hash: %s \ntarget: %s\n",
- CTransaction::_GetVerusPOSHash(&(pblock->nNonce), pblock->vtx[pblock->vtx.size()-1].vin[0].prevout.hash, 0, Mining_height, pindexPrev->GetBlockHeader().GetVerusEntropyHash(Mining_height - 100), pblock->vtx[pblock->vtx.size()-1].vout[0].nValue).GetHex().c_str(), ArithToUint256(post).GetHex().c_str());
+ CTransaction &sTx = pblock->vtx[pblock->vtx.size()-1];
+ printf("POS hash: %s \ntarget: %s\n",
+ CTransaction::_GetVerusPOSHash(&(pblock->nNonce), sTx.vin[0].prevout.hash, sTx.vin[0].prevout.n, Mining_height, pindexPrev->GetBlockHeader().GetVerusEntropyHash(Mining_height - 100), sTx.vout[0].nValue).GetHex().c_str(), ArithToUint256(post).GetHex().c_str());
if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE)
printf("- timelocked until block %i\n", unlockTime);
else
}
}
+typedef bool (*minefunction)(CBlockHeader &bh, CVerusHashV2bWriter &vhw, uint256 &finalHash, uint256 &target, uint64_t start, uint64_t *count);
+bool mine_verus_v2(CBlockHeader &bh, CVerusHashV2bWriter &vhw, uint256 &finalHash, uint256 &target, uint64_t start, uint64_t *count);
+bool mine_verus_v2_port(CBlockHeader &bh, CVerusHashV2bWriter &vhw, uint256 &finalHash, uint256 &target, uint64_t start, uint64_t *count);
+
void static BitcoinMiner_noeq(CWallet *pwallet)
#else
void static BitcoinMiner_noeq()
const CChainParams& chainparams = Params();
// Each thread has its own counter
unsigned int nExtraNonce = 0;
- std::vector<unsigned char> solnPlaceholder = std::vector<unsigned char>();
- solnPlaceholder.resize(Eh200_9.SolutionWidth);
+
uint8_t *script; uint64_t total,checktoshis; int32_t i,j;
while ( (ASSETCHAIN_INIT == 0 || KOMODO_INITDONE == 0) ) //chainActive.Tip()->GetHeight() != 235300 &&
// this will not stop printing more than once in all cases, but it will allow us to print in all cases
// and print duplicates rarely without having to synchronize
static CBlockIndex *lastChainTipPrinted;
+ static int32_t lastMiningHeight = 0;
miningTimer.start();
try {
printf("Mining %s with %s\n", ASSETCHAINS_SYMBOL, ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+
+ // v2 hash writer
+ CVerusHashV2bWriter ss2 = CVerusHashV2bWriter(SER_GETHASH, PROTOCOL_VERSION);
+
while (true)
{
miningTimer.stop();
waitForPeers(chainparams);
pindexPrev = chainActive.LastTip();
- sleep(1);
- // prevent forking on startup before the diff algorithm kicks in
- if (pindexPrev->GetHeight() < 50 || pindexPrev != chainActive.LastTip())
+ // prevent forking on startup before the diff algorithm kicks in,
+ // but only for a startup Verus test chain. PBaaS chains have the difficulty inherited from
+ // their parent
+ if (chainparams.MiningRequiresPeers() && ((IsVerusActive() && pindexPrev->GetHeight() < 50) || pindexPrev != chainActive.LastTip()))
{
do {
pindexPrev = chainActive.LastTip();
- MilliSleep(5000 + rand() % 5000);
+ MilliSleep(3000 + rand() % 3000);
} while (pindexPrev != chainActive.LastTip());
}
if ( Mining_height != pindexPrev->GetHeight()+1 )
{
Mining_height = pindexPrev->GetHeight()+1;
+ if (lastMiningHeight != Mining_height)
+ {
+ lastMiningHeight = Mining_height;
+ printf("Mining height %d\n", Mining_height);
+ }
Mining_start = (uint32_t)time(NULL);
}
- if (lastChainTipPrinted != pindexPrev)
- {
- printf("Mining height %d\n", Mining_height);
- lastChainTipPrinted = pindexPrev;
- }
-
miningTimer.start();
#ifdef ENABLE_WALLET
static uint32_t counter;
if ( counter++ < 100 )
fprintf(stderr,"created illegal block, retry\n");
+ MilliSleep(500);
continue;
}
return;
}
CBlock *pblock = &pblocktemplate->block;
+
+ uint32_t savebits;
+ bool mergeMining = false;
+ savebits = pblock->nBits;
+
+ bool verusHashV2 = pblock->nVersion == CBlockHeader::VERUS_V2;
+ bool verusSolutionV3 = CConstVerusSolutionVector::Version(pblock->nSolution) == CActivationHeight::SOLUTION_VERUSV3;
+
if ( ASSETCHAINS_SYMBOL[0] != 0 )
{
if ( ASSETCHAINS_REWARD[0] == 0 && !ASSETCHAINS_LASTERA )
} else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT);
}
}
+
+ // this builds the Merkle tree
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
+
+ // update PBaaS header
+ if (verusSolutionV3)
+ {
+ uint256 mmvRoot;
+ {
+ LOCK(cs_main);
+ ChainMerkleMountainView mmv = chainActive.GetMMV();
+ mmvRoot = mmv.GetRoot();
+ }
+ pblock->AddUpdatePBaaSHeader(mmvRoot);
+
+ if (IsVerusActive())
+ {
+ // combine all merge mined headers into this header
+ // and get the easiest target of all chains in savebits
+ if (!(savebits = ConnectedChains.CombineBlocks(*pblock)))
+ {
+ savebits = pblock->nBits;
+ }
+
+ LOCK(cs_main);
+ // TODO: REMOVE OR COMMENT TESTS
+ // tests to validate a few transactions and all past blocks
+ ChainMerkleMountainView mmv = chainActive.GetMMV();
+ mmvRoot = mmv.GetRoot();
+ for (uint32_t i = 1; i <= pindexPrev->GetHeight(); i += 10)
+ {
+ CBlockIndex *pindex = chainActive[i - 1];
+ mmv.resize(i);
+ uint256 testRoot = mmv.GetRoot();
+ uint32_t testHeight = ((unsigned char *)&testRoot)[0] < i ? (i - ((unsigned char *)&testRoot)[0]) - 1 : i - 1;
+ CMerkleBranch branchMerkle, branchBlock;
+ chainActive.GetBlockProof(mmv, branchBlock, testHeight);
+ chainActive.GetMerkleProof(mmv, branchMerkle, testHeight);
+ uint256 merkleAnswer = branchMerkle.SafeCheck(chainActive[testHeight]->hashMerkleRoot);
+ uint256 blockAnswer = branchBlock.SafeCheck(chainActive[testHeight]->GetBlockHash());
+ if (merkleAnswer != testRoot)
+ {
+ printf("Failed merkle proof at testheight: %u\nexpected: %s\ncalculated: %s\n", testHeight, testRoot.GetHex().c_str(), merkleAnswer.GetHex().c_str());
+ printf("Bits for left (0) and right (1): \n");
+ std::vector<unsigned char> proofBits = ChainMerkleMountainView::GetProofBits(testHeight, i);
+ printf("right\n");
+ for (auto bit : proofBits)
+ {
+ printf("%s\n", bit ? "left" : "right");
+ }
+ }
+ if (blockAnswer != testRoot)
+ {
+ printf("Failed block proof at testheight: %u\nexpected: %s\ncalculated: %s\n", testHeight, testRoot.GetHex().c_str(), blockAnswer.GetHex().c_str());
+ printf("Bits for left (0) and right (1): \n");
+ std::vector<unsigned char> proofBits = ChainMerkleMountainView::GetProofBits(testHeight, i);
+ printf("left\n");
+ for (auto bit : proofBits)
+ {
+ printf("%s\n", bit ? "left" : "right");
+ }
+ }
+ const CMMRPowerNode *ppower = mmv.GetRootNode();
+ if (!ppower || ppower->Work() != pindex->chainPower.chainWork)
+ {
+ printf("Work did not match:\nexpected: %s\ncalculated: %s\n", ArithToUint256(pindex->chainPower.chainWork).GetHex().c_str(), ArithToUint256(ppower->Work()).GetHex().c_str());
+ }
+ if (!ppower || ppower->Stake() != pindex->chainPower.chainStake)
+ {
+ printf("Stake did not match:\nexpected: %s\ncalculated: %s\n", ArithToUint256(pindex->chainPower.chainStake).GetHex().c_str(), ArithToUint256(ppower->Stake()).GetHex().c_str());
+ }
+ }
+ // END TESTS
+ }
+ else
+ {
+ // submit the block for merge mining if Verus is present
+ // otherwise, mine solo
+ if (ConnectedChains.IsVerusPBaaSAvailable())
+ {
+
+ UniValue params(UniValue::VARR);
+ params.push_back(EncodeHexBlk(*pblock));
+ params.push_back(ASSETCHAINS_SYMBOL);
+ params.push_back(PBAAS_HOST);
+ params.push_back(PBAAS_PORT);
+ params.push_back(PBAAS_USERPASS);
+ try
+ {
+ params = RPCCallRoot("addmergedblock", params);
+ } catch (std::exception e)
+ {
+ printf("Failed to connect to %s chain\n", ConnectedChains.notaryChain.chainDefinition.name.c_str());
+ params = UniValue(e.what());
+ }
+ if (mergeMining = params.isNull())
+ {
+ printf("Merge mining -- deferring to %s as the actual mining chain\n", ConnectedChains.notaryChain.chainDefinition.name.c_str());
+ }
+ }
+ }
+ }
+
LogPrintf("Running %s miner with %u transactions in block (%u bytes)\n",ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO],
pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION));
//
// Search
//
- uint32_t savebits; int64_t nStart = GetTime();
+ int64_t nStart = GetTime();
- pblock->nSolution = solnPlaceholder;
- savebits = pblock->nBits;
- arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
- arith_uint256 mask(ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO]);
+ arith_uint256 hashTarget = arith_uint256().SetCompact(savebits);
+ uint256 uintTarget = ArithToUint256(hashTarget);
+
+ arith_uint256 ourTarget;
+ ourTarget.SetCompact(pblock->nBits);
Mining_start = 0;
lastChainTipPrinted = chainActive.LastTip();
printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
}
- MilliSleep(250);
+ MilliSleep(100);
continue;
}
fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%%\n",percPoS,(int32_t)ASSETCHAINS_STAKED);
}
- while (true)
+ uint64_t count, hashesToGo = 0;
+ if (!verusHashV2)
{
- arith_uint256 arNonce = UintToArith256(pblock->nNonce);
+ // must not be in sync
+ printf("Mining on incorrect block version.\n");
+ sleep(2);
+ continue;
+ }
+
+ count = ((ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] >> 3) + 1) / ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
+ CVerusHashV2 *vh2 = &ss2.GetState();
+ u128 *hashKey;
+ verusclhasher &vclh = vh2->vclh;
+ minefunction mine_verus;
+ mine_verus = IsCPUVerusOptimized() ? &mine_verus_v2 : &mine_verus_v2_port;
- CVerusHashWriter ss = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
- ss << *((CBlockHeader *)pblock);
- int64_t *extraPtr = ss.xI64p();
- CVerusHash &vh = ss.GetState();
+ while (true)
+ {
uint256 hashResult = uint256();
- vh.ClearExtra();
- int64_t i, count = ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1;
- int64_t hashesToGo = ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
- // for speed check NONCEMASK at a time
- for (i = 0; i < count; i++)
- {
- *extraPtr = i;
- vh.ExtraHash((unsigned char *)&hashResult);
+ unsigned char *curBuf;
- if ( UintToArith256(hashResult) <= hashTarget )
+ if (mergeMining)
+ {
+ // loop for about one minute before refreshing the block
+ for (uint64_t i = 0; i < 240; i++)
{
- if (pblock->nSolution.size() != 1344)
+ boost::this_thread::interruption_point();
+ MilliSleep(250);
+
+ if (vNodes.empty() && chainparams.MiningRequiresPeers())
{
- LogPrintf("ERROR: Block solution is not 1344 bytes as it should be");
- sleep(5);
- break;
+ if ( Mining_height > ASSETCHAINS_MINHEIGHT )
+ {
+ fprintf(stderr,"no nodes, attempting reconnect\n");
+ break;
+ }
}
- SetThreadPriority(THREAD_PRIORITY_NORMAL);
-
- *((int64_t *)&(pblock->nSolution.data()[pblock->nSolution.size() - 15])) = i;
-
- int32_t unlockTime = komodo_block_unlocktime(Mining_height);
- int64_t subsidy = (int64_t)(pblock->vtx[0].vout[0].nValue);
+ if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
+ {
+ break;
+ }
- LogPrintf("Using %s algorithm:\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
- LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex());
- printf("Found block %d \n", Mining_height );
- printf("mining reward %.8f %s!\n", (double)subsidy / (double)COIN, ASSETCHAINS_SYMBOL);
- printf(" hash: %s \ntarget: %s\n", pblock->GetHash().GetHex().c_str(), hashTarget.GetHex().c_str());
- if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE)
- printf("- timelocked until block %i\n", unlockTime);
- else
- printf("\n");
-#ifdef ENABLE_WALLET
- ProcessBlockFound(pblock, *pwallet, reservekey);
-#else
- ProcessBlockFound(pblock));
-#endif
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
- break;
- }
- // check periodically if we're stale
- if (!--hashesToGo)
- {
if ( pindexPrev != chainActive.LastTip() )
{
if (lastChainTipPrinted != chainActive.LastTip())
{
lastChainTipPrinted = chainActive.LastTip();
- printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
+ printf("Block %d added to chain\n\n", lastChainTipPrinted->GetHeight());
}
break;
}
- hashesToGo = ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
}
}
-
+ else
{
- LOCK(cs_metrics);
- nHashCount += i;
+ // check NONCEMASK at a time
+ for (uint64_t i = 0; i < count; i++)
+ {
+ // this is the merge mining loop, which enables us to drop out and queue a header anytime we earn a block that is good enough for a
+ // merge mined block, but not our own
+ uint64_t totalDone = 0;
+ bool blockFound;
+ arith_uint256 arithHash;
+ do
+ {
+ // hashesToGo gets updated with actual number run for metrics
+ hashesToGo = ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
+ uint64_t start = i * hashesToGo;
+ hashesToGo -= totalDone;
+
+ if (verusSolutionV3)
+ {
+ // mine on canonical header for merge mining
+ CPBaaSPreHeader savedHeader(*pblock);
+ pblock->ClearNonCanonicalData();
+ blockFound = (*mine_verus)(*pblock, ss2, hashResult, uintTarget, start, &hashesToGo);
+ savedHeader.SetBlockData(*pblock);
+ }
+ else
+ {
+ blockFound = (*mine_verus)(*pblock, ss2, hashResult, uintTarget, start, &hashesToGo);
+ }
+
+ arithHash = UintToArith256(hashResult);
+ totalDone += hashesToGo;
+ if (blockFound && IsVerusActive())
+ {
+ ConnectedChains.QueueNewBlockHeader(*pblock);
+ if (arithHash > ourTarget)
+ {
+ // all blocks qualified with this hash will be submitted
+ // until we redo the block, we might as well not try again with anything over this hash
+ hashTarget = arithHash;
+ uintTarget = ArithToUint256(hashTarget);
+ }
+ }
+ hashesToGo = totalDone;
+ } while (blockFound && arithHash > ourTarget);
+
+ if (!blockFound || arithHash > ourTarget)
+ {
+ // Check for stop or if block needs to be rebuilt
+ boost::this_thread::interruption_point();
+ if ( pindexPrev != chainActive.LastTip() )
+ {
+ if (lastChainTipPrinted != chainActive.LastTip())
+ {
+ lastChainTipPrinted = chainActive.LastTip();
+ printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
+ }
+ break;
+ }
+ else
+ {
+ {
+ LOCK(cs_metrics);
+ nHashCount += hashesToGo;
+ }
+ continue;
+ }
+ }
+ else
+ {
+ // Check for stop or if block needs to be rebuilt
+ boost::this_thread::interruption_point();
+
+ if (pblock->nSolution.size() != 1344)
+ {
+ LogPrintf("ERROR: Block solution is not 1344 bytes as it should be");
+ break;
+ }
+
+ SetThreadPriority(THREAD_PRIORITY_NORMAL);
+
+ int32_t unlockTime = komodo_block_unlocktime(Mining_height);
+ int64_t subsidy = (int64_t)(pblock->vtx[0].vout[0].nValue);
+
+#ifdef VERUSHASHDEBUG
+ std::string validateStr = hashResult.GetHex();
+ std::string hashStr = pblock->GetHash().GetHex();
+ uint256 *bhalf1 = (uint256 *)vh2->CurBuffer();
+ uint256 *bhalf2 = bhalf1 + 1;
+#else
+ std::string hashStr = hashResult.GetHex();
+#endif
+
+ LogPrintf("Using %s algorithm:\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+ LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", hashStr, ArithToUint256(ourTarget).GetHex());
+ printf("Found block %d \n", Mining_height );
+ printf("mining reward %.8f %s!\n", (double)subsidy / (double)COIN, ASSETCHAINS_SYMBOL);
+#ifdef VERUSHASHDEBUG
+ printf(" hash: %s\n val: %s \ntarget: %s\n\n", hashStr.c_str(), validateStr.c_str(), ArithToUint256(ourTarget).GetHex().c_str());
+ printf("intermediate %lx\n", intermediate);
+ printf("Curbuf: %s%s\n", bhalf1->GetHex().c_str(), bhalf2->GetHex().c_str());
+ bhalf1 = (uint256 *)verusclhasher_key.get();
+ bhalf2 = bhalf1 + ((vh2->vclh.keyMask + 1) >> 5);
+ printf(" Key: %s%s\n", bhalf1->GetHex().c_str(), bhalf2->GetHex().c_str());
+#else
+ printf(" hash: %s\ntarget: %s", hashStr.c_str(), ArithToUint256(ourTarget).GetHex().c_str());
+#endif
+ if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE)
+ printf(" - timelocked until block %i\n", unlockTime);
+ else
+ printf("\n");
+#ifdef ENABLE_WALLET
+ ProcessBlockFound(pblock, *pwallet, reservekey);
+#else
+ ProcessBlockFound(pblock);
+#endif
+ SetThreadPriority(THREAD_PRIORITY_LOWEST);
+ break;
+ }
+ }
+
+ {
+ LOCK(cs_metrics);
+ nHashCount += hashesToGo;
+ }
}
+
// Check for stop or if block needs to be rebuilt
boost::this_thread::interruption_point();
if (lastChainTipPrinted != chainActive.LastTip())
{
lastChainTipPrinted = chainActive.LastTip();
- printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
+ printf("Block %d added to chain\n\n", lastChainTipPrinted->GetHeight());
}
break;
}
#ifdef _WIN32
- printf("%llu mega hashes complete - working\n", (ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1) / 1048576);
+ printf("%llu mega hashes complete - working\n", ((ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] >> 3) + 1) / 1048576);
#else
- printf("%lu mega hashes complete - working\n", (ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1) / 1048576);
+ printf("%lu mega hashes complete - working\n", ((ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] >> 3) + 1) / 1048576);
#endif
break;
+
}
}
}
/*if ( NOTARY_PUBKEY33[0] == 0 )
{
int32_t percPoS;
- UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
- if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks)
+ UpdateTime(pblock, consensusParams, pindexPrev);
+ if (consensusParams.fPowAllowMinDifficultyBlocks)
{
// Changing pblock->nTime can change work required on testnet:
HASHTarget.SetCompact(pblock->nBits);
void GenerateBitcoins(bool fGenerate, int nThreads)
#endif
{
+ if (!AreParamsInitialized())
+ {
+ return;
+ }
+
+ // if we are supposed to catch stake cheaters, there must be a valid sapling parameter, we need it at
+ // initialization, and this is the first time we can get it. store the Sapling address here
+ extern boost::optional<libzcash::SaplingPaymentAddress> cheatCatcher;
+ extern std::string VERUS_CHEATCATCHER;
+ libzcash::PaymentAddress addr = DecodePaymentAddress(VERUS_CHEATCATCHER);
+ if (VERUS_CHEATCATCHER.size() > 0 && IsValidPaymentAddress(addr))
+ {
+ try
+ {
+ cheatCatcher = boost::get<libzcash::SaplingPaymentAddress>(addr);
+ }
+ catch (...)
+ {
+ }
+ }
+
+ VERUS_MINTBLOCKS = (VERUS_MINTBLOCKS && ASSETCHAINS_LWMAPOS != 0);
+
+ if (fGenerate == true || VERUS_MINTBLOCKS)
+ {
+ mapArgs["-gen"] = "1";
+
+ if (VERUS_CHEATCATCHER.size() > 0)
+ {
+ if (cheatCatcher == boost::none)
+ {
+ LogPrintf("ERROR: -cheatcatcher parameter is invalid Sapling payment address\n");
+ fprintf(stderr, "-cheatcatcher parameter is invalid Sapling payment address\n");
+ }
+ else
+ {
+ LogPrintf("StakeGuard searching for double stakes on %s\n", VERUS_CHEATCATCHER.c_str());
+ fprintf(stderr, "StakeGuard searching for double stakes on %s\n", VERUS_CHEATCATCHER.c_str());
+ }
+ }
+ }
+
static boost::thread_group* minerThreads = NULL;
-
+
if (nThreads < 0)
nThreads = GetNumCores();
if ( nThreads == 0 && ASSETCHAINS_STAKED )
nThreads = 1;
- if ((nThreads == 0 && ASSETCHAINS_LWMAPOS == 0) || !fGenerate)
+ if (!fGenerate)
return;
minerThreads = new boost::thread_group();
+ // add the PBaaS thread when mining or staking
+ minerThreads->create_thread(boost::bind(&CConnectedChains::SubmissionThreadStub));
+
#ifdef ENABLE_WALLET
- if (ASSETCHAINS_LWMAPOS != 0)
+ if (VERUS_MINTBLOCKS && pwallet != NULL)
{
minerThreads->create_thread(boost::bind(&VerusStaker, pwallet));
}