Beispiel #1
0
 public TxInput With(TxOutputKey previousTxOutput = null, ImmutableArray <byte>?scriptSignature = null, UInt32?sequence = null)
 {
     return(new TxInput
            (
                previousTxOutput ?? this.PreviousTxOutputKey,
                scriptSignature ?? this.ScriptSignature,
                sequence ?? this.Sequence
            ));
 }
Beispiel #2
0
 public TxInput With(TxOutputKey previousTxOutput = null, ImmutableArray<byte>? scriptSignature = null, UInt32? sequence = null)
 {
     return new TxInput
     (
         previousTxOutput ?? this.PreviousTxOutputKey,
         scriptSignature ?? this.ScriptSignature,
         sequence ?? this.Sequence
     );
 }
        private ImmutableDictionary<UInt256, UnspentTx> RollbackUtxo(Data.Blockchain blockchain, Block block, out List<TxOutputKey> spendOutputs, out List<TxOutputKey> receiveOutputs)
        {
            var blockHeight = blockchain.Height;
            var currentUtxo = blockchain.Utxo;

            // create builder for prev utxo
            var prevUtxoBuilder = currentUtxo.ToBuilder();
            spendOutputs = new List<TxOutputKey>();
            receiveOutputs = new List<TxOutputKey>();

            //TODO apply real coinbase rule
            // https://github.com/bitcoin/bitcoin/blob/481d89979457d69da07edd99fba451fd42a47f5c/src/core.h#L219
            var coinbaseTx = block.Transactions[0];

            for (var outputIndex = 0; outputIndex < coinbaseTx.Outputs.Length; outputIndex++)
            {
                var txOutputKey = new TxOutputKey(coinbaseTx.Hash, (UInt32)outputIndex);
                if (blockHeight > 0)
                {
                    // remove new outputs from the rolled back utxo
                    if (prevUtxoBuilder.Remove(coinbaseTx.Hash))
                    {
                        receiveOutputs.Add(txOutputKey);
                    }
                    else
                    {
                        // missing transaction output
                        Debug.WriteLine("Missing transaction at block {0:#,##0}, {1}, tx {2}, output {3}".Format2(blockHeight, block.Hash.ToHexNumberString(), 0, outputIndex));
                        Debugger.Break();
                        //TODO throw new Validation();

                        //TODO this needs to be tracked so that blocks can be rolled back accurately
                        //TODO track these separately on the blockchain info? gonna be costly to track on every transaction
                    }
                }
            }

            for (var txIndex = block.Transactions.Length - 1; txIndex >= 1; txIndex--)
            {
                var tx = block.Transactions[txIndex];

                for (var outputIndex = tx.Outputs.Length - 1; outputIndex >= 0; outputIndex--)
                {
                    var output = tx.Outputs[outputIndex];
                    var txOutputKey = new TxOutputKey(tx.Hash, (UInt32)outputIndex);
                    //TODO what if a transaction wasn't added to the utxo because it already existed?
                    //TODO the block would still pass without adding the tx to its utxo, but here it would get rolled back
                    //TODO maybe a flag bit to track this?

                    // remove new outputs from the rolled back utxo
                    if (prevUtxoBuilder.Remove(tx.Hash))
                    {
                        receiveOutputs.Add(txOutputKey);
                    }
                    else
                    {
                        // missing transaction output
                        Debug.WriteLine("Missing transaction at block {0:#,##0}, {1}, tx {2}, output {3}".Format2(blockHeight, block.Hash.ToHexNumberString(), txIndex, outputIndex));
                        Debugger.Break();
                        //TODO throw new Validation();

                        //TODO this needs to be tracked so that blocks can be rolled back accurately
                        //TODO track these separately on the blockchain info? gonna be costly to track on every transaction
                    }
                }

                for (var inputIndex = tx.Inputs.Length - 1; inputIndex >= 0; inputIndex--)
                {
                    var input = tx.Inputs[inputIndex];

                    // add spent outputs back into the rolled back utxo
                    if (prevUtxoBuilder.ContainsKey(input.PreviousTxOutputKey.TxHash))
                    {
                        var prevUnspentTx = prevUtxoBuilder[input.PreviousTxOutputKey.TxHash];

                        // check if output is out of bounds
                        if (input.PreviousTxOutputKey.TxOutputIndex >= prevUnspentTx.UnspentOutputs.Length)
                            throw new ValidationException();

                        // check that output isn't already considered unspent
                        if (prevUnspentTx.UnspentOutputs[input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked()])
                            throw new ValidationException();

                        // mark output as unspent
                        prevUtxoBuilder[input.PreviousTxOutputKey.TxHash] =
                            new UnspentTx(prevUnspentTx.BlockHash, prevUnspentTx.TxIndex, prevUnspentTx.TxHash,
                                prevUnspentTx.UnspentOutputs.Set(input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked(), true));
                    }
                    else
                    {
                        // fully spent transaction being added back in during roll back
                        //TODO
                        throw new NotImplementedException();
                    }

                    //TODO
                    //if (prevUtxoBuilder.Add(input.PreviousTxOutputKey))
                    //{
                    //    spendOutputs.Add(input.PreviousTxOutputKey);
                    //}
                    //else
                    //{
                    //    // missing transaction output
                    //    Debug.WriteLine("Duplicate transaction at block {0:#,##0}, {1}, tx {2}, input {3}".Format2(blockHeight, block.Hash.ToHexNumberString(), txIndex, inputIndex));
                    //    Debugger.Break();
                    //    //TODO throw new Validation();

                    //    //TODO this needs to be tracked so that blocks can be rolled back accurately
                    //    //TODO track these separately on the blockchain info? gonna be costly to track on every transaction
                    //}
                }
            }

            return prevUtxoBuilder.ToImmutable();
        }
