Example #1
0
        public void SpentOutput_Returns_False()
        {
            // Construct transaction.
            Transaction transaction = this.network.CreateTransaction();

            transaction.Inputs.Add(new TxIn(new OutPoint(uint256.One, 0)));

            // Setup coinview to return as if the PrevOut is spent (i.e. Coins is null).
            var unspentOutput = new UnspentOutput(transaction.Inputs[0].PrevOut, null);

            var unspentOutputArray = new UnspentOutput[]
            {
                unspentOutput
            };

            var fetchResponse = new FetchCoinsResponse();

            fetchResponse.UnspentOutputs.Add(unspentOutputArray[0].OutPoint, unspentOutput);

            this.coinView.Setup(x => x.FetchCoins(It.IsAny <OutPoint[]>()))
            .Returns(fetchResponse);

            var blockTxs = new List <Transaction>();

            // Retriever fails but doesn't throw exception
            GetSenderResult result = this.senderRetriever.GetSender(transaction, this.coinView.Object, blockTxs);

            Assert.False(result.Success);
            Assert.Equal(SenderRetriever.OutputAlreadySpent, result.Error);
        }
Example #2
0
        public async Task GetTxOutAsync_IncludeInMempool_UnspentTransactionFound_ReturnsModelAsync()
        {
            var         txId           = new uint256(1243124);
            Transaction transaction    = this.CreateTransaction();
            var         unspentOutputs = new UnspentOutput(new OutPoint(transaction, 0), new Coins(1, transaction.Outputs[0], transaction.IsCoinBase));

            this.pooledGetUnspentTransaction.Setup(s => s.GetUnspentTransactionAsync(new OutPoint(txId, 0)))
            .ReturnsAsync(unspentOutputs)
            .Verifiable();

            this.CreateNewController();

            string txid           = txId.ToString();
            uint   vout           = 0;
            bool   includeMemPool = true;

            var json = (JsonResult)await this.controller.GetTxOutAsync(txid, vout, includeMemPool).ConfigureAwait(false);

            var resultModel = (GetTxOutModel)json.Value;

            this.pooledGetUnspentTransaction.Verify();
            Assert.Equal(this.chainIndexer.Tip.HashBlock, resultModel.BestBlock);
            Assert.True(resultModel.Coinbase);
            Assert.Equal(3, resultModel.Confirmations);
            Assert.Equal(new ScriptPubKey(transaction.Outputs[0].ScriptPubKey, this.network).Hex, resultModel.ScriptPubKey.Hex);
            Assert.Equal(transaction.Outputs[0].Value, resultModel.Value);
        }
Example #3
0
        public void RunRule_ProvenHeadersActive_And_InvalidMerkleProof_BadMerkleProofErrorIsThrown()
        {
            // Setup previous chained header.
            PosBlock          prevPosBlock          = new PosBlockBuilder(this.network).Build();
            ProvenBlockHeader prevProvenBlockHeader = new ProvenBlockHeaderBuilder(prevPosBlock, this.network).Build();
            var previousChainedHeader = new ChainedHeader(prevProvenBlockHeader, prevProvenBlockHeader.GetHash(), null);

            previousChainedHeader.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 1);

            // Setup proven header with valid coinstake.
            PosBlock          posBlock          = new PosBlockBuilder(this.network).Build();
            ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build(prevProvenBlockHeader);

            provenBlockHeader.PosBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash();
            if (provenBlockHeader.Coinstake is IPosTransactionWithTime posTrx)
            {
                posTrx.Time = provenBlockHeader.Time;
            }

            // Corrupt merkle proof.
            provenBlockHeader.SetPrivateVariableValue("merkleProof", new PartialMerkleTree(new[] { new uint256(1234) }, new[] { false }));

            // Setup chained header and move it to the height higher than proven header activation height.
            this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(provenBlockHeader, provenBlockHeader.GetHash(), previousChainedHeader);
            this.ruleContext.ValidationContext.ChainedHeaderToValidate.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 2);

            // Ensure that coinview returns a UTXO with valid outputs.
            var utxoOne = new UnspentOutput(prevPosBlock.Transactions[1].Inputs[0].PrevOut, new Coins((uint)previousChainedHeader.Height, new TxOut(), false, true));

            // Setup coinstake transaction with a valid stake age.
            var res = new FetchCoinsResponse();

            res.UnspentOutputs.Add(utxoOne.OutPoint, utxoOne);

            this.coinView
            .Setup(m => m.FetchCoins(It.IsAny <OutPoint[]>()))
            .Returns(res);

            // Setup stake validator to pass stake age check.
            this.stakeValidator
            .Setup(m => m.IsConfirmedInNPrevBlocks(It.IsAny <UnspentOutput>(), It.IsAny <ChainedHeader>(), It.IsAny <long>()))
            .Returns(false);

            // Setup stake validator to pass signature validation.
            this.stakeValidator
            .Setup(m => m.VerifySignature(It.IsAny <UnspentOutput>(), It.IsAny <Transaction>(), It.IsAny <int>(), It.IsAny <ScriptVerify>()))
            .Returns(true);

            // Setup stake validator to pass stake kernel hash validation.
            this.stakeChain.Setup(m => m.Get(It.IsAny <uint256>())).Returns(new BlockStake());
            this.stakeValidator
            .Setup(m => m.CheckStakeKernelHash(It.IsAny <PosRuleContext>(), It.IsAny <uint>(), It.IsAny <uint256>(), It.IsAny <UnspentOutput>(), It.IsAny <OutPoint>(), It.IsAny <uint>())).Returns(true);

            // When we run the validation rule, we should hit bad merkle proof error.
            Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext);

            ruleValidation.Should().Throw <ConsensusErrorException>()
            .And.ConsensusError
            .Should().Be(ConsensusErrors.BadMerkleRoot);
        }
