private TxMempoolEntry[] SetupTxMempool(ConcurrentChain chain, PosConsensusOptions newOptions, Money txFee, params Transaction[] transactions) { var 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 (var 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.mempool.Setup(t => t.MapTx) .Returns(indexedTransactionSet); return(resultingTransactionEntries.ToArray()); }
public bool TestSequenceLocks(TestContext testContext, ChainedHeader chainedHeader, Transaction tx, Transaction.LockTimeFlags flags, LockPoints uselock = null) { var context = new MempoolValidationContext(tx, new MempoolValidationState(false)); context.View = new MempoolCoinView(this.network, testContext.cachedCoinView, testContext.mempool, testContext.mempoolLock, null); testContext.mempoolLock.ReadAsync(() => context.View.LoadViewLocked(tx)).GetAwaiter().GetResult(); return(CreateMempoolEntryMempoolRule.CheckSequenceLocks(testContext.network, chainedHeader, context, flags, uselock, false)); }
public bool TestSequenceLocks(TestContext testContext, ChainedBlock chainedBlock, Transaction tx, Transaction.LockTimeFlags flags, LockPoints uselock = null) { var context = new MempoolValidationContext(tx, new MempoolValidationState(false)); context.View = new MempoolCoinView(testContext.cachedCoinView, testContext.mempool, testContext.mempoolLock, null); context.View.LoadViewAsync(tx).GetAwaiter().GetResult(); return(MempoolValidator.CheckSequenceLocks(testContext.network, chainedBlock, context, flags, uselock, false)); }
/// <summary> /// 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. /// </summary> /// <param name="network">The blockchain network.</param> /// <param name="tip">Tip of the chain.</param> /// <param name="context">Validation context for the memory pool.</param> /// <param name="flags">Transaction lock time flags.</param> /// <param name="lp">Optional- existing lock points to use, and update during evaluation.</param> /// <param name="useExistingLockPoints">Whether to use the existing lock points during evaluation.</param> /// <returns>Whether sequence lock validated.</returns> /// <seealso cref="SequenceLock.Evaluate(ChainedHeader)"/> public static bool CheckSequenceLocks(Network network, ChainedHeader tip, MempoolValidationContext context, Transaction.LockTimeFlags flags, LockPoints lp = null, bool useExistingLockPoints = false) { Block dummyBlock = network.Consensus.ConsensusFactory.CreateBlock(); dummyBlock.Header.HashPrevBlock = tip.HashBlock; var index = new ChainedHeader(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() var prevheights = new List <int>(); foreach (TxIn txin in context.Transaction.Inputs) { UnspentOutput unspentOutput = context.View.Set.AccessCoins(txin.PrevOut); if (unspentOutput?.Coins == null) { return(false); } if (unspentOutput.Coins.Height == TxMempool.MempoolHeight) { // Assume all mempool transaction confirm in the next block prevheights.Add(tip.Height + 1); } else { prevheights.Add((int)unspentOutput.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 (int 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)); }