public DeferredChainStateCursor(IChainState chainState, IStorageManager storageManager)
        {
            this.chainState = chainState;
            this.storageManager = storageManager;

            UnspentOutputCount = chainState.UnspentOutputCount;
            UnspentTxCount = chainState.UnspentTxCount;
            TotalTxCount = chainState.TotalTxCount;
            TotalInputCount = chainState.TotalInputCount;
            TotalOutputCount = chainState.TotalOutputCount;

            headers = new DeferredDictionary<UInt256, ChainedHeader>(
                blockHash =>
                {
                    ChainedHeader header;
                    return Tuple.Create(chainState.TryGetHeader(blockHash, out header), header);
                });

            unspentTxes = new WorkQueueDictionary<UInt256, UnspentTx>(
                txHash =>
                {
                    UnspentTx unspentTx;
                    return Tuple.Create(chainState.TryGetUnspentTx(txHash, out unspentTx), unspentTx);
                });

            blockSpentTxes = new DeferredDictionary<int, BlockSpentTxes>(
                blockHeight =>
                {
                    BlockSpentTxes spentTxes;
                    return Tuple.Create(chainState.TryGetBlockSpentTxes(blockHeight, out spentTxes), spentTxes);
                });

            blockUnmintedTxes = new DeferredDictionary<UInt256, IImmutableList<UnmintedTx>>(
                blockHash =>
                {
                    IImmutableList<UnmintedTx> unmintedTxes;
                    return Tuple.Create(chainState.TryGetBlockUnmintedTxes(blockHash, out unmintedTxes), unmintedTxes);
                });
        }
        public DeferredChainStateCursor(IChainState chainState, IStorageManager storageManager)
        {
            this.chainState = chainState;
            this.storageManager = storageManager;

            headers = new DeferredDictionary<UInt256, ChainedHeader>(
                blockHash =>
                {
                    ChainedHeader header;
                    return Tuple.Create(chainState.TryGetHeader(blockHash, out header), header);
                });

            unspentTxes = new WorkQueueDictionary<UInt256, UnspentTx>(
                txHash =>
                {
                    UnspentTx unspentTx;
                    return Tuple.Create(chainState.TryGetUnspentTx(txHash, out unspentTx), unspentTx);
                });

            unspentTxOutputs = new WorkQueueDictionary<TxOutputKey, TxOutput>(
                txOutputKey =>
                {
                    TxOutput txOutput;
                    return Tuple.Create(chainState.TryGetUnspentTxOutput(txOutputKey, out txOutput), txOutput);
                });

            blockSpentTxes = new DeferredDictionary<int, BlockSpentTxes>(
                blockHeight =>
                {
                    BlockSpentTxes spentTxes;
                    return Tuple.Create(chainState.TryGetBlockSpentTxes(blockHeight, out spentTxes), spentTxes);
                });

            blockUnmintedTxes = new DeferredDictionary<UInt256, IImmutableList<UnmintedTx>>(
                blockHash =>
                {
                    IImmutableList<UnmintedTx> unmintedTxes;
                    return Tuple.Create(chainState.TryGetBlockUnmintedTxes(blockHash, out unmintedTxes), unmintedTxes);
                });

            utxoApplier = new ActionBlock<WorkQueueDictionary<UInt256, UnspentTx>.WorkItem>(
                workItem =>
                {
                    workItem.Consume(
                        (operation, unspentTxHash, unspentTx) =>
                        {
                            lock (parentCursor)
                                switch (operation)
                                {
                                    case WorkQueueOperation.Nothing:
                                        break;

                                    case WorkQueueOperation.Add:
                                        if (!parentCursor.TryAddUnspentTx(unspentTx))
                                            throw new InvalidOperationException();
                                        break;

                                    case WorkQueueOperation.Update:
                                        if (!parentCursor.TryUpdateUnspentTx(unspentTx))
                                            throw new InvalidOperationException();
                                        break;

                                    case WorkQueueOperation.Remove:
                                        if (!parentCursor.TryRemoveUnspentTx(unspentTxHash))
                                            throw new InvalidOperationException();
                                        break;

                                    default:
                                        throw new InvalidOperationException();
                                }
                        });
                });

            unspentTxes.WorkQueue.LinkTo(utxoApplier, new DataflowLinkOptions { PropagateCompletion = true });

            utxoApplier2 = new ActionBlock<WorkQueueDictionary<TxOutputKey, TxOutput>.WorkItem>(
                workItem =>
                {
                    workItem.Consume(
                        (operation, txOutputKey, txOutput) =>
                        {
                            lock (parentCursor)
                                switch (operation)
                                {
                                    case WorkQueueOperation.Nothing:
                                        break;

                                    case WorkQueueOperation.Add:
                                        if (!parentCursor.TryAddUnspentTxOutput(txOutputKey, txOutput))
                                            throw new InvalidOperationException();
                                        break;

                                    case WorkQueueOperation.Update:
                                        throw new InvalidOperationException();

                                    case WorkQueueOperation.Remove:
                                        if (!parentCursor.TryRemoveUnspentTxOutput(txOutputKey))
                                            throw new InvalidOperationException();
                                        break;

                                    default:
                                        throw new InvalidOperationException();
                                }
                        });
                });

            unspentTxOutputs.WorkQueue.LinkTo(utxoApplier2, new DataflowLinkOptions { PropagateCompletion = true });
        }