/// <summary>
        /// Constructs a transaction memory pool entry.
        /// </summary>
        /// <param name="transaction">Transaction for the entry.</param>
        /// <param name="nFee">Fee for the transaction in the entry in the memory pool.</param>
        /// <param name="nTime">The local time when entering the memory pool.</param>
        /// <param name="entryPriority">Priority when entering the memory pool.</param>
        /// <param name="entryHeight">The chain height when entering the mempool.</param>
        /// <param name="inChainInputValue">The sum of all txin values that are already in blockchain.</param>
        /// <param name="spendsCoinbase">Whether the transaction spends a coinbase.</param>
        /// <param name="nSigOpsCost">The total signature operations cost.</param>
        /// <param name="lp">Tthe lock points that track the height and time at which tx was final.</param>
        /// <param name="consensusOptions">Proof of work consensus options used to compute transaction weight and modified size.</param>
        public TxMempoolEntry(Transaction transaction, Money nFee,
                              long nTime, double entryPriority, int entryHeight,
                              Money inChainInputValue, bool spendsCoinbase,
                              long nSigOpsCost, LockPoints lp, PowConsensusOptions consensusOptions)
        {
            this.Transaction       = transaction;
            this.TransactionHash   = transaction.GetHash();
            this.Fee               = nFee;
            this.Time              = nTime;
            this.entryPriority     = entryPriority;
            this.EntryHeight       = entryHeight;
            this.InChainInputValue = inChainInputValue;
            this.SpendsCoinbase    = spendsCoinbase;
            this.SigOpCost         = nSigOpsCost;
            this.LockPoints        = lp;

            this.TxWeight = MempoolValidator.GetTransactionWeight(transaction, consensusOptions);
            this.nModSize = MempoolValidator.CalculateModifiedSize(this.Transaction.GetSerializedSize(), this.Transaction, consensusOptions);

            this.nUsageSize = transaction.GetSerializedSize(); // RecursiveDynamicUsage(*tx) + memusage::DynamicUsage(Transaction);

            this.CountWithDescendants   = 1;
            this.SizeWithDescendants    = this.GetTxSize();
            this.ModFeesWithDescendants = this.Fee;
            Money nValueIn = transaction.TotalOut + this.Fee;

            Guard.Assert(this.InChainInputValue <= nValueIn);

            this.feeDelta = 0;

            this.CountWithAncestors     = 1;
            this.SizeWithAncestors      = this.GetTxSize();
            this.ModFeesWithAncestors   = this.Fee;
            this.SigOpCostWithAncestors = this.SigOpCost;
        }
Пример #2
0
        public void TestBlockValidity_UsesRuleContextToValidateBlock()
        {
            var newOptions = new PowConsensusOptions()
            {
                MaxBlockWeight = 1500
            };

            this.ExecuteWithConsensusOptions(newOptions, () =>
            {
                ConcurrentChain chain = GenerateChainWithHeight(5, this.network, new Key());
                this.consensusLoop.Setup(c => c.Tip).Returns(chain.GetBlock(5));

                ValidationContext validationContext = null;
                var powRuleContext = new PowRuleContext(new ValidationContext(), this.network.Consensus, chain.Tip, this.dateTimeProvider.Object.GetTimeOffset());
                this.consensusRules
                .Setup(s => s.CreateRuleContext(It.IsAny <ValidationContext>(), It.IsAny <ChainedHeader>())).Callback <ValidationContext, ChainedHeader>((r, s) => validationContext = r)
                .Returns(powRuleContext);

                var powBlockAssembler = new PowTestBlockDefinition(this.consensusLoop.Object, this.dateTimeProvider.Object, this.LoggerFactory.Object, this.txMempool.Object, new MempoolSchedulerLock(), this.network, this.consensusRules.Object);
                Block block           = powBlockAssembler.TestBlockValidity();

                Assert.NotNull(this.callbackRuleContext);

                Assert.True(this.callbackRuleContext.MinedBlock);
                Assert.Equal(block.GetHash(), validationContext.Block.GetHash());
                Assert.Equal(chain.GetBlock(5).HashBlock, powRuleContext.ConsensusTip.HashBlock);
                Assert.Equal(1500, this.callbackRuleContext.Consensus.Option <PowConsensusOptions>().MaxBlockWeight);
                this.consensusLoop.Verify();
            });
        }