Beispiel #4
0
 public TxInput(TxOutputKey previousTxOutputKey, ImmutableArray <byte> scriptSignature, UInt32 sequence)
 {
     this._previousTxOutputKey = previousTxOutputKey;
     this._scriptSignature     = scriptSignature;
     this._sequence            = sequence;
 }
Beispiel #5
0
        public Data.Blockchain ReadBlockchain(BlockchainKey blockchainKey)
        {
            CheckDatabaseFolder();

            var blockListBuilder = ImmutableList.CreateBuilder<ChainedBlock>();
            var utxoBuilder = ImmutableDictionary.CreateBuilder<UInt256, UnspentTx>();

            var connString = @"Server=localhost; Database=BitSharp_Blockchains; Trusted_Connection=true;";
            using (var conn = new SqlConnection(connString))
            {
                conn.Open();

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = @"
                        SELECT BlockHash, PreviousBlockHash, Height, TotalWork
                        FROM ChainedBlocks
                        WHERE Guid = @guid AND RootBlockHash = @rootBlockHash
                        ORDER BY Height ASC";

                    cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value = blockchainKey.Guid.ToByteArray();
                    cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();

                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var blockHash = reader.GetUInt256(0);
                            var previousBlockHash = reader.GetUInt256(1);
                            var height = reader.GetInt32(2);
                            var totalWork = reader.GetBigInteger(3);

                            blockListBuilder.Add(new ChainedBlock(blockHash, previousBlockHash, height, totalWork));
                        }
                    }
                }

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = @"
                        SELECT UtxoChunkBytes
                        FROM UtxoData
                        WHERE Guid = @guid AND RootBlockHash = @rootBlockHash";

                    cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value = blockchainKey.Guid.ToByteArray();
                    cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();

                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var chunkBytes = reader.GetBytes(0);
                            var chunkStream = new MemoryStream(chunkBytes);
                            using (var chunkReader = new BinaryReader(chunkStream))
                            {
                                var chunkLength = chunkReader.Read4Bytes().ToIntChecked();

                                var outputs = new TxOutputKey[chunkLength];

                                for (var i = 0; i < chunkLength; i++)
                                {
                                    var prevTxHash = chunkReader.Read32Bytes();
                                    var prevTxOutputIndex = chunkReader.Read4Bytes();

                                    outputs[i] = new TxOutputKey(prevTxHash, prevTxOutputIndex);
                                }

                                //TODO utxoBuilder.UnionWith(outputs);
                            }
                        }
                    }
                }
            }

            return new Data.Blockchain(blockListBuilder.ToImmutable(), blockListBuilder.Select(x => x.BlockHash).ToImmutableHashSet(), utxoBuilder.ToImmutable());
        }
