Ejemplo n.º 1
0
        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);
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        public CoreDaemon(ICoreRules rules, IStorageManager storageManager)
        {
            this.rules          = rules;
            this.storageManager = storageManager;
            coreStorage         = new CoreStorage(storageManager);

            // create chain state builder
            chainStateBuilder = new ChainStateBuilder(this.rules, coreStorage, this.storageManager);

            // create unconfirmed txes builder
            unconfirmedTxesBuilder = new UnconfirmedTxesBuilder(this, coreStorage, this.storageManager);

            // create workers
            targetChainWorker = new TargetChainWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(30)),
                ChainParams, coreStorage);

            chainStateWorker = new ChainStateWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(5)),
                targetChainWorker, chainStateBuilder, this.rules, coreStorage);

            unconfirmedTxesWorker = new UnconfirmedTxesWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(5)),
                chainStateWorker, unconfirmedTxesBuilder, coreStorage);

            pruningWorker = new PruningWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromSeconds(0), maxIdleTime: TimeSpan.FromMinutes(5)),
                this, this.storageManager, chainStateWorker);

            defragWorker = new DefragWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5)),
                this.storageManager);

            gcWorker = new WorkerMethod("GC Worker", GcWorker,
                                        initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5));

            utxoScanWorker = new WorkerMethod("UTXO Scan Worker", UtxoScanWorker,
                                              initialNotify: true, minIdleTime: TimeSpan.FromSeconds(60), maxIdleTime: TimeSpan.FromSeconds(60));

            statsWorker = new StatsWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMinutes(0), maxIdleTime: TimeSpan.MaxValue),
                this);

            // wire events
            chainStateWorker.BlockMissed              += HandleBlockMissed;
            targetChainWorker.OnTargetChainChanged    += HandleTargetChainChanged;
            chainStateWorker.OnChainStateChanged      += HandleChainStateChanged;
            pruningWorker.OnWorkFinished              += defragWorker.NotifyWork;
            unconfirmedTxesBuilder.UnconfirmedTxAdded += RaiseUnconfirmedTxAdded;
            unconfirmedTxesBuilder.TxesConfirmed      += RaiseTxesConfirmed;
            unconfirmedTxesBuilder.TxesUnconfirmed    += RaiseTxesUnconfirmed;
        }
Ejemplo n.º 5
0
        public CoreDaemon(ICoreRules rules, IStorageManager storageManager)
        {
            this.rules = rules;
            this.storageManager = storageManager;
            coreStorage = new CoreStorage(storageManager);

            // create chain state builder
            chainStateBuilder = new ChainStateBuilder(this.rules, coreStorage, this.storageManager);

            // create unconfirmed txes builder
            unconfirmedTxesBuilder = new UnconfirmedTxesBuilder(this, coreStorage, this.storageManager);

            // create workers
            targetChainWorker = new TargetChainWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(30)),
                ChainParams, coreStorage);

            chainStateWorker = new ChainStateWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(5)),
                targetChainWorker, chainStateBuilder, this.rules, coreStorage);

            unconfirmedTxesWorker = new UnconfirmedTxesWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(5)),
                chainStateWorker, unconfirmedTxesBuilder, coreStorage);

            pruningWorker = new PruningWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromSeconds(0), maxIdleTime: TimeSpan.FromMinutes(5)),
                this, this.storageManager, chainStateWorker);

            defragWorker = new DefragWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5)),
                this.storageManager);

            gcWorker = new WorkerMethod("GC Worker", GcWorker,
                initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5));

            utxoScanWorker = new WorkerMethod("UTXO Scan Worker", UtxoScanWorker,
                initialNotify: true, minIdleTime: TimeSpan.FromSeconds(60), maxIdleTime: TimeSpan.FromSeconds(60));

            statsWorker = new StatsWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMinutes(0), maxIdleTime: TimeSpan.MaxValue),
                this);

            // wire events
            chainStateWorker.BlockMissed += HandleBlockMissed;
            targetChainWorker.OnTargetChainChanged += HandleTargetChainChanged;
            chainStateWorker.OnChainStateChanged += HandleChainStateChanged;
            pruningWorker.OnWorkFinished += defragWorker.NotifyWork;
            unconfirmedTxesBuilder.UnconfirmedTxAdded += RaiseUnconfirmedTxAdded;
            unconfirmedTxesBuilder.TxesConfirmed += RaiseTxesConfirmed;
            unconfirmedTxesBuilder.TxesUnconfirmed += RaiseTxesUnconfirmed;
        }
Ejemplo n.º 6
0
        public PruningWorker(WorkerConfig workerConfig, CoreStorage coreStorage, ChainStateWorker chainStateWorker, ChainStateBuilder chainStateBuilder, Logger logger, IBlockchainRules rules)
            : base("PruningWorker", workerConfig.initialNotify, workerConfig.minIdleTime, workerConfig.maxIdleTime, logger)
        {
            this.logger            = logger;
            this.coreStorage       = coreStorage;
            this.chainStateWorker  = chainStateWorker;
            this.chainStateBuilder = chainStateBuilder;
            this.rules             = rules;

            this.lastPruneHeight = 0;

            this.Mode = PruningMode.RollbackAndBlocks;
        }
Ejemplo n.º 7
0
        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);
        }
Ejemplo n.º 8
0
        public void TestInvalidMerkleRoot()
        {
            // prepare mocks
            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.CursorCount).Returns(1);
            chainStateCursor.Setup(x => x.DataFlowBlocks).Returns(new IDataflowBlock[0]);

            // prepare a test block
            var testBlocks = new TestBlocks();
            var rules      = testBlocks.Rules;

            var block         = testBlocks.MineAndAddBlock(txCount: 10);
            var chainedHeader = testBlocks.Chain.LastBlock;

            // create an invalid version of the header where the merkle root is incorrect
            var invalidChainedHeader = ChainedHeader.CreateFromPrev(rules.ChainParams.GenesisChainedHeader, block.Header.With(MerkleRoot: UInt256.Zero), DateTimeOffset.Now);

            // mock genesis block & chain tip
            var genesisHeader = rules.ChainParams.GenesisChainedHeader;

            chainStateCursor.Setup(x => x.ChainTip).Returns(genesisHeader);
            chainStateCursor.Setup(x => x.TryGetHeader(genesisHeader.Hash, out genesisHeader)).Returns(true);

            // mock invalid block
            chainStateCursor.Setup(x => x.TryGetHeader(chainedHeader.Hash, out invalidChainedHeader)).Returns(true);

            // init chain state builder
            var chainStateBuilder = new ChainStateBuilder(rules, coreStorage.Object, storageManager.Object);

            Assert.AreEqual(rules.ChainParams.GenesisBlock.Hash, chainStateBuilder.Chain.LastBlock.Hash);

            // attempt to add block with invalid merkle root
            ValidationException actualEx;

            AssertMethods.AssertAggregateThrows <ValidationException>(() =>
                                                                      chainStateBuilder.AddBlockAsync(invalidChainedHeader, Enumerable.Empty <BlockTx>()).Wait(),
                                                                      out actualEx);

            // verify error
            Assert.AreEqual($"Failing block {invalidChainedHeader.Hash} at height 1: Merkle root is invalid", actualEx.Message);
        }
