Replace kernel scanning function with more usable implementation.
authorMASM fan <masmfan@gmail.com>
Tue, 7 Apr 2015 21:30:15 +0000 (14:30 -0700)
committerMASM fan <masmfan@gmail.com>
Tue, 7 Apr 2015 21:30:15 +0000 (14:30 -0700)
* Move midstate calculation to separate function;
* Don't operate with transaction index and other low level data inside the scanning function.

TODO:

* Automatic kernel midstate precalculation for all unspent inputs;
* Multi-threaded scanning.

src/kernel.cpp
src/kernel.h
src/rpcmining.cpp

index a3ec598..12b26aa 100644 (file)
@@ -509,35 +509,24 @@ bool ScanForStakeKernelHash(MetaMap &mapMeta, uint32_t nBits, uint32_t nTime, ui
     return false;
 }
 
-// Scan given input for kernel solution
-bool ScanInputForStakeKernelHash(CTransaction &tx, uint32_t nOut, uint32_t nBits, uint32_t nSearchInterval, std::pair<uint256, uint32_t> &solution)
+// Precompute hashing state for static part of kernel
+void GetKernelMidstate(uint64_t nStakeModifier, uint32_t nBlockTime, uint32_t nTxOffset, uint32_t nInputTxTime, uint32_t nOut, SHA256_CTX &ctx)
 {
-    CTxDB txdb("r");
-
-    CBlock block;
-    CTxIndex txindex;
-
-    // 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;
-
-    uint64_t nStakeModifier = 0;
-    if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier))
-        return false;
-
-    uint32_t nTime = GetTime();
-    // Only count coins meeting min age requirement
-    if (nStakeMinAge + block.nTime > nTime)
-        nTime += (nStakeMinAge + block.nTime - nTime);
+    // Build static part of kernel
+    CDataStream ssKernel(SER_GETHASH, 0);
+    ssKernel << nStakeModifier;
+    ssKernel << nBlockTime << nTxOffset << nInputTxTime << nOut;
+    CDataStream::const_iterator it = ssKernel.begin();
 
-    // Transaction offset inside block
-    uint32_t nTxOffset = txindex.pos.nTxPos - txindex.pos.nBlockPos;
-    int64_t nValueIn = tx.vout[nOut].nValue;
+    // Init sha256 context and update it 
+    //   with first 24 bytes of kernel
+    SHA256_Init(&ctx);
+    SHA256_Update(&ctx, (unsigned char*)&it[0], 8 + 16);
+}
 