Example #4
0
        /// <inheritdoc />
        public void SaveChanges(IList <UnspentOutput> unspentOutputs, HashHeightPair oldBlockHash, HashHeightPair nextBlockHash, List <RewindData> rewindDataList = null)
        {
            Guard.NotNull(oldBlockHash, nameof(oldBlockHash));
            Guard.NotNull(nextBlockHash, nameof(nextBlockHash));
            Guard.NotNull(unspentOutputs, nameof(unspentOutputs));

            using (this.lockobj.LockWrite())
            {
                if ((this.tipHash != null) && (oldBlockHash != this.tipHash))
                {
                    throw new InvalidOperationException("Invalid oldBlockHash");
                }

                this.tipHash = nextBlockHash;
                foreach (UnspentOutput unspent in unspentOutputs)
                {
                    UnspentOutput existing;
                    if (this.unspents.TryGetValue(unspent.OutPoint, out existing))
                    {
                        existing.Spend();
                    }
                    else
                    {
                        existing = new UnspentOutput(unspent.OutPoint, unspent.Coins);
                        this.unspents.Add(unspent.OutPoint, existing);
                    }

                    if (existing.Coins?.IsPrunable ?? false)
                    {
                        this.unspents.Remove(unspent.OutPoint);
                    }
                }
            }
        }
        /// <inheritdoc />>
        /// <exception cref="ConsensusErrors.BadTransactionBIP30"> Thrown if BIP30 is not passed.</exception>
        public override Task RunAsync(RuleContext context)
        {
            if (!context.SkipValidation)
            {
                Block            block           = context.ValidationContext.BlockToValidate;
                DeploymentFlags  flags           = context.Flags;
                var              utxoRuleContext = context as UtxoRuleContext;
                UnspentOutputSet view            = utxoRuleContext.UnspentOutputSet;

                if (flags.EnforceBIP30)
                {
                    foreach (Transaction tx in block.Transactions)
                    {
                        foreach (IndexedTxOut indexedTxOut in tx.Outputs.AsIndexedOutputs())
                        {
                            UnspentOutput coins = view.AccessCoins(indexedTxOut.ToOutPoint());
                            if ((coins?.Coins != null) && !coins.Coins.IsPrunable)
                            {
                                this.Logger.LogDebug("Transaction '{0}' already found in store", tx.GetHash());
                                this.Logger.LogTrace("(-)[BAD_TX_BIP_30]");
                                ConsensusErrors.BadTransactionBIP30.Throw();
                            }
                        }
                    }
                }
            }
            else
            {
                this.Logger.LogDebug("BIP30 validation skipped for checkpointed block at height {0}.", context.ValidationContext.ChainedHeaderToValidate.Height);
            }

            return(Task.CompletedTask);
        }
        public async Task GetTxOutAsync_IncludeInMempool_UnspentTransactionFound_ReturnsModelAsync()
        {
            var         txId           = new uint256(1243124);
            Transaction transaction    = this.CreateTransaction();
            var         unspentOutputs = new UnspentOutput(new OutPoint(transaction, 0), new Coins(1, transaction.Outputs[0], transaction.IsCoinBase));

            this.pooledGetUnspentTransaction.Setup(s => s.GetUnspentTransactionAsync(new OutPoint(txId, 0)))
            .ReturnsAsync(unspentOutputs)
            .Verifiable();
            this.controller = new NodeController(this.chainIndexer, this.chainState.Object,
                                                 this.connectionManager.Object, this.dateTimeProvider.Object, this.fullNode.Object,
                                                 this.LoggerFactory.Object, this.nodeSettings, this.network, this.asyncProvider.Object, this.selfEndpointTracker.Object, this.consensusManager.Object, this.blockStore.Object, this.getUnspentTransaction.Object,
                                                 this.networkDifficulty.Object, this.pooledGetUnspentTransaction.Object, this.pooledTransaction.Object);
            string txid           = txId.ToString();
            uint   vout           = 0;
            bool   includeMemPool = true;

            var json = (JsonResult)await this.controller.GetTxOutAsync(txid, vout, includeMemPool).ConfigureAwait(false);

            var resultModel = (GetTxOutModel)json.Value;

            this.pooledGetUnspentTransaction.Verify();
            Assert.Equal(this.chainIndexer.Tip.HashBlock, resultModel.BestBlock);
            Assert.True(resultModel.Coinbase);
            Assert.Equal(3, resultModel.Confirmations);
            Assert.Equal(new ScriptPubKey(transaction.Outputs[0].ScriptPubKey, this.network).Hex, resultModel.ScriptPubKey.Hex);
            Assert.Equal(transaction.Outputs[0].Value, resultModel.Value);
        }