Пример #3
0
        private TxMempoolEntry[] SetupTxMempool(ConcurrentChain chain, PowConsensusOptions newOptions, Money txFee, params Transaction[] transactions)
        {
            uint txTime     = Utils.DateTimeToUnixTime(chain.Tip.Header.BlockTime.AddSeconds(25));
            var  lockPoints = new LockPoints()
            {
                Height        = 4,
                MaxInputBlock = chain.GetBlock(4),
                Time          = chain.GetBlock(4).Header.Time
            };

            var resultingTransactionEntries = new List <TxMempoolEntry>();
            var indexedTransactionSet       = new TxMempool.IndexedTransactionSet();

            foreach (Transaction transaction in transactions)
            {
                var txPoolEntry = new TxMempoolEntry(transaction, txFee, txTime, 1, 4, new Money(400000000), false, 2, lockPoints, newOptions);
                indexedTransactionSet.Add(txPoolEntry);
                resultingTransactionEntries.Add(txPoolEntry);
            }


            this.txMempool.Setup(t => t.MapTx)
            .Returns(indexedTransactionSet);

            return(resultingTransactionEntries.ToArray());
        }
Пример #4
0
        public void CreateNewBlock_WithScript_ValidatesTemplateUsingRuleContext()
        {
            var newOptions = new PowConsensusOptions()
            {
                MaxBlockWeight = 1500
            };

            this.ExecuteWithConsensusOptions(newOptions, () =>
            {
                var chain = GenerateChainWithHeight(5, this.network, this.key);
                this.dateTimeProvider.Setup(d => d.GetAdjustedTimeAsUnixTimestamp())
                .Returns(new DateTime(2017, 1, 7, 0, 0, 1, DateTimeKind.Utc).ToUnixTimestamp());
                this.consensusLoop.Setup(c => c.Tip)
                .Returns(chain.GetBlock(5));

                var transaction = CreateTransaction(this.network, this.key, 5, new Money(400 * 1000 * 1000), new Key(), new uint256(124124));
                var txFee       = new Money(1000);
                SetupTxMempool(chain, this.network.Consensus.Options as PowConsensusOptions, txFee, transaction);

                var powBlockAssembler = new PowBlockAssembler(this.consensusLoop.Object, this.dateTimeProvider.Object, this.LoggerFactory.Object, this.txMempool.Object, new MempoolSchedulerLock(), this.network);

                var blockTemplate = powBlockAssembler.Build(chain.Tip, this.key.ScriptPubKey);
                Assert.NotNull(this.callbackRuleContext);

                Assert.False(this.callbackRuleContext.CheckMerkleRoot);
                Assert.False(this.callbackRuleContext.CheckPow);
                Assert.Equal(blockTemplate.Block.GetHash(), this.callbackRuleContext.BlockValidationContext.Block.GetHash());
                Assert.Equal(chain.GetBlock(5).HashBlock, this.callbackRuleContext.ConsensusTip.HashBlock);
                Assert.Equal(1500, this.callbackRuleContext.Consensus.Option <PowConsensusOptions>().MaxBlockWeight);
                this.consensusLoop.Verify();
            });
        }
Пример #5
0
        public void AddTransactions_WithoutTransactionsInMempool_DoesNotAddEntriesToBlock()
        {
            var newOptions = new PowConsensusOptions()
            {
                MaxBlockWeight = 1500
            };

            this.ExecuteWithConsensusOptions(newOptions, () =>
            {
                ConcurrentChain chain = GenerateChainWithHeight(5, this.network, new Key());
                this.consensusLoop.Setup(c => c.Tip)
                .Returns(chain.GetBlock(5));
                var indexedTransactionSet = new TxMempool.IndexedTransactionSet();
                this.txMempool.Setup(t => t.MapTx)
                .Returns(indexedTransactionSet);

                var blockDefinition = new PowTestBlockDefinition(this.consensusLoop.Object, this.dateTimeProvider.Object, this.LoggerFactory.Object, this.txMempool.Object,
                                                                 new MempoolSchedulerLock(), this.network, this.consensusRules.Object);

                (Block Block, int Selected, int Updated)result = blockDefinition.AddTransactions();

                Assert.Empty(result.Block.Transactions);
                Assert.Equal(0, result.Selected);
                Assert.Equal(0, result.Updated);
            });
        }