Ejemplo n.º 9
0
        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);
        }
Ejemplo n.º 10
0
        public void TestDoubleSpend()
        {
            // prepare test kernel
            var kernel = new StandardKernel(new MemoryStorageModule());

            // prepare block
            var fakeHeaders   = new FakeHeaders();
            var chainedHeader = new ChainedHeader(fakeHeaders.Genesis(), height: 0, totalWork: 0);

            // prepare an unspent transaction
            var txHash    = new UInt256(100);
            var unspentTx = new UnspentTx(chainedHeader.Hash, 1, OutputState.Unspent);

            // mock a parent utxo containing the unspent transaction
            var unspentTransactions         = ImmutableDictionary.Create <UInt256, UnspentTx>().Add(txHash, unspentTx);
            var mockParentChainStateStorage = new Mock <IChainStateStorage>();

            mockParentChainStateStorage.Setup(utxo => utxo.UnspentTransactions()).Returns(unspentTransactions);
            var parentUtxo = new Utxo(mockParentChainStateStorage.Object);

            // initialize memory utxo builder storage
            var memoryChainStateBuilderStorage = new MemoryChainStateBuilderStorage(mockParentChainStateStorage.Object);

            kernel.Rebind <IChainStateBuilderStorage>().ToConstant(memoryChainStateBuilderStorage);

            // initialize utxo builder
            var chainStateBuilder = new ChainStateBuilder(null, parentUtxo, LogManager.CreateNullLogger(), kernel, null, null, null, null, null);

            // create an input to spend the unspent transaction
            var input = new TxInput(new TxOutputKey(txHash, txOutputIndex: 0), ImmutableArray.Create <byte>(), 0);
            var tx    = new Transaction(0, ImmutableArray.Create(input), ImmutableArray.Create <TxOutput>(), 0);

            // spend the input
            chainStateBuilder.Spend(0, tx, 0, input, chainedHeader);

            // verify utxo storage
            Assert.IsFalse(memoryChainStateBuilderStorage.UnspentTransactionsDictionary.ContainsKey(txHash));

            // attempt to spend the input again
            chainStateBuilder.Spend(0, tx, 0, input, chainedHeader);

            // validation exception should be thrown
        }
Ejemplo n.º 11
0
        public ChainStateWorker(WorkerConfig workerConfig, TargetChainWorker targetChainWorker, ChainStateBuilder chainStateBuilder, IBlockchainRules rules, CoreStorage coreStorage)
            : base("ChainStateWorker", workerConfig.initialNotify, workerConfig.minIdleTime, workerConfig.maxIdleTime)
        {
            this.rules = rules;
            this.coreStorage = coreStorage;

            this.blockProcessingDurationMeasure = new DurationMeasure(sampleCutoff: TimeSpan.FromMinutes(5));
            this.blockMissCountMeasure = new CountMeasure(TimeSpan.FromSeconds(30));

            this.targetChainWorker = targetChainWorker;
            this.chainStateBuilder = chainStateBuilder;
            this.currentChain = new Lazy<Chain>(() => this.chainStateBuilder.Chain);

            this.coreStorage.BlockInvalidated += HandleChanged;
            this.coreStorage.BlockTxesAdded += HandleChanged;
            this.coreStorage.BlockTxesRemoved += HandleChanged;
            this.coreStorage.ChainedHeaderAdded += HandleChanged;
            this.targetChainWorker.OnTargetChainChanged += HandleChanged;
        }
Ejemplo n.º 12
0
        public ChainStateWorker(WorkerConfig workerConfig, TargetChainWorker targetChainWorker, ChainStateBuilder chainStateBuilder, ICoreRules rules, CoreStorage coreStorage)
            : base("ChainStateWorker", workerConfig.initialNotify, workerConfig.minIdleTime, workerConfig.maxIdleTime)
        {
            this.rules       = rules;
            this.coreStorage = coreStorage;

            this.blockProcessingDurationMeasure = new DurationMeasure(sampleCutoff: TimeSpan.FromMinutes(5));
            this.blockMissCountMeasure          = new CountMeasure(TimeSpan.FromSeconds(30));

            this.targetChainWorker = targetChainWorker;
            this.chainStateBuilder = chainStateBuilder;
            this.currentChain      = new Lazy <Chain>(() => this.chainStateBuilder.Chain);

            this.coreStorage.BlockInvalidated           += HandleChanged;
            this.coreStorage.BlockTxesAdded             += HandleChanged;
            this.coreStorage.BlockTxesRemoved           += HandleChanged;
            this.coreStorage.ChainedHeaderAdded         += HandleChanged;
            this.coreStorage.ChainedHeaderRemoved       += HandleChanged;
            this.targetChainWorker.OnTargetChainChanged += HandleChanged;
        }