Example #7
0
        public async Task <GetTxOutModel> GetTxOutAsync(string txid, uint vout, bool includeMemPool = true)
        {
            uint256 trxid;

            if (!uint256.TryParse(txid, out trxid))
            {
                throw new ArgumentException(nameof(txid));
            }

            UnspentOutput unspentOutputs = null;
            OutPoint      outPoint       = new OutPoint(trxid, vout);

            if (includeMemPool && this.pooledGetUnspentTransaction != null)
            {
                unspentOutputs = await this.pooledGetUnspentTransaction.GetUnspentTransactionAsync(outPoint).ConfigureAwait(false);
            }

            if (!includeMemPool && this.getUnspentTransaction != null)
            {
                unspentOutputs = await this.getUnspentTransaction.GetUnspentTransactionAsync(outPoint).ConfigureAwait(false);
            }

            if (unspentOutputs != null)
            {
                return(new GetTxOutModel(unspentOutputs, this.Network, this.ChainIndexer.Tip));
            }

            return(null);
        }
Example #8
0
        /// <summary>
        /// Gets the priority of this memory pool transaction based upon chain height.
        /// </summary>
        /// <param name="tx">Memory pool transaction.</param>
        /// <param name="nHeight">Chain height.</param>
        /// <returns>Tuple of priority value and sum of all txin values that are already in blockchain.</returns>
        public (double priority, Money inChainInputValue) GetPriority(Transaction tx, int nHeight)
        {
            Money inChainInputValue = 0;

            if (tx.IsCoinBase)
            {
                return(0.0, inChainInputValue);
            }
            double dResult = 0.0;

            foreach (TxIn txInput in tx.Inputs)
            {
                UnspentOutput coins = this.Set.AccessCoins(txInput.PrevOut);

                if (coins == null)
                {
                    continue;
                }

                if (coins.Coins.Height <= nHeight)
                {
                    dResult           += (double)coins.Coins.TxOut.Value.Satoshi * (nHeight - coins.Coins.Height);
                    inChainInputValue += coins.Coins.TxOut.Value;
                }
            }
            return(this.ComputePriority(tx, dResult), inChainInputValue);
        }
Example #9
0
        /// <summary>
        /// Gets and validates unspent outputs based of coins fetched from coin view.
        /// </summary>
        /// <param name="header">The header.</param>
        /// <param name="context">Rule context.</param>
        /// <returns>The validated previous <see cref="UnspentOutput"/></returns>
        private UnspentOutput GetAndValidatePreviousUtxo(ProvenBlockHeader header, PosRuleContext context)
        {
            // First try and find the previous trx in the database.
            TxIn txIn = header.Coinstake.Inputs[0];

            UnspentOutput prevUtxo = null;

            FetchCoinsResponse coins = this.PosParent.UtxoSet.FetchCoins(new[] { txIn.PrevOut });

            prevUtxo = coins.UnspentOutputs[txIn.PrevOut];
            if (prevUtxo?.Coins == null)
            {
                // We did not find the previous trx in the database, look in rewind data.
                prevUtxo = this.CheckIfCoinstakeIsSpentOnAnotherChain(header, context);
            }
            else
            {
                // The trx was found now check if the UTXO is spent.
                TxOut utxo = prevUtxo.Coins.TxOut;

                if (utxo == null)
                {
                    // UTXO is spent so find it in rewind data.
                    prevUtxo = this.CheckIfCoinstakeIsSpentOnAnotherChain(header, context);
                }
            }

            return(prevUtxo);
        }
Example #10
0
 /// <see cref="IStakeValidator.VerifySignature"/>
 /// <exception cref="ConsensusException">
 /// Throws exception with error <see cref="ConsensusErrors.CoinstakeVerifySignatureFailed" /> if check fails.
 /// </exception>
 private void CheckSignature(ProvenBlockHeader header, UnspentOutput unspentOutputs)
 {
     if (!this.stakeValidator.VerifySignature(unspentOutputs, header.Coinstake, 0, ScriptVerify.None))
     {
         this.Logger.LogTrace("(-)[BAD_SIGNATURE]");
         ConsensusErrors.CoinstakeVerifySignatureFailed.Throw();
     }
 }