Beispiel #6
0
        private Transaction GetPreviousTransaction(Block block, int txIndex, TxOutputKey prevTxOutputKey, ImmutableDictionary<UInt256, UnspentTx> utxo, ImmutableDictionary<UInt256, ImmutableHashSet<int>> newTransactions)
        {
            if (newTransactions.ContainsKey(prevTxOutputKey.TxHash))
            {
                var eligible = newTransactions[prevTxOutputKey.TxHash].Where(x => x < txIndex).ToList();
                if (eligible.Count > 0)
                {
                    var max = eligible.Max();

                    if (max >= block.Transactions.Length)
                        throw new Exception();

                    var prevTx1 = block.Transactions[max];
                    if (prevTx1.Hash != prevTxOutputKey.TxHash)
                        throw new Exception();

                    return prevTx1;
                }
            }

            // find previous transaction
            if (!utxo.ContainsKey(prevTxOutputKey.TxHash))
                throw new MissingDataException(DataType.Transaction, prevTxOutputKey.TxHash);

            var prevTxKey = utxo[prevTxOutputKey.TxHash].ToTxKey();

            var prevTx2 = this.CacheContext.GetTransaction(prevTxKey);
            if (prevTx2.Hash != prevTxOutputKey.TxHash)
                throw new Exception();

            return prevTx2;
        }
Beispiel #7
0
 public TxInput(TxOutputKey previousTxOutputKey, ImmutableArray<byte> scriptSignature, UInt32 sequence)
 {
     this._previousTxOutputKey = previousTxOutputKey;
     this._scriptSignature = scriptSignature;
     this._sequence = sequence;
 }
Beispiel #8
0
        public Data.Blockchain ReadBlockchain(BlockchainKey blockchainKey)
        {
            CheckDatabaseFolder();

            var blockListBuilder = ImmutableList.CreateBuilder<ChainedBlock>();
            var utxoBuilder = ImmutableHashSet.CreateBuilder<TxOutputKey>();

            var dbPath = GetDatabasePath(blockchainKey.Guid);
            var connString = @"ServerType=1; DataSource=localhost; Database={0}; Pooling=false; User=SYSDBA; Password=NA;".Format2(dbPath);
            using (var conn = new FbConnection(connString))
            {
                conn.Open();

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = @"
                        SELECT BlockHash, PreviousBlockHash, Height, TotalWork
                        FROM ChainedBlocks
                        WHERE Guid = @guid AND RootBlockHash = @rootBlockHash
                        ORDER BY Height ASC";

                    cmd.Parameters.SetValue("@guid", FbDbType.Char, FbCharset.Octets, 16).Value = blockchainKey.Guid.ToByteArray();
                    cmd.Parameters.SetValue("@rootBlockHash", FbDbType.Char, FbCharset.Octets, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();

                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var blockHash = reader.GetUInt256(0);
                            var previousBlockHash = reader.GetUInt256(1);
                            var height = reader.GetInt32(2);
                            var totalWork = reader.GetBigInteger(3);

                            blockListBuilder.Add(new ChainedBlock(blockHash, previousBlockHash, height, totalWork));
                        }
                    }
                }

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = @"
                        SELECT UtxoChunkBytes
                        FROM UtxoData
                        WHERE Guid = @guid AND RootBlockHash = @rootBlockHash";

                    cmd.Parameters.SetValue("@guid", FbDbType.Char, FbCharset.Octets, 16).Value = blockchainKey.Guid.ToByteArray();
                    cmd.Parameters.SetValue("@rootBlockHash", FbDbType.Char, FbCharset.Octets, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();

                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var chunkBytes = reader.GetBytes(0);
                            var chunkStream = new MemoryStream(chunkBytes);
                            using (var chunkReader = new BinaryReader(chunkStream))
                            {
                                var chunkLength = chunkReader.Read4Bytes().ToIntChecked();

                                var outputs = new TxOutputKey[chunkLength];

                                for (var i = 0; i < chunkLength; i++)
                                {
                                    var prevTxHash = chunkReader.Read32Bytes();
                                    var prevTxOutputIndex = chunkReader.Read4Bytes();

                                    outputs[i] = new TxOutputKey(prevTxHash, prevTxOutputIndex);
                                }

                                utxoBuilder.UnionWith(outputs);
                            }
                        }
                    }
                }
            }

            return new Data.Blockchain(blockListBuilder.ToImmutable(), blockListBuilder.Select(x => x.BlockHash).ToImmutableHashSet(), utxoBuilder.ToImmutable());
        }