X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fkernel.cpp;h=c878a51dd22b32a8a99c4d3c2781d60d774a6d7b;hb=57bbd632f98812ffc41e9d868512f02765d4ca29;hp=30816d91d1cdf1a83ae8607f32f2543b0b0b6a12;hpb=2372eb3b09c3d1a2df75d6d8b5d712db1b16fa52;p=novacoin.git diff --git a/src/kernel.cpp b/src/kernel.cpp index 30816d9..c878a51 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -32,6 +32,7 @@ static std::map mapStakeModifierCheckpoints = (143990, 0x9c592c78u ) (149000, 0x48f2bdc4u ) (160000, 0x789df0f0u ) + (200000, 0x01ec1503u ) ; // Hard checkpoints of stake modifiers to ensure they are deterministic (testNet) @@ -94,18 +95,6 @@ bool IsFixedModifierInterval(unsigned int nTimeBlock) return (nTimeBlock >= (fTestNet? nModifierTestSwitchTime : nModifierSwitchTime)); } -// Get time weight -int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd) -{ - // Kernel hash weight starts from 0 at the 30-day min age - // this change increases active coins participating the hash and helps - // to secure the network when proof-of-stake difficulty is low - // - // Maximum TimeWeight is 90 days. - - return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64_t)nStakeMaxAge); -} - // Get the last stake modifier and its generation time from a given block static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64_t& nStakeModifier, int64_t& nModifierTime) { @@ -388,7 +377,7 @@ bool CheckStakeKernelHash(uint32_t nBits, const CBlock& blockFrom, uint32_t nTxP uint256 hashBlockFrom = blockFrom.GetHash(); - CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)txPrev.nTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60); + CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)txPrev.nTime, (int64_t)nTimeTx) / COIN / nOneDay; targetProofOfStake = (bnCoinDayWeight * bnTargetPerCoinDay).getuint256(); // Calculate hash @@ -410,10 +399,10 @@ bool CheckStakeKernelHash(uint32_t nBits, const CBlock& blockFrom, uint32_t nTxP DateTimeStrFormat(nStakeModifierTime).c_str(), mapBlockIndex[hashBlockFrom]->nHeight, DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); - printf("CheckStakeKernelHash() : check modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", + printf("CheckStakeKernelHash() : check modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashTarget=%s hashProof=%s\n", nStakeModifier, nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx, - hashProofOfStake.ToString().c_str()); + targetProofOfStake.ToString().c_str(), hashProofOfStake.ToString().c_str()); } // Now check if proof-of-stake hash meets target protocol @@ -426,144 +415,261 @@ bool CheckStakeKernelHash(uint32_t nBits, const CBlock& blockFrom, uint32_t nTxP DateTimeStrFormat(nStakeModifierTime).c_str(), mapBlockIndex[hashBlockFrom]->nHeight, DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); - printf("CheckStakeKernelHash() : pass modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", + printf("CheckStakeKernelHash() : pass modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashTarget=%s hashProof=%s\n", nStakeModifier, nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx, - hashProofOfStake.ToString().c_str()); + targetProofOfStake.ToString().c_str(), hashProofOfStake.ToString().c_str()); } return true; } -// Scan given coins set for kernel solution -bool ScanForStakeKernelHash(MetaMap &mapMeta, uint32_t nBits, uint32_t nTime, uint32_t nSearchInterval, CoinsSet::value_type &kernelcoin, uint32_t &nTimeTx, uint32_t &nBlockTime, uint64_t &nKernelsTried, uint64_t &nCoinDaysTried) -{ - uint256 hashProofOfStake = 0; - // (txid, vout.n) => ((txindex, (tx, vout.n)), (block, modifier)) - for(MetaMap::const_iterator meta_item = mapMeta.begin(); meta_item != mapMeta.end(); meta_item++) - { - if (!fCoinsDataActual) - break; +#ifdef USE_YASM - CTxIndex txindex = (*meta_item).second.first.first; - CBlock block = (*meta_item).second.second.first; - uint64_t nStakeModifier = (*meta_item).second.second.second; +// SHA256 initial state +static const uint32_t init[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; - // Get coin - CoinsSet::value_type pcoin = meta_item->second.first.second; +// 8000000000000000000000000000000000000000000000000000000000000000000000e0 +static const uint32_t block1_suffix[9] = { 0x00000080, 0, 0, 0, 0, 0, 0, 0, 0xe0000000 }; - static unsigned int nMaxStakeSearchInterval = 60; +// 8000000000000000000000000000000000000000000000000000000000000100 +static const uint32_t block2_suffix[8] = { 0x00000080, 0, 0, 0, 0, 0, 0, 0x00010000 }; - // only count coins meeting min age requirement - if (nStakeMinAge + block.nTime > nTime - nMaxStakeSearchInterval) - continue; +// TODO: cpuid detection of supported instruction sets + +extern "C" void sha256_avx(void *input_data, uint32_t digest[8], uint64_t num_blks); +extern "C" void sha256_avx_swap(void *input_data, uint32_t digest[8], uint64_t num_blks); + +// Not used yet +extern "C" void sha256_sse4(void *input_data, uint32_t digest[8], uint64_t num_blks); +extern "C" void sha256_sse4_swap(void *input_data, uint32_t digest[8], uint64_t num_blks); - // Transaction offset inside block - uint32_t nTxOffset = txindex.pos.nTxPos - txindex.pos.nBlockPos; +class ScanMidstateWorker +{ +public: + ScanMidstateWorker() + { } + ScanMidstateWorker(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, uint32_t nIntervalBegin, uint32_t nIntervalEnd) + : kernel(kernel), nBits(nBits), nInputTxTime(nInputTxTime), bnValueIn(nValueIn), nIntervalBegin(nIntervalBegin), nIntervalEnd(nIntervalEnd) + { + solutions = vector >(); + } + + void Do() + { + SetThreadPriority(THREAD_PRIORITY_LOWEST); - // Current timestamp scanning interval - unsigned int nCurrentSearchInterval = min(nSearchInterval, nMaxStakeSearchInterval); + // Sha256 result buffer + uint32_t hashProofOfStake[8]; - nBlockTime = block.nTime; + // Compute maximum possible target to filter out majority of obviously insufficient hashes CBigNum bnTargetPerCoinDay; bnTargetPerCoinDay.SetCompact(nBits); - int64_t nValueIn = pcoin.first->vout[pcoin.second].nValue; - // Search backward in time from the given timestamp - // Search nSearchInterval seconds back up to nMaxStakeSearchInterval - // Stopping search in case of shutting down or cache invalidation - for (unsigned int n=0; nnTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60); + memcpy(&data_block[24], &nTimeTx, 4); // Timestamp + + memcpy(&data_block2[0], &init[0], 8 * sizeof(uint32_t)); + sha256_avx_swap(&data_block[0], (uint32_t *) &data_block2[0], 1); + memcpy(&hashProofOfStake[0], &init[0], 8 * sizeof(uint32_t)); + sha256_avx(&data_block2[0], &hashProofOfStake[0], 1); + + // Skip if hash doesn't satisfy the maximum target + if (__builtin_bswap32(hashProofOfStake[7]) > nMaxTarget32) + continue; + + // Swap byte order + for(int i = 0; i < 8; i++) + hashProofOfStake[i] = __builtin_bswap32(hashProofOfStake[i]); + + CBigNum bnCoinDayWeight = bnValueIn * GetWeight((int64_t)nInputTxTime, (int64_t)nTimeTx) / COIN / nOneDay; CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; - // Build kernel - CDataStream ss(SER_GETHASH, 0); - ss << nStakeModifier; - ss << nBlockTime << nTxOffset << pcoin.first->nTime << pcoin.second << nTimeTx; + if (bnTargetProofOfStake >= CBigNum(*pnHashProofOfStake)) + solutions.push_back(std::pair(*pnHashProofOfStake, nTimeTx)); + } + } - // Calculate kernel hash - hashProofOfStake = Hash(ss.begin(), ss.end()); + vector >& GetSolutions() + { + return solutions; + } - // Update statistics - nKernelsTried += 1; - nCoinDaysTried += bnCoinDayWeight.getuint64(); +private: + std::vector > solutions; - if (bnTargetProofOfStake >= CBigNum(hashProofOfStake)) - { - if (fDebug) - printf("nStakeModifier=0x%016" PRIx64 ", nBlockTime=%u nTxOffset=%u nTxPrevTime=%u nVout=%u nTimeTx=%u hashProofOfStake=%s Success=true\n", - nStakeModifier, nBlockTime, nTxOffset, pcoin.first->nTime, pcoin.second, nTimeTx, hashProofOfStake.GetHex().c_str()); + uint8_t *kernel; + uint32_t nBits; + uint32_t nInputTxTime; + CBigNum bnValueIn; + uint32_t nIntervalBegin; + uint32_t nIntervalEnd; +}; - kernelcoin = pcoin; - return true; - } +#else +class ScanMidstateWorker +{ +public: + ScanMidstateWorker() + { } + ScanMidstateWorker(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, uint32_t nIntervalBegin, uint32_t nIntervalEnd) + : nBits(nBits), nInputTxTime(nInputTxTime), bnValueIn(nValueIn), nIntervalBegin(nIntervalBegin), nIntervalEnd(nIntervalEnd) + { + // Init new sha256 context and update it + // with first 24 bytes of kernel + SHA256_Init(&workerCtx); + SHA256_Update(&workerCtx, kernel, 8 + 16); + solutions = vector >(); + } - if (fDebug) - printf("nStakeModifier=0x%016" PRIx64 ", nBlockTime=%u nTxOffset=%u nTxPrevTime=%u nTxNumber=%u nTimeTx=%u hashProofOfStake=%s Success=false\n", - nStakeModifier, nBlockTime, nTxOffset, pcoin.first->nTime, pcoin.second, nTimeTx, hashProofOfStake.GetHex().c_str()); + void Do() + { + SetThreadPriority(THREAD_PRIORITY_LOWEST); + SHA256_CTX ctx = workerCtx; + + // Sha256 result buffer + uint32_t hashProofOfStake[8]; + + // Compute maximum possible target to filter out majority of obviously insufficient hashes + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + + uint256 nMaxTarget = (bnTargetPerCoinDay * bnValueIn * nStakeMaxAge / COIN / nOneDay).getuint256(), + *pnHashProofOfStake = (uint256 *)&hashProofOfStake; + + // Search forward in time from the given timestamp + // Stopping search in case of shutting down + for (uint32_t nTimeTx=nIntervalBegin, nMaxTarget32 = nMaxTarget.Get32(7); nTimeTx nMaxTarget32) + continue; + + CBigNum bnCoinDayWeight = bnValueIn * GetWeight((int64_t)nInputTxTime, (int64_t)nTimeTx) / COIN / nOneDay; + CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; + + if (bnTargetProofOfStake >= CBigNum(*pnHashProofOfStake)) + solutions.push_back(std::pair(*pnHashProofOfStake, nTimeTx)); } } - return false; -} + vector >& GetSolutions() + { + return solutions; + } + +private: + SHA256_CTX workerCtx; + std::vector > solutions; + + uint32_t nBits; + uint32_t nInputTxTime; + CBigNum bnValueIn; + uint32_t nIntervalBegin; + uint32_t nIntervalEnd; +}; -// Scan given input for kernel solution -bool ScanInputForStakeKernelHash(CTransaction &tx, uint32_t nOut, uint32_t nBits, uint32_t nSearchInterval, std::pair &solution) +#endif +// Scan given kernel for solution +bool ScanKernelForward(unsigned char *kernel, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::vector > &solutions) { - CTxDB txdb("r"); + // TODO: custom threads amount - CBlock block; - CTxIndex txindex; + uint32_t nThreads = boost::thread::hardware_concurrency(); + uint32_t nPart = (SearchInterval.second - SearchInterval.first) / nThreads; - // Load transaction index item - if (!txdb.ReadTxIndex(tx.GetHash(), txindex)) - return false; - // Read block header - if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) - return false; + ScanMidstateWorker *workers = new ScanMidstateWorker[nThreads]; - uint64_t nStakeModifier = 0; - if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier)) - return false; + boost::thread_group group; + for(size_t i = 0; i < nThreads; i++) + { + uint32_t nBegin = SearchInterval.first + nPart * i; + uint32_t nEnd = SearchInterval.first + nPart * (i + 1); + workers[i] = ScanMidstateWorker(kernel, nBits, nInputTxTime, nValueIn, nBegin, nEnd); + boost::function workerFnc = boost::bind(&ScanMidstateWorker::Do, &workers[i]); + group.create_thread(workerFnc); + } + + group.join_all(); + solutions.clear(); + + for(size_t i = 0; i < nThreads; i++) + { + std::vector > ws = workers[i].GetSolutions(); + solutions.insert(solutions.end(), ws.begin(), ws.end()); + } + + delete [] workers; - uint32_t nTime = GetTime(); - // Only count coins meeting min age requirement - if (nStakeMinAge + block.nTime > nTime) - nTime += (nStakeMinAge + block.nTime - nTime); + if (solutions.size() == 0) + { + // no solutions + return false; + } - // Transaction offset inside block - uint32_t nTxOffset = txindex.pos.nTxPos - txindex.pos.nBlockPos; - int64_t nValueIn = tx.vout[nOut].nValue; + return true; +} +// Scan given midstate for solution +bool ScanContextBackward(SHA256_CTX &ctx, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::pair &solution) +{ CBigNum bnTargetPerCoinDay; bnTargetPerCoinDay.SetCompact(nBits); // Get maximum possible target to filter out the majority of obviously insufficient hashes - CBigNum bnMaxTargetPerCoinDay = bnTargetPerCoinDay * CBigNum(nValueIn) * nStakeMaxAge / COIN / (24 * 60 * 60); + CBigNum bnMaxTargetPerCoinDay = bnTargetPerCoinDay * CBigNum(nValueIn) * nStakeMaxAge / COIN / nOneDay; uint256 maxTarget = bnMaxTargetPerCoinDay.getuint256(); - // Search forward in time from the given timestamp + SHA256_CTX ctxCopy = ctx; + + // Search backward in time from the given timestamp // Stopping search in case of shutting down - for (unsigned int n=0; nSearchInterval.second && !fShutdown; nTimeTx--) { - uint32_t nTimeTx = nTime + n; + // Complete first hashing iteration + uint256 hash1; + SHA256_Update(&ctxCopy, (unsigned char*)&nTimeTx, 4); + SHA256_Final((unsigned char*)&hash1, &ctxCopy); - // Build kernel - CDataStream ss(SER_GETHASH, 0); - ss << nStakeModifier; - ss << block.nTime << nTxOffset << tx.nTime << nOut << nTimeTx; + // Restore context + ctxCopy = ctx; - // Calculate kernel hash - uint256 hashProofOfStake = Hash(ss.begin(), ss.end()); + // Finally, calculate kernel hash + uint256 hashProofOfStake; + SHA256((unsigned char*)&hash1, sizeof(hashProofOfStake), (unsigned char*)&hashProofOfStake); // Skip if hash doesn't satisfy the maximum target if (hashProofOfStake > maxTarget) continue; - CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)tx.nTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60); + CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)nInputTxTime, (int64_t)nTimeTx) / COIN / nOneDay; CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; if (bnTargetProofOfStake >= CBigNum(hashProofOfStake)) @@ -624,7 +730,7 @@ uint32_t GetStakeModifierChecksum(const CBlockIndex* pindex) ss << pindex->nFlags << pindex->hashProofOfStake << pindex->nStakeModifier; uint256 hashChecksum = Hash(ss.begin(), ss.end()); hashChecksum >>= (256 - 32); - return hashChecksum.Get64(); + return static_cast(hashChecksum.Get64()); } // Check stake modifier hard checkpoints