Example #11
0
        public void InvalidStakeKernelHash_CoinstakeVerifySignatureErrorIsThrown()
        {
            // Setup previous chained header.
            PosBlock          prevPosBlock          = new PosBlockBuilder(this.network).Build();
            ProvenBlockHeader prevProvenBlockHeader = new ProvenBlockHeaderBuilder(prevPosBlock, this.network).Build();
            var previousChainedHeader = new ChainedHeader(prevProvenBlockHeader, prevProvenBlockHeader.GetHash(), null);

            previousChainedHeader.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 1);

            // Setup proven header with valid coinstake.
            PosBlock          posBlock          = new PosBlockBuilder(this.network).Build();
            ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build(prevProvenBlockHeader);

            provenBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash();

            // Setup chained header and move it to the height higher than proven header activation height.
            this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(provenBlockHeader, provenBlockHeader.GetHash(), previousChainedHeader);
            this.ruleContext.ValidationContext.ChainedHeaderToValidate.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 2);

            // Ensure that coinview returns a UTXO with valid outputs.
            var utxoOneTransaction = this.network.CreateTransaction();

            utxoOneTransaction.AddOutput(new TxOut());
            var utxoOne = new UnspentOutput(new OutPoint(utxoOneTransaction, 0), new Coins((uint)this.provenHeadersActivationHeight + 10, utxoOneTransaction.Outputs.First(), false));

            // Setup coinstake transaction with a valid stake age.
            var res = new FetchCoinsResponse();

            res.UnspentOutputs.Add(utxoOne.OutPoint, utxoOne);

            this.coinView
            .Setup(m => m.FetchCoins(It.IsAny <OutPoint[]>()))
            .Returns(res);

            // Setup stake validator to pass stake age check.
            this.stakeValidator
            .Setup(m => m.IsConfirmedInNPrevBlocks(It.IsAny <UnspentOutput>(), It.IsAny <ChainedHeader>(), It.IsAny <long>()))
            .Returns(false);

            // Setup stake validator to pass signature validation.
            this.stakeValidator
            .Setup(m => m.VerifySignature(It.IsAny <UnspentOutput>(), It.IsAny <Transaction>(), It.IsAny <int>(), It.IsAny <ScriptVerify>()))
            .Returns(true);

            // Setup stake validator to fail stake kernel hash validation.
            this.stakeChain.Setup(m => m.Get(It.IsAny <uint256>())).Returns(new BlockStake());
            this.stakeValidator
            .Setup(m => m.CheckStakeKernelHash(It.IsAny <PosRuleContext>(), It.IsAny <uint>(), It.IsAny <uint256>(), It.IsAny <UnspentOutput>(), It.IsAny <OutPoint>(), It.IsAny <uint>()))
            .Throws(new ConsensusErrorException(ConsensusErrors.StakeHashInvalidTarget));

            // When we run the validation rule, we should hit stake hash invalid target error.
            Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext);

            ruleValidation.Should().Throw <ConsensusErrorException>()
            .And.ConsensusError
            .Should().Be(ConsensusErrors.StakeHashInvalidTarget);
        }
Example #12
0
        public void RunRule_ProvenHeadersActive_And_NullPreviousStake_InvalidPreviousProvenHeaderStakeModifierErrorIsThrown()
        {
            // Setup previous chained header.
            PosBlock          prevPosBlock          = new PosBlockBuilder(this.network).Build();
            ProvenBlockHeader prevProvenBlockHeader = new ProvenBlockHeaderBuilder(prevPosBlock, this.network).Build();

            prevProvenBlockHeader.StakeModifierV2 = null; // Forcing previous stake modifier to null.
            var previousChainedHeader = new ChainedHeader(prevProvenBlockHeader, prevProvenBlockHeader.GetHash(), null);

            previousChainedHeader.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 1);

            // Setup proven header with valid coinstake.
            PosBlock          posBlock          = new PosBlockBuilder(this.network).Build();
            ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build();

            provenBlockHeader.PosBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash();
            if (provenBlockHeader.Coinstake is IPosTransactionWithTime posTrx)
            {
                posTrx.Time = provenBlockHeader.Time;
            }

            // Setup chained header and move it to the height higher than proven header activation height.
            this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(provenBlockHeader, provenBlockHeader.GetHash(), previousChainedHeader);
            this.ruleContext.ValidationContext.ChainedHeaderToValidate.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 2);

            // Ensure that coinview returns a UTXO with valid outputs.
            var utxoOneTransaction = this.network.CreateTransaction();

            utxoOneTransaction.AddOutput(new TxOut());
            var utxoOne = new UnspentOutput(new OutPoint(utxoOneTransaction, 0), new Coins((uint)this.provenHeadersActivationHeight + 10, utxoOneTransaction.Outputs.First(), false));

            // Setup coinstake transaction with a valid stake age.
            var res = new FetchCoinsResponse();

            res.UnspentOutputs.Add(utxoOne.OutPoint, utxoOne);

            this.coinView
            .Setup(m => m.FetchCoins(It.IsAny <OutPoint[]>()))
            .Returns(res);

            // Setup stake validator to pass stake age check.
            this.stakeValidator
            .Setup(m => m.IsConfirmedInNPrevBlocks(It.IsAny <UnspentOutput>(), It.IsAny <ChainedHeader>(), It.IsAny <long>()))
            .Returns(false);

            // Setup stake validator to pass signature validation.
            this.stakeValidator
            .Setup(m => m.VerifySignature(It.IsAny <UnspentOutput>(), It.IsAny <Transaction>(), It.IsAny <int>(), It.IsAny <ScriptVerify>()))
            .Returns(true);

            // When we run the validation rule, we should hit previous stake null error.
            Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext);

            ruleValidation.Should().Throw <ConsensusErrorException>()
            .And.ConsensusError
            .Should().Be(ConsensusErrors.InvalidPreviousProvenHeaderStakeModifier);
        }
 public static SpentOutput Create(StoredBlock block, UnspentOutput unspentOutput)
 {
     return new SpentOutput(
         unspentOutput.SourceBlockHeight,
         block.Height,
         unspentOutput.TransactionHash,
         unspentOutput.OutputNumber,
         unspentOutput.Sum,
         unspentOutput.PublicScript);
 }
