コード例 #1
0
        private void Unmint(IChainStateCursor chainStateCursor, Transaction tx, ChainedHeader chainedHeader)
        {
            // check that transaction exists
            UnspentTx unspentTx;

            if (!chainStateCursor.TryGetUnspentTx(tx.Hash, out unspentTx))
            {
                // missing transaction output
                logger.Warn($"Missing transaction at block {chainedHeader.Height:N0}, {chainedHeader.Hash}, tx {tx.Hash}");
                throw new ValidationException(chainedHeader.Hash);
            }

            //TODO verify blockheight

            // verify all outputs are unspent before unminting
            if (!unspentTx.OutputStates.All(x => x == OutputState.Unspent))
            {
                throw new ValidationException(chainedHeader.Hash);
            }

            // remove the transaction
            if (!chainStateCursor.TryRemoveUnspentTx(tx.Hash))
            {
                throw new ValidationException(chainedHeader.Hash);
            }

            // remove the tx outputs
            for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++)
            {
                if (!chainStateCursor.TryRemoveUnspentTxOutput(new TxOutputKey(tx.Hash, (uint)outputIndex)))
                {
                    throw new ValidationException(chainedHeader.Hash);
                }
            }
        }
コード例 #2
0
ファイル: UtxoBuilder.cs プロジェクト: sph001/BitSharp
        public UtxoBuilder(IChainStateCursor chainStateCursor, Logger logger)
        {
            this.logger = logger;
            this.sha256 = new SHA256Managed();

            this.chainStateCursor = chainStateCursor;
        }
コード例 #3
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));
        }
コード例 #4
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));
        }
コード例 #5
0
        private void CheckChainTip(IChainStateCursor chainStateCursor)
        {
            var chainTip = chainStateCursor.ChainTip;

            // verify storage chain tip matches this chain state builder's chain tip
            if (!(chainTip == null && chain.Value.Height == -1) &&
                (chainTip.Hash != chain.Value.LastBlock.Hash))
            {
                throw new ChainStateOutOfSyncException(chain.Value.LastBlock, chainTip);
            }
        }
コード例 #6
0
        public void BeginTransaction(bool readOnly)
        {
            CheckNotInTransaction();

            if (wasInTransaction)
            {
                throw new InvalidOperationException("DeferredChainStateCursor may only be used for a single transaction");
            }
            wasInTransaction = true;

            parentHandle = storageManager.OpenChainStateCursor();
            try
            {
                parentCursor = parentHandle.Item;

                parentCursor.BeginTransaction();
                inTransaction = true;

                this.chainTip           = parentCursor.ChainTip;
                this.unspentTxCount     = parentCursor.UnspentTxCount;
                this.unspentOutputCount = parentCursor.UnspentOutputCount;
                this.totalTxCount       = parentCursor.TotalTxCount;
                this.totalInputCount    = parentCursor.TotalInputCount;
                this.totalOutputCount   = parentCursor.TotalOutputCount;

                this.readOnly = readOnly;
            }
            catch (Exception ex)
            {
                inTransaction = false;

                // ensure utxo applier has completed, but do not propagate exceptions
                ((IDataflowBlock)unspentTxes.WorkQueue).Fault(ex);
                ((IDataflowBlock)utxoApplier).Fault(ex);
                try { utxoApplier.Completion.Wait(); }
                catch (Exception) { }

                ((IDataflowBlock)unspentTxOutputs.WorkQueue).Fault(ex);
                ((IDataflowBlock)utxoApplier2).Fault(ex);
                try { utxoApplier2.Completion.Wait(); }
                catch (Exception) { }

                parentHandle.Dispose();

                parentHandle = null;
                parentCursor = null;
                utxoApplier  = null;

                throw;
            }
        }