Ejemplo n.º 13
0
        public ChainStateWorker(TargetChainWorker targetChainWorker, ChainStateBuilder chainStateBuilder, Func <Chain> getTargetChain, WorkerConfig workerConfig, Logger logger, IKernel kernel, IBlockchainRules rules, BlockCache blockCache, SpentTransactionsCache spentTransactionsCache, InvalidBlockCache invalidBlockCache)
            : base("ChainStateWorker", workerConfig.initialNotify, workerConfig.minIdleTime, workerConfig.maxIdleTime, logger)
        {
            this.logger                 = logger;
            this.getTargetChain         = getTargetChain;
            this.kernel                 = kernel;
            this.rules                  = rules;
            this.blockCache             = blockCache;
            this.spentTransactionsCache = spentTransactionsCache;
            this.invalidBlockCache      = invalidBlockCache;

            this.blockProcessingDurationMeasure = new DurationMeasure();

            this.targetChainWorker = targetChainWorker;
            this.chainStateBuilder = chainStateBuilder;
            this.currentChain      = this.chainStateBuilder.Chain.ToImmutable();

            this.pruningWorker = kernel.Get <PruningWorker>(
                new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: false, minIdleTime: TimeSpan.FromSeconds(30), maxIdleTime: TimeSpan.FromMinutes(5))),
                new ConstructorArgument("getChainStateBuilder", (Func <ChainStateBuilder>)(() => this.chainStateBuilder)));
        }
Ejemplo n.º 14
0
        public CoreDaemon(IBlockchainRules rules, IStorageManager storageManager)
        {
            this.rules = rules;
            this.storageManager = storageManager;
            this.coreStorage = new CoreStorage(storageManager);

            // create chain state builder
            this.chainStateBuilder = new ChainStateBuilder(this.rules, this.coreStorage, this.storageManager);

            // create workers
            this.targetChainWorker = new TargetChainWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(50), maxIdleTime: TimeSpan.FromSeconds(30)),
                this.rules, this.coreStorage);

            this.chainStateWorker = new ChainStateWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(5)),
                this.targetChainWorker, this.chainStateBuilder, this.rules, this.coreStorage);

            this.pruningWorker = new PruningWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromSeconds(0), maxIdleTime: TimeSpan.FromMinutes(5)),
                this, this.storageManager, this.chainStateWorker);

            this.defragWorker = new DefragWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5)),
                this.storageManager);

            this.gcWorker = new WorkerMethod("GC Worker", GcWorker,
                initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5));

            this.utxoScanWorker = new WorkerMethod("UTXO Scan Worker", UtxoScanWorker,
                initialNotify: true, minIdleTime: TimeSpan.FromSeconds(60), maxIdleTime: TimeSpan.FromSeconds(60));

            // wire events
            this.chainStateWorker.BlockMissed += HandleBlockMissed;
            this.targetChainWorker.OnTargetChainChanged += HandleTargetChainChanged;
            this.chainStateWorker.OnChainStateChanged += HandleChainStateChanged;
            this.pruningWorker.OnWorkFinished += this.defragWorker.NotifyWork;
        }
Ejemplo n.º 15
0
        private TxOutput LookupPreviousOutput(TxOutputKey txOutputKey, ChainedBlock chainedBlock, Dictionary <UInt256, int> blockTxIndices, ChainStateBuilder chainStateBuilder)
        {
            TxOutput prevOutput;

            if (chainStateBuilder.TryGetOutput(txOutputKey, out prevOutput))
            {
                return(prevOutput);
            }
            else
            {
                Transaction prevTx;
                int         prevTxIndex;
                if (blockTxIndices.TryGetValue(txOutputKey.TxHash, out prevTxIndex))
                {
                    Debug.Assert(prevTxIndex >= 0 && prevTxIndex < chainedBlock.Transactions.Count);
                    prevTx = chainedBlock.Transactions[prevTxIndex];
                    Debug.Assert(prevTx.Hash == txOutputKey.TxHash);
                }
                else
                {
                    throw new ValidationException(chainedBlock.Hash);
                }

                var outputIndex = unchecked ((int)txOutputKey.TxOutputIndex);
                if (outputIndex < 0 || outputIndex >= prevTx.Outputs.Count)
                {
                    throw new ValidationException(chainedBlock.Hash);
                }

                return(prevTx.Outputs[outputIndex]);
            }
        }
Ejemplo n.º 16
0
        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);
        }
Ejemplo n.º 17
0
        public void TestInvalidMerkleRoot()
        {
            // prepare mocks
            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.CursorCount).Returns(1);
            chainStateCursor.Setup(x => x.DataFlowBlocks).Returns(new IDataflowBlock[0]);

            // prepare a test block
            var testBlocks = new TestBlocks();
            var rules = testBlocks.Rules;

            var block = testBlocks.MineAndAddBlock(txCount: 10);
            var chainedHeader = testBlocks.Chain.LastBlock;

            // create an invalid version of the header where the merkle root is incorrect
            var invalidChainedHeader = ChainedHeader.CreateFromPrev(rules.ChainParams.GenesisChainedHeader, block.Header.With(MerkleRoot: UInt256.Zero), DateTimeOffset.Now);

            // mock genesis block & chain tip
            var genesisHeader = rules.ChainParams.GenesisChainedHeader;
            chainStateCursor.Setup(x => x.ChainTip).Returns(genesisHeader);
            chainStateCursor.Setup(x => x.TryGetHeader(genesisHeader.Hash, out genesisHeader)).Returns(true);

            // mock invalid block
            chainStateCursor.Setup(x => x.TryGetHeader(chainedHeader.Hash, out invalidChainedHeader)).Returns(true);

            // init chain state builder
            var chainStateBuilder = new ChainStateBuilder(rules, coreStorage.Object, storageManager.Object);
            Assert.AreEqual(rules.ChainParams.GenesisBlock.Hash, chainStateBuilder.Chain.LastBlock.Hash);

            // attempt to add block with invalid merkle root
            ValidationException actualEx;
            AssertMethods.AssertAggregateThrows<ValidationException>(() =>
                chainStateBuilder.AddBlockAsync(invalidChainedHeader, Enumerable.Empty<BlockTx>()).Wait(),
                out actualEx);

            // verify error
            Assert.AreEqual($"Failing block {invalidChainedHeader.Hash} at height 1: Merkle root is invalid", actualEx.Message);
        }