Example #14
0
        /// <inheritdoc />
        public bool IsConfirmedInNPrevBlocks(UnspentOutput coins, ChainedHeader referenceChainedHeader, long targetDepth)
        {
            Guard.NotNull(coins, nameof(coins));
            Guard.NotNull(referenceChainedHeader, nameof(referenceChainedHeader));

            int  actualDepth = referenceChainedHeader.Height - (int)coins.Coins.Height;
            bool res         = actualDepth < targetDepth;

            return(res);
        }
Example #15
0
        public TxOut GetOutputFor(TxIn txIn)
        {
            UnspentOutput unspent = this.unspents.TryGet(txIn.PrevOut);

            if (unspent?.Coins == null)
            {
                return(null);
            }

            return(unspent.Coins.TxOut);
        }
        public static Money GetFee(this Transaction transaction, UnspentOutputSet inputs)
        {
            Money valueIn = Money.Zero;

            for (int i = 0; i < transaction.Inputs.Count; i++)
            {
                OutPoint      prevout = transaction.Inputs[i].PrevOut;
                UnspentOutput coins   = inputs.AccessCoins(prevout);
                valueIn += coins.Coins.TxOut.Value;
            }
            return(valueIn - transaction.TotalOut);
        }
Example #17
0
 /// <inheritdoc />
 protected override void CheckInputValidity(Transaction transaction, UnspentOutput coins)
 {
     // TODO: Keep this check to avoid a network split
     if (transaction is IPosTransactionWithTime posTrx)
     {
         // Transaction timestamp earlier than input transaction - main.cpp, CTransaction::ConnectInputs
         if (coins.Coins.Time > posTrx.Time)
         {
             ConsensusErrors.BadTransactionEarlyTimestamp.Throw();
         }
     }
 }
        /// <summary>
        /// Checks that transaction's inputs are valid.
        /// </summary>
        /// <param name="transaction">Transaction to check.</param>
        /// <param name="inputs">Map of previous transactions that have outputs we're spending.</param>
        /// <param name="spendHeight">Height at which we are spending coins.</param>
        /// <exception cref="ConsensusErrors.BadTransactionMissingInput">Thrown if transaction's inputs are missing.</exception>
        /// <exception cref="ConsensusErrors.BadTransactionInputValueOutOfRange">Thrown if input value is out of range.</exception>
        /// <exception cref="ConsensusErrors.BadTransactionInBelowOut">Thrown if transaction inputs are less then outputs.</exception>
        /// <exception cref="ConsensusErrors.BadTransactionNegativeFee">Thrown if fees sum is negative.</exception>
        /// <exception cref="ConsensusErrors.BadTransactionFeeOutOfRange">Thrown if fees value is out of range.</exception>
        public void CheckInputs(Transaction transaction, UnspentOutputSet inputs, int spendHeight)
        {
            if (!inputs.HaveInputs(transaction))
            {
                ConsensusErrors.BadTransactionMissingInput.Throw();
            }

            Money valueIn = Money.Zero;
            Money fees    = Money.Zero;

            for (int i = 0; i < transaction.Inputs.Count; i++)
            {
                OutPoint      prevout = transaction.Inputs[i].PrevOut;
                UnspentOutput coins   = inputs.AccessCoins(prevout);

                this.CheckMaturity(coins, spendHeight);

                this.CheckInputValidity(transaction, coins);

                // Check for negative or overflow input values.
                valueIn += coins.Coins.TxOut.Value;
                if (!this.MoneyRange(coins.Coins.TxOut.Value) || !this.MoneyRange(valueIn))
                {
                    this.Logger.LogTrace("(-)[BAD_TX_INPUT_VALUE]");
                    ConsensusErrors.BadTransactionInputValueOutOfRange.Throw();
                }
            }

            if (!transaction.IsProtocolTransaction())
            {
                if (valueIn < transaction.TotalOut)
                {
                    this.Logger.LogTrace("(-)[TX_IN_BELOW_OUT]");
                    ConsensusErrors.BadTransactionInBelowOut.Throw();
                }

                // Check transaction fees.
                Money txFee = valueIn - transaction.TotalOut;
                if (txFee < 0)
                {
                    this.Logger.LogTrace("(-)[NEGATIVE_FEE]");
                    ConsensusErrors.BadTransactionNegativeFee.Throw();
                }

                fees += txFee;
                if (!this.MoneyRange(fees))
                {
                    this.Logger.LogTrace("(-)[BAD_FEE]");
                    ConsensusErrors.BadTransactionFeeOutOfRange.Throw();
                }
            }
        }