コード例 #7
0
        //TODO with the rollback information that's now being stored, rollback could be down without needing the block
        public void RollbackUtxo(IChainStateCursor chainStateCursor, Chain chain, ChainedHeader chainedHeader, IEnumerable <BlockTx> blockTxes, ImmutableList <UnmintedTx> .Builder unmintedTxes)
        {
            //TODO don't reverse here, storage should be read in reverse
            foreach (var blockTx in blockTxes.Reverse())
            {
                var tx      = blockTx.Decode().Transaction;
                var txIndex = blockTx.Index;

                // remove transaction's outputs from utxo, except for the genesis block and the duplicate coinbases
                var isDupeCoinbase = IsDupeCoinbase(chainedHeader, tx);
                if (chainedHeader.Height > 0 && !isDupeCoinbase)
                {
                    this.Unmint(chainStateCursor, tx, chainedHeader);

                    // decrease unspent output count
                    chainStateCursor.UnspentOutputCount -= tx.Outputs.Length;

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

                    chainStateCursor.TotalTxCount--;
                    chainStateCursor.TotalInputCount  -= tx.Inputs.Length;
                    chainStateCursor.TotalOutputCount -= tx.Outputs.Length;
                }

                var prevTxOutputs = ImmutableArray.CreateBuilder <PrevTxOutput>(!tx.IsCoinbase ? tx.Inputs.Length : 0);

                if (!tx.IsCoinbase)
                {
                    // remove inputs in reverse order
                    for (var inputIndex = tx.Inputs.Length - 1; inputIndex >= 0; inputIndex--)
                    {
                        var input        = tx.Inputs[inputIndex];
                        var prevTxOutput = this.Unspend(chainStateCursor, input, chainedHeader);

                        // store rollback replay information
                        prevTxOutputs.Add(prevTxOutput);
                    }
                }

                // reverse output keys to match original input order, as the inputs were read in reverse here
                prevTxOutputs.Reverse();

                // store rollback replay information
                unmintedTxes.Add(new UnmintedTx(tx.Hash, prevTxOutputs.MoveToImmutable()));
            }
        }
コード例 #8
0
        public ChainStateBuilder(Logger logger, IBlockchainRules rules, CoreStorage coreStorage)
        {
            this.logger      = logger;
            this.sha256      = new SHA256Managed();
            this.rules       = rules;
            this.coreStorage = coreStorage;

            this.blockValidator = new BlockValidator(this.coreStorage, this.rules, this.logger);

            this.chainStateCursorHandle = coreStorage.OpenChainStateCursor();
            this.chainStateCursor       = this.chainStateCursorHandle.Item;

            this.chain       = new ChainBuilder(chainStateCursor.ReadChain());
            this.utxoBuilder = new UtxoBuilder(chainStateCursor, logger);

            this.commitLock = new ReaderWriterLockSlim();

            this.stats = new BuilderStats();
        }
コード例 #9
0
ファイル: UtxoBuilder.cs プロジェクト: cole2295/BitSharp
        private void Mint(IChainStateCursor chainStateCursor, Transaction tx, int txIndex, ChainedHeader chainedHeader)
        {
            // add transaction to the utxo
            var unspentTx = new UnspentTx(tx.Hash, chainedHeader.Height, txIndex, tx.Version, tx.IsCoinbase, tx.Outputs.Length, OutputState.Unspent);
            if (!chainStateCursor.TryAddUnspentTx(unspentTx))
            {
                // duplicate transaction
                logger.Warn($"Duplicate transaction at block {chainedHeader.Height:N0}, {chainedHeader.Hash}, coinbase");
                throw new ValidationException(chainedHeader.Hash);
            }

            // add transaction outputs to the utxo
            for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++)
            {
                var output = tx.Outputs[outputIndex];
                if (!chainStateCursor.TryAddUnspentTxOutput(new TxOutputKey(tx.Hash, (uint)outputIndex), output))
                    throw new ValidationException(chainedHeader.Hash);
            }
        }
コード例 #10
0
        private void Mint(IChainStateCursor chainStateCursor, Transaction tx, int txIndex, ChainedHeader chainedHeader)
        {
            // add transaction to the utxo
            var unspentTx = new UnspentTx(tx.Hash, chainedHeader.Height, txIndex, tx.Version, tx.IsCoinbase, tx.Outputs.Length, OutputState.Unspent);

            if (!chainStateCursor.TryAddUnspentTx(unspentTx))
            {
                // duplicate transaction
                logger.Warn($"Duplicate transaction at block {chainedHeader.Height:N0}, {chainedHeader.Hash}, coinbase");
                throw new ValidationException(chainedHeader.Hash);
            }

            // add transaction outputs to the utxo
            for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++)
            {
                var output = tx.Outputs[outputIndex];
                if (!chainStateCursor.TryAddUnspentTxOutput(new TxOutputKey(tx.Hash, (uint)outputIndex), output))
                {
                    throw new ValidationException(chainedHeader.Hash);
                }
            }
        }
