예제 #1
0
        /// <summary>
        ///     Validates that the transaction is the final transaction."/>
        ///     Validated by comparing the transaction vs chain tip.
        ///     If <see cref="CoinViewRule.StandardLocktimeVerifyFlags" /> flag is set then
        ///     use the block time at the end of the block chain for validation.
        ///     Otherwise use the current time for the block time.
        /// </summary>
        /// <param name="chainIndexer">Block chain used for computing time-locking on the transaction.</param>
        /// <param name="dateTimeProvider">Provides the current date and time.</param>
        /// <param name="tx">The transaction to validate.</param>
        /// <param name="flags">Flags for time-locking the transaction.</param>
        /// <returns>Whether the final transaction was valid.</returns>
        /// <seealso cref="Transaction.IsFinal(DateTimeOffset, int)" />
        public static bool CheckFinalTransaction(ChainIndexer chainIndexer, IDateTimeProvider dateTimeProvider,
                                                 Transaction tx, Transaction.LockTimeFlags flags)
        {
            // By convention a negative value for flags indicates that the
            // current network-enforced consensus rules should be used. In
            // a future soft-fork scenario that would mean checking which
            // rules would be enforced for the next block and setting the
            // appropriate flags. At the present time no soft-forks are
            // scheduled, so no flags are set.
            flags = (Transaction.LockTimeFlags)Math.Max((int)flags, (int)Transaction.LockTimeFlags.None);

            // CheckFinalTx() uses chainActive.Height()+1 to evaluate
            // nLockTime because when IsFinalTx() is called within
            // CBlock::AcceptBlock(), 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 call
            // IsFinalTx() with one more than chainActive.Height().
            var blockHeight = chainIndexer.Height + 1;

            // BIP113 will require that time-locked transactions have nLockTime set to
            // less than the median time of the previous block they're contained in.
            // When the next block is created its previous block will be the current
            // chain tip, so we use that to calculate the median time passed to
            // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set.
            var blockTime = flags.HasFlag(StandardLocktimeVerifyFlags)
                ? chainIndexer.Tip.Header.BlockTime
                : DateTimeOffset.FromUnixTimeMilliseconds(dateTimeProvider.GetTime());

            return(tx.IsFinal(blockTime, blockHeight));
        }
예제 #2
0
        public async Task MinerCreateBlockRelativeTimeLockedAsync()
        {
            var context = new TestContext();
            await context.InitializeAsync();

            var tx = context.network.CreateTransaction();

            tx.AddInput(new TxIn());
            tx.AddOutput(new TxOut());

            Transaction.LockTimeFlags flags = Transaction.LockTimeFlags.VerifySequence | Transaction.LockTimeFlags.MedianTimePast;

            // height map
            var prevheights = new List <int>();

            prevheights.Add(1);
            // relative time locked
            tx.Version = 2;
            tx.Inputs[0].PrevOut.Hash  = context.txFirst[1].GetHash();
            tx.Inputs[0].ScriptSig     = new Script(OpcodeType.OP_1);
            tx.Inputs[0].PrevOut.N     = 0;
            tx.Inputs[0].Sequence      = new Sequence(TimeSpan.FromMinutes(10)); // txFirst[1] is the 3rd block
            tx.Outputs[0].Value        = context.BLOCKSUBSIDY - context.HIGHFEE;
            tx.Outputs[0].ScriptPubKey = new Script(OpcodeType.OP_1);
            tx.LockTime    = 0;
            prevheights[0] = context.baseheight + 2;
            context.hash   = tx.GetHash();
            context.mempool.AddUnchecked(context.hash, context.entry.Time(context.DateTimeProvider.GetTime()).FromTx(tx));
            Assert.True(MempoolValidator.CheckFinalTransaction(context.ChainIndexer, context.DateTimeProvider, tx, flags)); // Locktime passes
            Assert.True(!this.TestSequenceLocks(context, context.ChainIndexer.Tip, tx, flags));                             // Sequence locks fail
        }