Example #19
0
        /// <summary>
        /// Whether memory pool transaction spends coin base.
        /// </summary>
        /// <param name="tx">Memory pool transaction.</param>
        /// <returns>Whether the transactions spends coin base.</returns>
        public bool SpendsCoinBase(Transaction tx)
        {
            foreach (TxIn txInput in tx.Inputs)
            {
                UnspentOutput coins = this.Set.AccessCoins(txInput.PrevOut);
                if (coins.Coins.IsCoinbase)
                {
                    return(true);
                }
            }

            return(false);
        }
Example #20
0
        /// <summary>
        /// Checks the coinstake age requirement.
        /// </summary>
        /// <param name="chainedHeader">The chained header.</param>
        /// <param name="unspentOutputs">The unspent outputs.</param>
        /// <exception cref="ConsensusException">
        /// Throws exception with error <see cref="ConsensusErrors.InvalidStakeDepth" /> if check fails.
        /// </exception>
        private void CheckCoinstakeAgeRequirement(ChainedHeader chainedHeader, UnspentOutput unspentOutputs)
        {
            ChainedHeader prevChainedHeader = chainedHeader.Previous;

            var options     = (PosConsensusOptions)this.PosParent.Network.Consensus.Options;
            int targetDepth = options.GetStakeMinConfirmations(chainedHeader.Height, this.PosParent.Network) - 1;

            if (this.stakeValidator.IsConfirmedInNPrevBlocks(unspentOutputs, prevChainedHeader, targetDepth))
            {
                this.Logger.LogTrace("(-)[BAD_STAKE_DEPTH]");
                ConsensusErrors.InvalidStakeDepth.Throw();
            }
        }
Example #21
0
        /// <summary>
        /// Initializes a GetTxOutModel instance.
        /// </summary>
        /// <param name="unspentOutputs">The <see cref="UnspentOutput"/>.</param>
        /// <param name="network">The network the transaction occurred on.</param>
        /// <param name="tip">The current consensus tip's <see cref="ChainedHeader"/>.</param>
        public GetTxOutModel(UnspentOutput unspentOutputs, Network network, ChainedHeader tip)
        {
            this.BestBlock = tip.HashBlock;

            if (unspentOutputs?.Coins != null)
            {
                TxOut output = unspentOutputs.Coins.TxOut;
                this.Coinbase      = unspentOutputs.Coins.IsCoinbase;
                this.Confirmations = NetworkExtensions.MempoolHeight == unspentOutputs.Coins.Height ? 0 : tip.Height - (int)unspentOutputs.Coins.Height + 1;
                this.Value         = output.Value;
                this.ScriptPubKey  = new ScriptPubKey(output.ScriptPubKey, network);
            }
        }
 /// <summary>
 /// Checks the maturity of UTXOs.
 /// </summary>
 /// <param name="coins">UTXOs to check the maturity of.</param>
 /// <param name="spendHeight">Height at which coins are attempted to be spent.</param>
 /// <exception cref="ConsensusErrors.BadTransactionPrematureCoinbaseSpending">Thrown if transaction tries to spend coins that are not mature.</exception>
 public void CheckCoinbaseMaturity(UnspentOutput coins, int spendHeight)
 {
     // If prev is coinbase, check that it's matured
     if (coins.Coins.IsCoinbase)
     {
         if ((spendHeight - coins.Coins.Height) < this.Consensus.CoinbaseMaturity)
         {
             this.Logger.LogDebug("Coinbase transaction height {0} spent at height {1}, but maturity is set to {2}.", coins.Coins.Height, spendHeight, this.Consensus.CoinbaseMaturity);
             this.Logger.LogTrace("(-)[COINBASE_PREMATURE_SPENDING]");
             ConsensusErrors.BadTransactionPrematureCoinbaseSpending.Throw();
         }
     }
 }