Пример #6
0
        public void AddTransactions_TransactionNotInblock_AddsTransactionToBlock()
        {
            var newOptions = new PowConsensusOptions()
            {
                MaxBlockWeight = 1500
            };

            this.ExecuteWithConsensusOptions(newOptions, () =>
            {
                var chain = GenerateChainWithHeight(5, this.network, this.key);
                this.consensusLoop.Setup(c => c.Tip).Returns(chain.GetBlock(5));
                var transaction = CreateTransaction(this.network, this.key, 5, new Money(400 * 1000 * 1000), new Key(), new uint256(124124));
                var txFee       = new Money(1000);
                SetupTxMempool(chain, newOptions, txFee, transaction);

                var powBlockAssembler = new PowTestBlockAssembler(this.consensusLoop.Object, this.dateTimeProvider.Object, this.LoggerFactory.Object, this.txMempool.Object,
                                                                  new MempoolSchedulerLock(), this.network);

                var result = powBlockAssembler.AddTransactions();

                Assert.NotEmpty(result.Block.Transactions);

                Assert.Equal(transaction.ToHex(), result.Block.Transactions[0].ToHex());
                Assert.Equal(1, result.Selected);
                Assert.Equal(0, result.Updated);
            });
        }
Пример #7
0
        public void TestBlockValidity_UsesRuleContextToValidateBlock()
        {
            var newOptions = new PowConsensusOptions()
            {
                MaxBlockWeight = 1500
            };

            this.ExecuteWithConsensusOptions(newOptions, () =>
            {
                var chain = GenerateChainWithHeight(5, this.network, new Key());
                this.consensusLoop.Setup(c => c.Tip).Returns(chain.GetBlock(5));

                var powBlockAssembler = new PowTestBlockAssembler(this.consensusLoop.Object, this.dateTimeProvider.Object, this.LoggerFactory.Object, this.txMempool.Object, new MempoolSchedulerLock(), this.network);
                var block             = powBlockAssembler.OnTestBlockValidity();

                Assert.NotNull(this.callbackRuleContext);

                Assert.False(this.callbackRuleContext.CheckMerkleRoot);
                Assert.False(this.callbackRuleContext.CheckPow);
                Assert.Equal(block.GetHash(), this.callbackRuleContext.BlockValidationContext.Block.GetHash());
                Assert.Equal(chain.GetBlock(5).HashBlock, this.callbackRuleContext.ConsensusTip.HashBlock);
                Assert.Equal(1500, this.callbackRuleContext.Consensus.Option <PowConsensusOptions>().MaxBlockWeight);
                this.consensusLoop.Verify();
            });
        }
Пример #8
0
        public void AddTransactions_TransactionAlreadyInInblock_DoesNotAddTransactionToBlock()
        {
            var newOptions = new PowConsensusOptions()
            {
                MaxBlockWeight = 1500
            };

            this.ExecuteWithConsensusOptions(newOptions, () =>
            {
                ConcurrentChain chain = GenerateChainWithHeight(5, this.network, this.key);
                this.consensusLoop.Setup(c => c.Tip)
                .Returns(chain.GetBlock(5));
                Transaction transaction = CreateTransaction(this.network, this.key, 5, new Money(400 * 1000 * 1000), new Key(), new uint256(124124));
                var txFee = new Money(1000);
                TxMempoolEntry[] entries = SetupTxMempool(chain, newOptions, txFee, transaction);

                var blockDefinition = new PowTestBlockDefinition(this.consensusLoop.Object, this.dateTimeProvider.Object, this.LoggerFactory.Object, this.txMempool.Object, new MempoolSchedulerLock(), this.network, this.consensusRules.Object);
                blockDefinition.AddInBlockTxEntries(entries);

                (Block Block, int Selected, int Updated)result = blockDefinition.AddTransactions();

                Assert.Empty(result.Block.Transactions);
                Assert.Equal(0, result.Selected);
                Assert.Equal(0, result.Updated);
            });
        }
        /// <inheritdoc />
        public override void Initialize()
        {
            this.Logger.LogTrace("()");

            this.PowConsensusOptions = this.Parent.Network.Consensus.Option <PowConsensusOptions>();

            this.Logger.LogTrace("(-)");
        }
 public static int GetTransactionWeight(Transaction tx, PowConsensusOptions consensusOptions)
 {
     return(tx.GetSerializedSize(
                (ProtocolVersion)
                ((uint)ProtocolVersion.PROTOCOL_VERSION | consensusOptions.SERIALIZE_TRANSACTION_NO_WITNESS),
                SerializationType.Network) * (consensusOptions.WITNESS_SCALE_FACTOR - 1) +
            tx.GetSerializedSize(ProtocolVersion.PROTOCOL_VERSION, SerializationType.Network));
 }
