BitSharp.Core.Builders.UnconfirmedTxesBuilder.AddBlock C# (CSharp) Method

AddBlock() public method

public AddBlock ( ChainedHeader chainedHeader, IEnumerable blockTxes, CancellationToken cancelToken = default(CancellationToken) ) : void
chainedHeader BitSharp.Core.Domain.ChainedHeader
blockTxes IEnumerable
cancelToken System.Threading.CancellationToken
return void
        public void AddBlock(ChainedHeader chainedHeader, IEnumerable<BlockTx> blockTxes, CancellationToken cancelToken = default(CancellationToken))
        {
            var confirmedTxes = ImmutableDictionary.CreateBuilder<UInt256, UnconfirmedTx>();

            updateLock.DoWrite(() =>
            {
                using (var handle = storageManager.OpenUnconfirmedTxesCursor())
                {
                    var unconfirmedTxesCursor = handle.Item;

                    unconfirmedTxesCursor.BeginTransaction();

                    var newChain = chain.Value.ToBuilder().AddBlock(chainedHeader).ToImmutable();

                    foreach (var blockTx in blockTxes)
                    {
                        // remove any txes confirmed in the block from the list of unconfirmed txes
                        UnconfirmedTx unconfirmedTx;
                        if (unconfirmedTxesCursor.TryGetTransaction(blockTx.Hash, out unconfirmedTx))
                        {
                            // track confirmed txes
                            confirmedTxes.Add(unconfirmedTx.Hash, unconfirmedTx);

                            if (!unconfirmedTxesCursor.TryRemoveTransaction(blockTx.Hash))
                                throw new InvalidOperationException();
                        }

                        // check for and remove any unconfirmed txes that conflict with the confirmed tx
                        var confirmedTx = blockTx.EncodedTx.Decode().Transaction;
                        foreach (var input in confirmedTx.Inputs)
                        {
                            var conflictingTxes = unconfirmedTxesCursor.GetTransactionsSpending(input.PrevTxOutputKey);
                            if (conflictingTxes.Count > 0)
                            {
                                logger.Warn($"Removing {conflictingTxes.Count} conflicting txes from the unconfirmed transaction pool");

                                // remove the conflicting unconfirmed txes
                                foreach (var conflictingTx in conflictingTxes.Keys)
                                    if (!unconfirmedTxesCursor.TryRemoveTransaction(conflictingTx))
                                        throw new StorageCorruptException(StorageType.UnconfirmedTxes, $"{conflictingTx} is indexed but not present");
                            }
                        }
                    }

                    unconfirmedTxesCursor.ChainTip = chainedHeader;

                    commitLock.DoWrite(() =>
                    {
                        unconfirmedTxesCursor.CommitTransaction();
                        chain = new Lazy<Chain>(() => newChain).Force();
                    });
                }
            });

            TxesConfirmed?.Invoke(this, new TxesConfirmedEventArgs(chainedHeader, confirmedTxes.ToImmutable()));
        }

Usage Example

        public void TestAddBlockConfirmingTx()
        {
            // create tx spending a previous output that exists
            var decodedTx = Transaction.Create(
                0,
                ImmutableArray.Create(new TxInput(UInt256.One, 0, ImmutableArray<byte>.Empty, 0)),
                ImmutableArray.Create(new TxOutput(0, ImmutableArray<byte>.Empty)),
                0);
            var tx = decodedTx.Transaction;

            // create prev output tx
            var unspentTx = new UnspentTx(tx.Inputs[0].PrevTxHash, 0, 1, 0, false, new OutputStates(1, OutputState.Unspent));
            var txOutput = new TxOutput(0, ImmutableArray<byte>.Empty);

            // create a fake chain
            var fakeHeaders = new FakeHeaders();
            var genesisHeader = fakeHeaders.GenesisChained();

            // create a block confirming the tx
            var block = Block.Create(RandomData.RandomBlockHeader().With(PreviousBlock: genesisHeader.Hash), ImmutableArray.Create(tx));
            var chainedHeader = new ChainedHeader(block.Header, 1, 0, DateTimeOffset.Now);

            // mock core storage with chained header
            var coreStorage = new Mock<ICoreStorage>();
            var initialChain = new ChainBuilder().ToImmutable();
            coreStorage.Setup(x => x.TryReadChain(null, out initialChain)).Returns(true);
            coreStorage.Setup(x => x.TryGetChainedHeader(chainedHeader.Hash, out chainedHeader)).Returns(true);

            // mock chain state with prev output
            var chainState = new Mock<IChainState>();
            chainState.Setup(x => x.TryGetUnspentTx(tx.Inputs[0].PrevTxHash, out unspentTx)).Returns(true);
            chainState.Setup(x => x.TryGetUnspentTxOutput(tx.Inputs[0].PrevTxOutputKey, out txOutput)).Returns(true);

            // mock core daemon for chain state retrieval
            var coreDaemon = new Mock<ICoreDaemon>();
            coreDaemon.Setup(x => x.GetChainState()).Returns(chainState.Object);

            using (var unconfirmedTxesBuilder = new UnconfirmedTxesBuilder(coreDaemon.Object, coreStorage.Object, storageManager))
            {
                // add the tx
                Assert.IsTrue(unconfirmedTxesBuilder.TryAddTransaction(decodedTx));

                // add the block
                unconfirmedTxesBuilder.AddBlock(genesisHeader, Enumerable.Empty<BlockTx>());
                unconfirmedTxesBuilder.AddBlock(chainedHeader, block.BlockTxes);

                // verify the confirmed tx was removed
                UnconfirmedTx unconfirmedTx;
                Assert.IsFalse(unconfirmedTxesBuilder.TryGetTransaction(tx.Hash, out unconfirmedTx));
                Assert.IsNull(unconfirmedTx);

                // verify the confirmed tx was de-indexed against its input
                Assert.AreEqual(0, unconfirmedTxesBuilder.GetTransactionsSpending(tx.Inputs[0].PrevTxOutputKey).Count);
            }
        }