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 TestCanSpend_NegativeIndex() { // prepare utxo storage var chain = new Chain(ImmutableList.Create(new FakeHeaders().GenesisChained())); var unspentTransactions = ImmutableSortedDictionary.CreateBuilder <UInt256, UnspentTx>(); // prepare unspent output var txHash = new UInt256(0); unspentTransactions.Add(txHash, new UnspentTx(txHash, blockIndex: 0, txIndex: 0, length: 1, state: OutputState.Unspent)); // prepare utxo var memoryStorage = new MemoryStorageManager(unspentTransactions: unspentTransactions.ToImmutable()); var chainStateStorage = memoryStorage.OpenChainStateCursor().Item; chainStateStorage.AddChainedHeader(chain.GenesisBlock); var utxo = new ChainState(chain, memoryStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash, txOutputIndex: UInt32.MaxValue); // 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 void TestCanSpend_NegativeIndex() { // prepare utxo storage var unspentTransactions = ImmutableDictionary.CreateBuilder <UInt256, UnspentTx>(); var unspentOutputs = ImmutableDictionary.CreateBuilder <TxOutputKey, TxOutput>(); // prepare unspent output var txHash = new UInt256(0); unspentTransactions.Add(txHash, new UnspentTx(confirmedBlockHash: 0, length: 1, state: OutputState.Unspent)); unspentOutputs.Add(new TxOutputKey(txHash, UInt32.MaxValue), new TxOutput(0, ImmutableArray.Create <byte>())); // prepare utxo var chainStateStorage = new MemoryChainStateStorage(0, unspentTransactions.ToImmutable(), unspentOutputs.ToImmutable()); var utxo = new Utxo(chainStateStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash, txOutputIndex: UInt32.MaxValue); // check if output can be spent var canSpend = utxo.CanSpend(prevTxOutput); // verify output cannot be spent Assert.IsFalse(canSpend); }
public bool CanSpend(TxOutputKey txOutputKey) { if (txOutputKey == null) { throw new ArgumentNullException("prevTxOutput"); } UnspentTx unspentTx; if (this.chainStateStorage.TryGetTransaction(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); } }
private TxOutput LookupPreviousOutput(TxOutputKey txOutputKey, ChainedBlock chainedBlock, Dictionary <UInt256, int> blockTxIndices, ChainStateBuilder chainStateBuilder) { TxOutput prevOutput; if (chainStateBuilder.TryGetOutput(txOutputKey, out prevOutput)) { return(prevOutput); } else { Transaction prevTx; int prevTxIndex; if (blockTxIndices.TryGetValue(txOutputKey.TxHash, out prevTxIndex)) { Debug.Assert(prevTxIndex >= 0 && prevTxIndex < chainedBlock.Transactions.Count); prevTx = chainedBlock.Transactions[prevTxIndex]; Debug.Assert(prevTx.Hash == txOutputKey.TxHash); } else { throw new ValidationException(chainedBlock.Hash); } var outputIndex = unchecked ((int)txOutputKey.TxOutputIndex); if (outputIndex < 0 || outputIndex >= prevTx.Outputs.Count) { throw new ValidationException(chainedBlock.Hash); } return(prevTx.Outputs[outputIndex]); } }
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 bool TryGetOutput(TxOutputKey txOutputKey, out TxOutput txOutput) { Api.JetBeginTransaction2(this.jetSession, BeginTransactionGrbit.ReadOnly); try { //Api.JetSetCurrentIndex(this.jetSession, this.unspentTxOutputsTableId, "IX_TxOutputKey"); Api.MakeKey(this.jetSession, this.unspentTxOutputsTableId, DataEncoder.EncodeTxOutputKey(txOutputKey), MakeKeyGrbit.NewKey); if (Api.TrySeek(this.jetSession, this.unspentTxOutputsTableId, SeekGrbit.SeekEQ)) { var txOutputBytes = Api.RetrieveColumn(this.jetSession, this.unspentTxOutputsTableId, this.txOutputSmallColumnId); if (txOutputBytes == null) { txOutputBytes = Api.RetrieveColumn(this.jetSession, this.unspentTxOutputsTableId, this.txOutputLargeColumnId); } txOutput = DataEncoder.DecodeTxOutput(txOutputBytes); return(true); } else { txOutput = default(TxOutput); return(false); } } finally { Api.JetCommitTransaction(this.jetSession, CommitTransactionGrbit.LazyFlush); } }
public static void EncodeTxOutputKey(Stream stream, TxOutputKey txOutputKey) { using (var writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: true)) { writer.WriteUInt256(txOutputKey.TxHash); writer.WriteUInt32(txOutputKey.TxOutputIndex); } }
public static byte[] EncodeTxOutputKey(TxOutputKey txOutputKey) { using (var stream = new MemoryStream()) { EncodeTxOutputKey(stream, txOutputKey); return(stream.ToArray()); } }
//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 static byte[] EncodeTxOutputKey(TxOutputKey txOutputKey) { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { EncodeTxOutputKey(writer, txOutputKey); return(stream.ToArray()); } }
public ImmutableDictionary <UInt256, UnconfirmedTx> GetTransactionsSpending(TxOutputKey txOutputKey) { using (var handle = storageManager.OpenUnconfirmedTxesCursor()) { var unconfirmedTxesCursor = handle.Item; unconfirmedTxesCursor.BeginTransaction(readOnly: true); return(unconfirmedTxesCursor.GetTransactionsSpending(txOutputKey)); } }
private byte[] MakeUnspentTxOutputKey(TxOutputKey txOutputKey) { var key = new byte[37]; key[0] = UNSPENT_TX_OUTPUT_PREFIX; txOutputKey.TxHash.ToByteArrayBE(key, 1); Buffer.BlockCopy(Bits.GetBytes(txOutputKey.TxOutputIndex), 0, key, 33, 4); return(key); }
public void MintTxOutput(ChainPosition chainPosition, TxOutputKey txOutputKey, TxOutput txOutput, UInt256 outputScriptHash, bool isCoinbase) { //this.Add(() => //{ foreach (var visitor in this.visitors) { visitor.MintTxOutput(chainPosition, txOutputKey, txOutput, outputScriptHash, isCoinbase); } //}); }
public void UnspendTxOutput(ChainPosition chainPosition, TxInput txInput, TxOutputKey txOutputKey, TxOutput txOutput, UInt256 outputScriptHash) { //this.Add(() => //{ foreach (var visitor in this.visitors) { visitor.UnspendTxOutput(chainPosition, txInput, txOutputKey, txOutput, outputScriptHash); } //}); }
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 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 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 TryAddUnspentTxOutput(TxOutputKey txOutputKey, TxOutput txOutput) { CheckWriteTransaction(); try { unspentTxOutputs.Modify(x => x.Add(txOutputKey, txOutput)); return(true); } catch (ArgumentException) { return(false); } }
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 Unmint(Transaction tx, ChainedHeader chainedHeader, bool isCoinbase) { // ignore duplicate coinbases if ((chainedHeader.Height == DUPE_COINBASE_1_HEIGHT && tx.Hash == DUPE_COINBASE_1_HASH) || (chainedHeader.Height == DUPE_COINBASE_2_HEIGHT && tx.Hash == DUPE_COINBASE_2_HASH)) { return; } // check that transaction exists UnspentTx unspentTx; if (!this.chainStateBuilderStorage.TryGetTransaction(tx.Hash, out unspentTx)) { // missing transaction output this.logger.Warn("Missing transaction at block {0:#,##0}, {1}, tx {2}".Format2(chainedHeader.Height, chainedHeader.Hash.ToHexNumberString(), 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 this.chainStateBuilderStorage.RemoveTransaction(tx.Hash); // remove the transaction outputs for (var outputIndex = 0; outputIndex < tx.Outputs.Count; outputIndex++) { var txOutput = tx.Outputs[outputIndex]; var txOutputKey = new TxOutputKey(tx.Hash, (UInt32)outputIndex); this.chainStateBuilderStorage.RemoveOutput(txOutputKey); // MONITOR: UnspendTxOutput if (this.chainStateMonitor != null) { this.chainStateMonitor.UnmintTxOutput(ChainPosition.Fake(), txOutputKey, txOutput, GetOutputScripHash(txOutput), isCoinbase); } } }
public void TestCanSpend_Missing() { // prepare utxo storage var unspentTransactions = ImmutableDictionary.CreateBuilder <UInt256, UnspentTx>(); var unspentOutputs = ImmutableDictionary.CreateBuilder <TxOutputKey, TxOutput>(); // prepare utxo var chainStateStorage = new MemoryChainStateStorage(0, unspentTransactions.ToImmutable(), unspentOutputs.ToImmutable()); var utxo = new Utxo(chainStateStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash: 0, txOutputIndex: 0); // check if output can be spent var canSpend = utxo.CanSpend(prevTxOutput); // verify output cannot be spent Assert.IsFalse(canSpend); }
public void TestCanSpend_Missing() { // prepare utxo var chain = new Chain(ImmutableList.Create(new FakeHeaders().GenesisChained())); var memoryStorage = new MemoryStorageManager(); var chainStateStorage = memoryStorage.OpenChainStateCursor().Item; chainStateStorage.AddChainedHeader(chain.GenesisBlock); var utxo = new ChainState(chain, memoryStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash: 0, txOutputIndex: 0); // check if output can be spent var canSpend = utxo.CanSpend(prevTxOutput); // verify output cannot be spent Assert.IsFalse(canSpend); }
public void AddOutput(TxOutputKey txOutputKey, TxOutput txOutput) { Api.JetBeginTransaction(this.jetSession); try { Api.JetPrepareUpdate(this.jetSession, this.unspentTxOutputsTableId, JET_prep.Insert); try { var txOutputBytes = DataEncoder.EncodeTxOutput(txOutput); Api.SetColumn(this.jetSession, this.unspentTxOutputsTableId, this.txOutputKeyColumnId, DataEncoder.EncodeTxOutputKey(txOutputKey)); if (txOutputBytes.Length <= 255) { Api.SetColumn(this.jetSession, this.unspentTxOutputsTableId, this.txOutputSmallColumnId, txOutputBytes); } else { Api.SetColumn(this.jetSession, this.unspentTxOutputsTableId, this.txOutputLargeColumnId, txOutputBytes); } if (IndexOutputs) { var sha256 = new SHA256Managed(); Api.SetColumn(this.jetSession, this.unspentTxOutputsTableId, this.outputScriptHashColumnId, sha256.ComputeHash(txOutput.ScriptPublicKey.ToArray())); } Api.JetUpdate(this.jetSession, this.unspentTxOutputsTableId); Api.JetCommitTransaction(this.jetSession, CommitTransactionGrbit.LazyFlush); this.unspentTxOutputsCount++; } catch (Exception) { Api.JetPrepareUpdate(this.jetSession, this.unspentTxOutputsTableId, JET_prep.Cancel); throw; } } catch (Exception) { Api.JetRollback(this.jetSession, RollbackTransactionGrbit.None); throw; } }
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); } } }
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 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 bool RemoveOutput(TxOutputKey txOutputKey) { Api.JetBeginTransaction(this.jetSession); try { //Api.JetSetCurrentIndex(this.jetSession, this.unspentTxOutputsTableId, "IX_TxOutputKey"); Api.MakeKey(this.jetSession, this.unspentTxOutputsTableId, DataEncoder.EncodeTxOutputKey(txOutputKey), MakeKeyGrbit.NewKey); if (!Api.TrySeek(this.jetSession, this.unspentTxOutputsTableId, SeekGrbit.SeekEQ)) { throw new KeyNotFoundException(); } Api.JetDelete(this.jetSession, this.unspentTxOutputsTableId); Api.JetCommitTransaction(this.jetSession, CommitTransactionGrbit.LazyFlush); this.unspentTxOutputsCount--; return(true); } catch (Exception) { Api.JetRollback(this.jetSession, RollbackTransactionGrbit.None); throw; } }
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); } }