public void TestCanSpend_NegativeIndex() { // prepare utxo storage var chain = new Chain(ImmutableList.Create(new FakeHeaders().GenesisChained())); var unspentTransactions = ImmutableSortedDictionary.CreateBuilder <UInt256, UnspentTx>(); // prepare unspent output var txHash = new UInt256(0); unspentTransactions.Add(txHash, new UnspentTx(txHash, blockIndex: 0, txIndex: 0, length: 1, state: OutputState.Unspent)); // prepare utxo var memoryStorage = new MemoryStorageManager(unspentTransactions: unspentTransactions.ToImmutable()); var chainStateStorage = memoryStorage.OpenChainStateCursor().Item; chainStateStorage.AddChainedHeader(chain.GenesisBlock); var utxo = new ChainState(chain, memoryStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash, txOutputIndex: UInt32.MaxValue); // check if output can be spent var canSpend = utxo.CanSpend(prevTxOutput); // verify output cannot be spent Assert.IsFalse(canSpend); }
private CoreDaemon CreateExampleDaemon(out BlockProvider embeddedBlocks, out IStorageManager storageManager, int?maxHeight = null) { // retrieve first 10,000 testnet3 blocks embeddedBlocks = new BlockProvider("BitSharp.Examples.Blocks.TestNet3.zip"); // initialize in-memory storage storageManager = new MemoryStorageManager(); // intialize testnet3 rules (ignore script errors, script engine is not and is not intended to be complete) var chainParams = new Testnet3Params(); var rules = new CoreRules(chainParams) { IgnoreScriptErrors = true }; // initialize & start core daemon var coreDaemon = new CoreDaemon(rules, storageManager) { MaxHeight = maxHeight, IsStarted = true }; // add embedded blocks coreDaemon.CoreStorage.AddBlocks(embeddedBlocks.ReadBlocks()); // wait for core daemon to finish processing any available data coreDaemon.WaitForUpdate(); return(coreDaemon); }
public void TestCanSpend_Spent() { // prepare utxo storage var chain = Chain.CreateForGenesisBlock(new FakeHeaders().GenesisChained()); var unspentTransactions = ImmutableSortedDictionary.CreateBuilder <UInt256, UnspentTx>(); // prepare spent output var txHash = new UInt256(0); unspentTransactions.Add(txHash, new UnspentTx(txHash, blockIndex: 0, txIndex: 0, txVersion: 0, isCoinbase: false, length: 1, state: OutputState.Spent)); // prepare utxo var memoryStorage = new MemoryStorageManager(unspentTransactions: unspentTransactions.ToImmutable()); var chainStateStorage = memoryStorage.OpenChainStateCursor().Item; chainStateStorage.BeginTransaction(); chainStateStorage.ChainTip = chain.GenesisBlock; chainStateStorage.CommitTransaction(); var utxo = new ChainState(chain, memoryStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash, txOutputIndex: 0); // check if output can be spent var canSpend = utxo.CanSpend(prevTxOutput); // verify output cannot be spent Assert.IsFalse(canSpend); }
public void DoABunchOfStuff() { var stopWatch = new Stopwatch(); // create the new storage manager using (var storageManager = new MemoryStorageManager()) { var rand = new Random(); using (var rtree = GetAFullTree(storageManager)) { // sanity check Assert.Equal(10000, rtree.Count); stopWatch.Start(); long totalMatch = 0; for (int i = 0; i < 10000; ++i) { var count = CountAnIntersection(rtree, rand); Assert.True(count < 10000); totalMatch += count; } stopWatch.Stop(); PrintPerformanceReport(stopWatch.ElapsedMilliseconds, 500, totalMatch); } } }
public void TestDoubleSpend() { // prepare block var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); var chain = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder(); var emptyCoinbaseTx0 = BlockTx.Create(0, Transaction.Create(0, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0)); var emptyCoinbaseTx1 = BlockTx.Create(0, Transaction.Create(1, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0)); // initialize memory utxo builder storage var memoryStorage = new MemoryStorageManager(); var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item; memoryChainStateCursor.BeginTransaction(); // initialize utxo builder var utxoBuilder = new UtxoBuilder(); // prepare an unspent transaction var txHash = new UInt256(100); var unspentTx = new UnspentTx(txHash, chainedHeader1.Height, 0, 0, false, 1, OutputState.Unspent); var txOutputKey = new TxOutputKey(txHash, 0); var txOutput = new TxOutput(0, ImmutableArray <byte> .Empty); // add the unspent transaction memoryChainStateCursor.TryAddUnspentTx(unspentTx); memoryChainStateCursor.TryAddUnspentTxOutput(txOutputKey, txOutput); // create an input to spend the unspent transaction var input = new TxInput(txHash, 0, ImmutableArray.Create <byte>(), 0); var tx = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input), ImmutableArray.Create <TxOutput>(), 0)); // spend the input chain.AddBlock(chainedHeader1); utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx }.ToBufferBlock()).ToEnumerable().ToList(); // verify utxo storage UnspentTx actualUnspentTx; TxOutput actualTxOutput; Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.IsFullySpent); Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutputKey, out actualTxOutput)); Assert.AreEqual(txOutput, actualTxOutput); // attempt to spend the input again, validation exception should be thrown chain.AddBlock(chainedHeader2); AssertMethods.AssertAggregateThrows <ValidationException>(() => utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx }.ToBufferBlock()).ToEnumerable().ToList()); }
public LevelDbStorageManager(string baseDirectory, ulong?blocksCacheSize = null, ulong?blocksWriteCacheSize = null, ulong?blockTxesCacheSize = null, ulong?blockTxesWriteCacheSize = null, ulong?chainStateCacheSize = null, ulong?chainStateWriteCacheSize = null, string[] blockTxesStorageLocations = null) { this.baseDirectory = baseDirectory; this.blockTxesStorageLocations = blockTxesStorageLocations; blockStorage = new Lazy <LevelDb.LevelDbBlockStorage>(() => new LevelDbBlockStorage(this.baseDirectory, blocksCacheSize, blocksWriteCacheSize)); blockTxesStorage = new Lazy <IBlockTxesStorage>(() => { if (blockTxesStorageLocations == null) { return(new LevelDbBlockTxesStorage(this.baseDirectory, blockTxesCacheSize, blockTxesWriteCacheSize)); } else { return(new SplitBlockTxesStorage(blockTxesStorageLocations, path => new LevelDbBlockTxesStorage(path, blockTxesCacheSize, blockTxesWriteCacheSize))); } }); chainStateManager = new Lazy <LevelDbChainStateManager>(() => new LevelDbChainStateManager(this.baseDirectory, chainStateCacheSize, chainStateWriteCacheSize)); this.memoryStorageManager = new Lazy <MemoryStorageManager>(() => { // create memory storage with the unconfirmed txes chain tip already in sync with chain state ChainedHeader chainTip; using (var handle = OpenChainStateCursor()) { handle.Item.BeginTransaction(readOnly: true); chainTip = handle.Item.ChainTip; } var memoryStorageManager = new MemoryStorageManager(); using (var handle = memoryStorageManager.OpenUnconfirmedTxesCursor()) { handle.Item.BeginTransaction(); handle.Item.ChainTip = chainTip; handle.Item.CommitTransaction(); } return(memoryStorageManager); }); }
public void TestCanSpend_Missing() { // prepare utxo var chain = new Chain(ImmutableList.Create(new FakeHeaders().GenesisChained())); var memoryStorage = new MemoryStorageManager(); var chainStateStorage = memoryStorage.OpenChainStateCursor().Item; chainStateStorage.AddChainedHeader(chain.GenesisBlock); var utxo = new ChainState(chain, memoryStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash: 0, txOutputIndex: 0); // check if output can be spent var canSpend = utxo.CanSpend(prevTxOutput); // verify output cannot be spent Assert.IsFalse(canSpend); }
/// <summary> /// Creates Storage Manager according to StorageType value in Settings file. /// </summary> /// <returns>Storame Manager</returns> public static IStorageManager CreateStorageManager() { //Get a reference to the settings var settings = MDSSettings.Instance; //Create storage manager according to the settings var storageType = settings["StorageType"]; IStorageManager storageManager; if (storageType.Equals("MySQL-ODBC", StringComparison.OrdinalIgnoreCase)) { storageManager = new MySqlOdbcStorageManager { ConnectionString = settings["ConnectionString"] }; } else if (storageType.Equals("MySQL-Net", StringComparison.OrdinalIgnoreCase)) { storageManager = new MySqlNetStorageManager { ConnectionString = settings["ConnectionString"] }; } else if (storageType.Equals("MSSQL", StringComparison.OrdinalIgnoreCase)) { storageManager = new MsSqlStorageManager { ConnectionString = settings["ConnectionString"] }; } else if (storageType.Equals("SQLite", StringComparison.OrdinalIgnoreCase)) { storageManager = new SqliteStorageManager(); } else if (storageType.Equals("MongoDB", StringComparison.OrdinalIgnoreCase)) { storageManager = new MongoDBStorageManager(); } else //Default storage manager { storageManager = new MemoryStorageManager(); } //Wrap storageManager with FaultToleratedStorageManagerWrapper and return it return(new FaultToleratedStorageManagerWrapper(storageManager)); }
public void MemoryStorageManager_CanStoreAndLoad() { var storageManager = new MemoryStorageManager(); // a random but repeatable seed var buffer = DataHelpers.GenerateSomeBytes(9087449); var nAccess = new StorageManagerAccess(storageManager); var page1 = nAccess.Store(-1, buffer); var buffer2 = DataHelpers.GenerateSomeBytes(37900452); var page2 = nAccess.Store(-1, buffer2); Assert.True(buffer.SequenceEqual(nAccess.Load(page1))); Assert.True(buffer2.SequenceEqual(nAccess.Load(page2))); buffer = DataHelpers.GenerateSomeBytes(678000023); page1 = nAccess.Store(page1, buffer); Assert.True(buffer.SequenceEqual(nAccess.Load(page1))); }
public void TestDoubleSpend() { // prepare block var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chain = new ChainBuilder(); var emptyCoinbaseTx0 = new Transaction(0, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0); var emptyCoinbaseTx1 = new Transaction(1, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0); // initialize memory utxo builder storage var memoryStorage = new MemoryStorageManager(); var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item; // initialize utxo builder var utxoBuilder = new UtxoBuilder(memoryChainStateCursor, LogManager.CreateNullLogger()); // prepare an unspent transaction var txHash = new UInt256(100); var unspentTx = new UnspentTx(txHash, chainedHeader0.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 Transaction(0, ImmutableArray.Create(input), ImmutableArray.Create <TxOutput>(), 0); // spend the input chain.AddBlock(chainedHeader0); utxoBuilder.CalculateUtxo(chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx }).ToList(); // verify utxo storage Assert.IsFalse(memoryChainStateCursor.ContainsUnspentTx(txHash)); // attempt to spend the input again chain.AddBlock(chainedHeader1); utxoBuilder.CalculateUtxo(chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx }).ToList(); // validation exception should be thrown }
public void DoABunchOfStuffAsync() { var stopWatch = new Stopwatch(); // create the new storage manager using (var storageManager = new MemoryStorageManager()) { var rand = new Random(); using (var rtree = GetAFullTree(storageManager)) { // sanity check Assert.Equal(10000, rtree.Count); stopWatch.Start(); long totalCount = 0; var tasks = new Task <bool> [10]; for (var taskIndex = 0; taskIndex < 10; ++taskIndex) { tasks[taskIndex] = Task.Factory.StartNew(() => { for (int i = 0; i < 1000; ++i) { var count = CountAnIntersection(rtree, rand); Interlocked.Add(ref totalCount, count); if (count > 10000) { return(false); } } return(true); }); } Task.WaitAll(tasks); Assert.True(tasks.All(tsk => tsk.Result)); stopWatch.Stop(); PrintPerformanceReport(stopWatch.ElapsedMilliseconds, 500, totalCount); } } }
public EsentStorageManager(string baseDirectory, string[] blockTxesStorageLocations = null) { this.baseDirectory = baseDirectory; this.blockTxesStorageLocations = blockTxesStorageLocations; this.blockStorage = new Lazy <Esent.EsentBlockStorage>(() => new EsentBlockStorage(this.baseDirectory)); this.blockTxesStorage = new Lazy <IBlockTxesStorage>(() => { if (blockTxesStorageLocations == null) { return(new EsentBlockTxesStorage(this.baseDirectory)); } else { return(new SplitBlockTxesStorage(blockTxesStorageLocations, path => new EsentBlockTxesStorage(path))); } }); this.chainStateManager = new Lazy <EsentChainStateManager>(() => new EsentChainStateManager(this.baseDirectory)); this.memoryStorageManager = new Lazy <MemoryStorageManager>(() => { // create memory storage with the unconfirmed txes chain tip already in sync with chain state ChainedHeader chainTip; using (var handle = OpenChainStateCursor()) { handle.Item.BeginTransaction(readOnly: true); chainTip = handle.Item.ChainTip; } var memoryStorageManager = new MemoryStorageManager(); using (var handle = memoryStorageManager.OpenUnconfirmedTxesCursor()) { handle.Item.BeginTransaction(); handle.Item.ChainTip = chainTip; handle.Item.CommitTransaction(); } return(memoryStorageManager); }); }
public void TestCanSpend_Missing() { // prepare utxo var chain = Chain.CreateForGenesisBlock(new FakeHeaders().GenesisChained()); var memoryStorage = new MemoryStorageManager(); var chainStateStorage = memoryStorage.OpenChainStateCursor().Item; chainStateStorage.BeginTransaction(); chainStateStorage.ChainTip = chain.GenesisBlock; chainStateStorage.CommitTransaction(); var utxo = new ChainState(chain, memoryStorage); // prepare output reference var prevTxOutput = new TxOutputKey(txHash: UInt256.Zero, txOutputIndex: 0); // check if output can be spent var canSpend = utxo.CanSpend(prevTxOutput); // verify output cannot be spent Assert.IsFalse(canSpend); }
public void TestPruneAllData() { // create genesis block var genesisblock = CreateFakeBlock(1); var genesisHeader = new ChainedHeader(genesisblock.Header, height: 0, totalWork: genesisblock.Header.CalculateWork().ToBigInteger(), 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 void TestSimpleSpend() { // prepare block var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); var chain = new ChainBuilder(); var emptyCoinbaseTx0 = new Transaction(0, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0); var emptyCoinbaseTx1 = new Transaction(1, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0); var emptyCoinbaseTx2 = new Transaction(2, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0); // initialize memory utxo builder storage var memoryStorage = new MemoryStorageManager(); var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item; // initialize utxo builder var utxoBuilder = new UtxoBuilder(memoryChainStateCursor, LogManager.CreateNullLogger()); // prepare an unspent transaction var txHash = new UInt256(100); var unspentTx = new UnspentTx(txHash, chainedHeader0.Height, 0, 3, OutputState.Unspent); // prepare unspent output var unspentTransactions = ImmutableDictionary.Create <UInt256, UnspentTx>().Add(txHash, unspentTx); // add the unspent transaction memoryChainStateCursor.TryAddUnspentTx(unspentTx); // create an input to spend the unspent transaction's first output var input0 = new TxInput(new TxOutputKey(txHash, txOutputIndex: 0), ImmutableArray.Create <byte>(), 0); var tx0 = new Transaction(0, ImmutableArray.Create(input0), ImmutableArray.Create <TxOutput>(), 0); // spend the input chain.AddBlock(chainedHeader0); utxoBuilder.CalculateUtxo(chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx0 }).ToList(); // verify utxo storage UnspentTx actualUnspentTx; 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); // create an input to spend the unspent transaction's second output var input1 = new TxInput(new TxOutputKey(txHash, txOutputIndex: 1), ImmutableArray.Create <byte>(), 0); var tx1 = new Transaction(0, ImmutableArray.Create(input1), ImmutableArray.Create <TxOutput>(), 0); // spend the input chain.AddBlock(chainedHeader1); utxoBuilder.CalculateUtxo(chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx1 }).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(new TxOutputKey(txHash, txOutputIndex: 2), ImmutableArray.Create <byte>(), 0); var tx2 = new Transaction(0, ImmutableArray.Create(input2), ImmutableArray.Create <TxOutput>(), 0); // spend the input chain.AddBlock(chainedHeader2); utxoBuilder.CalculateUtxo(chain.ToImmutable(), new[] { emptyCoinbaseTx2, tx2 }).ToList(); // verify utxo storage Assert.IsFalse(memoryChainStateCursor.ContainsUnspentTx(txHash)); }
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); }