예제 #3
0
        public async Task MinerCreateBlockAbsoluteHeightLockedAsync()
        {
            var context = new TestContext();
            await context.InitializeAsync();

            var tx = new Transaction();

            tx.AddInput(new TxIn());
            tx.AddOutput(new TxOut());
            Transaction.LockTimeFlags flags = Transaction.LockTimeFlags.VerifySequence | Transaction.LockTimeFlags.MedianTimePast;

            int MedianTimeSpan = 11;
            var prevheights    = new List <int>();

            prevheights.Add(1);
            tx.Version = 2;
            tx.Inputs[0].PrevOut.Hash  = context.txFirst[1].GetHash();
            tx.Inputs[0].ScriptSig     = new Script(OpcodeType.OP_1);
            tx.Inputs[0].PrevOut.N     = 0;
            tx.Inputs[0].Sequence      = new Sequence(TimeSpan.FromMinutes(10)); // txFirst[1] is the 3rd block
            tx.Outputs[0].Value        = context.BLOCKSUBSIDY - context.HIGHFEE;
            tx.Outputs[0].ScriptPubKey = new Script(OpcodeType.OP_1);
            tx.LockTime    = 0;
            prevheights[0] = context.baseheight + 2;

            for (int i = 0; i < MedianTimeSpan; i++)
            {
                context.chain.SetTip(new BlockHeader {
                    HashPrevBlock = context.chain.Tip.HashBlock, Time = Utils.DateTimeToUnixTime(context.chain.Tip.GetMedianTimePast()) + 512
                });
            }
            SequenceLock locks = (tx.CalculateSequenceLocks(prevheights.ToArray(), context.chain.Tip, flags));

            Assert.True(locks.Evaluate(context.chain.Tip));

            context = new TestContext();
            await context.InitializeAsync();

            // absolute height locked
            tx.Inputs[0].PrevOut.Hash = context.txFirst[2].GetHash();
            tx.Inputs[0].Sequence     = Sequence.Final - 1;
            prevheights[0]            = context.baseheight + 3;
            tx.LockTime  = context.chain.Tip.Height + 1;
            context.hash = tx.GetHash();
            context.mempool.AddUnchecked(context.hash, context.entry.Time(context.DateTimeProvider.GetTime()).FromTx(tx));
            Assert.True(!MempoolValidator.CheckFinalTransaction(context.chain, context.DateTimeProvider, tx, flags)); // Locktime fails
            Assert.True(this.TestSequenceLocks(context, context.chain.Tip, tx, flags));                               // Sequence locks pass
            context.chain.SetTip(new BlockHeader {
                HashPrevBlock = context.chain.Tip.HashBlock, Time = Utils.DateTimeToUnixTime(context.chain.Tip.GetMedianTimePast()) + 512
            });
            context.chain.SetTip(new BlockHeader {
                HashPrevBlock = context.chain.Tip.HashBlock, Time = Utils.DateTimeToUnixTime(context.chain.Tip.GetMedianTimePast()) + 512
            });
            Assert.True(tx.IsFinal(context.chain.Tip.GetMedianTimePast(), context.chain.Tip.Height + 2)); // Locktime passes on 2nd block
        }
예제 #4
0
        public async Task MinerCreateBlockNonFinalTxsInMempoolAsync()
        {
            var context = new TestContext();
            await context.InitializeAsync();

            var tx = context.network.CreateTransaction();

            tx.AddInput(new TxIn());
            tx.AddOutput(new TxOut());

            // non - final txs in mempool
            (context.DateTimeProvider as DateTimeProviderSet).time = context.ChainIndexer.Tip.Header.Time + 1;
            //SetMockTime(chainActive.Tip().GetMedianTimePast() + 1);
            Transaction.LockTimeFlags flags = Transaction.LockTimeFlags.VerifySequence | Transaction.LockTimeFlags.MedianTimePast;
            // height map
            var prevheights = new List <int>();

            // relative height locked
            tx.Version = 2;
            prevheights.Add(1);
            tx.Inputs[0].PrevOut.Hash  = context.txFirst[0].GetHash(); // only 1 transaction
            tx.Inputs[0].PrevOut.N     = 0;
            tx.Inputs[0].ScriptSig     = new Script(OpcodeType.OP_1);
            tx.Inputs[0].Sequence      = new Sequence(context.ChainIndexer.Tip.Height + 1); // txFirst[0] is the 2nd block
            prevheights[0]             = context.baseheight + 1;
            tx.Outputs[0].Value        = context.BLOCKSUBSIDY - context.HIGHFEE;
            tx.Outputs[0].ScriptPubKey = new Script(OpcodeType.OP_1);
            tx.LockTime  = 0;
            context.hash = tx.GetHash();
            context.mempool.AddUnchecked(context.hash, context.entry.Fee(context.HIGHFEE).Time(context.DateTimeProvider.GetTime()).SpendsCoinbase(true).FromTx(tx));
            Assert.True(MempoolValidator.CheckFinalTransaction(context.ChainIndexer, context.DateTimeProvider, tx, flags)); // Locktime passes
            Assert.True(!this.TestSequenceLocks(context, context.ChainIndexer.Tip, tx, flags));                             // Sequence locks fail

            BlockHeader blockHeader = context.network.Consensus.ConsensusFactory.CreateBlockHeader();

            blockHeader.HashPrevBlock = context.ChainIndexer.Tip.HashBlock;
            blockHeader.Time          = Utils.DateTimeToUnixTime(context.ChainIndexer.Tip.GetMedianTimePast()) + 1;
            context.ChainIndexer.SetTip(blockHeader);

            blockHeader = context.network.Consensus.ConsensusFactory.CreateBlockHeader();
            blockHeader.HashPrevBlock = context.ChainIndexer.Tip.HashBlock;
            blockHeader.Time          = Utils.DateTimeToUnixTime(context.ChainIndexer.Tip.GetMedianTimePast()) + 1;
            context.ChainIndexer.SetTip(blockHeader);

            blockHeader = context.network.Consensus.ConsensusFactory.CreateBlockHeader();
            blockHeader.HashPrevBlock = context.ChainIndexer.Tip.HashBlock;
            blockHeader.Time          = Utils.DateTimeToUnixTime(context.ChainIndexer.Tip.GetMedianTimePast()) + 1;
            context.ChainIndexer.SetTip(blockHeader);

            SequenceLock locks = tx.CalculateSequenceLocks(prevheights.ToArray(), context.ChainIndexer.Tip, flags);

            Assert.True(locks.Evaluate(context.ChainIndexer.Tip)); // Sequence locks pass on 2nd block
        }
