/// <inheritdoc/> public bool CheckStakeKernelHash(PosRuleContext context, uint headerBits, uint256 prevStakeModifier, UnspentOutput stakingCoins, OutPoint prevout, uint transactionTime) { Guard.NotNull(context, nameof(context)); Guard.NotNull(prevout, nameof(prevout)); Guard.NotNull(stakingCoins, nameof(stakingCoins)); if (transactionTime < stakingCoins.Coins.Time) { this.logger.LogDebug("Coinstake transaction timestamp {0} is lower than it's own UTXO timestamp {1}.", transactionTime, stakingCoins.Coins.Time); this.logger.LogTrace("(-)[BAD_STAKE_TIME]"); ConsensusErrors.StakeTimeViolation.Throw(); } // Base target. BigInteger target = new Target(headerBits).ToBigInteger(); // Weighted target. long valueIn = stakingCoins.Coins.TxOut.Value.Satoshi; BigInteger weight = BigInteger.ValueOf(valueIn); BigInteger weightedTarget = target.Multiply(weight); context.TargetProofOfStake = this.ToUInt256(weightedTarget); this.logger.LogDebug("POS target is '{0}', weighted target for {1} coins is '{2}'.", this.ToUInt256(target), valueIn, context.TargetProofOfStake); // Calculate hash. using (var ms = new MemoryStream()) { var serializer = new BitcoinStream(ms, true); serializer.ReadWrite(prevStakeModifier); if (this.network.Consensus.PosUseTimeFieldInKernalHash) // old posv3 time field { serializer.ReadWrite(stakingCoins.Coins.Time); } serializer.ReadWrite(prevout.Hash); serializer.ReadWrite(prevout.N); serializer.ReadWrite(transactionTime); context.HashProofOfStake = Hashes.Hash256(ms.ToArray()); } this.logger.LogDebug("Stake modifier V2 is '{0}', hash POS is '{1}'.", prevStakeModifier, context.HashProofOfStake); // Now check if proof-of-stake hash meets target protocol. var hashProofOfStakeTarget = new BigInteger(1, context.HashProofOfStake.ToBytes(false)); if (hashProofOfStakeTarget.CompareTo(weightedTarget) > 0) { this.logger.LogTrace("(-)[TARGET_MISSED]"); return(false); } return(true); }
/// <inheritdoc/> public bool CheckKernel(PosRuleContext context, ChainedHeader prevChainedHeader, uint headerBits, long transactionTime, OutPoint prevout) { Guard.NotNull(context, nameof(context)); Guard.NotNull(prevout, nameof(prevout)); Guard.NotNull(prevChainedHeader, nameof(prevChainedHeader)); FetchCoinsResponse coins = this.coinView.FetchCoins(new[] { prevout }); if ((coins == null) || (coins.UnspentOutputs.Count != 1)) { this.logger.LogTrace("(-)[READ_PREV_TX_FAILED]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } ChainedHeader prevBlock = this.chainIndexer.GetHeader(this.coinView.GetTipHash().Hash); if (prevBlock == null) { this.logger.LogTrace("(-)[REORG]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } UnspentOutput prevUtxo = coins.UnspentOutputs.Single().Value; if (prevUtxo == null) { this.logger.LogTrace("(-)[PREV_UTXO_IS_NULL]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } if (this.IsConfirmedInNPrevBlocks(prevUtxo, prevChainedHeader, this.GetTargetDepthRequired(prevChainedHeader))) { this.logger.LogTrace("(-)[LOW_COIN_AGE]"); ConsensusErrors.InvalidStakeDepth.Throw(); } BlockStake prevBlockStake = this.stakeChain.Get(prevChainedHeader.HashBlock); if (prevBlockStake == null) { this.logger.LogTrace("(-)[BAD_STAKE_BLOCK]"); ConsensusErrors.BadStakeBlock.Throw(); } return(this.CheckStakeKernelHash(context, headerBits, prevBlockStake.StakeModifierV2, prevUtxo, prevout, (uint)transactionTime)); }
/// <inheritdoc/> public void CheckProofOfStake(PosRuleContext context, ChainedHeader prevChainedHeader, BlockStake prevBlockStake, Transaction transaction, uint headerBits) { Guard.NotNull(context, nameof(context)); Guard.NotNull(prevChainedHeader, nameof(prevChainedHeader)); Guard.NotNull(prevBlockStake, nameof(prevBlockStake)); Guard.NotNull(transaction, nameof(transaction)); if (!transaction.IsCoinStake) { this.logger.LogTrace("(-)[NO_COINSTAKE]"); ConsensusErrors.NonCoinstake.Throw(); } TxIn txIn = transaction.Inputs[0]; UnspentOutput prevUtxo = context.UnspentOutputSet.AccessCoins(txIn.PrevOut); if (prevUtxo == null) { this.logger.LogTrace("(-)[PREV_UTXO_IS_NULL]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } // Verify signature. if (!this.VerifySignature(prevUtxo, transaction, 0, ScriptVerify.None)) { this.logger.LogTrace("(-)[BAD_SIGNATURE]"); ConsensusErrors.CoinstakeVerifySignatureFailed.Throw(); } // Min age requirement. if (this.IsConfirmedInNPrevBlocks(prevUtxo, prevChainedHeader, this.GetTargetDepthRequired(prevChainedHeader))) { this.logger.LogTrace("(-)[BAD_STAKE_DEPTH]"); ConsensusErrors.InvalidStakeDepth.Throw(); } if (!this.CheckStakeKernelHash(context, headerBits, prevBlockStake.StakeModifierV2, prevUtxo, txIn.PrevOut, context.ValidationContext.ChainedHeaderToValidate.Header.Time)) { this.logger.LogTrace("(-)[INVALID_STAKE_HASH_TARGET]"); ConsensusErrors.StakeHashInvalidTarget.Throw(); } }