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