// Check kernel hash target and coinstake signature public static bool CheckProofOfStake(INBitcoinBlockRepository blockStore, ITransactionRepository trasnactionStore, IBlockTransactionMapStore mapStore, ChainedBlock pindexPrev, BlockStake prevBlockStake, Transaction tx, uint nBits, out uint256 hashProofOfStake, out uint256 targetProofOfStake) { targetProofOfStake = null; hashProofOfStake = null; // todo: Comments on this mehtod: // the store objects (IBlockRepository and ITransactionRepository) should be a singleton instance of // the BlockValidator and would be initiated as part of a Dependency Injection freamwork if (!tx.IsCoinStake) { return(false); // error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString()); } // Kernel (input 0) must match the stake hash target per coin age (nBits) var txIn = tx.Inputs[0]; // First try finding the previous transaction in database var txPrev = trasnactionStore.Get(txIn.PrevOut.Hash); if (txPrev == null) { return(false); // tx.DoS(1, error("CheckProofOfStake() : INFO: read txPrev failed")); // previous transaction not in main chain, may occur during initial download } // Verify signature if (!VerifySignature(txPrev, tx, 0, ScriptVerify.None)) { return(false); // tx.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString())); } // Read block header var blockHashPrev = mapStore.GetBlockHash(txIn.PrevOut.Hash); var block = blockHashPrev == null ? null : blockStore.GetBlock(blockHashPrev); if (block == null) { return(false); //fDebug? error("CheckProofOfStake() : read block failed") : false; // unable to read block of previous transaction } // Min age requirement if (IsProtocolV3((int)tx.Time)) { int nDepth = 0; if (IsConfirmedInNPrevBlocks(blockStore, txPrev, pindexPrev, StakeMinConfirmations - 1, ref nDepth)) { return(false); // tx.DoS(100, error("CheckProofOfStake() : tried to stake at depth %d", nDepth + 1)); } } else { var nTimeBlockFrom = block.Header.Time; if (nTimeBlockFrom + StakeMinAge > tx.Time) { return(false); // error("CheckProofOfStake() : min age violation"); } } if (!CheckStakeKernelHash(pindexPrev, nBits, block, txPrev, prevBlockStake, txIn.PrevOut, tx.Time, out hashProofOfStake, out targetProofOfStake, false)) { return(false); // tx.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString(), hashProofOfStake.ToString())); // may occur during initial download or if behind on block chain sync } return(true); }
public static bool CheckKernel(INBitcoinBlockRepository blockStore, ITransactionRepository trasnactionStore, IBlockTransactionMapStore mapStore, StakeChain stakeChain, ChainedBlock pindexPrev, uint nBits, long nTime, OutPoint prevout, ref long pBlockTime) { uint256 hashProofOfStake = null, targetProofOfStake = null; var txPrev = trasnactionStore.Get(prevout.Hash); if (txPrev == null) { return(false); } // Read block header var blockHashPrev = mapStore.GetBlockHash(prevout.Hash); var block = blockHashPrev == null ? null : blockStore.GetBlock(blockHashPrev); if (block == null) { return(false); } if (IsProtocolV3((int)nTime)) { int nDepth = 0; if (IsConfirmedInNPrevBlocks(blockStore, txPrev, pindexPrev, StakeMinConfirmations - 1, ref nDepth)) { return(false); // tx.DoS(100, error("CheckProofOfStake() : tried to stake at depth %d", nDepth + 1)); } } else { var nTimeBlockFrom = block.Header.Time; if (nTimeBlockFrom + StakeMinAge > nTime) { return(false); // error("CheckProofOfStake() : min age violation"); } } var prevBlockStake = stakeChain.Get(pindexPrev.HashBlock); if (prevBlockStake == null) { return(false); // the stake proof of the previous block is not set } // todo: check this unclear logic //if (pBlockTime) // pBlockTime = block.Header.Time; return(CheckStakeKernelHash(pindexPrev, nBits, block, txPrev, prevBlockStake, prevout, (uint)nTime, out hashProofOfStake, out targetProofOfStake, false)); }
private static bool IsConfirmedInNPrevBlocks(INBitcoinBlockRepository blockStore, Transaction txPrev, ChainedBlock pindexFrom, int maxDepth, ref int actualDepth) { // note: this method can be optimized by ensuring that blockstore keeps // in memory at least maxDepth blocks twards genesis var hashPrev = txPrev.GetHash(); for (var pindex = pindexFrom; pindex != null && pindexFrom.Height - pindex.Height < maxDepth; pindex = pindex.Previous) { var block = blockStore.GetBlock(pindex.HashBlock); if (block == null) { return(false); } if (block.Transactions.Any(b => b.GetHash() == hashPrev)) //pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) { actualDepth = pindexFrom.Height - pindex.Height; return(true); } } return(false); }
// ppcoin: total coin age spent in transaction, in the unit of coin-days. // Only those coins meeting minimum age requirement counts. As those // transactions not in main chain are not currently indexed so we // might not find out about their coin age. Older transactions are // guaranteed to be in main chain by sync-checkpoint. This rule is // introduced to help nodes establish a consistent view of the coin // age (trust score) of competing branches. public static bool GetCoinAge(INBitcoinBlockRepository blockStore, ITransactionRepository trasnactionStore, IBlockTransactionMapStore mapStore, Transaction trx, ChainedBlock pindexPrev, out ulong nCoinAge) { BigInteger bnCentSecond = BigInteger.Zero; // coin age in the unit of cent-seconds nCoinAge = 0; if (trx.IsCoinBase) { return(true); } foreach (var txin in trx.Inputs) { // First try finding the previous transaction in database Transaction txPrev = trasnactionStore.Get(txin.PrevOut.Hash); if (txPrev == null) { continue; // previous transaction not in main chain } if (trx.Time < txPrev.Time) { return(false); // Transaction timestamp violation } if (IsProtocolV3((int)trx.Time)) { int nSpendDepth = 0; if (IsConfirmedInNPrevBlocks(blockStore, txPrev, pindexPrev, StakeMinConfirmations - 1, ref nSpendDepth)) { //LogPrint("coinage", "coin age skip nSpendDepth=%d\n", nSpendDepth + 1); continue; // only count coins meeting min confirmations requirement } } else { // Read block header var block = blockStore.GetBlock(txPrev.GetHash()); if (block == null) { return(false); // unable to read block of previous transaction } if (block.Header.Time + StakeMinAge > trx.Time) { continue; // only count coins meeting min age requirement } } long nValueIn = txPrev.Outputs[txin.PrevOut.N].Value; var multiplier = BigInteger.ValueOf((trx.Time - txPrev.Time) / CENT); bnCentSecond = bnCentSecond.Add(BigInteger.ValueOf(nValueIn).Multiply(multiplier)); //bnCentSecond += new BigInteger(nValueIn) * (trx.Time - txPrev.Time) / CENT; //LogPrint("coinage", "coin age nValueIn=%d nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString()); } BigInteger bnCoinDay = bnCentSecond.Multiply(BigInteger.ValueOf(CENT / COIN / (24 * 60 * 60))); //BigInteger bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); //LogPrint("coinage", "coin age bnCoinDay=%s\n", bnCoinDay.ToString()); nCoinAge = new Target(bnCoinDay).ToCompact(); return(true); }