Пример #11
0
        private void ExecuteWithConsensusOptions(PowConsensusOptions newOptions, Action action)
        {
            NBitcoin.Consensus.ConsensusOptions options = this.network.Consensus.Options;
            try
            {
                this.network.Consensus.Options = newOptions;

                action();
            }
            finally
            {
                // This is a static in the global context so be careful updating it. I'm resetting it after being done testing so I don't influence other tests.
                this.network.Consensus.Options            = options;
                this.network.Consensus.BIP9Deployments[0] = null;
            }
        }
        public static int CalculateModifiedSize(int nTxSize, Transaction trx, PowConsensusOptions consensusOptions)
        {
            // In order to avoid disincentivizing cleaning up the UTXO set we don't count
            // the constant overhead for each txin and up to 110 bytes of scriptSig (which
            // is enough to cover a compressed pubkey p2sh redemption) for priority.
            // Providing any more cleanup incentive than making additional inputs free would
            // risk encouraging people to create junk outputs to redeem later.
            if (nTxSize == 0)
            {
                nTxSize = (GetTransactionWeight(trx, consensusOptions) + consensusOptions.WITNESS_SCALE_FACTOR - 1) / consensusOptions.WITNESS_SCALE_FACTOR;
            }

            foreach (var txInput in trx.Inputs)
            {
                var offset = 41U + Math.Min(110U, txInput.ScriptSig.Length);
                if (nTxSize > offset)
                {
                    nTxSize -= (int)offset;
                }
            }
            return(nTxSize);
        }
Пример #13
0
        public void ComputeBlockVersion_UsingChainTipAndConsensus_Bip9DeploymentActive_UpdatesHeightAndVersion()
        {
            var options = this.network.Consensus.Options;
            var minerConfirmationWindow       = this.network.Consensus.MinerConfirmationWindow;
            var ruleChangeActivationThreshold = this.network.Consensus.RuleChangeActivationThreshold;

            try
            {
                var newOptions = new PowConsensusOptions();
                this.network.Consensus.Options            = newOptions;
                this.network.Consensus.BIP9Deployments[0] = new BIP9DeploymentsParameters(19,
                                                                                          new DateTimeOffset(new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)),
                                                                                          new DateTimeOffset(new DateTime(2018, 1, 1, 0, 0, 0, DateTimeKind.Utc)));
                this.network.Consensus.MinerConfirmationWindow       = 2;
                this.network.Consensus.RuleChangeActivationThreshold = 2;

                var chain = GenerateChainWithHeightAndActivatedBip9(5, this.network, new Key(), this.network.Consensus.BIP9Deployments[0]);

                var powBlockAssembler = new PowTestBlockAssembler(this.consensusLoop.Object, this.dateTimeProvider.Object, this.LoggerFactory.Object, this.txMempool.Object,
                                                                  new MempoolSchedulerLock(), this.network);

                var result = powBlockAssembler.ComputeBlockVersion(chain.GetBlock(4));

                Assert.Equal(5, result.Height);
                uint version         = ThresholdConditionCache.VersionbitsTopBits;
                int  expectedVersion = (int)(version |= (((uint)1) << 19));
                Assert.Equal(expectedVersion, result.Version);
                Assert.NotEqual((int)ThresholdConditionCache.VersionbitsTopBits, result.Version);
            }
            finally
            {
                // This is a static in the global context so be careful updating it. I'm resetting it after being done testing so I don't influence other tests.
                this.network.Consensus.Options                       = options;
                this.network.Consensus.BIP9Deployments[0]            = null;
                this.network.Consensus.MinerConfirmationWindow       = minerConfirmationWindow;
                this.network.Consensus.RuleChangeActivationThreshold = ruleChangeActivationThreshold;
            }
        }
