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); }
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); }
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); }
/// <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); }
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); }
/// <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); }
/// <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); }
/// <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(); } }
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); }
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); }
/// <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); }
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); }
/// <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(); } } }
/// <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); }
/// <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(); } }
/// <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(); } } }
/// <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); }
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); }
/// <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); }
/// <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); } }