コード例 #11
0
ファイル: UtxoBuilder.cs プロジェクト: cole2295/BitSharp
        //TODO with the rollback information that's now being stored, rollback could be down without needing the block
        public void RollbackUtxo(IChainStateCursor chainStateCursor, Chain chain, ChainedHeader chainedHeader, IEnumerable<BlockTx> blockTxes, ImmutableList<UnmintedTx>.Builder unmintedTxes)
        {
            //TODO don't reverse here, storage should be read in reverse
            foreach (var blockTx in blockTxes.Reverse())
            {
                var tx = blockTx.Decode().Transaction;
                var txIndex = blockTx.Index;

                // remove transaction's outputs from utxo, except for the genesis block and the duplicate coinbases
                var isDupeCoinbase = IsDupeCoinbase(chainedHeader, tx);
                if (chainedHeader.Height > 0 && !isDupeCoinbase)
                {
                    this.Unmint(chainStateCursor, tx, chainedHeader);

                    // decrease unspent output count
                    chainStateCursor.UnspentOutputCount -= tx.Outputs.Length;

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

                    chainStateCursor.TotalTxCount--;
                    chainStateCursor.TotalInputCount -= tx.Inputs.Length;
                    chainStateCursor.TotalOutputCount -= tx.Outputs.Length;
                }

                var prevTxOutputs = ImmutableArray.CreateBuilder<PrevTxOutput>(!tx.IsCoinbase ? tx.Inputs.Length : 0);

                if (!tx.IsCoinbase)
                {
                    // remove inputs in reverse order
                    for (var inputIndex = tx.Inputs.Length - 1; inputIndex >= 0; inputIndex--)
                    {
                        var input = tx.Inputs[inputIndex];
                        var prevTxOutput = this.Unspend(chainStateCursor, input, chainedHeader);

                        // store rollback replay information
                        prevTxOutputs.Add(prevTxOutput);
                    }
                }

                // reverse output keys to match original input order, as the inputs were read in reverse here
                prevTxOutputs.Reverse();

                // store rollback replay information
                unmintedTxes.Add(new UnmintedTx(tx.Hash, prevTxOutputs.MoveToImmutable()));
            }
        }
コード例 #12
0
ファイル: UtxoBuilder.cs プロジェクト: cole2295/BitSharp
        private void Unmint(IChainStateCursor chainStateCursor, Transaction tx, ChainedHeader chainedHeader)
        {
            // check that transaction exists
            UnspentTx unspentTx;
            if (!chainStateCursor.TryGetUnspentTx(tx.Hash, out unspentTx))
            {
                // missing transaction output
                logger.Warn($"Missing transaction at block {chainedHeader.Height:N0}, {chainedHeader.Hash}, tx {tx.Hash}");
                throw new ValidationException(chainedHeader.Hash);
            }

            //TODO verify blockheight

            // verify all outputs are unspent before unminting
            if (!unspentTx.OutputStates.All(x => x == OutputState.Unspent))
            {
                throw new ValidationException(chainedHeader.Hash);
            }

            // remove the transaction
            if (!chainStateCursor.TryRemoveUnspentTx(tx.Hash))
            {
                throw new ValidationException(chainedHeader.Hash);
            }

            // remove the tx outputs
            for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++)
            {
                if (!chainStateCursor.TryRemoveUnspentTxOutput(new TxOutputKey(tx.Hash, (uint)outputIndex)))
                    throw new ValidationException(chainedHeader.Hash);
            }
        }