+// Scan given midstate for solution
+bool ScanMidstateForward(SHA256_CTX &ctx, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair<uint32_t, uint32_t> &SearchInterval, std::pair<uint256, uint32_t> &solution)
+{
     CBigNum bnTargetPerCoinDay;
     bnTargetPerCoinDay.SetCompact(nBits);
 
@@ -545,31 +534,19 @@ bool ScanInputForStakeKernelHash(CTransaction &tx, uint32_t nOut, uint32_t nBits
     CBigNum bnMaxTargetPerCoinDay = bnTargetPerCoinDay * CBigNum(nValueIn) * nStakeMaxAge / COIN / (24 * 60 * 60);
     uint256 maxTarget = bnMaxTargetPerCoinDay.getuint256();
 
-
-    // Build static part of kernel
-    CDataStream ssKernel(SER_GETHASH, 0);
-    ssKernel << nStakeModifier;
-    ssKernel << block.nTime << nTxOffset << tx.nTime << nOut;
-    CDataStream::const_iterator it = ssKernel.begin();
-
-    // Init sha256 context and update it 
-    //   with first 16 bytes of kernel
-    SHA256_CTX ctxCurrent;
-    SHA256_Init(&ctxCurrent);
-    SHA256_Update(&ctxCurrent, (unsigned char*)&it[0], 8 + 16);
-    SHA256_CTX ctxCopy = ctxCurrent;
+    SHA256_CTX ctxCopy = ctx;
 
     // Search forward in time from the given timestamp
     // Stopping search in case of shutting down
-    for (uint32_t nTimeTx=nTime; nTimeTx<nTime+nSearchInterval && !fShutdown; nTimeTx++)
+    for (uint32_t nTimeTx=SearchInterval.first; nTimeTx<SearchInterval.second && !fShutdown; nTimeTx++)
     {
         // Complete first hashing iteration
         uint256 hash1;
-        SHA256_Update(&ctxCurrent, (unsigned char*)&nTimeTx, 4);
-        SHA256_Final((unsigned char*)&hash1, &ctxCurrent);
+        SHA256_Update(&ctxCopy, (unsigned char*)&nTimeTx, 4);
+        SHA256_Final((unsigned char*)&hash1, &ctxCopy);
 
-        // Restore old context
-        ctxCurrent = ctxCopy;
+        // Restore context
+        ctxCopy = ctx;
 
         // Finally, calculate kernel hash
         uint256 hashProofOfStake;
@@ -579,7 +556,7 @@ bool ScanInputForStakeKernelHash(CTransaction &tx, uint32_t nOut, uint32_t nBits
         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 / (24 * 60 * 60);
         CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay;
 
         if (bnTargetProofOfStake >= CBigNum(hashProofOfStake))
index 9772782..a94ace2 100644 (file)
@@ -36,7 +36,11 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, uint32_t
 // 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);
 
-bool ScanInputForStakeKernelHash(CTransaction &tx, uint32_t nOut, uint32_t nBits, uint32_t nSearchInterval, std::pair<uint256, uint32_t> &solution);
+// Precompute hashing state for static part of kernel
+void GetKernelMidstate(uint64_t nStakeModifier, uint32_t nBlockTime, uint32_t nTxOffset, uint32_t nInputTxTime, uint32_t nOut, SHA256_CTX &ctx);
+
+// Scan given midstate for kernel solutions
+bool ScanMidstateForward(SHA256_CTX &ctx, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair<uint32_t, uint32_t> &SearchInterval, std::pair<uint256, uint32_t> &solution);
 
 // Check kernel hash target and coinstake signature
 // Sets hashProofOfStake on success return
index 8f02836..28d6ed8 100644 (file)
@@ -106,8 +106,41 @@ Value scaninput(const Array& params, bool fHelp)
     uint256 hashBlock = 0;
     if (GetTransaction(hash, tx, hashBlock))
     {
+        if (nOut > tx.vout.size())
+            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Incorrect output number");
+
+        if (hashBlock == 0)
+            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to find transaction in the blockchain");
+
+        CTxDB txdb("r");
+
+        CBlock block;
+        CTxIndex txindex;
+
+        // Load transaction index item
+        if (!txdb.ReadTxIndex(tx.GetHash(), txindex))
+            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to read block index item");
+
+        // Read block header
+        if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "CBlock::ReadFromDisk() failed");
+
+        uint64_t nStakeModifier = 0;
+        if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier))
+            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No kernel stake modifier generated yet");
+
+        std::pair<uint32_t, uint32_t> interval;
+        interval.first = GetTime();
+        // Only count coins meeting min age requirement
+        if (nStakeMinAge + block.nTime > interval.first)
+            interval.first += (nStakeMinAge + block.nTime - interval.first);
+        interval.second = interval.first + nDays * 86400;
+
+        SHA256_CTX ctx;
+        GetKernelMidstate(nStakeModifier, block.nTime, txindex.pos.nTxPos - txindex.pos.nBlockPos, tx.nTime, nOut, ctx);
+
         std::pair<uint256, uint32_t> solution;
-        if (ScanInputForStakeKernelHash(tx, nOut, nBits, nDays * 86400, solution))
+        if (ScanMidstateForward(ctx, nBits, tx.nTime, tx.vout[nOut].nValue, interval, solution))
         {
             Object r;
             r.push_back(Pair("hash", solution.first.GetHex()));