Esempio n. 1
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
        }
Esempio n. 2
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
        }
        /// <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));
        }
Esempio n. 4
0
		private void CanVerifySequenceLockCore(Sequence[] sequences, int[] prevHeights, int currentHeight, DateTimeOffset first, bool expected, SequenceLock expectedLock)
		{
			ConcurrentChain chain = new ConcurrentChain(new BlockHeader()
			{
				BlockTime = first
			});
			first = first + TimeSpan.FromMinutes(10);
			while(currentHeight != chain.Height)
			{
				chain.SetTip(new BlockHeader()
				{
					BlockTime = first,
					HashPrevBlock = chain.Tip.HashBlock
				});
				first = first + TimeSpan.FromMinutes(10);
			}
			Transaction tx = new Transaction();
			tx.Version = 2;
			for(int i = 0 ; i < sequences.Length ; i++)
			{
				TxIn input = new TxIn();
				input.Sequence = sequences[i];
				tx.Inputs.Add(input);
			}
			Assert.Equal(expected, tx.CheckSequenceLocks(prevHeights, chain.Tip));
			var actualLock = tx.CalculateSequenceLocks(prevHeights, chain.Tip);
			Assert.Equal(expectedLock.MinTime, actualLock.MinTime);
			Assert.Equal(expectedLock.MinHeight, actualLock.MinHeight);
		}