Ejemplo n.º 18
0
        private void TestRollback(ITestStorageProvider provider)
        {
            ConsoleLoggingModule.Configure();
            var logger = LogManager.GetCurrentClassLogger();

            var blockCount = 10.THOUSAND();
            var checkUtxoHashFrequencey = 1000;

            var blockProvider = new TestNet3BlockProvider();
            var blocks = blockProvider.ReadBlocks().Take(blockCount).ToList();

            var genesisBlock = blocks[0];
            var genesisHeader = new ChainedHeader(genesisBlock.Header, height: 0, totalWork: 0, dateSeen: DateTimeOffset.Now);
            var genesisChain = Chain.CreateForGenesisBlock(genesisHeader);

            var chainParams = new Testnet3Params();
            var rules = new CoreRules(chainParams)
            {
                IgnoreScripts = true,
                IgnoreSignatures = true,
                IgnoreScriptErrors = true
            };

            using (var storageManager = provider.OpenStorageManager())
            using (var coreStorage = new CoreStorage(storageManager))
            using (var chainStateBuilder = new ChainStateBuilder(rules, coreStorage, storageManager))
            {
                // add blocks to storage
                coreStorage.AddGenesisBlock(ChainedHeader.CreateForGenesisBlock(blocks[0].Header));
                foreach (var block in blocks)
                    coreStorage.TryAddBlock(block);

                // store empty utxo hash
                var expectedUtxoHashes = new List<UInt256>();
                using (var chainState = chainStateBuilder.ToImmutable())
                    expectedUtxoHashes.Add(UtxoCommitment.ComputeHash(chainState));

                // calculate utxo forward and store its state at each step along the way
                for (var blockIndex = 0; blockIndex < blocks.Count; blockIndex++)
                {
                    logger.Info($"Adding: {blockIndex:N0}");

                    var block = blocks[blockIndex];
                    var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now);

                    chainStateBuilder.AddBlockAsync(chainedHeader, block.Transactions.Select(
                        (tx, txIndex) => BlockTx.Create(txIndex, tx))).Wait();

                    if (blockIndex % checkUtxoHashFrequencey == 0 || blockIndex == blocks.Count - 1)
                        using (var chainState = chainStateBuilder.ToImmutable())
                            expectedUtxoHashes.Add(UtxoCommitment.ComputeHash(chainState));
                }

                // verify the utxo state before rolling back
                var expectedLastUtxoHash = UInt256.ParseHex("5f155c7d8a5c850d5fb2566aec5110caa40e270184126d17022ae9780fd65fd9");
                Assert.AreEqual(expectedLastUtxoHash, expectedUtxoHashes.Last());
                expectedUtxoHashes.RemoveAt(expectedUtxoHashes.Count - 1);

                // roll utxo backwards and validate its state at each step along the way
                for (var blockIndex = blocks.Count - 1; blockIndex >= 0; blockIndex--)
                {
                    logger.Info($"Rolling back: {blockIndex:N0}");

                    var block = blocks[blockIndex];
                    var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now);
                    var blockTxes = block.Transactions.Select((tx, txIndex) => BlockTx.Create(txIndex, tx));

                    chainStateBuilder.RollbackBlock(chainedHeader, blockTxes);

                    if ((blockIndex - 1) % checkUtxoHashFrequencey == 0 || blockIndex == 0)
                    {
                        var expectedUtxoHash = expectedUtxoHashes.Last();
                        expectedUtxoHashes.RemoveAt(expectedUtxoHashes.Count - 1);

                        using (var chainState = chainStateBuilder.ToImmutable())
                            Assert.AreEqual(expectedUtxoHash, UtxoCommitment.ComputeHash(chainState));
                    }
                }

                // verify chain state was rolled all the way back
                Assert.AreEqual(-1, chainStateBuilder.Chain.Height);
                Assert.AreEqual(0, expectedUtxoHashes.Count);

                // calculate utxo forward again
                for (var blockIndex = 0; blockIndex < blocks.Count; blockIndex++)
                {
                    logger.Info($"Adding: {blockIndex:N0}");

                    var block = blocks[blockIndex];
                    var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now);

                    chainStateBuilder.AddBlockAsync(chainedHeader, block.Transactions.Select(
                        (tx, txIndex) => BlockTx.Create(txIndex, tx))).Wait();
                }

                // verify final utxo state again
                using (var chainState = chainStateBuilder.ToImmutable())
                    Assert.AreEqual(expectedLastUtxoHash, UtxoCommitment.ComputeHash(chainState));
            }
        }
Ejemplo n.º 19
0
        public void TestSimpleSpend()
        {
            // prepare test kernel
            var kernel = new StandardKernel(new MemoryStorageModule());

            // prepare block
            var fakeHeaders   = new FakeHeaders();
            var chainedHeader = new ChainedHeader(fakeHeaders.Genesis(), height: 0, totalWork: 0);

            // prepare an unspent transaction
            var txHash    = new UInt256(100);
            var unspentTx = new UnspentTx(chainedHeader.Hash, 3, OutputState.Unspent);

            // prepare unspent output
            var unspentTransactions = ImmutableDictionary.Create <UInt256, UnspentTx>().Add(txHash, unspentTx);
            var unspentOutputs      = ImmutableDictionary.Create <TxOutputKey, TxOutput>()
                                      .Add(new TxOutputKey(txHash, 0), new TxOutput(0, ImmutableArray.Create <byte>()))
                                      .Add(new TxOutputKey(txHash, 1), new TxOutput(0, ImmutableArray.Create <byte>()))
                                      .Add(new TxOutputKey(txHash, 2), new TxOutput(0, ImmutableArray.Create <byte>()));

            // mock a parent utxo containing the unspent transaction
            var mockParentChainStateStorage = new Mock <IChainStateStorage>();

            mockParentChainStateStorage.Setup(utxo => utxo.UnspentTransactions()).Returns(unspentTransactions);
            mockParentChainStateStorage.Setup(utxo => utxo.UnspentOutputs()).Returns(unspentOutputs);
            var parentUtxo = new Utxo(mockParentChainStateStorage.Object);

            // initialize memory utxo builder storage
            var memoryChainStateBuilderStorage = new MemoryChainStateBuilderStorage(mockParentChainStateStorage.Object);

            kernel.Rebind <IChainStateBuilderStorage>().ToConstant(memoryChainStateBuilderStorage);

            // initialize utxo builder
            var chainStateBuilder = new ChainStateBuilder(null, parentUtxo, LogManager.CreateNullLogger(), kernel, null, null, null, null, null);

            // create an input to spend the unspent transaction's first output
            var input0 = new TxInput(new TxOutputKey(txHash, txOutputIndex: 0), ImmutableArray.Create <byte>(), 0);
            var tx0    = new Transaction(0, ImmutableArray.Create(input0), ImmutableArray.Create <TxOutput>(), 0);

            // spend the input
            chainStateBuilder.Spend(0, tx0, 0, input0, chainedHeader);

            // verify utxo storage
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary.ContainsKey(txHash));
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary[txHash].OutputStates.Length == 3);
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary[txHash].OutputStates[0] == OutputState.Spent);
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary[txHash].OutputStates[1] == OutputState.Unspent);
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary[txHash].OutputStates[2] == OutputState.Unspent);

            // create an input to spend the unspent transaction's second output
            var input1 = new TxInput(new TxOutputKey(txHash, txOutputIndex: 1), ImmutableArray.Create <byte>(), 0);
            var tx1    = new Transaction(0, ImmutableArray.Create(input1), ImmutableArray.Create <TxOutput>(), 0);

            // spend the input
            chainStateBuilder.Spend(1, tx1, 1, input1, chainedHeader);

            // verify utxo storage
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary.ContainsKey(txHash));
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary[txHash].OutputStates.Length == 3);
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary[txHash].OutputStates[0] == OutputState.Spent);
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary[txHash].OutputStates[1] == OutputState.Spent);
            Assert.IsTrue(memoryChainStateBuilderStorage.UnspentTransactionsDictionary[txHash].OutputStates[2] == OutputState.Unspent);

            // create an input to spend the unspent transaction's third output
            var input2 = new TxInput(new TxOutputKey(txHash, txOutputIndex: 2), ImmutableArray.Create <byte>(), 0);
            var tx2    = new Transaction(0, ImmutableArray.Create(input2), ImmutableArray.Create <TxOutput>(), 0);

            // spend the input
            chainStateBuilder.Spend(2, tx2, 2, input2, chainedHeader);

            // verify utxo storage
            Assert.IsFalse(memoryChainStateBuilderStorage.UnspentTransactionsDictionary.ContainsKey(txHash));
        }