コード例 #13
0
        public ISourceBlock <ValidatableTx> CalculateUtxo(IChainStateCursor chainStateCursor, Chain chain, ISourceBlock <DecodedBlockTx> blockTxes, CancellationToken cancelToken = default(CancellationToken))
        {
            var chainedHeader  = chain.LastBlock;
            var blockSpentTxes = new BlockSpentTxesBuilder();

            var utxoCalculator = new TransformBlock <DecodedBlockTx, ValidatableTx>(
                blockTx =>
            {
                var tx      = blockTx.Transaction;
                var txIndex = blockTx.Index;

                var prevTxOutputs = ImmutableArray.CreateBuilder <PrevTxOutput>(!blockTx.IsCoinbase ? tx.Inputs.Length : 0);

                //TODO apply real coinbase rule
                // https://github.com/bitcoin/bitcoin/blob/481d89979457d69da07edd99fba451fd42a47f5c/src/core.h#L219
                if (!blockTx.IsCoinbase)
                {
                    // spend each of the transaction's inputs in the utxo
                    for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++)
                    {
                        var input        = tx.Inputs[inputIndex];
                        var prevTxOutput = this.Spend(chainStateCursor, txIndex, tx, inputIndex, input, chainedHeader, blockSpentTxes);

                        prevTxOutputs.Add(prevTxOutput);
                    }
                }

                // there exist two duplicate coinbases in the blockchain, which the design assumes to be impossible
                // ignore the first occurrences of these duplicates so that they do not need to later be deleted from the utxo, an unsupported operation
                // no other duplicates will occur again, it is now disallowed
                var isDupeCoinbase = IsDupeCoinbase(chainedHeader, tx);

                // add transaction's outputs to utxo, except for the genesis block and the duplicate coinbases
                if (chainedHeader.Height > 0 && !isDupeCoinbase)
                {
                    // mint the transaction's outputs in the utxo
                    this.Mint(chainStateCursor, tx, txIndex, chainedHeader);

                    // increase unspent output count
                    chainStateCursor.UnspentOutputCount += tx.Outputs.Length;

                    // increment unspent tx count
                    chainStateCursor.UnspentTxCount++;

                    chainStateCursor.TotalTxCount++;
                    chainStateCursor.TotalInputCount  += tx.Inputs.Length;
                    chainStateCursor.TotalOutputCount += tx.Outputs.Length;
                }

                return(new ValidatableTx(blockTx, chainedHeader, prevTxOutputs.MoveToImmutable()));
            },
                new ExecutionDataflowBlockOptions {
                CancellationToken = cancelToken
            });

            blockTxes.LinkTo(utxoCalculator, new DataflowLinkOptions {
                PropagateCompletion = true
            });

            return(OnCompleteBlock.Create(utxoCalculator, () =>
            {
                if (!chainStateCursor.TryAddBlockSpentTxes(chainedHeader.Height, blockSpentTxes.ToImmutable()))
                {
                    throw new ValidationException(chainedHeader.Hash);
                }
            }, cancelToken));
        }
