public bool TryGetUnspentTx(UInt256 txHash, out UnspentTx unspentTx) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item.Item; return(cursor.TryGetUnspentTx(txHash, out unspentTx)); } }
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 = new BlockTx(0, new Transaction(0, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); var emptyCoinbaseTx1 = new BlockTx(0, new Transaction(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, 1, OutputState.Unspent); // add the unspent transaction memoryChainStateCursor.TryAddUnspentTx(unspentTx); // create an input to spend the unspent transaction var input = new TxInput(new TxOutputKey(txHash, txOutputIndex: 0), ImmutableArray.Create<byte>(), 0); var tx = new BlockTx(1, new Transaction(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; Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.IsFullySpent); // 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 void TestUnconfTxAdded() { // create tx spending a previous output that exists var decodedTx = Transaction.Create( 0, ImmutableArray.Create(new TxInput(UInt256.One, 0, ImmutableArray<byte>.Empty, 0)), ImmutableArray.Create(new TxOutput(0, ImmutableArray<byte>.Empty)), 0); var tx = decodedTx.Transaction; // create prev output tx var unspentTx = new UnspentTx(tx.Inputs[0].PrevTxHash, 0, 1, 0, false, new OutputStates(1, OutputState.Unspent)); var txOutput = new TxOutput(0, ImmutableArray<byte>.Empty); // mock chain state with prev output var chainState = new Mock<IChainState>(); chainState.Setup(x => x.TryGetUnspentTx(tx.Inputs[0].PrevTxHash, out unspentTx)).Returns(true); chainState.Setup(x => x.TryGetUnspentTxOutput(tx.Inputs[0].PrevTxOutputKey, out txOutput)).Returns(true); // mock core daemon for chain state retrieval var coreDaemon = new Mock<ICoreDaemon>(); coreDaemon.Setup(x => x.GetChainState()).Returns(chainState.Object); using (var unconfirmedTxesBuilder = new UnconfirmedTxesBuilder(coreDaemon.Object, Mock.Of<ICoreStorage>(), storageManager)) { // try to add the tx Assert.IsTrue(unconfirmedTxesBuilder.TryAddTransaction(decodedTx)); // verify unconfirmed tx was added UnconfirmedTx unconfirmedTx; Assert.IsTrue(unconfirmedTxesBuilder.TryGetTransaction(tx.Hash, out unconfirmedTx)); Assert.IsNotNull(unconfirmedTx); // verify tx was indexed against its input var txesSpending = unconfirmedTxesBuilder.GetTransactionsSpending(tx.Inputs[0].PrevTxOutputKey); Assert.AreEqual(1, txesSpending.Count); Assert.AreEqual(tx.Hash, txesSpending.Values.Single().Hash); } }
public bool TryAddUnspentTx(UnspentTx unspentTx) { CheckWriteTransaction(); using (SetSessionContext()) { try { using (var jetUpdate = this.jetSession.BeginUpdate(this.unspentTxTableId, JET_prep.Insert)) { Api.SetColumns(this.jetSession, this.unspentTxTableId, new BytesColumnValue { Columnid = this.txHashColumnId, Value = DbEncoder.EncodeUInt256(unspentTx.TxHash) }, new Int32ColumnValue { Columnid = this.blockIndexColumnId, Value = unspentTx.BlockIndex }, new Int32ColumnValue { Columnid = this.txIndexColumnId, Value = unspentTx.TxIndex }, new UInt32ColumnValue { Columnid = this.txVersionColumnId, Value = unspentTx.TxVersion }, new BoolColumnValue { Columnid = this.isCoinbaseColumnId, Value = unspentTx.IsCoinbase }, new BytesColumnValue { Columnid = this.outputStatesColumnId, Value = DataEncoder.EncodeOutputStates(unspentTx.OutputStates) }); jetUpdate.Save(); } return true; } catch (EsentKeyDuplicateException) { return false; } } }
public bool TryUpdateUnspentTx(UnspentTx unspentTx) { CheckWriteTransaction(); if (unspentTransactions.Value.ContainsKey(unspentTx.TxHash)) { unspentTransactions.Modify(x => x[unspentTx.TxHash] = unspentTx); return true; } else { return false; } }
private void TestContainsUnspentTx(ITestStorageProvider provider) { var unspentTx0 = new UnspentTx(txHash: (UInt256)0, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); var unspentTx1 = new UnspentTx(txHash: (UInt256)1, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify presence Assert.IsFalse(chainStateCursor.ContainsUnspentTx(unspentTx0.TxHash)); Assert.IsFalse(chainStateCursor.ContainsUnspentTx(unspentTx1.TxHash)); // add unspent tx 0 chainStateCursor.TryAddUnspentTx(unspentTx0); // verify presence Assert.IsTrue(chainStateCursor.ContainsUnspentTx(unspentTx0.TxHash)); Assert.IsFalse(chainStateCursor.ContainsUnspentTx(unspentTx1.TxHash)); // add unspent tx 1 chainStateCursor.TryAddUnspentTx(unspentTx1); // verify presence Assert.IsTrue(chainStateCursor.ContainsUnspentTx(unspentTx0.TxHash)); Assert.IsTrue(chainStateCursor.ContainsUnspentTx(unspentTx1.TxHash)); // remove unspent tx 1 chainStateCursor.TryRemoveUnspentTx(unspentTx1.TxHash); // verify presence Assert.IsTrue(chainStateCursor.ContainsUnspentTx(unspentTx0.TxHash)); Assert.IsFalse(chainStateCursor.ContainsUnspentTx(unspentTx1.TxHash)); // remove unspent tx 0 chainStateCursor.TryRemoveUnspentTx(unspentTx0.TxHash); // verify presence Assert.IsFalse(chainStateCursor.ContainsUnspentTx(unspentTx0.TxHash)); Assert.IsFalse(chainStateCursor.ContainsUnspentTx(unspentTx1.TxHash)); } }
/// <summary> /// Verify that chain state cursor does not allow write operations in read-only transaction. /// </summary> /// <param name="provider"></param> public void TestWriteOperationInReadonlyTransaction(ITestStorageProvider provider) { var unspentTx = new UnspentTx(txHash: UInt256.Zero, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; chainStateCursor.BeginTransaction(readOnly: true); var actions = new Action[] { () => { chainStateCursor.ChainTip = RandomData.RandomChainedHeader(); }, () => { chainStateCursor.UnspentTxCount = 0; }, () => { chainStateCursor.UnspentOutputCount = 0; }, () => { chainStateCursor.TotalTxCount = 0; }, () => { chainStateCursor.TotalInputCount = 0; }, () => { chainStateCursor. TotalOutputCount = 0; }, () => { chainStateCursor.TryAddUnspentTx(unspentTx); }, () => { chainStateCursor.TryRemoveUnspentTx(UInt256.Zero); }, () => { chainStateCursor.TryUpdateUnspentTx(unspentTx); }, () => { chainStateCursor.TryAddBlockSpentTxes(0, BlockSpentTxes.CreateRange(Enumerable.Empty<SpentTx>())); }, () => { chainStateCursor.TryRemoveBlockSpentTxes(0); }, () => { chainStateCursor.TryAddBlockUnmintedTxes(UInt256.Zero , ImmutableList<UnmintedTx>.Empty); }, () => { chainStateCursor.TryRemoveBlockUnmintedTxes(UInt256.Zero); }, }; foreach (var action in actions) AssertMethods.AssertThrows<InvalidOperationException>(action); chainStateCursor.RollbackTransaction(); } }
/// <summary> /// Verify that chain state cursor does not allow use outside of a transaction. /// </summary> /// <param name="provider"></param> public void TestOperationOutsideTransaction(ITestStorageProvider provider) { var unspentTx = new UnspentTx(txHash: UInt256.Zero, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; Assert.IsFalse(chainStateCursor.InTransaction); var actions = new Action[] { () => { var x = chainStateCursor.ChainTip; }, () => { chainStateCursor.ChainTip = RandomData.RandomChainedHeader(); }, () => { var x = chainStateCursor.UnspentTxCount; }, () => { chainStateCursor.UnspentTxCount = 0; }, () => { var x = chainStateCursor.UnspentOutputCount; }, () => { chainStateCursor.UnspentOutputCount = 0; }, () => { var x = chainStateCursor.TotalTxCount; }, () => { chainStateCursor.TotalTxCount = 0; }, () => { var x = chainStateCursor.TotalInputCount; }, () => { chainStateCursor.TotalInputCount = 0; }, () => { var x = chainStateCursor. TotalOutputCount; }, () => { chainStateCursor. TotalOutputCount = 0; }, () => { var x = chainStateCursor.ContainsUnspentTx(UInt256.Zero); }, () => { UnspentTx _; chainStateCursor.TryGetUnspentTx(UInt256.Zero, out _); }, () => { chainStateCursor.TryAddUnspentTx(unspentTx); }, () => { chainStateCursor.TryRemoveUnspentTx(UInt256.Zero); }, () => { chainStateCursor.TryUpdateUnspentTx(unspentTx); }, () => { chainStateCursor.ReadUnspentTransactions(); }, () => { chainStateCursor.ContainsBlockSpentTxes(0); }, () => { BlockSpentTxes _; chainStateCursor.TryGetBlockSpentTxes(0, out _); }, () => { chainStateCursor.TryAddBlockSpentTxes(0, BlockSpentTxes.CreateRange(Enumerable.Empty<SpentTx>())); }, () => { chainStateCursor.TryRemoveBlockSpentTxes(0); }, () => { chainStateCursor.ContainsBlockUnmintedTxes(UInt256.Zero); }, () => { IImmutableList<UnmintedTx> _; chainStateCursor.TryGetBlockUnmintedTxes(UInt256.Zero, out _); }, () => { chainStateCursor.TryAddBlockUnmintedTxes(UInt256.Zero , ImmutableList<UnmintedTx>.Empty); }, () => { chainStateCursor.TryRemoveBlockUnmintedTxes(UInt256.Zero); }, }; foreach (var action in actions) AssertMethods.AssertThrows<InvalidOperationException>(action); } }
private void TestUnspentTxCount(ITestStorageProvider provider) { var unspentTx0 = new UnspentTx(txHash: (UInt256)0, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); var unspentTx1 = new UnspentTx(txHash: (UInt256)1, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); var unspentTx2 = new UnspentTx(txHash: (UInt256)2, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify initial count Assert.AreEqual(0, chainStateCursor.UnspentTxCount); // increment count chainStateCursor.UnspentTxCount++; // verify count Assert.AreEqual(1, chainStateCursor.UnspentTxCount); // set count chainStateCursor.UnspentTxCount = 10; // verify count Assert.AreEqual(10, chainStateCursor.UnspentTxCount); } }
public PrevTxOutput(TxOutput txOutput, UnspentTx unspentTx) : this(txOutput, unspentTx.BlockIndex, unspentTx.TxIndex, unspentTx.TxVersion, unspentTx.IsCoinbase) { }
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 TryUpdateUnspentTx(UnspentTx unspentTx) { CheckWriteTransaction(); var key = DbEncoder.EncodeUInt256(unspentTx.TxHash); if (this.txn.ContainsKey(unspentTxTableId, key)) { var value = DataEncoder.EncodeUnspentTx(unspentTx); this.txn.Put(unspentTxTableId, key, value); return true; } else { return false; } }
public void TestAddBlockConfirmingTx() { // create tx spending a previous output that exists var decodedTx = Transaction.Create( 0, ImmutableArray.Create(new TxInput(UInt256.One, 0, ImmutableArray<byte>.Empty, 0)), ImmutableArray.Create(new TxOutput(0, ImmutableArray<byte>.Empty)), 0); var tx = decodedTx.Transaction; // create prev output tx var unspentTx = new UnspentTx(tx.Inputs[0].PrevTxHash, 0, 1, 0, false, new OutputStates(1, OutputState.Unspent)); var txOutput = new TxOutput(0, ImmutableArray<byte>.Empty); // create a fake chain var fakeHeaders = new FakeHeaders(); var genesisHeader = fakeHeaders.GenesisChained(); // create a block confirming the tx var block = Block.Create(RandomData.RandomBlockHeader().With(PreviousBlock: genesisHeader.Hash), ImmutableArray.Create(tx)); var chainedHeader = new ChainedHeader(block.Header, 1, 0, DateTimeOffset.Now); // mock core storage with chained header var coreStorage = new Mock<ICoreStorage>(); var initialChain = new ChainBuilder().ToImmutable(); coreStorage.Setup(x => x.TryReadChain(null, out initialChain)).Returns(true); coreStorage.Setup(x => x.TryGetChainedHeader(chainedHeader.Hash, out chainedHeader)).Returns(true); // mock chain state with prev output var chainState = new Mock<IChainState>(); chainState.Setup(x => x.TryGetUnspentTx(tx.Inputs[0].PrevTxHash, out unspentTx)).Returns(true); chainState.Setup(x => x.TryGetUnspentTxOutput(tx.Inputs[0].PrevTxOutputKey, out txOutput)).Returns(true); // mock core daemon for chain state retrieval var coreDaemon = new Mock<ICoreDaemon>(); coreDaemon.Setup(x => x.GetChainState()).Returns(chainState.Object); using (var unconfirmedTxesBuilder = new UnconfirmedTxesBuilder(coreDaemon.Object, coreStorage.Object, storageManager)) { // add the tx Assert.IsTrue(unconfirmedTxesBuilder.TryAddTransaction(decodedTx)); // add the block unconfirmedTxesBuilder.AddBlock(genesisHeader, Enumerable.Empty<BlockTx>()); unconfirmedTxesBuilder.AddBlock(chainedHeader, block.BlockTxes); // verify the confirmed tx was removed UnconfirmedTx unconfirmedTx; Assert.IsFalse(unconfirmedTxesBuilder.TryGetTransaction(tx.Hash, out unconfirmedTx)); Assert.IsNull(unconfirmedTx); // verify the confirmed tx was de-indexed against its input Assert.AreEqual(0, unconfirmedTxesBuilder.GetTransactionsSpending(tx.Inputs[0].PrevTxOutputKey).Count); } }
public bool TryGetUnspentTx(UInt256 txHash, out UnspentTx unspentTx) { CheckTransaction(); byte[] unspentTxBytes; if (this.txn.TryGet(unspentTxTableId, DbEncoder.EncodeUInt256(txHash), out unspentTxBytes)) { unspentTx = DataEncoder.DecodeUnspentTx(unspentTxBytes); return true; } else { unspentTx = default(UnspentTx); return false; } }
public void TestReplayForward() { var coreStorage = new Mock<ICoreStorage>(); var chainState = new Mock<IChainState>(); chainState.Setup(x => x.CursorCount).Returns(4); var testBlocks = new TestBlocks(); var block = testBlocks.MineAndAddBlock(txCount: 10); var chainedHeader = testBlocks.Chain.LastBlock; chainState.Setup(x => x.Chain).Returns(() => testBlocks.Chain); // mock block txes read var blockTxes = block.Transactions.Select((tx, txIndex) => new BlockTx(txIndex, tx)).GetEnumerator(); coreStorage.Setup(x => x.TryReadBlockTransactions(chainedHeader.Hash, true, out blockTxes)).Returns(true); // mock unspent tx lookup for (var txIndex = 0; txIndex < block.Transactions.Length; txIndex++) { var tx = block.Transactions[txIndex]; for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; // create a fake unspent tx, with enough outputs for this input var unspentTx = new UnspentTx(input.PreviousTxOutputKey.TxHash, blockIndex: 1, txIndex: txIndex * inputIndex, outputStates: new OutputStates(input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked() + 1, OutputState.Unspent)); chainState.Setup(x => x.TryGetUnspentTx(unspentTx.TxHash, out unspentTx)).Returns(true); } } { var loadingTxes = UtxoReplayer.ReplayCalculateUtxo(coreStorage.Object, chainState.Object, chainedHeader); var txIndex = -1; foreach (var loadingTx in loadingTxes.ToEnumerable()) { txIndex++; // verify loading tx matches original block tx Assert.AreEqual(block.Transactions[txIndex].Hash, loadingTx.Transaction.Hash); // if coinbase, verify no lookup keys for coinbase inputs if (txIndex == 0) { Assert.AreEqual(0, loadingTx.PrevOutputTxKeys.Length); } else { // verify there is a lookup key for each input Assert.AreEqual(block.Transactions[txIndex].Inputs.Length, loadingTx.PrevOutputTxKeys.Length); // verify each lookup key matches the mocked data for (var inputIndex = 0; inputIndex < loadingTx.Transaction.Inputs.Length; inputIndex++) { var prevOutputTxKey = loadingTx.PrevOutputTxKeys[inputIndex]; Assert.AreEqual(prevOutputTxKey.BlockHash, block.Hash); Assert.AreEqual(prevOutputTxKey.TxIndex, txIndex * inputIndex); } } } // verify correct number of transactions were replayed Assert.AreEqual(txIndex + 1, block.Transactions.Length); } }
public bool TryUpdateUnspentTx(UnspentTx unspentTx) { CheckWriteTransaction(); using (SetSessionContext()) { Api.JetSetCurrentIndex(this.jetSession, this.unspentTxTableId, "IX_TxHash"); Api.MakeKey(this.jetSession, this.unspentTxTableId, DbEncoder.EncodeUInt256(unspentTx.TxHash), MakeKeyGrbit.NewKey); if (Api.TrySeek(this.jetSession, this.unspentTxTableId, SeekGrbit.SeekEQ)) { using (var jetUpdate = this.jetSession.BeginUpdate(this.unspentTxTableId, JET_prep.Replace)) { Api.SetColumn(this.jetSession, this.unspentTxTableId, this.outputStatesColumnId, DataEncoder.EncodeOutputStates(unspentTx.OutputStates)); jetUpdate.Save(); } return true; } else { return false; } } }
private void TestTryAddGetRemoveUnspentTx(ITestStorageProvider provider) { var unspentTx0 = new UnspentTx(txHash: (UInt256)0, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); var unspentTx1 = new UnspentTx(txHash: (UInt256)1, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify initial empty state UnspentTx actualUnspentTx0, actualUnspentTx1; Assert.IsFalse(chainStateCursor.TryGetUnspentTx(unspentTx0.TxHash, out actualUnspentTx0)); Assert.IsFalse(chainStateCursor.TryGetUnspentTx(unspentTx1.TxHash, out actualUnspentTx1)); // add unspent tx 0 Assert.IsTrue(chainStateCursor.TryAddUnspentTx(unspentTx0)); // verify unspent txes Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx0.TxHash, out actualUnspentTx0)); Assert.AreEqual(unspentTx0, actualUnspentTx0); Assert.IsFalse(chainStateCursor.TryGetUnspentTx(unspentTx1.TxHash, out actualUnspentTx1)); // add unspent tx 1 Assert.IsTrue(chainStateCursor.TryAddUnspentTx(unspentTx1)); // verify unspent txes Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx0.TxHash, out actualUnspentTx0)); Assert.AreEqual(unspentTx0, actualUnspentTx0); Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx1.TxHash, out actualUnspentTx1)); Assert.AreEqual(unspentTx1, actualUnspentTx1); // remove unspent tx 1 Assert.IsTrue(chainStateCursor.TryRemoveUnspentTx(unspentTx1.TxHash)); // verify unspent txes Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx0.TxHash, out actualUnspentTx0)); Assert.AreEqual(unspentTx0, actualUnspentTx0); Assert.IsFalse(chainStateCursor.TryGetUnspentTx(unspentTx1.TxHash, out actualUnspentTx1)); // remove unspent tx 0 Assert.IsTrue(chainStateCursor.TryRemoveUnspentTx(unspentTx0.TxHash)); // verify unspent txes Assert.IsFalse(chainStateCursor.TryGetUnspentTx(unspentTx0.TxHash, out actualUnspentTx0)); Assert.IsFalse(chainStateCursor.TryGetUnspentTx(unspentTx1.TxHash, out actualUnspentTx1)); } }
public bool TryGetUnspentTx(UInt256 txHash, out UnspentTx unspentTx) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item.Item; return cursor.TryGetUnspentTx(txHash, out unspentTx); } }
public void TestReplayForward() { var coreStorage = new Mock<ICoreStorage>(); var chainState = new Mock<IChainState>(); chainState.Setup(x => x.CursorCount).Returns(4); var testBlocks = new TestBlocks(); var block = testBlocks.MineAndAddBlock(txCount: 10); var chainedHeader = testBlocks.Chain.LastBlock; chainState.Setup(x => x.Chain).Returns(() => testBlocks.Chain); // mock block txes read var blockTxes = block.Transactions.Select((tx, txIndex) => (BlockTx)BlockTx.Create(txIndex, tx)).GetEnumerator(); coreStorage.Setup(x => x.TryReadBlockTransactions(chainedHeader.Hash, out blockTxes)).Returns(true); // mock unspent tx lookup var expectedValue = 50UL * (ulong)100.MILLION(); for (var txIndex = 0; txIndex < block.Transactions.Length; txIndex++) { var tx = block.Transactions[txIndex]; for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; // create a fake unspent tx, with enough outputs for this input var unspentTx = new UnspentTx(input.PrevTxOutputKey.TxHash, blockIndex: 1, txIndex: txIndex * inputIndex, txVersion: 0, isCoinbase: false, outputStates: tx.IsCoinbase ? OutputStates.Empty : new OutputStates(input.PrevTxOutputKey.TxOutputIndex.ToIntChecked() + 1, OutputState.Unspent)); var txOutput = new TxOutput(tx.Outputs[0].Value, tx.Outputs[0].ScriptPublicKey); chainState.Setup(x => x.TryGetUnspentTx(unspentTx.TxHash, out unspentTx)).Returns(true); chainState.Setup(x => x.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput)).Returns(true); } } var validatableTxes = UtxoReplayer.ReplayCalculateUtxo(coreStorage.Object, chainState.Object, chainedHeader).ToEnumerable().ToList(); // verify correct number of transactions were replayed Assert.AreEqual(validatableTxes.Count, block.Transactions.Length); expectedValue = 50UL * (ulong)100.MILLION(); foreach (var validatableTx in validatableTxes) { // verify validatable tx matches original block tx Assert.AreEqual(block.Transactions[validatableTx.Index].Hash, validatableTx.Transaction.Hash); // if coinbase, verify no tx outputs for coinbase inputs if (validatableTx.IsCoinbase) { Assert.AreEqual(0, validatableTx.PrevTxOutputs.Length); } else { // verify there is a tx output for each input Assert.AreEqual(block.Transactions[validatableTx.Index].Inputs.Length, validatableTx.PrevTxOutputs.Length); // verify each tx output matches the mocked data for (var inputIndex = 0; inputIndex < validatableTx.Transaction.Inputs.Length; inputIndex++) { var prevTxOutput = validatableTx.PrevTxOutputs[inputIndex]; expectedValue -= 1; Assert.AreEqual(expectedValue, prevTxOutput.Value); CollectionAssert.AreEqual(block.Transactions[0].Outputs[0].ScriptPublicKey, prevTxOutput.ScriptPublicKey); } } } }
public void TestPruneAllData() { var logger = LogManager.CreateNullLogger(); // create genesis block var genesisblock = CreateFakeBlock(1); var genesisHeader = new ChainedHeader(genesisblock.Header, height: 0, totalWork: genesisblock.Header.CalculateWork(), dateSeen: DateTimeOffset.Now); // create a block var txCount = 100; var block = CreateFakeBlock(txCount, genesisblock.Hash); var chainedHeader = ChainedHeader.CreateFromPrev(genesisHeader, block.Header, dateSeen: DateTimeOffset.Now); // create a long chain based off the block, to account for pruning buffer var fakeHeaders = new FakeHeaders(new[] { genesisHeader, chainedHeader }); var chain = new ChainBuilder(Enumerable.Concat(new[] { genesisHeader, chainedHeader }, Enumerable.Range(0, 2000).Select(x => fakeHeaders.NextChained()))).ToImmutable(); // mock core daemon to return the chain var coreDaemon = new Mock<ICoreDaemon>(); coreDaemon.Setup(x => x.CurrentChain).Returns(chain); // create memory storage with the block var storageManager = new MemoryStorageManager(); storageManager.BlockTxesStorage.TryAddBlockTransactions(block.Hash, block.BlockTxes); // initialize the pruning worker var workerConfig = new WorkerConfig(initialNotify: false, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue); using (var pruningWorker = new PruningWorker(workerConfig, coreDaemon.Object, storageManager, null)) // get a chain state cursor using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // set the pruning worker to prune all data pruningWorker.Mode = PruningMode.TxIndex | PruningMode.BlockSpentIndex | PruningMode.BlockTxesPreserveMerkle; // wire event to wait for work var workFinishedEvent = new AutoResetEvent(false); pruningWorker.OnWorkFinished += () => workFinishedEvent.Set(); // wire event to track exceptions Exception workException = null; pruningWorker.OnWorkError += e => workException = e; // start the worker pruningWorker.Start(); // pick a random pruning order var random = new Random(); var pruneOrderSource = Enumerable.Range(0, txCount).ToList(); var pruneOrder = new List<int>(txCount); while (pruneOrderSource.Count > 0) { var randomIndex = random.Next(pruneOrderSource.Count); pruneOrder.Add(pruneOrderSource[randomIndex]); pruneOrderSource.RemoveAt(randomIndex); } // add an unspent tx for each transaction to storage var unspentTxes = new UnspentTx[block.Transactions.Length]; chainStateCursor.BeginTransaction(); for (var txIndex = 0; txIndex < block.Transactions.Length; txIndex++) { var tx = block.Transactions[txIndex]; var unspentTx = new UnspentTx(tx.Hash, blockIndex: 1, txIndex: txIndex, txVersion: tx.Version, isCoinbase: txIndex == 0, outputStates: new OutputStates(1, OutputState.Spent)); unspentTxes[txIndex] = unspentTx; chainStateCursor.TryAddUnspentTx(unspentTx); } chainStateCursor.CommitTransaction(); // create a memory pruning cursor to verify expected pruning results var pruneCursor = new MemoryMerkleTreePruningCursor<BlockTxNode>(block.BlockTxes.Select(x => (BlockTxNode)x)); // prune each transaction in random order var pruneHeight = 0; foreach (var pruneTxIndex in pruneOrder) { // create a spent tx to prune the transaction var pruneTx = block.Transactions[pruneTxIndex]; var spentTxes = BlockSpentTxes.CreateRange(new[] { unspentTxes[pruneTxIndex].ToSpentTx() }); // store the spent txes for the current pruning block pruneHeight++; chainStateCursor.BeginTransaction(); Assert.IsTrue(chainStateCursor.TryAddBlockSpentTxes(pruneHeight, spentTxes)); pruningWorker.PrunableHeight = pruneHeight; // verify unspent tx is present before pruning Assert.IsTrue(chainStateCursor.ContainsUnspentTx(pruneTx.Hash)); chainStateCursor.CommitTransaction(); // notify the pruning worker and wait pruningWorker.NotifyWork(); workFinishedEvent.WaitOne(); // verify unspent tx is removed after pruning chainStateCursor.BeginTransaction(); Assert.IsFalse(chainStateCursor.ContainsUnspentTx(pruneTx.Hash)); // verify unspent tx outputs are removed after pruning for (var outputIndex = 0; outputIndex < pruneTx.Outputs.Length; outputIndex++) Assert.IsFalse(chainStateCursor.ContainsUnspentTxOutput(new TxOutputKey(pruneTx.Hash, (uint)outputIndex))); // verify the spent txes were removed Assert.IsFalse(chainStateCursor.ContainsBlockSpentTxes(pruneHeight)); chainStateCursor.RollbackTransaction(); // prune to determine expected results MerkleTree.PruneNode(pruneCursor, pruneTxIndex); var expectedPrunedTxes = pruneCursor.ReadNodes().ToList(); // retrieve the actual transaction after pruning IEnumerator<BlockTxNode> actualPrunedTxNodes; Assert.IsTrue(storageManager.BlockTxesStorage.TryReadBlockTxNodes(block.Hash, out actualPrunedTxNodes)); // verify the actual pruned transactions match the expected results CollectionAssert.AreEqual(expectedPrunedTxes, actualPrunedTxNodes.UsingAsEnumerable().ToList()); } // verify all unspent txes were removed chainStateCursor.BeginTransaction(); Assert.AreEqual(0, chainStateCursor.ReadUnspentTransactions().Count()); chainStateCursor.CommitTransaction(); // verify final block with all transactions pruned IEnumerator<BlockTxNode> finalPrunedTxNodes; Assert.IsTrue(storageManager.BlockTxesStorage.TryReadBlockTxNodes(block.Hash, out finalPrunedTxNodes)); var finalPrunedTxesList = finalPrunedTxNodes.UsingAsEnumerable().ToList(); Assert.AreEqual(1, finalPrunedTxesList.Count); Assert.AreEqual(block.Header.MerkleRoot, finalPrunedTxesList.Single().Hash); // verify no work exceptions occurred Assert.IsNull(workException); } }
public static void EncodeUnspentTx(BinaryWriter writer, UnspentTx unspentTx) { writer.WriteUInt256(unspentTx.TxHash); writer.WriteInt32(unspentTx.BlockIndex); writer.WriteInt32(unspentTx.TxIndex); writer.WriteUInt32(unspentTx.TxVersion); writer.WriteBool(unspentTx.IsCoinbase); writer.WriteVarBytes(unspentTx.OutputStates.ToByteArray()); writer.WriteInt32(unspentTx.OutputStates.Length); }
private void TestTryUpdateUnspentTx(ITestStorageProvider provider) { var unspentTx = new UnspentTx(txHash: UInt256.Zero, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); var unspentTxUpdated = unspentTx.SetOutputState(0, OutputState.Spent); Assert.AreNotEqual(unspentTx, unspentTxUpdated); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify can't update missing unspent tx Assert.IsFalse(chainStateCursor.TryUpdateUnspentTx(unspentTx)); // add unspent tx Assert.IsTrue(chainStateCursor.TryAddUnspentTx(unspentTx)); // verify unspent tx UnspentTx actualUnspentTx; Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx.TxHash, out actualUnspentTx)); Assert.AreEqual(unspentTx, actualUnspentTx); // update unspent tx Assert.IsTrue(chainStateCursor.TryUpdateUnspentTx(unspentTxUpdated)); // verify updated unspent tx Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx.TxHash, out actualUnspentTx)); Assert.AreEqual(unspentTxUpdated, actualUnspentTx); // remove unspent tx Assert.IsTrue(chainStateCursor.TryRemoveUnspentTx(unspentTx.TxHash)); // verify can't update missing unspent tx Assert.IsFalse(chainStateCursor.TryUpdateUnspentTx(unspentTx)); } }
public static byte[] EncodeUnspentTx(UnspentTx unspentTx) { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { EncodeUnspentTx(writer, unspentTx); return stream.ToArray(); } }
public void TestAccessAcrossThreads(ITestStorageProvider provider) { var unspentTx = new UnspentTx(txHash: UInt256.Zero, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) using (var thread1Done = new AutoResetEvent(false)) using (var thread2Done = new AutoResetEvent(false)) { var chainStateCursor = handle.Item; var thread1Actions = new BlockingCollection<Action>(); var thread2Actions = new BlockingCollection<Action>(); var thread1 = new Thread(() => { foreach (var action in thread1Actions.GetConsumingEnumerable()) { action(); thread1Done.Set(); } }); var thread2 = new Thread(() => { foreach (var action in thread2Actions.GetConsumingEnumerable()) { action(); thread2Done.Set(); } }); thread1.Start(); thread2.Start(); // begin transaction on thread #1 thread1Actions.Add(() => chainStateCursor.BeginTransaction()); thread1Done.WaitOne(); // commit transaction on thread #2 thread2Actions.Add(() => chainStateCursor.CommitTransaction()); thread2Done.WaitOne(); // begin transaction on thread #1 thread1Actions.Add(() => chainStateCursor.BeginTransaction()); thread1Done.WaitOne(); // rollback transaction on thread #2 thread2Actions.Add(() => chainStateCursor.RollbackTransaction()); thread2Done.WaitOne(); thread1Actions.CompleteAdding(); thread2Actions.CompleteAdding(); thread1.Join(); thread2.Join(); } }
public bool TryAddUnspentTx(UnspentTx unspentTx) { CheckWriteTransaction(); try { this.unspentTransactions.Add(unspentTx.TxHash, unspentTx); this.unspentTxesModified = true; return true; } catch (ArgumentException) { return false; } }
public void TestReadUnspentTransactions(ITestStorageProvider provider) { var unspentTx0 = new UnspentTx(txHash: (UInt256)0, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); var unspentTx1 = new UnspentTx(txHash: (UInt256)1, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); var unspentTx2 = new UnspentTx(txHash: (UInt256)2, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify initial empty state Assert.AreEqual(0, chainStateCursor.ReadUnspentTransactions().Count()); // add unspent tx 0 Assert.IsTrue(chainStateCursor.TryAddUnspentTx(unspentTx0)); // verify unspent txes CollectionAssert.AreEquivalent(new[] { unspentTx0 }, chainStateCursor.ReadUnspentTransactions().ToList()); // add unspent tx 1 Assert.IsTrue(chainStateCursor.TryAddUnspentTx(unspentTx1)); // verify unspent txes CollectionAssert.AreEquivalent(new[] { unspentTx0, unspentTx1 }, chainStateCursor.ReadUnspentTransactions().ToList()); // add unspent tx 2 Assert.IsTrue(chainStateCursor.TryAddUnspentTx(unspentTx2)); // verify unspent txes CollectionAssert.AreEquivalent(new[] { unspentTx0, unspentTx1, unspentTx2 }, chainStateCursor.ReadUnspentTransactions().ToList()); // remove unspent tx 2 Assert.IsTrue(chainStateCursor.TryRemoveUnspentTx(unspentTx2.TxHash)); // verify unspent txes CollectionAssert.AreEquivalent(new[] { unspentTx0, unspentTx1 }, chainStateCursor.ReadUnspentTransactions().ToList()); // remove unspent tx 1 Assert.IsTrue(chainStateCursor.TryRemoveUnspentTx(unspentTx1.TxHash)); // verify unspent txes CollectionAssert.AreEquivalent(new[] { unspentTx0 }, chainStateCursor.ReadUnspentTransactions().ToList()); // remove unspent tx 0 Assert.IsTrue(chainStateCursor.TryRemoveUnspentTx(unspentTx0.TxHash)); // verify unspent txes Assert.AreEqual(0, chainStateCursor.ReadUnspentTransactions().Count()); } }
public bool TryGetUnspentTx(UInt256 txHash, out UnspentTx unspentTx) { CheckTransaction(); return this.unspentTransactions.TryGetValue(txHash, out unspentTx); }
private void TestCommitTransaction(ITestStorageProvider provider) { var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var unspentTx = new UnspentTx(txHash: UInt256.Zero, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Spent)); var spentTxes = BlockSpentTxes.CreateRange(new[] { unspentTx.ToSpentTx() }); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // add data chainStateCursor.ChainTip = chainedHeader0; chainStateCursor.TryAddUnspentTx(unspentTx); chainStateCursor.UnspentTxCount++; chainStateCursor.TryAddBlockSpentTxes(0, spentTxes); // verify data Assert.AreEqual(chainedHeader0, chainStateCursor.ChainTip); Assert.AreEqual(1, chainStateCursor.UnspentTxCount); UnspentTx actualUnspentTx; Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx.TxHash, out actualUnspentTx)); Assert.AreEqual(unspentTx, actualUnspentTx); BlockSpentTxes actualSpentTxes; Assert.IsTrue(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes)); CollectionAssert.AreEqual(spentTxes.ToList(), actualSpentTxes.ToList()); // commit transaction chainStateCursor.CommitTransaction(); chainStateCursor.BeginTransaction(); // verify data Assert.AreEqual(chainedHeader0, chainStateCursor.ChainTip); Assert.AreEqual(1, chainStateCursor.UnspentTxCount); Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx.TxHash, out actualUnspentTx)); Assert.AreEqual(unspentTx, actualUnspentTx); Assert.IsTrue(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes)); CollectionAssert.AreEqual(spentTxes.ToList(), actualSpentTxes.ToList()); chainStateCursor.RollbackTransaction(); } }
public bool TryUpdateUnspentTx(UnspentTx unspentTx) { CheckWriteTransaction(); if (this.unspentTransactions.ContainsKey(unspentTx.TxHash)) { this.unspentTransactions[unspentTx.TxHash] = unspentTx; this.unspentTxesModified = true; return true; } else { return false; } }
private void TestRollbackTransaction(ITestStorageProvider provider) { var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); var unspentTx = new UnspentTx(txHash: UInt256.Zero, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Spent)); var spentTxes = BlockSpentTxes.CreateRange(new[] { unspentTx.ToSpentTx() }); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // add header 0 chainStateCursor.ChainTip = chainedHeader0; // verify chain Assert.AreEqual(chainedHeader0, chainStateCursor.ChainTip); // add unspent tx chainStateCursor.TryAddUnspentTx(unspentTx); chainStateCursor.UnspentTxCount++; // verify unspent tx Assert.IsTrue(chainStateCursor.ContainsUnspentTx(unspentTx.TxHash)); Assert.AreEqual(1, chainStateCursor.UnspentTxCount); // add spent txes chainStateCursor.TryAddBlockSpentTxes(0, spentTxes); // verify spent txes BlockSpentTxes actualSpentTxes; Assert.IsTrue(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes)); CollectionAssert.AreEqual(spentTxes.ToList(), actualSpentTxes.ToList()); // rollback transaction chainStateCursor.RollbackTransaction(); chainStateCursor.BeginTransaction(); // verify chain Assert.IsNull(chainStateCursor.ChainTip); // verify unspent tx Assert.IsFalse(chainStateCursor.ContainsUnspentTx(unspentTx.TxHash)); Assert.AreEqual(0, chainStateCursor.UnspentTxCount); // verify spent txes Assert.IsFalse(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes)); chainStateCursor.RollbackTransaction(); } }
public bool TryGetUnspentTx(UInt256 txHash, out UnspentTx unspentTx) { CheckTransaction(); using (SetSessionContext()) { Api.JetSetCurrentIndex(this.jetSession, this.unspentTxTableId, "IX_TxHash"); Api.MakeKey(this.jetSession, this.unspentTxTableId, DbEncoder.EncodeUInt256(txHash), MakeKeyGrbit.NewKey); if (Api.TrySeek(this.jetSession, this.unspentTxTableId, SeekGrbit.SeekEQ)) { var blockIndexColumn = new Int32ColumnValue { Columnid = this.blockIndexColumnId }; var txIndexColumn = new Int32ColumnValue { Columnid = this.txIndexColumnId }; var txVersionColumn = new UInt32ColumnValue { Columnid = this.txVersionColumnId }; var isCoinbaseColumn = new BoolColumnValue { Columnid = this.isCoinbaseColumnId }; var outputStatesColumn = new BytesColumnValue { Columnid = this.outputStatesColumnId }; var txOutputBytesColumn = new BytesColumnValue { Columnid = this.txOutputBytesColumnId }; Api.RetrieveColumns(this.jetSession, this.unspentTxTableId, blockIndexColumn, txIndexColumn, txVersionColumn, isCoinbaseColumn, outputStatesColumn, txOutputBytesColumn); var blockIndex = blockIndexColumn.Value.Value; var txIndex = txIndexColumn.Value.Value; var txVersion = txVersionColumn.Value.Value; var isCoinbase = isCoinbaseColumn.Value.Value; var outputStates = DataDecoder.DecodeOutputStates(outputStatesColumn.Value); unspentTx = new UnspentTx(txHash, blockIndex, txIndex, txVersion, isCoinbase, outputStates); return true; } unspentTx = default(UnspentTx); return false; } }