Ejemplo n.º 20
0
        private void TestRollback(ITestStorageProvider provider)
        {
            ConsoleLoggingModule.Configure();
            var logger = LogManager.GetCurrentClassLogger();

            var blockCount = 10.THOUSAND();
            var checkUtxoHashFrequencey = 1000;

            var blockProvider = new TestNet3BlockProvider();
            var blocks        = blockProvider.ReadBlocks().Take(blockCount).ToList();

            var genesisBlock  = blocks[0];
            var genesisHeader = new ChainedHeader(genesisBlock.Header, height: 0, totalWork: 0, dateSeen: DateTimeOffset.Now);
            var genesisChain  = Chain.CreateForGenesisBlock(genesisHeader);

            var chainParams = new Testnet3Params();
            var rules       = new CoreRules(chainParams)
            {
                IgnoreScripts      = true,
                IgnoreSignatures   = true,
                IgnoreScriptErrors = true
            };

            using (var storageManager = provider.OpenStorageManager())
                using (var coreStorage = new CoreStorage(storageManager))
                    using (var chainStateBuilder = new ChainStateBuilder(rules, coreStorage, storageManager))
                    {
                        // add blocks to storage
                        coreStorage.AddGenesisBlock(ChainedHeader.CreateForGenesisBlock(blocks[0].Header));
                        foreach (var block in blocks)
                        {
                            coreStorage.TryAddBlock(block);
                        }

                        // store empty utxo hash
                        var expectedUtxoHashes = new List <UInt256>();
                        using (var chainState = chainStateBuilder.ToImmutable())
                            expectedUtxoHashes.Add(UtxoCommitment.ComputeHash(chainState));

                        // calculate utxo forward and store its state at each step along the way
                        for (var blockIndex = 0; blockIndex < blocks.Count; blockIndex++)
                        {
                            logger.Info($"Adding: {blockIndex:N0}");

                            var block         = blocks[blockIndex];
                            var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now);

                            chainStateBuilder.AddBlockAsync(chainedHeader, block.Transactions.Select(
                                                                (tx, txIndex) => BlockTx.Create(txIndex, tx))).Wait();

                            if (blockIndex % checkUtxoHashFrequencey == 0 || blockIndex == blocks.Count - 1)
                            {
                                using (var chainState = chainStateBuilder.ToImmutable())
                                    expectedUtxoHashes.Add(UtxoCommitment.ComputeHash(chainState));
                            }
                        }

                        // verify the utxo state before rolling back
                        var expectedLastUtxoHash = UInt256.ParseHex("5f155c7d8a5c850d5fb2566aec5110caa40e270184126d17022ae9780fd65fd9");
                        Assert.AreEqual(expectedLastUtxoHash, expectedUtxoHashes.Last());
                        expectedUtxoHashes.RemoveAt(expectedUtxoHashes.Count - 1);

                        // roll utxo backwards and validate its state at each step along the way
                        for (var blockIndex = blocks.Count - 1; blockIndex >= 0; blockIndex--)
                        {
                            logger.Info($"Rolling back: {blockIndex:N0}");

                            var block         = blocks[blockIndex];
                            var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now);
                            var blockTxes     = block.Transactions.Select((tx, txIndex) => BlockTx.Create(txIndex, tx));

                            chainStateBuilder.RollbackBlock(chainedHeader, blockTxes);

                            if ((blockIndex - 1) % checkUtxoHashFrequencey == 0 || blockIndex == 0)
                            {
                                var expectedUtxoHash = expectedUtxoHashes.Last();
                                expectedUtxoHashes.RemoveAt(expectedUtxoHashes.Count - 1);

                                using (var chainState = chainStateBuilder.ToImmutable())
                                    Assert.AreEqual(expectedUtxoHash, UtxoCommitment.ComputeHash(chainState));
                            }
                        }

                        // verify chain state was rolled all the way back
                        Assert.AreEqual(-1, chainStateBuilder.Chain.Height);
                        Assert.AreEqual(0, expectedUtxoHashes.Count);

                        // calculate utxo forward again
                        for (var blockIndex = 0; blockIndex < blocks.Count; blockIndex++)
                        {
                            logger.Info($"Adding: {blockIndex:N0}");

                            var block         = blocks[blockIndex];
                            var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now);

                            chainStateBuilder.AddBlockAsync(chainedHeader, block.Transactions.Select(
                                                                (tx, txIndex) => BlockTx.Create(txIndex, tx))).Wait();
                        }

                        // verify final utxo state again
                        using (var chainState = chainStateBuilder.ToImmutable())
                            Assert.AreEqual(expectedLastUtxoHash, UtxoCommitment.ComputeHash(chainState));
                    }
        }