コード例 #14
0
ファイル: UtxoBuilder.cs プロジェクト: cole2295/BitSharp
        public ISourceBlock<ValidatableTx> CalculateUtxo(IChainStateCursor chainStateCursor, Chain chain, ISourceBlock<DecodedBlockTx> blockTxes, CancellationToken cancelToken = default(CancellationToken))
        {
            var chainedHeader = chain.LastBlock;
            var blockSpentTxes = new BlockSpentTxesBuilder();

            var utxoCalculator = new TransformBlock<DecodedBlockTx, ValidatableTx>(
                blockTx =>
                {
                    var tx = blockTx.Transaction;
                    var txIndex = blockTx.Index;

                    var prevTxOutputs = ImmutableArray.CreateBuilder<PrevTxOutput>(!blockTx.IsCoinbase ? tx.Inputs.Length : 0);

                    //TODO apply real coinbase rule
                    // https://github.com/bitcoin/bitcoin/blob/481d89979457d69da07edd99fba451fd42a47f5c/src/core.h#L219
                    if (!blockTx.IsCoinbase)
                    {
                        // spend each of the transaction's inputs in the utxo
                        for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++)
                        {
                            var input = tx.Inputs[inputIndex];
                            var prevTxOutput = this.Spend(chainStateCursor, txIndex, tx, inputIndex, input, chainedHeader, blockSpentTxes);

                            prevTxOutputs.Add(prevTxOutput);
                        }
                    }

                    // there exist two duplicate coinbases in the blockchain, which the design assumes to be impossible
                    // ignore the first occurrences of these duplicates so that they do not need to later be deleted from the utxo, an unsupported operation
                    // no other duplicates will occur again, it is now disallowed
                    var isDupeCoinbase = IsDupeCoinbase(chainedHeader, tx);

                    // add transaction's outputs to utxo, except for the genesis block and the duplicate coinbases
                    if (chainedHeader.Height > 0 && !isDupeCoinbase)
                    {
                        // mint the transaction's outputs in the utxo
                        this.Mint(chainStateCursor, tx, txIndex, chainedHeader);

                        // increase unspent output count
                        chainStateCursor.UnspentOutputCount += tx.Outputs.Length;

                        // increment unspent tx count
                        chainStateCursor.UnspentTxCount++;

                        chainStateCursor.TotalTxCount++;
                        chainStateCursor.TotalInputCount += tx.Inputs.Length;
                        chainStateCursor.TotalOutputCount += tx.Outputs.Length;
                    }

                    return new ValidatableTx(blockTx, chainedHeader, prevTxOutputs.MoveToImmutable());
                },
                new ExecutionDataflowBlockOptions { CancellationToken = cancelToken });

            blockTxes.LinkTo(utxoCalculator, new DataflowLinkOptions { PropagateCompletion = true });

            return OnCompleteBlock.Create(utxoCalculator, () =>
                {
                    if (!chainStateCursor.TryAddBlockSpentTxes(chainedHeader.Height, blockSpentTxes.ToImmutable()))
                        throw new ValidationException(chainedHeader.Hash);
                }, cancelToken);
        }
コード例 #15
0
        public void BeginTransaction(bool readOnly)
        {
            CheckNotInTransaction();

            if (wasInTransaction)
                throw new InvalidOperationException("DeferredChainStateCursor may only be used for a single transaction");
            wasInTransaction = true;

            parentHandle = storageManager.OpenChainStateCursor();
            try
            {
                parentCursor = parentHandle.Item;

                parentCursor.BeginTransaction();
                inTransaction = true;

                this.chainTip = parentCursor.ChainTip;
                this.unspentTxCount = parentCursor.UnspentTxCount;
                this.unspentOutputCount = parentCursor.UnspentOutputCount;
                this.totalTxCount = parentCursor.TotalTxCount;
                this.totalInputCount = parentCursor.TotalInputCount;
                this.totalOutputCount = parentCursor.TotalOutputCount;

                this.readOnly = readOnly;
            }
            catch (Exception ex)
            {
                inTransaction = false;

                // ensure utxo applier has completed, but do not propagate exceptions
                ((IDataflowBlock)unspentTxes.WorkQueue).Fault(ex);
                ((IDataflowBlock)utxoApplier).Fault(ex);
                try { utxoApplier.Completion.Wait(); }
                catch (Exception) { }

                ((IDataflowBlock)unspentTxOutputs.WorkQueue).Fault(ex);
                ((IDataflowBlock)utxoApplier2).Fault(ex);
                try { utxoApplier2.Completion.Wait(); }
                catch (Exception) { }

                parentHandle.Dispose();

                parentHandle = null;
                parentCursor = null;
                utxoApplier = null;

                throw;
            }
        }
コード例 #16
0
ファイル: UtxoBuilder.cs プロジェクト: cole2295/BitSharp
        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);
        }
コード例 #17
0
        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;
                }
            }
        }
コード例 #18
0
        public void RollbackTransaction()
        {
            if (!inTransaction)
                throw new InvalidOperationException();

            parentCursor.RollbackTransaction();
            parentHandle.Dispose();

            parentHandle = null;
            parentCursor = null;
            utxoApplier = null;

            inTransaction = false;
        }
コード例 #19
0
ファイル: UtxoBuilder.cs プロジェクト: cole2295/BitSharp
        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);
        }