Example #23
0
        /// <inheritdoc />
        public GetSenderResult GetSender(Transaction tx, ICoinView coinView, IList <Transaction> blockTxs)
        {
            OutPoint prevOut = tx.Inputs[0].PrevOut;

            // Check the txes in this block first
            if (blockTxs != null && blockTxs.Count > 0)
            {
                foreach (Transaction btx in blockTxs)
                {
                    if (btx.GetHash() == prevOut.Hash)
                    {
                        if (prevOut.N >= btx.Outputs.Count)
                        {
                            return(GetSenderResult.CreateFailure(InvalidOutputIndex));
                        }

                        Script script = btx.Outputs[prevOut.N].ScriptPubKey;
                        return(this.GetAddressFromScript(script));
                    }
                }
            }

            // Check the utxoset for the p2pk of the unspent output for this transaction
            if (coinView != null)
            {
                FetchCoinsResponse fetchCoinResult = coinView.FetchCoins(new OutPoint[] { prevOut });

                // The result from the coinview should never be null, so we do not check for that condition here.
                // It will simply not contain the requested outputs in the dictionary if they did not exist in the coindb.
                if (fetchCoinResult.UnspentOutputs.All(o => o.Key != prevOut))
                {
                    return(GetSenderResult.CreateFailure(OutputsNotInCoinView));
                }

                UnspentOutput unspentOutputs = fetchCoinResult.UnspentOutputs.First(o => o.Key == prevOut).Value;

                // Since we now fetch a specific UTXO from the coindb instead of an entire transaction, it is no longer meaningful to check
                // (for the coindb at least - block transactions are handled separately above) whether the prevOut index is within bounds.
                // So that check has been removed from here and we proceed directly to checking spent-ness.
                if (unspentOutputs.Coins == null)
                {
                    return(GetSenderResult.CreateFailure(OutputAlreadySpent));
                }

                TxOut senderOutput = unspentOutputs.Coins.TxOut;

                return(this.GetAddressFromScript(senderOutput.ScriptPubKey));
            }

            return(GetSenderResult.CreateFailure(UnableToGetSender));
        }
        /// <inheritdoc />
        public override void CheckMaturity(UnspentOutput coins, int spendHeight)
        {
            base.CheckCoinbaseMaturity(coins, spendHeight);

            if (coins.Coins.IsCoinstake)
            {
                if ((spendHeight - coins.Coins.Height) < this.consensus.CoinbaseMaturity)
                {
                    this.Logger.LogDebug("Coinstake transaction height {0} spent at height {1}, but maturity is set to {2}.", coins.Coins.Height, spendHeight, this.consensus.CoinbaseMaturity);
                    this.Logger.LogTrace("(-)[COINSTAKE_PREMATURE_SPENDING]");
                    ConsensusErrors.BadTransactionPrematureCoinstakeSpending.Throw();
                }
            }
        }
        public void AddUnspentOutput(UnspentOutput unspentOutput)
        {
            var command = CreateCommand(
                "insert into UnspentOutputs (SourceBlockHeight, TransactionHash, OutputNumber, Sum, PublicScript)" +
                "values (@SourceBlockHeight, @TransactionHash, @OutputNumber, @Sum, @PublicScript)");

            command.Parameters.Add("@SourceBlockHeight", DbType.Int32).Value = unspentOutput.SourceBlockHeight;
            command.Parameters.Add("@TransactionHash", DbType.Binary).Value = unspentOutput.TransactionHash;
            command.Parameters.Add("@OutputNumber", DbType.Int32).Value = unspentOutput.OutputNumber;
            command.Parameters.Add("@Sum", DbType.UInt64).Value = unspentOutput.Sum;
            command.Parameters.Add("@PublicScript", DbType.Binary).Value = unspentOutput.PublicScript;

            command.ExecuteNonQuery();
        }
        public void Constructor_InitializesClass()
        {
            var unspentOutputs = new UnspentOutput[] {
                new UnspentOutput(new OutPoint(new Transaction(), 0), new Coins(1, new TxOut(), false)),
                new UnspentOutput(new OutPoint(new Transaction(), 1), new Coins(2, new TxOut(), false))
            };

            var fetchCoinsResponse = new FetchCoinsResponse();

            fetchCoinsResponse.UnspentOutputs.Add(unspentOutputs[0].OutPoint, unspentOutputs[0]);
            fetchCoinsResponse.UnspentOutputs.Add(unspentOutputs[1].OutPoint, unspentOutputs[1]);

            Assert.Equal(2, fetchCoinsResponse.UnspentOutputs.Count);
            Assert.Equal((uint)1, fetchCoinsResponse.UnspentOutputs.ToList()[0].Value.Coins.Height);
            Assert.Equal((uint)2, fetchCoinsResponse.UnspentOutputs.ToList()[1].Value.Coins.Height);
        }