Пример #14
0
        public void CreateNewBlock_WithScript_ValidatesTemplateUsingRuleContext()
        {
            var newOptions = new PowConsensusOptions()
            {
                MaxBlockWeight = 1500
            };

            this.ExecuteWithConsensusOptions(newOptions, () =>
            {
                ConcurrentChain chain = GenerateChainWithHeight(5, this.network, this.key);
                this.SetupRulesEngine(chain);

                this.dateTimeProvider.Setup(d => d.GetAdjustedTimeAsUnixTimestamp())
                .Returns(new DateTime(2017, 1, 7, 0, 0, 1, DateTimeKind.Utc).ToUnixTimestamp());
                this.consensusLoop.Setup(c => c.Tip)
                .Returns(chain.GetBlock(5));

                Transaction transaction = CreateTransaction(this.network, this.key, 5, new Money(400 * 1000 * 1000), new Key(), new uint256(124124));
                var txFee = new Money(1000);
                SetupTxMempool(chain, this.network.Consensus.Options as PowConsensusOptions, txFee, transaction);
                ValidationContext validationContext = null;
                var powRuleContext = new PowRuleContext(new ValidationContext(), this.network.Consensus, chain.Tip, this.dateTimeProvider.Object.GetTimeOffset());
                this.consensusRules
                .Setup(s => s.CreateRuleContext(It.IsAny <ValidationContext>(), It.IsAny <ChainedHeader>())).Callback <ValidationContext, ChainedHeader>((r, s) => validationContext = r)
                .Returns(powRuleContext);

                var blockDefinition = new PowBlockDefinition(this.consensusLoop.Object, this.dateTimeProvider.Object, this.LoggerFactory.Object, this.txMempool.Object, new MempoolSchedulerLock(), this.network, this.consensusRules.Object);

                BlockTemplate blockTemplate = blockDefinition.Build(chain.Tip, this.key.ScriptPubKey);
                Assert.NotNull(this.callbackRuleContext);

                Assert.True(this.callbackRuleContext.MinedBlock);
                Assert.Equal(blockTemplate.Block.GetHash(), validationContext.Block.GetHash());
                Assert.Equal(chain.GetBlock(5).HashBlock, powRuleContext.ConsensusTip.HashBlock);
                Assert.Equal(1500, this.callbackRuleContext.Consensus.Option <PowConsensusOptions>().MaxBlockWeight);
                this.consensusLoop.Verify();
            });
        }
Пример #15
0
 public CheckSigOpsRuleTest()
 {
     this.options = this.network.Consensus.Option <PowConsensusOptions>();
     this.ruleContext.ValidationContext.Block = this.network.CreateBlock();
     this.ruleContext.Consensus = this.network.Consensus;
 }
 public BlockSizeRuleTest()
 {
     this.options = this.network.Consensus.Option <PowConsensusOptions>();
     this.ruleContext.Consensus = this.network.Consensus;
 }
 private bool MoneyRange(PowConsensusOptions options, long nValue)
 {
     return((nValue >= 0) && (nValue <= options.MaxMoney));
 }
 public CheckPowTransactionRuleTest()
 {
     this.consensus = this.network.Consensus;
     this.options   = this.consensus.Option <PowConsensusOptions>();
 }
