/// <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; }
/// <summary> /// Update the <see cref="LockPoints"/> after a reorg. /// </summary> /// <param name="lp">New lockpoints.</param> public void UpdateLockPoints(LockPoints lp) { this.LockPoints = lp; }
// Check if transaction will be BIP 68 final in the next block to be created. // Simulates calling SequenceLocks() with data from the tip of the current active chain. // Optionally stores in LockPoints the resulting height and time calculated and the hash // of the block needed for calculation or skips the calculation and uses the LockPoints // passed in for evaluation. // The LockPoints should not be considered valid if CheckSequenceLocks returns false. // See consensus/consensus.h for flag definitions. public static bool CheckSequenceLocks(ChainedBlock tip, MempoolValidationContext context, Transaction.LockTimeFlags flags, LockPoints lp = null, bool useExistingLockPoints = false) { var dummyBlock = new Block { Header = { HashPrevBlock = tip.HashBlock } }; ChainedBlock index = new ChainedBlock(dummyBlock.Header, dummyBlock.GetHash(), tip); // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate // height based locks because when SequenceLocks() is called within // ConnectBlock(), the height of the block *being* // evaluated is what is used. // Thus if we want to know if a transaction can be part of the // *next* block, we need to use one more than chainActive.Height() SequenceLock lockPair; if (useExistingLockPoints) { Guard.Assert(lp != null); lockPair = new SequenceLock(lp.Height, lp.Time); } else { // pcoinsTip contains the UTXO set for chainActive.Tip() List <int> prevheights = new List <int>(); foreach (var txin in context.Transaction.Inputs) { var coins = context.View.GetCoins(txin.PrevOut.Hash); if (coins == null) { return(false); } if (coins.Height == TxMempool.MempoolHeight) { // Assume all mempool transaction confirm in the next block prevheights.Add(tip.Height + 1); } else { prevheights.Add((int)coins.Height); } } lockPair = context.Transaction.CalculateSequenceLocks(prevheights.ToArray(), index, flags); if (lp != null) { lp.Height = lockPair.MinHeight; lp.Time = lockPair.MinTime.ToUnixTimeMilliseconds(); // Also store the hash of the block with the highest height of // all the blocks which have sequence locked prevouts. // This hash needs to still be on the chain // for these LockPoint calculations to be valid // Note: It is impossible to correctly calculate a maxInputBlock // if any of the sequence locked inputs depend on unconfirmed txs, // except in the special case where the relative lock time/height // is 0, which is equivalent to no sequence lock. Since we assume // input height of tip+1 for mempool txs and test the resulting // lockPair from CalculateSequenceLocks against tip+1. We know // EvaluateSequenceLocks will fail if there was a non-zero sequence // lock on a mempool input, so we can use the return value of // CheckSequenceLocks to indicate the LockPoints validity int maxInputHeight = 0; foreach (var height in prevheights) { // Can ignore mempool inputs since we'll fail if they had non-zero locks if (height != tip.Height + 1) { maxInputHeight = Math.Max(maxInputHeight, height); } } lp.MaxInputBlock = tip.GetAncestor(maxInputHeight); } } return(lockPair.Evaluate(index)); }