public void CheckProofOfStake(ContextInformation context, ChainedBlock pindexPrev, BlockStake prevBlockStake, Transaction tx, uint nBits) { if (!tx.IsCoinStake) { ConsensusErrors.NonCoinstake.Throw(); } // 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 coins = coinView.FetchCoinsAsync(new[] { txIn.PrevOut.Hash }).GetAwaiter().GetResult(); if (coins == null || coins.UnspentOutputs.Length != 1) { ConsensusErrors.ReadTxPrevFailed.Throw(); } var prevBlock = chain.GetBlock(coins.BlockHash); var prevUtxo = coins.UnspentOutputs[0]; // Verify signature if (!this.VerifySignature(prevUtxo, tx, 0, ScriptVerify.None)) { ConsensusErrors.CoinstakeVerifySignatureFailed.Throw(); } // Min age requirement if (IsProtocolV3((int)tx.Time)) { if (IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1)) { ConsensusErrors.InvalidStakeDepth.Throw(); } } else { var nTimeBlockFrom = prevBlock.Header.Time; if (nTimeBlockFrom + this.consensusOptions.StakeMinAge > tx.Time) { ConsensusErrors.MinAgeViolation.Throw(); } } this.CheckStakeKernelHash(context, pindexPrev, nBits, prevBlock, prevUtxo, prevBlockStake, txIn.PrevOut, tx.Time); }
// 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 bool GetCoinAge(ConcurrentChain chain, CoinView coinView, 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) { var coins = coinView.FetchCoinsAsync(new[] { txin.PrevOut.Hash }).GetAwaiter().GetResult(); if (coins == null || coins.UnspentOutputs.Length != 1) { continue; } var prevBlock = chain.GetBlock(coins.BlockHash); var prevUtxo = coins.UnspentOutputs[0]; // 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 < prevUtxo.Time) { return(false); // Transaction timestamp violation } if (IsProtocolV3((int)trx.Time)) { if (IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1)) { //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 (prevBlock.Header.Time + this.consensusOptions.StakeMinAge > trx.Time) { continue; // only count coins meeting min age requirement } } long nValueIn = prevUtxo._Outputs[txin.PrevOut.N].Value; var multiplier = BigInteger.ValueOf((trx.Time - prevUtxo.Time) / Money.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(Money.CENT / Money.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); }
public override async Task <FetchCoinsResponse> FetchCoinsAsync(uint256[] txIds) { Guard.NotNull(txIds, nameof(txIds)); FetchCoinsResponse result = null; uint256 innerBlockHash = null; UnspentOutputs[] outputs = new UnspentOutputs[txIds.Length]; List <int> miss = new List <int>(); List <uint256> missedTxIds = new List <uint256>(); using (_Lock.LockRead()) { WaitOngoingTasks(); for (int i = 0; i < txIds.Length; i++) { CacheItem cache; if (!_Unspents.TryGetValue(txIds[i], out cache)) { miss.Add(i); missedTxIds.Add(txIds[i]); } else { outputs[i] = cache.UnspentOutputs == null ? null : cache.UnspentOutputs.IsPrunable ? null : cache.UnspentOutputs.Clone(); } } PerformanceCounter.AddMissCount(miss.Count); PerformanceCounter.AddHitCount(txIds.Length - miss.Count); } var fetchedCoins = await Inner.FetchCoinsAsync(missedTxIds.ToArray()).ConfigureAwait(false); using (_Lock.LockWrite()) { _Flushing.Wait(); innerBlockHash = fetchedCoins.BlockHash; if (_BlockHash == null) { Debug.Assert(_Unspents.Count == 0); _InnerBlockHash = innerBlockHash; _BlockHash = innerBlockHash; } for (int i = 0; i < miss.Count; i++) { var index = miss[i]; var unspent = fetchedCoins.UnspentOutputs[i]; outputs[index] = unspent; CacheItem cache = new CacheItem(); cache.ExistInInner = unspent != null; cache.IsDirty = false; cache.UnspentOutputs = unspent; cache.OriginalOutputs = unspent?._Outputs.ToArray(); _Unspents.TryAdd(txIds[index], cache); } result = new FetchCoinsResponse(outputs, _BlockHash); } if (CacheEntryCount > MaxItems) { Evict(); if (CacheEntryCount > MaxItems) { await FlushAsync().ConfigureAwait(false); Evict(); } } return(result); }