Represents a transaction that has unspent outputs in the UTXO.
示例#1
0
 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));
     }
 }
示例#2
0
        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);
            }
        }
示例#4
0
        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);
            }
        }
示例#10
0
 public PrevTxOutput(TxOutput txOutput, UnspentTx unspentTx)
     : this(txOutput, unspentTx.BlockIndex, unspentTx.TxIndex, unspentTx.TxVersion, unspentTx.IsCoinbase)
 {
 }
示例#11
0
        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;
            }
        }
示例#15
0
        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);
            }
        }
示例#16
0
 public PrevTxOutput(TxOutput txOutput, UnspentTx unspentTx)
     : this(txOutput, unspentTx.BlockIndex, unspentTx.TxIndex, unspentTx.TxVersion, unspentTx.IsCoinbase)
 { }
示例#17
0
        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));
            }
        }
示例#19
0
 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);
     }
 }
示例#20
0
        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);
                    }
                }
            }
        }
示例#21
0
        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);
            }
        }
示例#22
0
 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));
            }
        }
示例#24
0
 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();
            }
        }
示例#32
0
        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;
            }
        }