예제 #1
0
        private PrevTxOutput Unspend(IChainStateCursor chainStateCursor, TxInput input, ChainedHeader chainedHeader)
        {
            UnspentTx unspentTx;

            if (!chainStateCursor.TryGetUnspentTx(input.PrevTxOutputKey.TxHash, out unspentTx))
            {
                // unable to rollback, the unspent tx has been pruned
                //TODO better exception
                throw new InvalidOperationException();
            }

            // retrieve previous output index
            var outputIndex = unchecked ((int)input.PrevTxOutputKey.TxOutputIndex);

            if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length)
            {
                throw new Exception("TODO - corruption");
            }

            // check that output isn't already considered unspent
            if (unspentTx.OutputStates[outputIndex] == OutputState.Unspent)
            {
                throw new ValidationException(chainedHeader.Hash);
            }

            var wasFullySpent = unspentTx.IsFullySpent;

            // mark output as unspent
            unspentTx = unspentTx.SetOutputState(outputIndex, OutputState.Unspent);

            // increment unspent output count
            chainStateCursor.UnspentOutputCount++;

            // update storage
            var wasUpdated = chainStateCursor.TryUpdateUnspentTx(unspentTx);

            if (!wasUpdated)
            {
                throw new ValidationException(chainedHeader.Hash);
            }

            // increment unspent tx count
            if (wasFullySpent)
            {
                chainStateCursor.UnspentTxCount++;
            }

            TxOutput txOutput;

            if (!chainStateCursor.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput))
            {
                // output missing
                throw new ValidationException(chainedHeader.Hash);
            }

            return(new PrevTxOutput(txOutput, unspentTx));
        }
예제 #2
0
        private PrevTxOutput Spend(IChainStateCursor chainStateCursor, int txIndex, Transaction tx, int inputIndex, TxInput input, ChainedHeader chainedHeader, BlockSpentTxesBuilder blockSpentTxes)
        {
            UnspentTx unspentTx;

            if (!chainStateCursor.TryGetUnspentTx(input.PrevTxOutputKey.TxHash, out unspentTx))
            {
                // output wasn't present in utxo, invalid block
                throw new ValidationException(chainedHeader.Hash);
            }

            var outputIndex = unchecked ((int)input.PrevTxOutputKey.TxOutputIndex);

            if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length)
            {
                // output was out of bounds
                throw new ValidationException(chainedHeader.Hash);
            }

            if (unspentTx.OutputStates[outputIndex] == OutputState.Spent)
            {
                // output was already spent
                throw new ValidationException(chainedHeader.Hash);
            }

            // update output states
            unspentTx = unspentTx.SetOutputState(outputIndex, OutputState.Spent);

            // decrement unspent output count
            chainStateCursor.UnspentOutputCount--;

            // update transaction output states in the utxo
            var wasUpdated = chainStateCursor.TryUpdateUnspentTx(unspentTx);

            if (!wasUpdated)
            {
                throw new ValidationException(chainedHeader.Hash);
            }

            // store pruning information for a fully spent transaction
            if (unspentTx.IsFullySpent)
            {
                blockSpentTxes.AddSpentTx(unspentTx.ToSpentTx());

                // decrement unspent tx count
                chainStateCursor.UnspentTxCount--;
            }

            TxOutput txOutput;

            if (!chainStateCursor.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput))
            {
                // output missing
                throw new ValidationException(chainedHeader.Hash);
            }

            return(new PrevTxOutput(txOutput, unspentTx));
        }
