// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "txdb.h"
+#include "txdb-leveldb.h"
#include "miner.h"
#include "kernel.h"
+#include "kernel_worker.h"
using namespace std;
int64_t nReserveBalance = 0;
static unsigned int nMaxStakeSearchInterval = 60;
+uint64_t nStakeInputsMapSize = 0;
int static FormatHashBlocks(void* pbuffer, unsigned int len)
{
ptx = ptxIn;
dPriority = dFeePerKb = 0;
}
-
- void print() const
- {
- printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n",
- ptx->GetHash().ToString().substr(0,10).c_str(), dPriority, dFeePerKb);
- BOOST_FOREACH(uint256 hash, setDependsOn)
- printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str());
- }
};
};
// CreateNewBlock: create new block (without proof-of-work/with provided coinstake)
-CBlock* CreateNewBlock(CWallet* pwallet, CTransaction *txCoinStake)
+std::shared_ptr<CBlock> CreateNewBlock(CWallet* pwallet, CTransaction *txCoinStake)
{
bool fProofOfStake = txCoinStake != NULL;
// Create new block
- auto_ptr<CBlock> pblock(new CBlock());
+ shared_ptr<CBlock> pblock(new CBlock());
if (!pblock.get())
return NULL;
}
// Largest block you're willing to create:
- unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2);
+ unsigned int nBlockMaxSize = GetArgUInt("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2);
// Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
- nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize));
+ nBlockMaxSize = std::max(1000u, std::min(MAX_BLOCK_SIZE-1000u, nBlockMaxSize));
// How much of the block should be dedicated to high-priority transactions,
// included regardless of the fees they pay
- unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000);
+ unsigned int nBlockPrioritySize = GetArgUInt("-blockprioritysize", 27000);
nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
// Minimum block size you want to create; block will be filled with free transactions
// until there are no more or the block reaches this size:
- unsigned int nBlockMinSize = GetArg("-blockminsize", 0);
+ unsigned int nBlockMinSize = GetArgUInt("-blockminsize", 0);
nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
// Fee-per-kilobyte amount considered the same as "free"
if (tx.nTime > GetAdjustedTime() || (fProofOfStake && tx.nTime > txCoinStake->nTime))
continue;
- // Simplify transaction fee - allow free = false
- int64_t nMinFee = tx.GetMinFee(nBlockSize, true, GMF_BLOCK, nTxSize);
-
// Skip free transactions if we're past the minimum block size:
if (fSortedByFee && (dFeePerKb < nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
continue;
if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid))
continue;
+ // Transaction fee
int64_t nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
+ int64_t nMinFee = tx.GetMinFee(nBlockSize, true, GMF_BLOCK, nTxSize);
if (nTxFees < nMinFee)
continue;
+ // Sigops accumulation
nTxSigOps += tx.GetP2SHSigOpCount(mapInputs);
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
pblock->nNonce = 0;
}
- return pblock.release();
+ return pblock;
}
-void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
+void IncrementExtraNonce(shared_ptr<CBlock>& pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
{
// Update nExtraNonce
static uint256 hashPrevBlock;
}
-void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1)
+void FormatHashBuffers(const shared_ptr<CBlock>& pblock, char* pmidstate, char* pdata, char* phash1)
{
//
// Pre-build hash buffers
}
-bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
+bool CheckWork(const std::shared_ptr<CBlock>& pblock, CWallet& wallet, CReserveKey& reservekey)
{
uint256 hashBlock = pblock->GetHash();
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
}
// Process this block the same as if we had received it from another node
- if (!ProcessBlock(NULL, pblock))
+ if (!ProcessBlock(NULL, pblock.get()))
return error("CheckWork() : ProcessBlock, block not accepted");
}
return true;
}
-bool CheckStake(CBlock* pblock, CWallet& wallet)
+bool CheckStake(const std::shared_ptr<CBlock>& pblock, CWallet& wallet)
{
uint256 proofHash = 0, hashTarget = 0;
uint256 hashBlock = pblock->GetHash();
}
// Process this block the same as if we had received it from another node
- if (!ProcessBlock(NULL, pblock))
+ if (!ProcessBlock(NULL, pblock.get()))
return error("CheckStake() : ProcessBlock, block not accepted");
}
}
// Precalculated SHA256 contexts and metadata
-// (txid, vout.n) => (SHA256_CTX, (tx.nTime, nAmount))
-typedef std::map<std::pair<uint256, unsigned int>, std::pair<SHA256_CTX, std::pair<uint32_t, uint64_t> > > MidstateMap;
+// (txid, vout.n) => (kernel, (tx.nTime, nAmount))
+typedef std::map<std::pair<uint256, unsigned int>, std::pair<std::vector<unsigned char>, std::pair<uint32_t, uint64_t> > > MidstateMap;
-// Fill the inputs map with precalculated states and metadata
+// Fill the inputs map with precalculated contexts and metadata
bool FillMap(CWallet *pwallet, uint32_t nUpperTime, MidstateMap &inputsMap)
{
// Choose coins to use
if (nStakeMinAge + block.nTime > nTime - nMaxStakeSearchInterval)
continue;
+ // Get stake modifier
uint64_t nStakeModifier = 0;
if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier))
continue;
- SHA256_CTX ctx;
- // Calculate midstate using (modifier, block time, tx offset, tx time, output number)
- GetKernelMidstate(nStakeModifier, block.nTime, txindex.pos.nTxPos - txindex.pos.nBlockPos, pcoin->first->nTime, pcoin->second, ctx);
+ // Build static part of kernel
+ CDataStream ssKernel(SER_GETHASH, 0);
+ ssKernel << nStakeModifier;
+ ssKernel << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << pcoin->first->nTime << pcoin->second;
- // (txid, vout.n) => (SHA256_CTX, (tx.nTime, nAmount))
- inputsMap[key] = make_pair(ctx, make_pair(pcoin->first->nTime, pcoin->first->vout[pcoin->second].nValue));
+ // (txid, vout.n) => (kernel, (tx.nTime, nAmount))
+ inputsMap[key] = make_pair(std::vector<unsigned char>(ssKernel.begin(), ssKernel.end()), make_pair(pcoin->first->nTime, pcoin->first->vout[pcoin->second].nValue));
}
+ nStakeInputsMapSize = inputsMap.size();
+
if (fDebug)
- printf("Stake miner: %" PRIszu " precalculated contexts created\n", inputsMap.size());
+ printf("FillMap() : Map of %" PRIu64 " precalculated contexts has been created by stake miner\n", nStakeInputsMapSize);
}
return true;
interval.first = nSearchTime;
interval.second = nSearchTime - min(nSearchTime-nLastCoinStakeSearchTime, nMaxStakeSearchInterval);
- // (txid, nout) => (SHA256_CTX, (tx.nTime, nAmount))
+ // (txid, nout) => (kernel, (tx.nTime, nAmount))
for(MidstateMap::const_iterator input = inputsMap.begin(); input != inputsMap.end(); input++)
{
- SHA256_CTX ctx = input->second.first;
+ unsigned char *kernel = (unsigned char *) &input->second.first[0];
// scan(State, Bits, Time, Amount, ...)
- if (ScanMidstateBackward(ctx, nBits, input->second.second.first, input->second.second.second, interval, solution))
+ if (ScanKernelBackward(kernel, nBits, input->second.second.first, input->second.second.second, interval, solution))
{
// Solution found
LuckyInput = input->first; // (txid, nout)
return false;
}
-void StakeMiner(CWallet *pwallet)
+// Stake miner thread
+void ThreadStakeMiner(void* parg)
{
SetThreadPriority(THREAD_PRIORITY_LOWEST);
// Make this thread recognisable as the mining thread
RenameThread("novacoin-miner");
+ CWallet* pwallet = (CWallet*)parg;
MidstateMap inputsMap;
if (!FillMap(pwallet, GetAdjustedTime(), inputsMap))
CBlockIndex* pindexPrev = pindexBest;
uint32_t nBits = GetNextTargetRequired(pindexPrev, true);
- while (true)
+ printf("ThreadStakeMinter started\n");
+
+ try
{
- if (fShutdown)
- return;
+ vnThreadsRunning[THREAD_MINTER]++;
- while (pwallet->IsLocked())
- {
- Sleep(1000);
- if (fShutdown)
- return;
- }
+ MidstateMap::key_type LuckyInput;
+ std::pair<uint256, uint32_t> solution;
- while (vNodes.empty() || IsInitialBlockDownload())
+ // Main miner loop
+ do
{
- fTrySync = true;
-
- Sleep(1000);
if (fShutdown)
- return;
- }
+ goto _endloop;
- if (fTrySync)
- {
- fTrySync = false;
- if (vNodes.size() < 3 || nBestHeight < GetNumBlocksOfPeers())
+ while (pwallet->IsLocked())
{
Sleep(1000);
- continue;
+ if (fShutdown)
+ goto _endloop; // Don't be afraid to use a goto if that's the best option.
}
- }
-
- MidstateMap::key_type LuckyInput;
- std::pair<uint256, uint32_t> solution;
-
- if (ScanMap(inputsMap, nBits, LuckyInput, solution))
- {
- SetThreadPriority(THREAD_PRIORITY_NORMAL);
- // Remove lucky input from the map
- inputsMap.erase(inputsMap.find(LuckyInput));
+ while (vNodes.empty() || IsInitialBlockDownload())
+ {
+ fTrySync = true;
- CKey key;
- CTransaction txCoinStake;
+ Sleep(1000);
+ if (fShutdown)
+ goto _endloop;
+ }
- // Create new coinstake transaction
- if (!pwallet->CreateCoinStake(LuckyInput.first, LuckyInput.second, solution.second, pindexPrev->nBits, txCoinStake, key))
+ if (fTrySync)
{
- string strMessage = _("Warning: Unable to create coinstake transaction, see debug.log for the details. Mining thread has been stopped.");
- strMiscWarning = strMessage;
- printf("*** %s\n", strMessage.c_str());
-
- return;
+ // Don't try mine blocks unless we're at the top of chain and have at least three p2p connections.
+ fTrySync = false;
+ if (vNodes.size() < 3 || nBestHeight < GetNumBlocksOfPeers())
+ {
+ Sleep(1000);
+ continue;
+ }
}
- // Now we have new coinstake, it's time to create the block ...
- CBlock* pblock;
- pblock = CreateNewBlock(pwallet, &txCoinStake);
- if (!pblock)
+ if (ScanMap(inputsMap, nBits, LuckyInput, solution))
{
- string strMessage = _("Warning: Unable to allocate memory for the new block object. Mining thread has been stopped.");
- strMiscWarning = strMessage;
- printf("*** %s\n", strMessage.c_str());
+ SetThreadPriority(THREAD_PRIORITY_NORMAL);
- return;
- }
+ // Remove lucky input from the map
+ inputsMap.erase(inputsMap.find(LuckyInput));
- unsigned int nExtraNonce = 0;
- IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
+ CKey key;
+ CTransaction txCoinStake;
- // ... and sign it
- if (!key.Sign(pblock->GetHash(), pblock->vchBlockSig))
- {
- string strMessage = _("Warning: Proof-of-Stake miner is unable to sign the block (locked wallet?). Mining thread has been stopped.");
- strMiscWarning = strMessage;
- printf("*** %s\n", strMessage.c_str());
+ // Create new coinstake transaction
+ if (!pwallet->CreateCoinStake(LuckyInput.first, LuckyInput.second, solution.second, nBits, txCoinStake, key))
+ {
+ string strMessage = _("Warning: Unable to create coinstake transaction, see debug.log for the details. Mining thread has been stopped.");
+ strMiscWarning = strMessage;
+ printf("*** %s\n", strMessage.c_str());
- return;
- }
+ break;
+ }
- CheckStake(pblock, *pwallet);
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
- Sleep(500);
- }
+ // Now we have new coinstake, it's time to create the block ...
+ std::shared_ptr<CBlock> pblock = CreateNewBlock(pwallet, &txCoinStake);
+ if (!pblock)
+ {
+ string strMessage = _("Warning: Unable to allocate memory for the new block object. Mining thread has been stopped.");
+ strMiscWarning = strMessage;
+ printf("*** %s\n", strMessage.c_str());
- if (pindexPrev != pindexBest)
- {
- // The best block has been changed, we need to refill the map
- if (FillMap(pwallet, GetAdjustedTime(), inputsMap))
- {
- pindexPrev = pindexBest;
- nBits = GetNextTargetRequired(pindexPrev, true);
+ break;
+ }
+
+ unsigned int nExtraNonce = 0;
+ IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
+
+ // ... and sign it
+ if (!key.Sign(pblock->GetHash(), pblock->vchBlockSig))
+ {
+ string strMessage = _("Warning: Proof-of-Stake miner is unable to sign the block (locked wallet?). Mining thread has been stopped.");
+ strMiscWarning = strMessage;
+ printf("*** %s\n", strMessage.c_str());
+
+ break;
+ }
+
+ CheckStake(pblock, *pwallet);
+ SetThreadPriority(THREAD_PRIORITY_LOWEST);
+ Sleep(500);
}
- else
+
+ if (pindexPrev != pindexBest)
{
- // Clear existent data if FillMap failed
- inputsMap.clear();
+ // The best block has been changed, we need to refill the map
+ if (FillMap(pwallet, GetAdjustedTime(), inputsMap))
+ {
+ pindexPrev = pindexBest;
+ nBits = GetNextTargetRequired(pindexPrev, true);
+ }
+ else
+ {
+ // Clear existent data if FillMap failed
+ inputsMap.clear();
+ }
}
- }
- Sleep(500);
+ Sleep(500);
- continue;
+ _endloop:
+ (void)0; // do nothing
+ }
+ while(!fShutdown);
+
+ vnThreadsRunning[THREAD_MINTER]--;
+ }
+ catch (std::exception& e) {
+ vnThreadsRunning[THREAD_MINTER]--;
+ PrintException(&e, "ThreadStakeMinter()");
+ } catch (...) {
+ vnThreadsRunning[THREAD_MINTER]--;
+ PrintException(NULL, "ThreadStakeMinter()");
}
+ printf("ThreadStakeMinter exiting, %d threads remaining\n", vnThreadsRunning[THREAD_MINTER]);
}