예제 #5
0
        public async Task MinerCreateBlockAbsoluteTimeLockedAsync()
        {
            var context = new TestContext();
            await context.InitializeAsync();

            var tx = context.network.CreateTransaction();

            tx.AddInput(new TxIn());
            tx.AddOutput(new TxOut());
            Transaction.LockTimeFlags flags = Transaction.LockTimeFlags.VerifySequence | Transaction.LockTimeFlags.MedianTimePast;

            var prevheights = new List <int>();

            prevheights.Add(1);
            tx.Version = 2;
            tx.Inputs[0].PrevOut.Hash  = context.txFirst[3].GetHash();
            tx.Inputs[0].ScriptSig     = new Script(OpcodeType.OP_1);
            tx.Inputs[0].PrevOut.N     = 0;
            tx.Outputs[0].Value        = context.BLOCKSUBSIDY - context.HIGHFEE;
            tx.Outputs[0].ScriptPubKey = new Script(OpcodeType.OP_1);

            // absolute time locked
            tx.LockTime           = context.ChainIndexer.Tip.GetMedianTimePast().AddMinutes(1);
            tx.Inputs[0].Sequence = Sequence.Final - 1;
            prevheights[0]        = context.baseheight + 4;
            context.hash          = tx.GetHash();
            context.mempool.AddUnchecked(context.hash, context.entry.Time(context.DateTimeProvider.GetTime()).FromTx(tx));
            Assert.True(!MempoolValidator.CheckFinalTransaction(context.ChainIndexer, context.DateTimeProvider, tx, flags)); // Locktime fails
            Assert.True(this.TestSequenceLocks(context, context.ChainIndexer.Tip, tx, flags));                               // Sequence locks pass

            BlockHeader blockHeader = context.network.Consensus.ConsensusFactory.CreateBlockHeader();

            blockHeader.HashPrevBlock = context.ChainIndexer.Tip.HashBlock;
            blockHeader.Time          = Utils.DateTimeToUnixTime(context.ChainIndexer.Tip.GetMedianTimePast()) + 512;
            context.ChainIndexer.SetTip(blockHeader);

            blockHeader = context.network.Consensus.ConsensusFactory.CreateBlockHeader();
            blockHeader.HashPrevBlock = context.ChainIndexer.Tip.HashBlock;
            blockHeader.Time          = Utils.DateTimeToUnixTime(context.ChainIndexer.Tip.GetMedianTimePast()) + 512;
            context.ChainIndexer.SetTip(blockHeader);

            Assert.True(tx.IsFinal(context.ChainIndexer.Tip.GetMedianTimePast().AddMinutes(2), context.ChainIndexer.Tip.Height + 2)); // Locktime passes 2 min later
        }
예제 #6
0
        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));
        }
예제 #7
0
        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));
        }
 public BIP9DeploymentFlags()
 {
     this.ScriptFlags   = ScriptVerify.None;
     this.LockTimeFlags = Transaction.LockTimeFlags.None;
 }