Ejemplo n.º 21
0
        //TODO utxo needs to be as-at transaction, with regards to a transaction being fully spent and added back in in the same block
        public virtual void ValidateTransaction(ChainedBlock chainedBlock, Transaction tx, int txIndex, ChainStateBuilder chainStateBuilder, out long unspentValue, Dictionary <UInt256, int> blockTxIndices)
        {
            unspentValue = -1;

            // verify spend amounts
            var txInputValue  = (UInt64)0;
            var txOutputValue = (UInt64)0;

            for (var inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
            {
                var input      = tx.Inputs[inputIndex];
                var prevOutput = LookupPreviousOutput(input.PreviousTxOutputKey, chainedBlock, blockTxIndices, chainStateBuilder);

                // add transactions previous value to unspent amount (used to calculate allowed coinbase reward)
                txInputValue += prevOutput.Value;
            }

            for (var outputIndex = 0; outputIndex < tx.Outputs.Count; outputIndex++)
            {
                // remove transactions spend value from unspent amount (used to calculate allowed coinbase reward)
                var output = tx.Outputs[outputIndex];
                txOutputValue += output.Value;
            }

            // ensure that amount being output from transaction isn't greater than amount being input
            if (txOutputValue > txInputValue)
            {
                throw new ValidationException(chainedBlock.Hash, "Failing tx {0}: Transaction output value is greater than input value".Format2(tx.Hash.ToHexNumberString()));
            }

            // calculate unspent value
            unspentValue = (long)(txInputValue - txOutputValue);

            // sanity check
            if (unspentValue < 0)
            {
                throw new ValidationException(chainedBlock.Hash);
            }

            // all validation has passed
        }
Ejemplo n.º 22
0
        public CoreDaemon(Logger logger, IKernel kernel, IBlockchainRules rules, IStorageManager storageManager)
        {
            this.logger        = logger;
            this.shutdownToken = new CancellationTokenSource();

            this.kernel         = kernel;
            this.rules          = rules;
            this.storageManager = storageManager;
            this.coreStorage    = new CoreStorage(storageManager, logger);

            // write genesis block out to storage
            this.coreStorage.AddGenesisBlock(this.rules.GenesisChainedHeader);
            this.coreStorage.TryAddBlock(this.rules.GenesisBlock);

            // create chain state builder
            this.chainStateBuilder = new ChainStateBuilder(this.logger, this.rules, this.coreStorage);

            // add genesis block to chain state, if needed
            if (this.chainStateBuilder.Chain.Height < 0)
            {
                this.chainStateBuilder.AddBlock(this.rules.GenesisChainedHeader, this.rules.GenesisBlock.Transactions);
            }

            // create workers
            this.targetChainWorker = new TargetChainWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(50), maxIdleTime: TimeSpan.FromSeconds(30)),
                this.logger, this.rules, this.coreStorage);

            this.chainStateWorker = new ChainStateWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(50), maxIdleTime: TimeSpan.FromSeconds(5)),
                this.targetChainWorker, this.chainStateBuilder, this.logger, this.rules, this.coreStorage);

            this.pruningWorker = new PruningWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromSeconds(60), maxIdleTime: TimeSpan.FromMinutes(15)),
                this.coreStorage, this.chainStateWorker, this.chainStateBuilder, this.logger, this.rules);

            this.defragWorker = new DefragWorker(
                new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5)),
                this.coreStorage, this.logger);

            // notify defrag worker after pruning
            this.pruningWorker.OnWorkFinished += this.defragWorker.NotifyWork;

            this.chainStateWorker.BlockMissed += HandleBlockMissed;

            this.targetChainWorker.OnTargetChainChanged +=
                () =>
            {
                var handler = this.OnTargetChainChanged;
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            };

            this.chainStateWorker.OnChainStateChanged +=
                () =>
            {
                this.pruningWorker.NotifyWork();
                this.utxoScanWorker.NotifyWork();

                var handler = this.OnChainStateChanged;
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            };

            this.gcWorker = new WorkerMethod("GC Worker",
                                             _ =>
            {
                this.logger.Info(
                    string.Join("\n",
                                new string('-', 80),
                                "GC Memory:      {0,10:#,##0.00} MB",
                                "Process Memory: {1,10:#,##0.00} MB",
                                new string('-', 80)
                                )
                    .Format2
                    (
                        /*0*/ (float)GC.GetTotalMemory(false) / 1.MILLION(),
                        /*1*/ (float)Process.GetCurrentProcess().PrivateMemorySize64 / 1.MILLION()
                    ));
            }, initialNotify: true, minIdleTime: TimeSpan.FromSeconds(30), maxIdleTime: TimeSpan.FromSeconds(30), logger: this.logger);

            this.utxoScanWorker = new WorkerMethod("UTXO Scan Worker",
                                                   _ =>
            {
                // time taking chain state snapshots
                var stopwatch = Stopwatch.StartNew();
                int chainStateHeight;
                using (var chainState = this.GetChainState())
                {
                    chainStateHeight = chainState.Chain.Height;
                }
                stopwatch.Stop();
                this.logger.Info("GetChainState at {0:#,##0}: {1:#,##0.00}s".Format2(chainStateHeight, stopwatch.Elapsed.TotalSeconds));

                // time enumerating chain state snapshots
                stopwatch = Stopwatch.StartNew();
                using (var chainState = this.GetChainState())
                {
                    chainStateHeight = chainState.Chain.Height;
                    chainState.ReadUnspentTransactions().Count();
                }
                stopwatch.Stop();
                this.logger.Info("Enumerate chain state at {0:#,##0}: {1:#,##0.00}s".Format2(chainStateHeight, stopwatch.Elapsed.TotalSeconds));

                //using (var chainStateLocal = this.GetChainState())
                //{
                //    new MethodTimer(this.logger).Time("UTXO Commitment: {0:#,##0}".Format2(chainStateLocal.UnspentTxCount), () =>
                //    {
                //        using (var utxoStream = new UtxoStream(this.logger, chainStateLocal.ReadUnspentTransactions()))
                //        {
                //            var sha256 = new SHA256Managed();
                //            var utxoHash = sha256.ComputeHash(utxoStream);
                //            this.logger.Info("UXO Commitment Hash: {0}".Format2(utxoHash.ToHexNumberString()));
                //        }
                //    });

                //    //new MethodTimer().Time("Full UTXO Scan: {0:#,##0}".Format2(chainStateLocal.Utxo.TransactionCount), () =>
                //    //{
                //    //    var sha256 = new SHA256Managed();
                //    //    foreach (var output in chainStateLocal.Utxo.GetUnspentTransactions())
                //    //    {
                //    //    }
                //    //});
                //}
            }, initialNotify: true, minIdleTime: TimeSpan.FromSeconds(60), maxIdleTime: TimeSpan.FromSeconds(60), logger: this.logger);
        }
