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 )); }
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(); }
public TxInput(TxOutputKey previousTxOutputKey, ImmutableArray <byte> scriptSignature, UInt32 sequence) { this._previousTxOutputKey = previousTxOutputKey; this._scriptSignature = scriptSignature; this._sequence = sequence; }
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()); }
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; }
public TxInput(TxOutputKey previousTxOutputKey, ImmutableArray<byte> scriptSignature, UInt32 sequence) { this._previousTxOutputKey = previousTxOutputKey; this._scriptSignature = scriptSignature; this._sequence = sequence; }
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()); }