예제 #3
0
        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
            });
        }
        public void BeginTransaction(bool readOnly, bool pruneOnly)
        {
            if (inTransaction)
                throw new InvalidOperationException();

            parentHandle = storageManager.OpenChainStateCursor();
            parentCursor = parentHandle.Item;
            changesApplied = false;

            try
            {
                parentCursor.BeginTransaction();

                this.ChainTip = parentCursor.ChainTip;
                this.UnspentTxCount = UnspentTxCount;
                this.UnspentOutputCount = UnspentOutputCount;
                this.TotalTxCount = TotalTxCount;
                this.TotalInputCount = TotalInputCount;
                this.TotalOutputCount = TotalOutputCount;

                utxoApplier = new ActionBlock<WorkQueueDictionary<UInt256, UnspentTx>.WorkItem>(
                    workItem =>
                    {
                        workItem.Consume(
                            (operation, unspentTxHash, unspentTx) =>
                            {
                                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 });

                inTransaction = true;
            }
            finally
            {
                if (!inTransaction)
                {
                    parentHandle.Dispose();

                    parentHandle = null;
                    parentCursor = null;
                    utxoApplier = null;
                }
            }
        }
예제 #5
0
        private PrevTxOutput Unspend(IChainStateCursor chainStateCursor, TxInput input, ChainedHeader chainedHeader)
        {
            UnspentTx unspentTx;
            if (!chainStateCursor.TryGetUnspentTx(input.PrevTxOutputKey.TxHash, out unspentTx))
            {
                // unable to rollback, the unspent tx has been pruned
                //TODO better exception
                throw new InvalidOperationException();
            }

            // retrieve previous output index
            var outputIndex = unchecked((int)input.PrevTxOutputKey.TxOutputIndex);
            if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length)
                throw new Exception("TODO - corruption");

            // check that output isn't already considered unspent
            if (unspentTx.OutputStates[outputIndex] == OutputState.Unspent)
                throw new ValidationException(chainedHeader.Hash);

            var wasFullySpent = unspentTx.IsFullySpent;

            // mark output as unspent
            unspentTx = unspentTx.SetOutputState(outputIndex, OutputState.Unspent);

            // increment unspent output count
            chainStateCursor.UnspentOutputCount++;

            // update storage
            var wasUpdated = chainStateCursor.TryUpdateUnspentTx(unspentTx);
            if (!wasUpdated)
                throw new ValidationException(chainedHeader.Hash);

            // increment unspent tx count
            if (wasFullySpent)
                chainStateCursor.UnspentTxCount++;

            TxOutput txOutput;
            if (!chainStateCursor.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput))
                // output missing
                throw new ValidationException(chainedHeader.Hash);

            return new PrevTxOutput(txOutput, unspentTx);
        }
예제 #6
0
        private PrevTxOutput Spend(IChainStateCursor chainStateCursor, int txIndex, Transaction tx, int inputIndex, TxInput input, ChainedHeader chainedHeader, BlockSpentTxesBuilder blockSpentTxes)
        {
            UnspentTx unspentTx;
            if (!chainStateCursor.TryGetUnspentTx(input.PrevTxOutputKey.TxHash, out unspentTx))
            {
                // output wasn't present in utxo, invalid block
                throw new ValidationException(chainedHeader.Hash);
            }

            var outputIndex = unchecked((int)input.PrevTxOutputKey.TxOutputIndex);

            if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length)
            {
                // output was out of bounds
                throw new ValidationException(chainedHeader.Hash);
            }

            if (unspentTx.OutputStates[outputIndex] == OutputState.Spent)
            {
                // output was already spent
                throw new ValidationException(chainedHeader.Hash);
            }

            // update output states
            unspentTx = unspentTx.SetOutputState(outputIndex, OutputState.Spent);

            // decrement unspent output count
            chainStateCursor.UnspentOutputCount--;

            // update transaction output states in the utxo
            var wasUpdated = chainStateCursor.TryUpdateUnspentTx(unspentTx);
            if (!wasUpdated)
                throw new ValidationException(chainedHeader.Hash);

            // store pruning information for a fully spent transaction
            if (unspentTx.IsFullySpent)
            {
                blockSpentTxes.AddSpentTx(unspentTx.ToSpentTx());

                // decrement unspent tx count
                chainStateCursor.UnspentTxCount--;
            }

            TxOutput txOutput;
            if (!chainStateCursor.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput))
                // output missing
                throw new ValidationException(chainedHeader.Hash);

            return new PrevTxOutput(txOutput, unspentTx);
        }