Ejemplo n.º 23
0
        public CoreDaemon(Logger logger, IKernel kernel, IBlockchainRules rules, BlockHeaderCache blockHeaderCache, ChainedHeaderCache chainedHeaderCache, BlockTxHashesCache blockTxHashesCache, TransactionCache transactionCache, BlockCache blockCache)
        {
            this.logger        = logger;
            this.shutdownToken = new CancellationTokenSource();

            this.kernel             = kernel;
            this.rules              = rules;
            this.blockHeaderCache   = blockHeaderCache;
            this.chainedHeaderCache = chainedHeaderCache;
            this.blockTxHashesCache = blockTxHashesCache;
            this.transactionCache   = transactionCache;
            this.blockCache         = blockCache;

            // write genesis block out to storage
            this.blockHeaderCache[this.rules.GenesisBlock.Hash]           = this.rules.GenesisBlock.Header;
            this.blockCache[this.rules.GenesisBlock.Hash]                 = this.rules.GenesisBlock;
            this.chainedHeaderCache[this.rules.GenesisChainedHeader.Hash] = this.rules.GenesisChainedHeader;

            // wire up cache events
            this.blockHeaderCache.OnAddition       += OnBlockHeaderAddition;
            this.blockHeaderCache.OnModification   += OnBlockHeaderModification;
            this.blockCache.OnAddition             += OnBlockAddition;
            this.blockCache.OnModification         += OnBlockModification;
            this.blockTxHashesCache.OnAddition     += OnBlockTxHashesAddition;
            this.blockTxHashesCache.OnModification += OnBlockTxHashesModification;
            this.chainedHeaderCache.OnAddition     += OnChainedHeaderAddition;
            this.chainedHeaderCache.OnModification += OnChainedHeaderModification;

            // create chain state builder
            this.chainStateBuilder =
                this.kernel.Get <ChainStateBuilder>(
                    new ConstructorArgument("chain", Chain.CreateForGenesisBlock(this.rules.GenesisChainedHeader).ToBuilder()),
                    new ConstructorArgument("parentUtxo", Utxo.CreateForGenesisBlock(this.rules.GenesisBlock.Hash)));

            this.chainStateLock = new ReaderWriterLockSlim();

            // create workers
            this.chainingWorker = kernel.Get <ChainingWorker>(
                new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromSeconds(0), maxIdleTime: TimeSpan.FromSeconds(30))));

            this.targetChainWorker = kernel.Get <TargetChainWorker>(
                new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromSeconds(0), maxIdleTime: TimeSpan.FromSeconds(30))));

            this.chainStateWorker = kernel.Get <ChainStateWorker>(
                new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.FromSeconds(5))),
                new ConstructorArgument("getTargetChain", (Func <Chain>)(() => this.targetChainWorker.TargetChain)),
                new ConstructorArgument("targetChainWorker", this.targetChainWorker),
                new ConstructorArgument("chainStateBuilder", this.chainStateBuilder));

            this.targetChainWorker.OnTargetBlockChanged +=
                () =>
            {
                var handler = this.OnTargetBlockChanged;
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            };

            this.targetChainWorker.OnTargetChainChanged +=
                () =>
            {
                this.chainStateWorker.NotifyWork();

                var handler = this.OnTargetChainChanged;
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            };

            this.chainStateWorker.OnChainStateChanged +=
                () =>
            {
                this.utxoScanWorker.NotifyWork();

                //TODO once fully synced, this should save off the immutable snapshot immediately
                //TODO this will allow there to always be an active chain state once synced
                this.chainStateLock.DoWrite(() =>
                                            this.chainState = null);

                var handler = this.OnChainStateChanged;
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            };

            this.gcWorker = new WorkerMethod("GC Worker",
                                             () =>
            {
                this.logger.Info(
                    string.Join("\n",
                                new string('-', 80),
                                "GC Memory:      {0,10:#,##0.00} MB",
                                "Process Memory: {1,10:#,##0.00} MB",
                                new string('-', 80)
                                )
                    .Format2
                    (
                        /*0*/ (float)GC.GetTotalMemory(false) / 1.MILLION(),
                        /*1*/ (float)Process.GetCurrentProcess().PrivateMemorySize64 / 1.MILLION()
                    ));
            }, initialNotify: true, minIdleTime: TimeSpan.FromSeconds(30), maxIdleTime: TimeSpan.FromSeconds(30), logger: this.logger);

            this.utxoScanWorker = new WorkerMethod("UTXO Scan Worker",
                                                   () =>
            {
                var chainStateLocal = this.GetChainState();
                if (chainStateLocal == null)
                {
                    return;
                }

                new MethodTimer().Time("Full UTXO Scan: {0:#,##0}".Format2(chainStateLocal.Utxo.OutputCount), () =>
                {
                    var sha256 = new SHA256Managed();
                    foreach (var output in chainStateLocal.Utxo.GetUnspentOutputs())
                    {
                        if (new UInt256(sha256.ComputeDoubleHash(output.Value.ScriptPublicKey.ToArray())) == UInt256.Zero)
                        {
                        }
                    }
                });
            }, initialNotify: true, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue, logger: this.logger);
        }