Example #27
0
        public void RunRule_ProvenHeadersActive_And_InvalidStakeDepth_StakeDepthErrorIsThrown()
        {
            // Setup previous chained header.
            PosBlock          prevPosBlock          = new PosBlockBuilder(this.network).Build();
            ProvenBlockHeader prevProvenBlockHeader = new ProvenBlockHeaderBuilder(prevPosBlock, this.network).Build();
            var previousChainedHeader = new ChainedHeader(prevProvenBlockHeader, prevProvenBlockHeader.GetHash(), null);

            previousChainedHeader.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 1);

            // Setup proven header with valid coinstake.
            PosBlock          posBlock          = new PosBlockBuilder(this.network).Build();
            ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build();

            provenBlockHeader.PosBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash();
            if (provenBlockHeader.Coinstake is IPosTransactionWithTime posTrx)
            {
                posTrx.Time = provenBlockHeader.Time;
            }

            // Setup chained header and move it to the height higher than proven header activation height.
            this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(provenBlockHeader, provenBlockHeader.GetHash(), previousChainedHeader);
            this.ruleContext.ValidationContext.ChainedHeaderToValidate.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 2);

            // Ensure that coinview returns a UTXO with valid outputs.
            var utxoOne = new UnspentOutput(prevPosBlock.Transactions[1].Inputs[0].PrevOut, new Coins((uint)previousChainedHeader.Height, new TxOut(), false, true));

            // Setup coinstake transaction with an invalid stake age.
            var res = new FetchCoinsResponse();

            res.UnspentOutputs.Add(utxoOne.OutPoint, utxoOne);

            this.coinView
            .Setup(m => m.FetchCoins(It.IsAny <OutPoint[]>()))
            .Returns(res);

            // Setup stake validator to fail stake age check.
            this.stakeValidator
            .Setup(m => m.IsConfirmedInNPrevBlocks(It.IsAny <UnspentOutput>(), It.IsAny <ChainedHeader>(), It.IsAny <long>()))
            .Returns(true);

            // When we run the validation rule, we should hit coinstake depth error.
            Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext);

            ruleValidation.Should().Throw <ConsensusErrorException>()
            .And.ConsensusError
            .Should().Be(ConsensusErrors.InvalidStakeDepth);
        }
        public async Task GetTxOutAsync_IncludeInMempool_UnspentTransactionFound_VOutNotFound_ReturnsModelAsync()
        {
            var         txId           = new uint256(1243124);
            Transaction transaction    = this.CreateTransaction();
            var         unspentOutputs = new UnspentOutput(new OutPoint(transaction, 0), new Coins(1, transaction.Outputs[0], transaction.IsCoinBase));

            this.pooledGetUnspentTransaction.Setup(s => s.GetUnspentTransactionAsync(new OutPoint(txId, 0)))
            .ReturnsAsync(unspentOutputs)
            .Verifiable();

            GetTxOutModel model = await this.controller.GetTxOutAsync(txId.ToString(), 0, true).ConfigureAwait(false);

            this.pooledGetUnspentTransaction.Verify();
            Assert.Equal(this.chain.Tip.HashBlock, model.BestBlock);
            Assert.True(model.Coinbase);
            Assert.Equal(3, model.Confirmations);
        }
Example #29
0
        /// <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));
        }
        private UnspentOutputSet GetMockOutputSet()
        {
            Transaction transaction = this.network.Consensus.ConsensusFactory.CreateTransaction();

            var fakeTxOut = new TxOut
            {
                Value = 100_000_000
            };

            var coins = new UnspentOutput(new OutPoint(uint256.Zero, 0), new Coins(0, fakeTxOut, false));

            var unspentOutputs = new UnspentOutputSet();

            unspentOutputs.SetCoins(new[] { coins });

            return(unspentOutputs);
        }
        public List <(Coins, OutPoint)> CreateCoins(int coinCount)
        {
            var tx = new Transaction();

            tx.Outputs.AddRange(Enumerable.Range(0, coinCount)
                                .Select(t => new TxOut(Money.Zero, new Key()))
                                .ToArray());

            List <(Coins, OutPoint)> lst = new List <(Coins, OutPoint)>();

            foreach (var trxo in tx.Outputs.AsIndexedOutputs())
            {
                var output = new UnspentOutput(trxo.ToOutPoint(), new Coins(0, trxo.TxOut, false));
                this.pendingCoins.Add(output);
                lst.Add((output.Coins, output.OutPoint));
            }
            return(lst);
        }
Example #32
0
        /// <inheritdoc />
        public async Task <UnspentOutput> GetUnspentTransactionAsync(OutPoint outPoint)
        {
            TxMempoolInfo txInfo = this.Info(outPoint.Hash);

            if (txInfo == null)
            {
                this.logger.LogTrace("(-):[TX_IS_NULL]");
                return(null);
            }

            var memPoolCoinView = new MempoolCoinView(this.network, this.coinView, this.memPool, this.MempoolLock, this.Validator);

            await this.MempoolLock.ReadAsync(() => { memPoolCoinView.LoadViewLocked(txInfo.Trx); });

            UnspentOutput unspentOutput = memPoolCoinView.Set.AccessCoins(outPoint);

            return(unspentOutput);
        }
 public void AddUnspentOutput(UnspentOutput unspentOutput)
 {
     JoinCurrentTransaction();
     using (BlockchainRepository repo = new BlockchainRepository(conn))
     {
         repo.AddUnspentOutput(unspentOutput);
     }
 }