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 var chainEmpty = chainBuilder.ToImmutable(); Assert.AreEqual(0, chainEmpty.Blocks.Count); // verify block list with 1 block chainBuilder.AddBlock(header0); var chain0 = chainBuilder.ToImmutable(); CollectionAssert.AreEqual(new[] { header0 }, chain0.Blocks); // verify block list with 2 blocks chainBuilder.AddBlock(header1); var chain1 = chainBuilder.ToImmutable(); CollectionAssert.AreEqual(new[] { header0, header1 }, chain1.Blocks); }
public void TestToBuilder() { 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(); var chainBuilderEmpty = chainEmpty.ToBuilder(); Assert.AreEqual(0, chainBuilderEmpty.Blocks.Count); Assert.AreEqual(0, chainBuilderEmpty.BlocksByHash.Count); // verify to builder with 1 block chainBuilder.AddBlock(header0); var chain0 = chainBuilder.ToImmutable(); var chainBuilder0 = chain0.ToBuilder(); CollectionAssert.AreEqual(new[] { header0 }, chainBuilder0.Blocks); CollectionAssert.AreEquivalent(new Dictionary <UInt256, ChainedHeader> { { header0.Hash, header0 } }, chainBuilder0.BlocksByHash); // verify to builder with 2 blocks chainBuilder.AddBlock(header1); var chain1 = chainBuilder.ToImmutable(); var chainBuilder1 = chain1.ToBuilder(); CollectionAssert.AreEqual(new[] { header0, header1 }, chainBuilder1.Blocks); CollectionAssert.AreEquivalent(new Dictionary <UInt256, ChainedHeader> { { header0.Hash, header0 }, { header1.Hash, header1 } }, chainBuilder1.BlocksByHash); }
// 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)); } }
// 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 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()); }
// IBlockStorage.IsBlockInvalid // IBlockStorage.MarkBlockInvalid private void TestMarkBlockInvalid(ITestStorageProvider provider) { using (var storageManager = provider.OpenStorageManager()) { var blockStorage = storageManager.BlockStorage; // create chained headers var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); // add headers blockStorage.TryAddChainedHeader(chainedHeader0); blockStorage.TryAddChainedHeader(chainedHeader1); // verify no blocks invalid Assert.IsFalse(blockStorage.IsBlockInvalid(chainedHeader0.Hash)); Assert.IsFalse(blockStorage.IsBlockInvalid(chainedHeader1.Hash)); // mark blocks invalid and verify // 0 blockStorage.MarkBlockInvalid(chainedHeader0.Hash); Assert.IsTrue(blockStorage.IsBlockInvalid(chainedHeader0.Hash)); Assert.IsFalse(blockStorage.IsBlockInvalid(chainedHeader1.Hash)); // 1 blockStorage.MarkBlockInvalid(chainedHeader1.Hash); Assert.IsTrue(blockStorage.IsBlockInvalid(chainedHeader0.Hash)); Assert.IsTrue(blockStorage.IsBlockInvalid(chainedHeader1.Hash)); } }
// IBlockStorage.TryAddChainedHeader // IBlockStorage.TryRemoveChainedHeader private void TestTryAddRemoveChainedHeader(ITestStorageProvider provider) { using (var storageManager = provider.OpenStorageManager()) { var blockStorage = storageManager.BlockStorage; // create a chained header var fakeHeaders = new FakeHeaders(); var chainedHeader = fakeHeaders.GenesisChained(); // verify header can be added Assert.IsTrue(blockStorage.TryAddChainedHeader(chainedHeader)); Assert.IsTrue(blockStorage.ContainsChainedHeader(chainedHeader.Hash)); // verify header cannot be added again Assert.IsFalse(blockStorage.TryAddChainedHeader(chainedHeader)); // remove the header Assert.IsTrue(blockStorage.TryRemoveChainedHeader(chainedHeader.Hash)); Assert.IsFalse(blockStorage.ContainsChainedHeader(chainedHeader.Hash)); // verify header cannot be removed again Assert.IsFalse(blockStorage.TryRemoveChainedHeader(chainedHeader.Hash)); // verify header can be added again, after being removed Assert.IsTrue(blockStorage.TryAddChainedHeader(chainedHeader)); Assert.IsTrue(blockStorage.ContainsChainedHeader(chainedHeader.Hash)); // verify header can be removed again, after being added again Assert.IsTrue(blockStorage.TryRemoveChainedHeader(chainedHeader.Hash)); Assert.IsFalse(blockStorage.ContainsChainedHeader(chainedHeader.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 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 TestLastBlock() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(); // verify last block with 0 blocks var chainEmpty = chainBuilder.ToImmutable(); Assert.IsNull(chainEmpty.LastBlock); // verify last block with 1 block chainBuilder.AddBlock(header0); var chain0 = chainBuilder.ToImmutable(); Assert.AreEqual(header0, chain0.LastBlock); // verify last block with 2 blocks chainBuilder.AddBlock(header1); var chain1 = chainBuilder.ToImmutable(); Assert.AreEqual(header1, chain1.LastBlock); }
public void Init() { var fakeHeaders = new FakeHeaders(); this.chainedHeader0 = fakeHeaders.GenesisChained(); this.chainedHeader1 = fakeHeaders.NextChained(); var fakeHeadersA = new FakeHeaders(fakeHeaders); this.chainedHeaderA2 = fakeHeadersA.NextChained(); this.chainedHeaderA3 = fakeHeadersA.NextChained(); this.chainedHeaderA4 = fakeHeadersA.NextChained(); var fakeHeadersB = new FakeHeaders(fakeHeaders); this.chainedHeaderB2 = fakeHeadersB.NextChained(); this.chainedHeaderB3 = fakeHeadersB.NextChained(); this.chainedHeaderB4 = fakeHeadersB.NextChained(); var fakeHeadersX = new FakeHeaders(); this.chainedHeaderX0 = fakeHeadersX.GenesisChained(); this.chainedHeaderX1 = fakeHeadersX.NextChained(); this.chain = ImmutableDictionary.CreateRange( new[] { chainedHeader0, chainedHeader1, chainedHeaderA2, chainedHeaderA3, chainedHeaderA4, chainedHeaderB2, chainedHeaderB3, chainedHeaderB4, chainedHeaderX0, chainedHeaderX1 } .Select(x => new KeyValuePair <UInt256, ChainedHeader>(x.Hash, x))); this.getChainedHeader = blockHash => this.chain[blockHash]; }
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 var chainEmpty = chainBuilder.ToImmutable(); Assert.AreEqual(0, chainEmpty.TotalWork); // verify total work with 1 block chainBuilder.AddBlock(header0); var chain0 = chainBuilder.ToImmutable(); Assert.AreEqual(totalWork0.ToBigInteger(), chain0.TotalWork); // verify total work with 2 blocks chainBuilder.AddBlock(header1); var chain1 = chainBuilder.ToImmutable(); Assert.AreEqual(totalWork1.ToBigInteger(), chain1.TotalWork); }
public void TestMissingHeader() { 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 <IChainStateCursor>(); storageManager.Setup(x => x.OpenChainStateCursor()).Returns( new DisposeHandle <IChainStateCursor>(_ => { }, chainStateCursor.Object)); // don't mock header 1 so it is missing chainStateCursor.Setup(x => x.TryGetHeader(header0.Hash, out header0)).Returns(true); chainStateCursor.Setup(x => x.TryGetHeader(header2.Hash, out header2)).Returns(true); // return header 2 as the chain tip chainStateCursor.Setup(x => x.ChainTip).Returns(header2); // init chain state builder with missing header StorageCorruptException actualEx; AssertMethods.AssertThrows <StorageCorruptException>(() => { var chain = new ChainStateBuilder(rules, coreStorage.Object, storageManager.Object).Chain; }, out actualEx); Assert.AreEqual(StorageType.ChainState, actualEx.StorageType); Assert.AreEqual("ChainState is missing header.", actualEx.Message); }
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 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 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); }
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 void TestCreateForGenesisBlock() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var chain = Chain.CreateForGenesisBlock(header0); CollectionAssert.AreEqual(new[] { header0 }, 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 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 })); }
private void TestReadChain(ITestStorageProvider provider) { var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify initial empty chain Assert.AreEqual(0, chainStateCursor.ReadChain().Count()); // add header 0 chainStateCursor.AddChainedHeader(chainedHeader0); // verify chain CollectionAssert.AreEqual(new[] { chainedHeader0 }, chainStateCursor.ReadChain().ToList()); // add header 1 chainStateCursor.AddChainedHeader(chainedHeader1); // verify chain CollectionAssert.AreEqual(new[] { chainedHeader0, chainedHeader1 }, chainStateCursor.ReadChain().ToList()); // add header 2 chainStateCursor.AddChainedHeader(chainedHeader2); // verify chain CollectionAssert.AreEqual(new[] { chainedHeader0, chainedHeader1, chainedHeader2 }, chainStateCursor.ReadChain().ToList()); // remove header 2 chainStateCursor.RemoveChainedHeader(chainedHeader2); // verify chain CollectionAssert.AreEqual(new[] { chainedHeader0, chainedHeader1 }, chainStateCursor.ReadChain().ToList()); // remove header 1 chainStateCursor.RemoveChainedHeader(chainedHeader1); // verify chain CollectionAssert.AreEqual(new[] { chainedHeader0 }, chainStateCursor.ReadChain().ToList()); // remove header 0 chainStateCursor.RemoveChainedHeader(chainedHeader0); // verify chain Assert.AreEqual(0, chainStateCursor.ReadChain().Count()); } }
private void TestGetChainTip(ITestStorageProvider provider) { var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // verify initial empty chain Assert.IsNull(chainStateCursor.GetChainTip()); // add header 0 chainStateCursor.AddChainedHeader(chainedHeader0); // verify chain tip Assert.AreEqual(chainedHeader0, chainStateCursor.GetChainTip()); // add header 1 chainStateCursor.AddChainedHeader(chainedHeader1); // verify chain tip Assert.AreEqual(chainedHeader1, chainStateCursor.GetChainTip()); // add header 2 chainStateCursor.AddChainedHeader(chainedHeader2); // verify chain tip Assert.AreEqual(chainedHeader2, chainStateCursor.GetChainTip()); // remove header 2 chainStateCursor.RemoveChainedHeader(chainedHeader2); // verify chain tip Assert.AreEqual(chainedHeader1, chainStateCursor.GetChainTip()); // remove header 1 chainStateCursor.RemoveChainedHeader(chainedHeader1); // verify chain tip Assert.AreEqual(chainedHeader0, chainStateCursor.GetChainTip()); // remove header 0 chainStateCursor.RemoveChainedHeader(chainedHeader0); // verify chain tip Assert.IsNull(chainStateCursor.GetChainTip()); } }
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: 0, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); var spentTxes = ImmutableList.Create(new SpentTx(txHash: 1, confirmedBlockIndex: 0, txIndex: 0, outputCount: 1, spentBlockIndex: 0)); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // add header 0 chainStateCursor.AddChainedHeader(chainedHeader0); // verify chain CollectionAssert.AreEqual(new[] { chainedHeader0 }, chainStateCursor.ReadChain().ToList()); // add unspent tx chainStateCursor.TryAddUnspentTx(unspentTx); // verify unspent tx Assert.IsTrue(chainStateCursor.ContainsUnspentTx(unspentTx.TxHash)); Assert.AreEqual(1, chainStateCursor.UnspentTxCount); // add spent txes chainStateCursor.TryAddBlockSpentTxes(0, spentTxes); // verify spent txes IImmutableList <SpentTx> actualSpentTxes; Assert.IsTrue(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes)); CollectionAssert.AreEqual(spentTxes, (ICollection)actualSpentTxes); // rollback transaction chainStateCursor.RollbackTransaction(); // verify chain Assert.AreEqual(0, chainStateCursor.ReadChain().Count()); // verify unspent tx Assert.IsFalse(chainStateCursor.ContainsUnspentTx(unspentTx.TxHash)); Assert.AreEqual(0, chainStateCursor.UnspentTxCount); // verify spent txes Assert.IsFalse(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes)); } }
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 = 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 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 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); }
private void TestCommitTransaction(ITestStorageProvider provider) { var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var unspentTx = new UnspentTx(txHash: 0, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Unspent)); var spentTxes = ImmutableList.Create(new SpentTx(txHash: 1, confirmedBlockIndex: 0, txIndex: 0, outputCount: 1, spentBlockIndex: 0)); using (var storageManager = provider.OpenStorageManager()) using (var handle = storageManager.OpenChainStateCursor()) { var chainStateCursor = handle.Item; // begin transaction chainStateCursor.BeginTransaction(); // add data chainStateCursor.AddChainedHeader(chainedHeader0); chainStateCursor.TryAddUnspentTx(unspentTx); chainStateCursor.TryAddBlockSpentTxes(0, spentTxes); // verify data Assert.AreEqual(chainedHeader0, chainStateCursor.GetChainTip()); Assert.AreEqual(1, chainStateCursor.UnspentTxCount); UnspentTx actualUnspentTx; Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx.TxHash, out actualUnspentTx)); Assert.AreEqual(unspentTx, actualUnspentTx); IImmutableList <SpentTx> actualSpentTxes; Assert.IsTrue(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes)); CollectionAssert.AreEqual((ICollection)spentTxes, (ICollection)actualSpentTxes); // commit transaction chainStateCursor.CommitTransaction(); // verify data Assert.AreEqual(chainedHeader0, chainStateCursor.GetChainTip()); 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((ICollection)spentTxes, (ICollection)actualSpentTxes); } }
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)); }
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 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()); }
// IBlockStorage.FindMaxTotalWork private void TestFindMaxTotalWork(ITestStorageProvider provider) { using (var storageManager = provider.OpenStorageManager()) { var blockStorage = storageManager.BlockStorage; // create chained headers var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); // verify initial null state Assert.IsNull(blockStorage.FindMaxTotalWork()); // add headers and verify max total work // 0 blockStorage.TryAddChainedHeader(chainedHeader0); Assert.AreEqual(chainedHeader0, blockStorage.FindMaxTotalWork()); // 1 blockStorage.TryAddChainedHeader(chainedHeader1); Assert.AreEqual(chainedHeader1, blockStorage.FindMaxTotalWork()); // 2 blockStorage.TryAddChainedHeader(chainedHeader2); Assert.AreEqual(chainedHeader2, blockStorage.FindMaxTotalWork()); // remove headers and verify max total work // 2 blockStorage.TryRemoveChainedHeader(chainedHeader2.Hash); Assert.AreEqual(chainedHeader1, blockStorage.FindMaxTotalWork()); // 1 blockStorage.TryRemoveChainedHeader(chainedHeader1.Hash); Assert.AreEqual(chainedHeader0, blockStorage.FindMaxTotalWork()); // 0 blockStorage.TryRemoveChainedHeader(chainedHeader0.Hash); Assert.IsNull(blockStorage.FindMaxTotalWork()); } }
// IBlockStorage.ReadChainedHeaders private void TestReadChainedHeaders(ITestStorageProvider provider) { using (var storageManager = provider.OpenStorageManager()) { var blockStorage = storageManager.BlockStorage; // create chained headers var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); // verify initial empty state Assert.AreEqual(0, blockStorage.ReadChainedHeaders().ToList().Count); // add headers and verify reading them // 0 blockStorage.TryAddChainedHeader(chainedHeader0); CollectionAssert.AreEquivalent(new[] { chainedHeader0 }, blockStorage.ReadChainedHeaders().ToList()); // 1 blockStorage.TryAddChainedHeader(chainedHeader1); CollectionAssert.AreEquivalent(new[] { chainedHeader0, chainedHeader1 }, blockStorage.ReadChainedHeaders().ToList()); // 2 blockStorage.TryAddChainedHeader(chainedHeader2); CollectionAssert.AreEquivalent(new[] { chainedHeader0, chainedHeader1, chainedHeader2 }, blockStorage.ReadChainedHeaders().ToList()); // remove headers and verify reading them // 2 blockStorage.TryRemoveChainedHeader(chainedHeader2.Hash); CollectionAssert.AreEquivalent(new[] { chainedHeader0, chainedHeader1 }, blockStorage.ReadChainedHeaders().ToList()); // 1 blockStorage.TryRemoveChainedHeader(chainedHeader1.Hash); CollectionAssert.AreEquivalent(new[] { chainedHeader0 }, blockStorage.ReadChainedHeaders().ToList()); // 0 blockStorage.TryRemoveChainedHeader(chainedHeader0.Hash); Assert.AreEqual(0, blockStorage.ReadChainedHeaders().ToList().Count); } }
public void TestGenesisBlock() { var fakeHeaders = new FakeHeaders(); var header0 = fakeHeaders.GenesisChained(); var header1 = fakeHeaders.NextChained(); var chainBuilder = new ChainBuilder(); // verify genesis with 0 blocks Assert.IsNull(chainBuilder.GenesisBlock); // verify genesis with 1 block chainBuilder.AddBlock(header0); Assert.AreEqual(header0, chainBuilder.GenesisBlock); // verify genesis with 2 blocks chainBuilder.AddBlock(header1); Assert.AreEqual(header0, chainBuilder.GenesisBlock); }
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 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 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 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 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 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 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); }
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 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 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 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 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 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)); }
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); }
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 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 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 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); }
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 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); }
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 TestNavigateTowards() { // create forked chains var fakeHeadersA = new FakeHeaders(); var header0 = fakeHeadersA.GenesisChained(); var header1 = fakeHeadersA.NextChained(); var fakeHeadersB = new FakeHeaders(fakeHeadersA); var header2A = fakeHeadersA.NextChained(); var header3A = fakeHeadersA.NextChained(); var header4A = fakeHeadersA.NextChained(); var header2B = fakeHeadersB.NextChained(); var header3B = fakeHeadersB.NextChained(); var header4B = fakeHeadersB.NextChained(); var chain0 = new ChainBuilder(new[] { header0, header1, header2A, header3A }).ToImmutable(); var chain1 = new ChainBuilder(new[] { header0, header1, header2B, header3B, header4B }).ToImmutable(); // verify path from chain 0 to chain 1 CollectionAssert.AreEqual( new[] { Tuple.Create(-1, header3A), Tuple.Create(-1, header2A), Tuple.Create(+1, header2B), Tuple.Create(+1, header3B), Tuple.Create(+1, header4B) } , chain0.NavigateTowards(chain1).ToList()); // verify path from chain 1 to chain 0 CollectionAssert.AreEqual( new[] { Tuple.Create(-1, header4B), Tuple.Create(-1, header3B), Tuple.Create(-1, header2B), Tuple.Create(+1, header2A), Tuple.Create(+1, header3A) } , chain1.NavigateTowards(chain0).ToList()); }