Ejemplo n.º 24
0
        private void TestRollback(ITestStorageProvider provider)
        {
            var logger = LogManager.CreateNullLogger();
            var sha256 = new SHA256Managed();

            var blockProvider = new MainnetBlockProvider();
            var blocks        = Enumerable.Range(0, 500).Select(x => blockProvider.GetBlock(x)).ToList();

            var genesisBlock  = blocks[0];
            var genesisHeader = new ChainedHeader(genesisBlock.Header, height: 0, totalWork: 0);
            var genesisChain  = Chain.CreateForGenesisBlock(genesisHeader);

            var rules = new MainnetRules(logger);

            using (var storageManager = provider.OpenStorageManager())
                using (var coreStorage = new CoreStorage(storageManager, logger))
                    using (var chainStateBuilder = new ChainStateBuilder(logger, rules, coreStorage))
                    {
                        // add blocks to storage
                        coreStorage.AddGenesisBlock(ChainedHeader.CreateForGenesisBlock(blocks[0].Header));
                        foreach (var block in blocks)
                        {
                            coreStorage.TryAddBlock(block);
                        }

                        // calculate utxo forward and store its state at each step along the way
                        var expectedUtxos = new List <List <UnspentTx> >();
                        for (var blockIndex = 0; blockIndex < blocks.Count; blockIndex++)
                        {
                            var block         = blocks[blockIndex];
                            var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0);

                            chainStateBuilder.AddBlock(chainedHeader, block.Transactions);

                            using (var chainState = chainStateBuilder.ToImmutable())
                            {
                                expectedUtxos.Add(chainState.ReadUnspentTransactions().ToList());
                            }
                        }

                        // verify the utxo state before rolling back
                        //TODO verify the UTXO hash hard-coded here is correct
                        var expectedUtxoHash = UInt256.Parse("609eb5882e0b71a707fb876c844fbfe6b4579e04eb27c7c0cefbb7478bac737b", NumberStyles.HexNumber);
                        using (var utxoStream = new UtxoStream(logger, expectedUtxos.Last()))
                        {
                            var utxoHash = new UInt256(sha256.ComputeDoubleHash(utxoStream));
                            Assert.AreEqual(expectedUtxoHash, utxoHash);
                        }
                        expectedUtxos.RemoveAt(expectedUtxos.Count - 1);

                        // roll utxo backwards and validate its state at each step along the way
                        for (var blockIndex = blocks.Count - 1; blockIndex >= 1; blockIndex--)
                        {
                            var block         = blocks[blockIndex];
                            var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0);
                            var blockTxes     = block.Transactions.Select((tx, txIndex) => new BlockTx(txIndex, 0, tx.Hash, /*pruned:*/ false, tx));

                            chainStateBuilder.RollbackBlock(chainedHeader, blockTxes);

                            var expectedUtxo = expectedUtxos.Last();
                            expectedUtxos.RemoveAt(expectedUtxos.Count - 1);

                            List <UnspentTx> actualUtxo;
                            using (var chainState = chainStateBuilder.ToImmutable())
                            {
                                actualUtxo = chainState.ReadUnspentTransactions().ToList();
                            }

                            CollectionAssert.AreEqual(expectedUtxo, actualUtxo, "UTXO differs at height: {0}".Format2(blockIndex));
                        }
                    }
        }
Ejemplo n.º 25
0
        public virtual void ValidateBlock(ChainedBlock chainedBlock, ChainStateBuilder chainStateBuilder)
        {
            //TODO
            if (BypassValidation)
            {
                return;
            }

            // calculate the next required target
            var requiredTarget = GetRequiredNextTarget(chainStateBuilder.Chain.ToImmutable());

            // validate block's target against the required target
            var blockTarget = chainedBlock.Header.CalculateTarget();

            if (blockTarget > requiredTarget)
            {
                throw new ValidationException(chainedBlock.Hash, "Failing block {0} at height {1}: Block target {2} did not match required target of {3}".Format2(chainedBlock.Hash.ToHexNumberString(), chainedBlock.Height, blockTarget.ToHexNumberString(), requiredTarget.ToHexNumberString()));
            }

            // validate block's proof of work against its stated target
            if (chainedBlock.Hash > blockTarget || chainedBlock.Hash > requiredTarget)
            {
                throw new ValidationException(chainedBlock.Hash, "Failing block {0} at height {1}: Block did not match its own target of {2}".Format2(chainedBlock.Hash.ToHexNumberString(), chainedBlock.Height, blockTarget.ToHexNumberString()));
            }

            // ensure there is at least 1 transaction
            if (chainedBlock.Transactions.Count == 0)
            {
                throw new ValidationException(chainedBlock.Hash, "Failing block {0} at height {1}: Zero transactions present".Format2(chainedBlock.Hash.ToHexNumberString(), chainedBlock.Height));
            }

            //TODO apply real coinbase rule
            // https://github.com/bitcoin/bitcoin/blob/481d89979457d69da07edd99fba451fd42a47f5c/src/core.h#L219
            var coinbaseTx = chainedBlock.Transactions[0];

            // check that coinbase has only one input
            if (coinbaseTx.Inputs.Count != 1)
            {
                throw new ValidationException(chainedBlock.Hash, "Failing block {0} at height {1}: Coinbase transaction does not have exactly one input".Format2(chainedBlock.Hash.ToHexNumberString(), chainedBlock.Height));
            }

            var blockTxIndices = new Dictionary <UInt256, int>();

            for (var i = 0; i < chainedBlock.Transactions.Count; i++)
            {
                blockTxIndices.Add(chainedBlock.Transactions[i].Hash, i);
            }

            // validate transactions
            long blockUnspentValue = 0L;

            for (var txIndex = 1; txIndex < chainedBlock.Transactions.Count; txIndex++)
            {
                var tx = chainedBlock.Transactions[txIndex];

                long txUnspentValue;
                ValidateTransaction(chainedBlock, tx, txIndex, chainStateBuilder, out txUnspentValue, blockTxIndices);

                blockUnspentValue += txUnspentValue;
            }

            // calculate the expected reward in coinbase
            var expectedReward = (long)(50 * SATOSHI_PER_BTC);

            if (chainedBlock.Height / 210000 <= 32)
            {
                expectedReward /= (long)Math.Pow(2, chainedBlock.Height / 210000);
            }
            expectedReward += blockUnspentValue;

            // calculate the actual reward in coinbase
            var actualReward = 0L;

            foreach (var txOutput in coinbaseTx.Outputs)
            {
                actualReward += (long)txOutput.Value;
            }

            // ensure coinbase has correct reward
            if (actualReward > expectedReward)
            {
                throw new ValidationException(chainedBlock.Hash, "Failing block {0} at height {1}: Coinbase value is greater than reward + fees".Format2(chainedBlock.Hash.ToHexNumberString(), chainedBlock.Height));
            }

            // all validation has passed
        }