// IBlockStorage.ContainsChainedHeader private void TestContainsChainedHeader(ITestStorageProvider provider) { using (var storageManager = provider.OpenStorageManager()) { var blockStorage = storageManager.BlockStorage; // create a chained header var fakeHeaders = new FakeHeaders(); var chainedHeader = fakeHeaders.GenesisChained(); // header should not be present Assert.IsFalse(blockStorage.ContainsChainedHeader(chainedHeader.Hash)); // add the header blockStorage.TryAddChainedHeader(chainedHeader); // header should be present Assert.IsTrue(blockStorage.ContainsChainedHeader(chainedHeader.Hash)); ; // remove the header blockStorage.TryRemoveChainedHeader(chainedHeader.Hash); // header should not be present Assert.IsFalse(blockStorage.ContainsChainedHeader(chainedHeader.Hash)); ; } }
public void TestInitWithChain() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var rules = Mock.Of<ICoreRules>(); var coreStorage = new Mock<ICoreStorage>(); var storageManager = new Mock<IStorageManager>(); var chainStateCursor = new Mock<IDeferredChainStateCursor>(); storageManager.Setup(x => x.OpenChainStateCursor()).Returns( new DisposeHandle<IChainStateCursor>(_ => { }, chainStateCursor.Object)); storageManager.Setup(x => x.OpenDeferredChainStateCursor(It.IsAny<IChainState>())).Returns( new DisposeHandle<IDeferredChainStateCursor>(_ => { }, chainStateCursor.Object)); chainStateCursor.Setup(x => x.ChainTip).Returns(header1); chainStateCursor.Setup(x => x.TryGetHeader(header0.Hash, out header0)).Returns(true); chainStateCursor.Setup(x => x.TryGetHeader(header1.Hash, out header1)).Returns(true); var chainStateBuilder = new ChainStateBuilder(rules, coreStorage.Object, storageManager.Object); CollectionAssert.AreEqual(new[] { header0, header1 }, chainStateBuilder.Chain.Blocks); }
public void TestCreateFromInvalidEnumerable() { // create a chain var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); // create builder from out of order enumerable AssertMethods.AssertThrows<InvalidOperationException>(() => new ChainBuilder(new[] { header1, header0 })); }
public void TestCreateFromEnumerable() { // create a chain var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); // create builder from enumerable var chainBuilder = new ChainBuilder(new[] { header0, header1 }); // verify CollectionAssert.AreEqual(new[] { header0, header1 }, chainBuilder.Blocks); CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 }, { header1.Hash, header1 } }, chainBuilder.BlocksByHash); }
public void TestChainTipOutOfSync() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var header2 = fakeHeaders.NextChained(); var rules = Mock.Of<ICoreRules>(); var coreStorage = new Mock<ICoreStorage>(); var storageManager = new Mock<IStorageManager>(); var chainStateCursor = new Mock<IDeferredChainStateCursor>(); storageManager.Setup(x => x.OpenChainStateCursor()).Returns( new DisposeHandle<IChainStateCursor>(_ => { }, chainStateCursor.Object)); storageManager.Setup(x => x.OpenDeferredChainStateCursor(It.IsAny<IChainState>())).Returns( new DisposeHandle<IDeferredChainStateCursor>(_ => { }, chainStateCursor.Object)); chainStateCursor.Setup(x => x.TryGetHeader(header0.Hash, out header0)).Returns(true); chainStateCursor.Setup(x => x.TryGetHeader(header1.Hash, out header1)).Returns(true); chainStateCursor.Setup(x => x.TryGetHeader(header2.Hash, out header2)).Returns(true); // return header 1 as the chain tip chainStateCursor.Setup(x => x.ChainTip).Returns(header1); // init chain state builder seeing header 1 var chainStateBuilder = new ChainStateBuilder(rules, coreStorage.Object, storageManager.Object); Assert.AreEqual(header1.Hash, chainStateBuilder.Chain.LastBlock.Hash); // alter the chain tip outside of the chain state builder chainStateCursor.Setup(x => x.ChainTip).Returns(header2); // attempt to add block when out of sync ChainStateOutOfSyncException actualEx; AssertMethods.AssertAggregateThrows<ChainStateOutOfSyncException>(() => chainStateBuilder.AddBlockAsync(header2, Enumerable.Empty<BlockTx>()).Wait(), out actualEx); Assert.AreEqual(header1.Hash, actualEx.ExpectedChainTip.Hash); Assert.AreEqual(header2.Hash, actualEx.ActualChainTip.Hash); // attempt to rollback block when out of sync AssertMethods.AssertThrows<ChainStateOutOfSyncException>(() => chainStateBuilder.RollbackBlock(header2, Enumerable.Empty<BlockTx>()), out actualEx); Assert.AreEqual(header1.Hash, actualEx.ExpectedChainTip.Hash); Assert.AreEqual(header2.Hash, actualEx.ActualChainTip.Hash); }
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 TestLastBlock() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(); // verify last block with 0 blocks Assert.IsNull(chainBuilder.LastBlock); // verify last block with 1 block chainBuilder.AddBlock(header0); Assert.AreEqual(header0, chainBuilder.LastBlock); // verify last block with 2 blocks chainBuilder.AddBlock(header1); Assert.AreEqual(header1, chainBuilder.LastBlock); }
public void TestGenesisBlock() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(); // verify genesis with 0 blocks var chainEmpty = chainBuilder.ToImmutable(); Assert.IsNull(chainEmpty.GenesisBlock); // verify genesis with 1 block chainBuilder.AddBlock(header0); var chain0 = chainBuilder.ToImmutable(); Assert.AreEqual(header0, chain0.GenesisBlock); // verify genesis with 2 blocks chainBuilder.AddBlock(header1); var chain1 = chainBuilder.ToImmutable(); Assert.AreEqual(header0, chain1.GenesisBlock); }
public void TestHeight() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(); // verify height with 0 blocks var chainEmpty = chainBuilder.ToImmutable(); Assert.AreEqual(-1, chainEmpty.Height); // verify height with 1 block chainBuilder.AddBlock(header0); var chain0 = chainBuilder.ToImmutable(); Assert.AreEqual(0, chain0.Height); // verify height with 2 blocks chainBuilder.AddBlock(header1); var chain1 = chainBuilder.ToImmutable(); Assert.AreEqual(1, chain1.Height); }
public void TestRemoveBlock() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(new[] { header0, header1 }); // remove header 1 and verify chainBuilder.RemoveBlock(header1); CollectionAssert.AreEqual(new[] { header0 }, chainBuilder.Blocks); CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 } }, chainBuilder.BlocksByHash); // remove header 0 and verify chainBuilder.RemoveBlock(header0); Assert.AreEqual(0, chainBuilder.Blocks.Count); Assert.AreEqual(0, chainBuilder.BlocksByHash.Count); }
public void TestRemoveBlockInvalid() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var header2 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(new[] { header0, header1, header2 }); // removing header 1 first should fail AssertMethods.AssertThrows<InvalidOperationException>(() => chainBuilder.RemoveBlock(header1)); // remove header 2 chainBuilder.RemoveBlock(header2); // removing header 0 with header 1 present should fail AssertMethods.AssertThrows<InvalidOperationException>(() => chainBuilder.AddBlock(header0)); }
public void TestBlocksByHash() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(); // verify blocks dictionary with 0 blocks Assert.AreEqual(0, chainBuilder.BlocksByHash.Count); // verify blocks dictionary with 1 block chainBuilder.AddBlock(header0); CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 } }, chainBuilder.BlocksByHash); // verify blocks dictionary with 2 blocks chainBuilder.AddBlock(header1); CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 }, { header1.Hash, header1 } }, chainBuilder.BlocksByHash); }
public void TestAddBlockInvalid() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var header2 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(); // adding header 1 first should fail AssertMethods.AssertThrows<InvalidOperationException>(() => chainBuilder.AddBlock(header1)); // add header 0 chainBuilder.AddBlock(header0); // adding header 2 without header 1 should fail AssertMethods.AssertThrows<InvalidOperationException>(() => chainBuilder.AddBlock(header2)); }
private void TestChainTip(ITestStorageProvider provider) { var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify initial tip Assert.IsNull(chainStateCursor.ChainTip); // set tip chainStateCursor.ChainTip = chainedHeader0; // verify tip Assert.AreEqual(chainedHeader0, chainStateCursor.ChainTip); // set tip chainStateCursor.ChainTip = chainedHeader1; // verify tip Assert.AreEqual(chainedHeader1, chainStateCursor.ChainTip); } }
public void TestNavigateTowardsEmpty() { // create chain var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var header2 = fakeHeaders.NextChained(); var chainEmpty = new ChainBuilder().ToImmutable(); var chain = new ChainBuilder(new[] { header0, header1, header2 }).ToImmutable(); // verify chaining to empty does nothing Assert.AreEqual(0, chainEmpty.NavigateTowards(chainEmpty).Count()); Assert.AreEqual(0, chain.NavigateTowards(chainEmpty).Count()); // verify path from empty chain to chain CollectionAssert.AreEqual( new[] { Tuple.Create(+1, header0), Tuple.Create(+1, header1), Tuple.Create(+1, header2) } , chainEmpty.NavigateTowards(chain).ToList()); }
private void TestTransactionIsolation(ITestStorageProvider provider) { var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); // open two chain state cursors using (var storageManager = provider.OpenStorageManager()) using (var txBeganEvent = new ManualResetEventSlim()) using (var headerAddedEvent = new ManualResetEventSlim()) using (var doCommitEvent = new ManualResetEventSlim()) using (var committedEvent = new ManualResetEventSlim()) { var thread1 = new Thread(() => { using (var handle1 = storageManager.OpenChainStateCursor()) { var chainStateCursor1 = handle1.Item; // open transactions on cusor 1 chainStateCursor1.BeginTransaction(); // add a header on cursor 1 chainStateCursor1.ChainTip = chainedHeader0; // alert header has been added headerAddedEvent.Set(); doCommitEvent.Wait(); // commit transaction on cursor 1 chainStateCursor1.CommitTransaction(); // alert transaction has been committed committedEvent.Set(); } }); thread1.Start(); // wait for header to be added on cursor 1 headerAddedEvent.Wait(); using (var handle2 = storageManager.OpenChainStateCursor()) { var chainStateCursor2 = handle2.Item; // open transactions on cusor 2 chainStateCursor2.BeginTransaction(readOnly: true); // verify empty chain Assert.IsNull(chainStateCursor2.ChainTip); doCommitEvent.Set(); committedEvent.Wait(); // verify empty chain Assert.IsNull(chainStateCursor2.ChainTip); chainStateCursor2.CommitTransaction(); chainStateCursor2.BeginTransaction(); // verify cursor 2 now sees the new header Assert.AreEqual(chainedHeader0, chainStateCursor2.ChainTip); chainStateCursor2.RollbackTransaction(); } thread1.Join(); } }
public void TestSimpleChain() { // prepare test kernel var kernel = new StandardKernel(new ConsoleLoggingModule(), new MemoryStorageModule()); kernel.Bind <CoreStorage>().ToSelf().InSingletonScope(); var coreStorage = kernel.Get <CoreStorage>(); // initialize data var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.Genesis(); var header1 = fakeHeaders.Next(); var header2 = fakeHeaders.Next(); // store genesis block var chainedHeader0 = ChainedHeader.CreateForGenesisBlock(header0); coreStorage.AddGenesisBlock(chainedHeader0); // mock rules var mockRules = new Mock <IBlockchainRules>(); mockRules.Setup(rules => rules.GenesisChainedHeader).Returns(chainedHeader0); kernel.Bind <IBlockchainRules>().ToConstant(mockRules.Object); // initialize the target chain worker using (var targetChainWorker = kernel.Get <TargetChainWorker>(new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue)))) { // verify initial state Assert.AreEqual(null, targetChainWorker.TargetChain); // monitor event firing var targetChainChangedEvent = new AutoResetEvent(false); var onTargetChainChangedCount = 0; targetChainWorker.OnTargetChainChanged += () => { onTargetChainChangedCount++; targetChainChangedEvent.Set(); }; // start worker and wait for initial chain targetChainWorker.Start(); targetChainChangedEvent.WaitOne(); // verify chained to block 0 Assert.AreEqual(chainedHeader0, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0 }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(1, onTargetChainChangedCount); // add block 1 ChainedHeader chainedHeader1; coreStorage.TryChainHeader(header1, out chainedHeader1); // wait for worker targetChainChangedEvent.WaitOne(); // verify chained to block 1 Assert.AreEqual(chainedHeader1, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1 }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(2, onTargetChainChangedCount); // add block 2 ChainedHeader chainedHeader2; coreStorage.TryChainHeader(header2, out chainedHeader2); // wait for worker targetChainChangedEvent.WaitOne(); // verify chained to block 2 Assert.AreEqual(chainedHeader2, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2 }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(3, onTargetChainChangedCount); // verify no other work was done Assert.IsFalse(targetChainChangedEvent.WaitOne(50)); } }
public void TestReadChainWithGap() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var header2 = fakeHeaders.NextChained(); var chainedHeaders = new Dictionary<UInt256, ChainedHeader>(); chainedHeaders.Add(header0.Hash, header0); chainedHeaders.Add(header2.Hash, header2); Chain chain; Assert.IsFalse(Chain.TryReadChain(header2.Hash, out chain, headerHash => { ChainedHeader chainedHeader; chainedHeaders.TryGetValue(headerHash, out chainedHeader); return chainedHeader; })); }
private void TestContainsHeader(ITestStorageProvider provider) { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify presence Assert.IsFalse(chainStateCursor.ContainsHeader(header0.Hash)); Assert.IsFalse(chainStateCursor.ContainsHeader(header1.Hash)); // add header 0 chainStateCursor.TryAddHeader(header0); // verify presence Assert.IsTrue(chainStateCursor.ContainsHeader(header0.Hash)); Assert.IsFalse(chainStateCursor.ContainsHeader(header1.Hash)); // add header 1 chainStateCursor.TryAddHeader(header1); // verify presence Assert.IsTrue(chainStateCursor.ContainsHeader(header0.Hash)); Assert.IsTrue(chainStateCursor.ContainsHeader(header1.Hash)); // remove header 1 chainStateCursor.TryRemoveHeader(header1.Hash); // verify presence Assert.IsTrue(chainStateCursor.ContainsHeader(header0.Hash)); Assert.IsFalse(chainStateCursor.ContainsHeader(header1.Hash)); // remove header 0 chainStateCursor.TryRemoveHeader(header0.Hash); // verify presence Assert.IsFalse(chainStateCursor.ContainsHeader(header0.Hash)); Assert.IsFalse(chainStateCursor.ContainsHeader(header1.Hash)); } }
public void TestReadChain() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var header2 = fakeHeaders.NextChained(); var chainedHeaders = new Dictionary<UInt256, ChainedHeader>(); chainedHeaders.Add(header0.Hash, header0); chainedHeaders.Add(header1.Hash, header1); chainedHeaders.Add(header2.Hash, header2); Chain chain; Assert.IsTrue(Chain.TryReadChain(header2.Hash, out chain, headerHash => { ChainedHeader chainedHeader; chainedHeaders.TryGetValue(headerHash, out chainedHeader); return chainedHeader; })); CollectionAssert.AreEqual(fakeHeaders.ChainedHeaders, chain.Blocks); }
public void TestCreateForGenesisBlock() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var chain = Chain.CreateForGenesisBlock(header0); CollectionAssert.AreEqual(new[] { header0 }, chain.Blocks); }
public void TestNavigateTowardsInvalidChains() { // create distinct chains var fakeHeadersA = new FakeHeaders(); var header0A = fakeHeadersA.GenesisChained(); var header1A = fakeHeadersA.NextChained(); var fakeHeadersB = new FakeHeaders(); var header0B = fakeHeadersB.GenesisChained(); var header1B = fakeHeadersB.NextChained(); var chainEmpty = new ChainBuilder().ToImmutable(); var chainA = new ChainBuilder(new[] { header0A, header1A }).ToImmutable(); var chainB = new ChainBuilder(new[] { header0B, header1B, }).ToImmutable(); // unrelated chains should error AssertMethods.AssertThrows<InvalidOperationException>(() => chainA.NavigateTowards(chainB).ToList()); AssertMethods.AssertThrows<InvalidOperationException>(() => chainB.NavigateTowards(chainA).ToList()); }
public void TestToImmutable() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(); // verify to builder with 0 blocks var chainEmpty = chainBuilder.ToImmutable(); Assert.AreEqual(0, chainEmpty.Blocks.Count); Assert.AreEqual(0, chainEmpty.BlocksByHash.Count); // verify to builder with 1 block chainBuilder.AddBlock(header0); var chain0 = chainBuilder.ToImmutable(); CollectionAssert.AreEqual(new[] { header0 }, chain0.Blocks); CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 } }, chain0.BlocksByHash); // verify to builder with 2 blocks chainBuilder.AddBlock(header1); var chain1 = chainBuilder.ToImmutable(); CollectionAssert.AreEqual(new[] { header0, header1 }, chain1.Blocks); CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 }, { header1.Hash, header1 } }, chain1.BlocksByHash); }
public void TestTargetChainReorganize() { // prepare test kernel var kernel = new StandardKernel(new ConsoleLoggingModule(), new MemoryStorageModule()); kernel.Bind <CoreStorage>().ToSelf().InSingletonScope(); var coreStorage = kernel.Get <CoreStorage>(); // initialize data var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.Genesis(); var header1 = fakeHeaders.Next(); var header2 = fakeHeaders.Next(); var fakeHeadersA = new FakeHeaders(fakeHeaders); var header3A = fakeHeadersA.Next(); var header4A = fakeHeadersA.Next(); var header5A = fakeHeadersA.Next(); var fakeHeadersB = new FakeHeaders(fakeHeaders); var header3B = fakeHeadersB.Next(); var header4B = fakeHeadersB.Next(DataCalculator.ToCompact(UnitTestParams.Target2)); // store genesis block var chainedHeader0 = ChainedHeader.CreateForGenesisBlock(header0); coreStorage.AddGenesisBlock(chainedHeader0); // mock chain params var mockChainParams = new Mock <IChainParams>(); mockChainParams.Setup(rules => rules.GenesisChainedHeader).Returns(chainedHeader0); kernel.Bind <IChainParams>().ToConstant(mockChainParams.Object); // initialize the target chain worker using (var targetChainWorker = kernel.Get <TargetChainWorker>(new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue)))) { // monitor event firing var targetChainChangedEvent = new AutoResetEvent(false); var onTargetChainChangedCount = 0; targetChainWorker.OnTargetChainChanged += () => { onTargetChainChangedCount++; targetChainChangedEvent.Set(); }; // start worker and wait for initial chain targetChainWorker.Start(); targetChainChangedEvent.WaitOne(); // verify chained to block 0 Assert.AreEqual(chainedHeader0, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0 }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(1, onTargetChainChangedCount); // add block 1 ChainedHeader chainedHeader1; coreStorage.TryChainHeader(header1, out chainedHeader1); // wait for worker targetChainChangedEvent.WaitOne(); // verify chained to block 1 Assert.AreEqual(chainedHeader1, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1 }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(2, onTargetChainChangedCount); // add block 2 ChainedHeader chainedHeader2; coreStorage.TryChainHeader(header2, out chainedHeader2); // wait for worker targetChainChangedEvent.WaitOne(); // verify chained to block 2 Assert.AreEqual(chainedHeader2, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2 }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(3, onTargetChainChangedCount); // add block 3A ChainedHeader chainedHeader3A; coreStorage.TryChainHeader(header3A, out chainedHeader3A); // wait for worker targetChainChangedEvent.WaitOne(); // verify chained to block 3A Assert.AreEqual(chainedHeader3A, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3A }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(4, onTargetChainChangedCount); // add block 4A ChainedHeader chainedHeader4A; coreStorage.TryChainHeader(header4A, out chainedHeader4A); // wait for worker targetChainChangedEvent.WaitOne(); // verify chained to block 4A Assert.AreEqual(chainedHeader4A, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3A, chainedHeader4A }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(5, onTargetChainChangedCount); // add block 5A ChainedHeader chainedHeader5A; coreStorage.TryChainHeader(header5A, out chainedHeader5A); // wait for worker targetChainChangedEvent.WaitOne(); // verify chained to block 5A Assert.AreEqual(chainedHeader5A, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3A, chainedHeader4A, chainedHeader5A }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(6, onTargetChainChangedCount); // add block 3B ChainedHeader chainedHeader3B; coreStorage.TryChainHeader(header3B, out chainedHeader3B); // wait for worker, it should not fire Assert.IsFalse(targetChainChangedEvent.WaitOne(50)); // verify no chaining done Assert.AreEqual(chainedHeader5A, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3A, chainedHeader4A, chainedHeader5A }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(6, onTargetChainChangedCount); // add block 4B ChainedHeader chainedHeader4B; coreStorage.TryChainHeader(header4B, out chainedHeader4B); // wait for worker targetChainChangedEvent.WaitOne(); // verify chained to block 4B Assert.AreEqual(chainedHeader4B, targetChainWorker.TargetChain.LastBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3B, chainedHeader4B }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(7, onTargetChainChangedCount); // verify no other work was done Assert.IsFalse(targetChainChangedEvent.WaitOne(50)); } }
public void TestSimpleChain() { // prepare test kernel var kernel = new StandardKernel(new ConsoleLoggingModule(), new MemoryStorageModule(), new CoreCacheModule()); // initialize data var fakeHeaders = new FakeHeaders(); var chainedHeader0 = new ChainedHeader(fakeHeaders.Genesis(), height: 0, totalWork: 0); var chainedHeader1 = new ChainedHeader(fakeHeaders.Next(), height: 1, totalWork: 1); var chainedHeader2 = new ChainedHeader(fakeHeaders.Next(), height: 2, totalWork: 2); // mock rules var mockRules = new Mock <IBlockchainRules>(); mockRules.Setup(rules => rules.GenesisChainedHeader).Returns(chainedHeader0); kernel.Bind <IBlockchainRules>().ToConstant(mockRules.Object); // store genesis block var chainedHeaderCache = kernel.Get <ChainedHeaderCache>(); chainedHeaderCache[chainedHeader0.Hash] = chainedHeader0; // initialize the target chain worker using (var targetChainWorker = kernel.Get <TargetChainWorker>(new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: false, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue)))) { // verify initial state Assert.AreEqual(null, targetChainWorker.TargetBlock); Assert.AreEqual(null, targetChainWorker.TargetChain); // monitor event firing var workNotifyEvent = new AutoResetEvent(false); var workStoppedEvent = new AutoResetEvent(false); var onTargetChainChangedCount = 0; targetChainWorker.OnNotifyWork += () => workNotifyEvent.Set(); targetChainWorker.OnWorkStopped += () => workStoppedEvent.Set(); targetChainWorker.OnTargetChainChanged += () => onTargetChainChangedCount++; // start worker and wait for initial chain targetChainWorker.Start(); workNotifyEvent.WaitOne(); workStoppedEvent.WaitOne(); // verify chained to block 0 Assert.AreEqual(chainedHeader0, targetChainWorker.TargetBlock); AssertBlockListEquals(new[] { chainedHeader0 }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(1, onTargetChainChangedCount); // add block 1 chainedHeaderCache[chainedHeader1.Hash] = chainedHeader1; // wait for worker (chained block addition) workNotifyEvent.WaitOne(); workStoppedEvent.WaitOne(); // wait for worker (target block changed) workNotifyEvent.WaitOneOrFail(1000); workStoppedEvent.WaitOneOrFail(1000); // verify chained to block 1 Assert.AreEqual(chainedHeader1, targetChainWorker.TargetBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1 }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(2, onTargetChainChangedCount); // add block 2 chainedHeaderCache[chainedHeader2.Hash] = chainedHeader2; // wait for worker (chained block addition) workNotifyEvent.WaitOne(); workStoppedEvent.WaitOne(); // wait for worker (target block changed) workNotifyEvent.WaitOneOrFail(1000); workStoppedEvent.WaitOneOrFail(1000); // verify chained to block 2 Assert.AreEqual(chainedHeader2, targetChainWorker.TargetBlock); AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2 }, targetChainWorker.TargetChain.Blocks); Assert.AreEqual(3, onTargetChainChangedCount); // verify no other work was done Assert.IsFalse(workNotifyEvent.WaitOne(0)); Assert.IsFalse(workStoppedEvent.WaitOne(0)); } }
public void TestTotalWork() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var totalWork0 = DataCalculator.CalculateWork(header0); var totalWork1 = totalWork0 + DataCalculator.CalculateWork(header1); var chainBuilder = new ChainBuilder(); // verify total work with 0 blocks Assert.AreEqual(0, chainBuilder.TotalWork); // verify total work with 1 block chainBuilder.AddBlock(header0); Assert.AreEqual(totalWork0, chainBuilder.TotalWork); // verify total work with 2 blocks chainBuilder.AddBlock(header1); Assert.AreEqual(totalWork1, chainBuilder.TotalWork); }
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 void TestBlocks() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(); // verify block list with 0 blocks Assert.AreEqual(0, chainBuilder.Blocks.Count); // verify block list with 1 block chainBuilder.AddBlock(header0); CollectionAssert.AreEqual(new[] { header0 }, chainBuilder.Blocks); // verify block list with 2 blocks chainBuilder.AddBlock(header1); CollectionAssert.AreEqual(new[] { header0, header1 }, chainBuilder.Blocks); }
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 void TestNavigateTowardsFunc() { // create chains var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var header2 = fakeHeaders.NextChained(); var header3 = fakeHeaders.NextChained(); var chain0 = new ChainBuilder(new[] { header0, }).ToImmutable(); var chain1 = new ChainBuilder(new[] { header0, header1, }).ToImmutable(); var chain2 = new ChainBuilder(new[] { header0, header1, header2 }).ToImmutable(); var chain3 = new ChainBuilder(new[] { header0, header1, header2, header3 }).ToImmutable(); // the list of target chains to use, stays 1 ahead and then catches up with chain 3 var targetStack = new Stack<Chain>(new[] { chain1, chain2, chain3, chain3 }.Reverse()); // verify navigating towards an updating chain CollectionAssert.AreEqual( new[] { Tuple.Create(+1, header1), Tuple.Create(+1, header2), Tuple.Create(+1, header3) } , chain0.NavigateTowards(() => targetStack.Pop()).ToList()); // verify all targets used Assert.AreEqual(0, targetStack.Count); }
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 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); }
private void TestTryAddGetRemoveHeader(ITestStorageProvider provider) { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify initial empty state ChainedHeader actualHeader0, actualHeader1; Assert.IsFalse(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0)); Assert.IsFalse(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader1)); // add header 0 Assert.IsTrue(chainStateCursor.TryAddHeader(header0)); // verify unspent txes Assert.IsTrue(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0)); Assert.AreEqual(header0, actualHeader0); Assert.IsFalse(chainStateCursor.TryGetHeader(header1.Hash, out actualHeader1)); // add header 1 Assert.IsTrue(chainStateCursor.TryAddHeader(header1)); // verify unspent txes Assert.IsTrue(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0)); Assert.AreEqual(header0, actualHeader0); Assert.IsTrue(chainStateCursor.TryGetHeader(header1.Hash, out actualHeader1)); Assert.AreEqual(header1, actualHeader1); // remove header 1 Assert.IsTrue(chainStateCursor.TryRemoveHeader(header1.Hash)); // verify unspent txes Assert.IsTrue(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0)); Assert.AreEqual(header0, actualHeader0); Assert.IsFalse(chainStateCursor.TryGetHeader(header1.Hash, out actualHeader1)); // remove header 0 Assert.IsTrue(chainStateCursor.TryRemoveHeader(header0.Hash)); // verify unspent txes Assert.IsFalse(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0)); Assert.IsFalse(chainStateCursor.TryGetHeader(header1.Hash, out actualHeader1)); } }
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); } }