public static bool CanSpend(this IChainState chainState, TxOutputKey txOutputKey) { if (txOutputKey == null) { throw new ArgumentNullException("prevTxOutput"); } UnspentTx unspentTx; if (chainState.TryGetUnspentTx(txOutputKey.TxHash, out unspentTx)) { var outputIndex = unchecked ((int)txOutputKey.TxOutputIndex); if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length) { return(false); } return(unspentTx.OutputStates[outputIndex] == OutputState.Unspent); } else { return(false); } }
public void TestCanSpend_Spent() { // prepare utxo storage var chain = Chain.CreateForGenesisBlock(new FakeHeaders().GenesisChained()); var unspentTransactions = ImmutableSortedDictionary.CreateBuilder<UInt256, UnspentTx>(); // prepare spent output var txHash = new UInt256(0); unspentTransactions.Add(txHash, new UnspentTx(txHash, blockIndex: 0, txIndex: 0, txVersion: 0, isCoinbase: false, length: 1, state: OutputState.Spent)); // prepare utxo var memoryStorage = new MemoryStorageManager(unspentTransactions: unspentTransactions.ToImmutable()); var chainStateStorage = memoryStorage.OpenChainStateCursor().Item; chainStateStorage.BeginTransaction(); chainStateStorage.ChainTip = chain.GenesisBlock; chainStateStorage.CommitTransaction(); var utxo = new ChainState(chain, memoryStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash, txOutputIndex: 0); // check if output can be spent var canSpend = utxo.CanSpend(prevTxOutput); // verify output cannot be spent Assert.IsFalse(canSpend); }
public void TestTxOutputKeyEquality() { var randomTxOutputKey = RandomData.RandomTxOutputKey(); var sameTxOutputKey = new TxOutputKey ( txHash: randomTxOutputKey.TxHash, txOutputIndex: randomTxOutputKey.TxOutputIndex ); var differentTxOutputKeyTxHash = new TxOutputKey ( txHash: ~randomTxOutputKey.TxHash, txOutputIndex: randomTxOutputKey.TxOutputIndex ); var differentTxOutputKeyTxOutputIndex = new TxOutputKey ( txHash: randomTxOutputKey.TxHash, txOutputIndex: ~randomTxOutputKey.TxOutputIndex ); Assert.IsTrue(randomTxOutputKey.Equals(sameTxOutputKey)); Assert.IsTrue(randomTxOutputKey == sameTxOutputKey); Assert.IsFalse(randomTxOutputKey != sameTxOutputKey); Assert.IsFalse(randomTxOutputKey.Equals(differentTxOutputKeyTxHash)); Assert.IsFalse(randomTxOutputKey == differentTxOutputKeyTxHash); Assert.IsTrue(randomTxOutputKey != differentTxOutputKeyTxHash); Assert.IsFalse(randomTxOutputKey.Equals(differentTxOutputKeyTxOutputIndex)); Assert.IsFalse(randomTxOutputKey == differentTxOutputKeyTxOutputIndex); Assert.IsTrue(randomTxOutputKey != differentTxOutputKeyTxOutputIndex); }
public static byte[] EncodeTxOutputKey(TxOutputKey txOutputKey) { var txOutputKeyBytes = new byte[36]; Buffer.BlockCopy(DbEncoder.EncodeUInt256(txOutputKey.TxHash), 0, txOutputKeyBytes, 0, 32); Buffer.BlockCopy(DbEncoder.EncodeInt32(txOutputKey.TxOutputIndex.ToIntChecked()), 0, txOutputKeyBytes, 32, 4); return txOutputKeyBytes; }
public bool TryGetUnspentTxOutput(TxOutputKey txOutputKey, out TxOutput txOutput) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item.Item; return(cursor.TryGetUnspentTxOutput(txOutputKey, out txOutput)); } }
public ImmutableDictionary <UInt256, UnconfirmedTx> GetTransactionsSpending(TxOutputKey txOutputKey) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item.Item; return(cursor.GetTransactionsSpending(txOutputKey)); } }
//public static void DecodeBlockHashTxIndex(byte[] bytes, out UInt256 blockHash, out int txIndex) //{ // var blockHashBytes = new byte[32]; // var txIndexBytes = new byte[4]; // Buffer.BlockCopy(bytes, 0, blockHashBytes, 0, 32); // Buffer.BlockCopy(bytes, 32, txIndexBytes, 0, 4); // blockHash = DbEncoder.DecodeUInt256(blockHashBytes); // txIndex = DbEncoder.DecodeInt32(txIndexBytes); //} //public static byte[] EncodeBlockHashTxIndex(UInt256 blockHash, int txIndex) //{ // var blockHashTxIndexBytes = new byte[36]; // Buffer.BlockCopy(DbEncoder.EncodeUInt256(blockHash), 0, blockHashTxIndexBytes, 0, 32); // Buffer.BlockCopy(DbEncoder.EncodeInt32(txIndex), 0, blockHashTxIndexBytes, 32, 4); // return blockHashTxIndexBytes; //} public static byte[] EncodeTxOutputKey(TxOutputKey txOutputKey) { var buffer = new byte[36]; txOutputKey.TxHash.ToByteArrayBE(buffer, 0); Buffer.BlockCopy(EncodeInt32((int)txOutputKey.TxOutputIndex), 0, buffer, 32, 4); return buffer; }
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 bool ContainsUnspentTxOutput(TxOutputKey txOutputKey) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item.Item; return(cursor.ContainsUnspentTxOutput(txOutputKey)); } }
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 static TransformManyBlock<DecodedBlockTx, Tuple<TxOutputKey, CompletionCount, DecodedBlockTx>> InitQueueUnspentTxLookup(CancellationToken cancelToken) { return new TransformManyBlock<DecodedBlockTx, Tuple<TxOutputKey, CompletionCount, DecodedBlockTx>>( blockTx => { var tx = blockTx.Transaction; var outputCount = tx.Outputs.Length; var inputCount = !blockTx.IsCoinbase ? tx.Inputs.Length * 2 : 0; var txOutputKeys = new Tuple<TxOutputKey, CompletionCount, DecodedBlockTx>[1 + outputCount + inputCount]; var completionCount = new CompletionCount(txOutputKeys.Length); var keyIndex = 0; // warm-up the UnspentTx entry that will be added for the new tx txOutputKeys[keyIndex++] = Tuple.Create(new TxOutputKey(blockTx.Hash, uint.MaxValue), completionCount, blockTx); // warm-up the TxOutput entries that will be added for each of the tx's outputs for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++) { var txOutputKey = new TxOutputKey(blockTx.Hash, (uint)outputIndex); txOutputKeys[keyIndex++] = Tuple.Create(txOutputKey, completionCount, blockTx); } // warm-up the previous UnspentTx and TxOutput entries that will be needed for each of the tx's inputs if (!blockTx.IsCoinbase) { for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; // input's previous tx's UnspentTx entry var txOutputKey = new TxOutputKey(input.PrevTxHash, uint.MaxValue); txOutputKeys[keyIndex++] = Tuple.Create(txOutputKey, completionCount, blockTx); // input's previous tx outputs's TxOutput entry txOutputKey = input.PrevTxOutputKey; txOutputKeys[keyIndex++] = Tuple.Create(txOutputKey, completionCount, blockTx); } } Debug.Assert(txOutputKeys.All(x => x != null)); return txOutputKeys; }, new ExecutionDataflowBlockOptions { CancellationToken = cancelToken }); }
public void TestCanSpend_Missing() { // prepare utxo var chain = Chain.CreateForGenesisBlock(new FakeHeaders().GenesisChained()); var memoryStorage = new MemoryStorageManager(); var chainStateStorage = memoryStorage.OpenChainStateCursor().Item; chainStateStorage.BeginTransaction(); chainStateStorage.ChainTip = chain.GenesisBlock; chainStateStorage.CommitTransaction(); var utxo = new ChainState(chain, memoryStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash: UInt256.Zero, txOutputIndex: 0); // check if output can be spent var canSpend = utxo.CanSpend(prevTxOutput); // verify output cannot be spent Assert.IsFalse(canSpend); }
public ImmutableDictionary<UInt256, UnconfirmedTx> GetTransactionsSpending(TxOutputKey txOutputKey) { using (var handle = storageManager.OpenUnconfirmedTxesCursor()) { var unconfirmedTxesCursor = handle.Item; unconfirmedTxesCursor.BeginTransaction(readOnly: true); return unconfirmedTxesCursor.GetTransactionsSpending(txOutputKey); } }
public void TestDoubleSpend() { // prepare block var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); var chain = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder(); var emptyCoinbaseTx0 = BlockTx.Create(0, Transaction.Create(0, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); var emptyCoinbaseTx1 = BlockTx.Create(0, Transaction.Create(1, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); // initialize memory utxo builder storage var memoryStorage = new MemoryStorageManager(); var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item; memoryChainStateCursor.BeginTransaction(); // initialize utxo builder var utxoBuilder = new UtxoBuilder(); // prepare an unspent transaction var txHash = new UInt256(100); var unspentTx = new UnspentTx(txHash, chainedHeader1.Height, 0, 0, false, 1, OutputState.Unspent); var txOutputKey = new TxOutputKey(txHash, 0); var txOutput = new TxOutput(0, ImmutableArray<byte>.Empty); // add the unspent transaction memoryChainStateCursor.TryAddUnspentTx(unspentTx); memoryChainStateCursor.TryAddUnspentTxOutput(txOutputKey, txOutput); // create an input to spend the unspent transaction var input = new TxInput(txHash, 0, ImmutableArray.Create<byte>(), 0); var tx = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input), ImmutableArray.Create<TxOutput>(), 0)); // spend the input chain.AddBlock(chainedHeader1); utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx }.ToBufferBlock()).ToEnumerable().ToList(); // verify utxo storage UnspentTx actualUnspentTx; TxOutput actualTxOutput; Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.IsFullySpent); Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutputKey, out actualTxOutput)); Assert.AreEqual(txOutput, actualTxOutput); // attempt to spend the input again, validation exception should be thrown chain.AddBlock(chainedHeader2); AssertMethods.AssertAggregateThrows<ValidationException>(() => utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx }.ToBufferBlock()).ToEnumerable().ToList()); }
public bool ContainsUnspentTxOutput(TxOutputKey txOutputKey) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item.Item; return cursor.ContainsUnspentTxOutput(txOutputKey); } }
public bool TryRemoveUnspentTxOutput(TxOutputKey txOutputKey) { CheckWriteTransaction(); using (SetSessionContext()) { Api.JetSetCurrentIndex(this.jetSession, this.unspentTxOutputTableId, "IX_TxOutputKey"); Api.MakeKey(this.jetSession, this.unspentTxOutputTableId, DbEncoder.EncodeTxOutputKey(txOutputKey), MakeKeyGrbit.NewKey); if (Api.TrySeek(this.jetSession, this.unspentTxOutputTableId, SeekGrbit.SeekEQ)) { Api.JetDelete(this.jetSession, this.unspentTxOutputTableId); return true; } else { return false; } } }
public bool TryGetUnspentTxOutput(TxOutputKey txOutputKey, out TxOutput txOutput) { CheckTransaction(); using (SetSessionContext()) { Api.JetSetCurrentIndex(this.jetSession, this.unspentTxOutputTableId, "IX_TxOutputKey"); Api.MakeKey(this.jetSession, this.unspentTxOutputTableId, DbEncoder.EncodeTxOutputKey(txOutputKey), MakeKeyGrbit.NewKey); if (Api.TrySeek(this.jetSession, this.unspentTxOutputTableId, SeekGrbit.SeekEQ)) { var txOutputBytesColumn = new BytesColumnValue { Columnid = this.txOutputBytesColumnId }; Api.RetrieveColumns(this.jetSession, this.unspentTxOutputTableId, txOutputBytesColumn); txOutput = DataDecoder.DecodeTxOutput(txOutputBytesColumn.Value); return true; } txOutput = default(TxOutput); return false; } }
public void WarmUnspentTxOutput(TxOutputKey txOutputKey) { unspentTxOutputs.WarmupValue(txOutputKey); }
public static byte[] EncodeTxOutputKey(TxOutputKey txOutputKey) { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { EncodeTxOutputKey(writer, txOutputKey); return stream.ToArray(); } }
private void TestContainsUnspentTxOutput(ITestStorageProvider provider) { var random = new Random(); var txOutput0Key = new TxOutputKey(UInt256.Zero, 0); var txOutput0 = new TxOutput(0, random.NextBytes(100).ToImmutableArray()); var txOutput1Key = new TxOutputKey(UInt256.One, 1); var txOutput1 = new TxOutput(1, random.NextBytes(100).ToImmutableArray()); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify presence Assert.IsFalse(chainStateCursor.ContainsUnspentTxOutput(txOutput0Key)); Assert.IsFalse(chainStateCursor.ContainsUnspentTxOutput(txOutput1Key)); // add unspent tx 0 chainStateCursor.TryAddUnspentTxOutput(txOutput0Key, txOutput0); // verify presence Assert.IsTrue(chainStateCursor.ContainsUnspentTxOutput(txOutput0Key)); Assert.IsFalse(chainStateCursor.ContainsUnspentTxOutput(txOutput1Key)); // add unspent tx 1 chainStateCursor.TryAddUnspentTxOutput(txOutput1Key, txOutput1); // verify presence Assert.IsTrue(chainStateCursor.ContainsUnspentTxOutput(txOutput0Key)); Assert.IsTrue(chainStateCursor.ContainsUnspentTxOutput(txOutput1Key)); // remove unspent tx 1 chainStateCursor.TryRemoveUnspentTxOutput(txOutput1Key); // verify presence Assert.IsTrue(chainStateCursor.ContainsUnspentTxOutput(txOutput0Key)); Assert.IsFalse(chainStateCursor.ContainsUnspentTxOutput(txOutput1Key)); // remove unspent tx 0 chainStateCursor.TryRemoveUnspentTxOutput(txOutput0Key); // verify presence Assert.IsFalse(chainStateCursor.ContainsUnspentTxOutput(txOutput0Key)); Assert.IsFalse(chainStateCursor.ContainsUnspentTxOutput(txOutput1Key)); } }
public bool ContainsUnspentTxOutput(TxOutputKey txOutputKey) { CheckTransaction(); return unspentTxOutputs.Value.ContainsKey(txOutputKey); }
public bool TryGetUnspentTxOutput(TxOutputKey txOutputKey, out TxOutput txOutput) { CheckTransaction(); return unspentTxOutputs.Value.TryGetValue(txOutputKey, out txOutput); }
public bool TryAddUnspentTxOutput(TxOutputKey txOutputKey, TxOutput txOutput) { CheckWriteTransaction(); try { unspentTxOutputs.Modify(x => x.Add(txOutputKey, txOutput)); return true; } catch (ArgumentException) { return false; } }
public static void EncodeTxOutputKey(BinaryWriter writer, TxOutputKey txOutputKey) { writer.WriteUInt256(txOutputKey.TxHash); writer.WriteUInt32(txOutputKey.TxOutputIndex); }
private void TestTryAddGetRemoveUnspentTxOutput(ITestStorageProvider provider) { var random = new Random(); var txOutput0Key = new TxOutputKey(UInt256.Zero, 0); var txOutput0 = new TxOutput(0, random.NextBytes(100).ToImmutableArray()); var txOutput1Key = new TxOutputKey(UInt256.One, 1); var txOutput1 = new TxOutput(1, random.NextBytes(100).ToImmutableArray()); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify initial empty state TxOutput actualTxOutput0, actualTxOutput1; Assert.IsFalse(chainStateCursor.TryGetUnspentTxOutput(txOutput0Key, out actualTxOutput0)); Assert.IsFalse(chainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput1)); // add unspent tx 0 Assert.IsTrue(chainStateCursor.TryAddUnspentTxOutput(txOutput0Key, txOutput0)); // verify unspent txes Assert.IsTrue(chainStateCursor.TryGetUnspentTxOutput(txOutput0Key, out actualTxOutput0)); Assert.AreEqual(txOutput0, actualTxOutput0); Assert.IsFalse(chainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput1)); // add unspent tx 1 Assert.IsTrue(chainStateCursor.TryAddUnspentTxOutput(txOutput1Key, txOutput1)); // verify unspent txes Assert.IsTrue(chainStateCursor.TryGetUnspentTxOutput(txOutput0Key, out actualTxOutput0)); Assert.AreEqual(txOutput0, actualTxOutput0); Assert.IsTrue(chainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput1)); Assert.AreEqual(txOutput1, actualTxOutput1); // remove unspent tx 1 Assert.IsTrue(chainStateCursor.TryRemoveUnspentTxOutput(txOutput1Key)); // verify unspent txes Assert.IsTrue(chainStateCursor.TryGetUnspentTxOutput(txOutput0Key, out actualTxOutput0)); Assert.AreEqual(txOutput0, actualTxOutput0); Assert.IsFalse(chainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput1)); // remove unspent tx 0 Assert.IsTrue(chainStateCursor.TryRemoveUnspentTxOutput(txOutput0Key)); // verify unspent txes Assert.IsFalse(chainStateCursor.TryGetUnspentTxOutput(txOutput0Key, out actualTxOutput0)); Assert.IsFalse(chainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput1)); } }
public ImmutableDictionary<UInt256, UnconfirmedTx> GetTransactionsSpending(TxOutputKey txOutputKey) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item.Item; return cursor.GetTransactionsSpending(txOutputKey); } }
public ImmutableDictionary<UInt256, UnconfirmedTx> GetTransactionsSpending(TxOutputKey prevTxOutputKey) { ImmutableDictionary<UInt256, UnconfirmedTx>.Builder unconfirmedTxes; if (unconfirmedTxesByPrevTxOutputKey.Value.TryGetValue(prevTxOutputKey, out unconfirmedTxes)) return unconfirmedTxes.ToImmutable(); else return ImmutableDictionary<UInt256, UnconfirmedTx>.Empty; }
public bool ContainsUnspentTxOutput(TxOutputKey txOutputKey) { CheckTransaction(); using (SetSessionContext()) { Api.JetSetCurrentIndex(this.jetSession, this.unspentTxOutputTableId, "IX_TxOutputKey"); Api.MakeKey(this.jetSession, this.unspentTxOutputTableId, DbEncoder.EncodeTxOutputKey(txOutputKey), MakeKeyGrbit.NewKey); return Api.TrySeek(this.jetSession, this.unspentTxOutputTableId, SeekGrbit.SeekEQ); } }
public TxInput(TxOutputKey previousTxOutputKey, ImmutableArray<byte> scriptSignature, UInt32 sequence) { PreviousTxOutputKey = previousTxOutputKey; ScriptSignature = scriptSignature; Sequence = sequence; }
public bool TryAddUnspentTxOutput(TxOutputKey txOutputKey, TxOutput txOutput) { CheckWriteTransaction(); using (SetSessionContext()) { try { using (var jetUpdate = this.jetSession.BeginUpdate(this.unspentTxOutputTableId, JET_prep.Insert)) { Api.SetColumns(this.jetSession, this.unspentTxOutputTableId, new BytesColumnValue { Columnid = this.txOutputKeyColumnId, Value = DbEncoder.EncodeTxOutputKey(txOutputKey) }, new BytesColumnValue { Columnid = this.txOutputBytesColumnId, Value = DataEncoder.EncodeTxOutput(txOutput) }); jetUpdate.Save(); } return true; } catch (EsentKeyDuplicateException) { return false; } } }
public TxInput(TxOutputKey prevTxOutputKey, ImmutableArray <byte> scriptSignature, UInt32 sequence) { PrevTxOutputKey = prevTxOutputKey; ScriptSignature = scriptSignature; Sequence = sequence; }
public void RemoveUnspentTxOutput(TxOutputKey txOutputKey) { TryRemoveUnspentTxOutput(txOutputKey); }
public static bool CanSpend(this IChainState chainState, TxOutputKey txOutputKey) { if (txOutputKey == null) throw new ArgumentNullException("prevTxOutput"); UnspentTx unspentTx; if (chainState.TryGetUnspentTx(txOutputKey.TxHash, out unspentTx)) { var outputIndex = unchecked((int)txOutputKey.TxOutputIndex); if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length) return false; return unspentTx.OutputStates[outputIndex] == OutputState.Unspent; } else { return false; } }
public bool TryGetUnspentTxOutput(TxOutputKey txOutputKey, out TxOutput txOutput) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item.Item; return cursor.TryGetUnspentTxOutput(txOutputKey, out txOutput); } }
public TxInput(TxOutputKey previousTxOutputKey, ImmutableArray <byte> scriptSignature, UInt32 sequence) { this._previousTxOutputKey = previousTxOutputKey; this._scriptSignature = scriptSignature; this._sequence = sequence; }
public void TestSimpleSpend() { // prepare block var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); var chainedHeader3 = fakeHeaders.NextChained(); var chain = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder(); var emptyCoinbaseTx0 = BlockTx.Create(0, Transaction.Create(0, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); var emptyCoinbaseTx1 = BlockTx.Create(1, Transaction.Create(1, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); var emptyCoinbaseTx2 = BlockTx.Create(2, Transaction.Create(2, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); // initialize memory utxo builder storage var memoryStorage = new MemoryStorageManager(); var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item; memoryChainStateCursor.BeginTransaction(); // initialize utxo builder var utxoBuilder = new UtxoBuilder(); // prepare an unspent transaction var txHash = new UInt256(100); var unspentTx = new UnspentTx(txHash, chainedHeader1.Height, 0, 0, false, 3, OutputState.Unspent); var txOutput1Key = new TxOutputKey(txHash, 0); var txOutput1 = new TxOutput(0, ImmutableArray<byte>.Empty); var txOutput2Key = new TxOutputKey(txHash, 1); var txOutput2 = new TxOutput(1, ImmutableArray<byte>.Empty); var txOutput3Key = new TxOutputKey(txHash, 2); var txOutput3 = new TxOutput(2, ImmutableArray<byte>.Empty); // prepare unspent output var unspentTransactions = ImmutableDictionary.Create<UInt256, UnspentTx>().Add(txHash, unspentTx); // add the unspent transaction memoryChainStateCursor.TryAddUnspentTx(unspentTx); memoryChainStateCursor.TryAddUnspentTxOutput(txOutput1Key, txOutput1); memoryChainStateCursor.TryAddUnspentTxOutput(txOutput2Key, txOutput2); memoryChainStateCursor.TryAddUnspentTxOutput(txOutput3Key, txOutput3); // create an input to spend the unspent transaction's first output var input0 = new TxInput(txHash, 0, ImmutableArray.Create<byte>(), 0); var tx0 = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input0), ImmutableArray.Create<TxOutput>(), 0)); // spend the input chain.AddBlock(chainedHeader1); utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx0 }.ToBufferBlock()).ToEnumerable().ToList(); // verify utxo storage UnspentTx actualUnspentTx; TxOutput actualTxOutput; Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3); Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent); Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Unspent); Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent); Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput)); Assert.AreEqual(txOutput1, actualTxOutput); Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput2Key, out actualTxOutput)); Assert.AreEqual(txOutput2, actualTxOutput); Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput3Key, out actualTxOutput)); Assert.AreEqual(txOutput3, actualTxOutput); // create an input to spend the unspent transaction's second output var input1 = new TxInput(txHash, 1, ImmutableArray.Create<byte>(), 0); var tx1 = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input1), ImmutableArray.Create<TxOutput>(), 0)); // spend the input chain.AddBlock(chainedHeader2); utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx1 }.ToBufferBlock()).ToEnumerable().ToList(); // verify utxo storage Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3); Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent); Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Spent); Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent); // create an input to spend the unspent transaction's third output var input2 = new TxInput(txHash, 2, ImmutableArray.Create<byte>(), 0); var tx2 = BlockTx.Create(2, Transaction.Create(0, ImmutableArray.Create(input2), ImmutableArray.Create<TxOutput>(), 0)); // spend the input chain.AddBlock(chainedHeader3); utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx2, tx2 }.ToBufferBlock()).ToEnumerable().ToList(); // verify utxo storage Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.IsFullySpent); }
public bool TryRemoveUnspentTxOutput(TxOutputKey txOutputKey) { CheckWriteTransaction(); return unspentTxOutputs.TryModify(x => x.Remove(txOutputKey)); }