Пример #19
0
 /// <summary>
 /// Gets the block weight.
 /// </summary>
 /// <remarks>
 /// This implements the <c>weight = (stripped_size * 4) + witness_size</c> formula, using only serialization with and without witness data.
 /// As witness_size is equal to total_size - stripped_size, this formula is identical to: <c>weight = (stripped_size * 3) + total_size</c>.
 /// </remarks>
 /// <param name="block">Block that we get weight of.</param>
 /// <param name="options">Options for POW networks.</param>
 /// <returns>Block weight.</returns>
 public long GetBlockWeight(Block block, PowConsensusOptions powOptions, NetworkOptions options)
 {
     return(GetSize(block, options & ~NetworkOptions.Witness) * (powOptions.WitnessScaleFactor - 1) + GetSize(block, options));
 }
        public virtual void CheckTransaction(Network network, PowConsensusOptions options, Transaction tx)
        {
            // Basic checks that don't depend on any context.
            if (tx.Inputs.Count == 0)
            {
                this.Logger.LogTrace("(-)[TX_NO_INPUT]");
                ConsensusErrors.BadTransactionNoInput.Throw();
            }

            if (tx.Outputs.Count == 0)
            {
                this.Logger.LogTrace("(-)[TX_NO_OUTPUT]");
                ConsensusErrors.BadTransactionNoOutput.Throw();
            }

            // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability).
            if (BlockSizeRule.GetSize(network, tx, TransactionOptions.None) > options.MaxBlockBaseSize)
            {
                this.Logger.LogTrace("(-)[TX_OVERSIZE]");
                ConsensusErrors.BadTransactionOversize.Throw();
            }

            // Check for negative or overflow output values
            long valueOut = 0;

            foreach (TxOut txout in tx.Outputs)
            {
                if (txout.Value.Satoshi < 0)
                {
                    this.Logger.LogTrace("(-)[TX_OUTPUT_NEGATIVE]");
                    ConsensusErrors.BadTransactionNegativeOutput.Throw();
                }

                if (txout.Value.Satoshi > options.MaxMoney)
                {
                    this.Logger.LogTrace("(-)[TX_OUTPUT_TOO_LARGE]");
                    ConsensusErrors.BadTransactionTooLargeOutput.Throw();
                }

                valueOut += txout.Value;
                if (!this.MoneyRange(options, valueOut))
                {
                    this.Logger.LogTrace("(-)[TX_TOTAL_OUTPUT_TOO_LARGE]");
                    ConsensusErrors.BadTransactionTooLargeTotalOutput.Throw();
                }
            }

            // Check for duplicate inputs.
            var inOutPoints = new HashSet <OutPoint>();

            foreach (TxIn txin in tx.Inputs)
            {
                if (inOutPoints.Contains(txin.PrevOut))
                {
                    this.Logger.LogTrace("(-)[TX_DUP_INPUTS]");
                    ConsensusErrors.BadTransactionDuplicateInputs.Throw();
                }

                inOutPoints.Add(txin.PrevOut);
            }

            if (tx.IsCoinBase)
            {
                if ((tx.Inputs[0].ScriptSig.Length < 2) || (tx.Inputs[0].ScriptSig.Length > 100))
                {
                    this.Logger.LogTrace("(-)[BAD_COINBASE_SIZE]");
                    ConsensusErrors.BadCoinbaseSize.Throw();
                }
            }
            else
            {
                foreach (TxIn txin in tx.Inputs)
                {
                    if (txin.PrevOut.IsNull)
                    {
                        this.Logger.LogTrace("(-)[TX_NULL_PREVOUT]");
                        ConsensusErrors.BadTransactionNullPrevout.Throw();
                    }
                }
            }
        }
Пример #21
0
 /// <summary>
 /// Gets the block weight.
 /// </summary>
 /// <remarks>
 /// This implements the <c>weight = (stripped_size * 4) + witness_size</c> formula, using only serialization with and without witness data.
 /// As witness_size is equal to total_size - stripped_size, this formula is identical to: <c>weight = (stripped_size * 3) + total_size</c>.
 /// </remarks>
 /// <param name="block">Block that we get weight of.</param>
 /// <param name="powOptions">The pow options.</param>
 /// <returns>Block weight.</returns>
 public long GetBlockWeight(Block block, PowConsensusOptions powOptions)
 {
     return(GetSize(this.Parent.Network, block, TransactionOptions.None) * (powOptions.WitnessScaleFactor - 1) + GetSize(this.Parent.Network, block, TransactionOptions.Witness));
 }