예제 #1
0
        public async Task PutAsync_WritesProvenBlockHeaderAndSavesBlockHashAsync()
        {
            string folder = CreateTestDir(this);

            ProvenBlockHeader provenBlockHeaderIn = CreateNewProvenBlockHeaderMock();

            var blockHashHieghtPair = new HashHeightPair(provenBlockHeaderIn.GetHash(), 0);
            var items = new List <ProvenBlockHeader> {
                provenBlockHeaderIn
            };

            using (IProvenBlockHeaderRepository repo = this.SetupRepository(this.Network, folder))
            {
                await repo.PutAsync(items, blockHashHieghtPair);
            }

            using (var engine = new DBreezeEngine(folder))
            {
                DBreeze.Transactions.Transaction txn = engine.GetTransaction();
                txn.SynchronizeTables(ProvenBlockHeaderTable);
                txn.ValuesLazyLoadingIsOn = false;

                var headerOut         = txn.Select <byte[], ProvenBlockHeader>(ProvenBlockHeaderTable, blockHashHieghtPair.Height.ToBytes(false)).Value;
                var hashHeightPairOut = txn.Select <byte[], HashHeightPair>(BlockHashTable, new byte[0].ToBytes()).Value;

                headerOut.Should().NotBeNull();
                headerOut.GetHash().Should().Be(provenBlockHeaderIn.GetHash());

                hashHeightPairOut.Should().NotBeNull();
                hashHeightPairOut.Hash.Should().Be(provenBlockHeaderIn.GetHash());
            }
        }
예제 #2
0
        public async Task PutAsync_WritesProvenBlockHeaderAndSavesBlockHashAsync()
        {
            string folder = CreateTestDir(this);

            ProvenBlockHeader provenBlockHeaderIn = CreateNewProvenBlockHeaderMock();

            var blockHashHeightPair = new HashHeightPair(provenBlockHeaderIn.GetHash(), 0);
            var items = new SortedDictionary <int, ProvenBlockHeader>()
            {
                { 0, provenBlockHeaderIn }
            };

            using (IProvenBlockHeaderRepository repo = this.SetupRepository(this.Network, folder))
            {
                await repo.PutAsync(items, blockHashHeightPair);
            }

            using (var engine = new DB(new Options()
            {
                CreateIfMissing = true
            }, folder))
            {
                var headerOut         = this.dataStoreSerializer.Deserialize <ProvenBlockHeader>(engine.Get(DBH.Key(ProvenBlockHeaderTable, BitConverter.GetBytes(blockHashHeightPair.Height))));
                var hashHeightPairOut = this.DataStoreSerializer.Deserialize <HashHeightPair>(engine.Get(DBH.Key(BlockHashHeightTable, new byte[] { 1 })));

                headerOut.Should().NotBeNull();
                headerOut.GetHash().Should().Be(provenBlockHeaderIn.GetHash());

                hashHeightPairOut.Should().NotBeNull();
                hashHeightPairOut.Hash.Should().Be(provenBlockHeaderIn.GetHash());
            }
        }
예제 #3
0
        /// <inheritdoc />
        public async Task <ChainedHeader> InitializeAsync(ChainedHeader highestHeader)
        {
            Guard.NotNull(highestHeader, nameof(highestHeader));
            await this.provenBlockHeaderRepository.InitializeAsync().ConfigureAwait(false);

            ChainedHeader  tip     = highestHeader;
            HashHeightPair repoTip = this.provenBlockHeaderRepository.TipHashHeight;

            if (repoTip.Hash != tip.HashBlock)
            {
                // Repository is behind chain of headers.
                tip = tip.FindAncestorOrSelf(repoTip.Hash, repoTip.Height);

                if (tip == null)
                {
                    // Start at one less of the current repo height as we have already checked
                    // the repo tip.
                    for (int height = repoTip.Height - 1; height > 0; height--)
                    {
                        ProvenBlockHeader provenBlockHeader = await this.provenBlockHeaderRepository.GetAsync(height).ConfigureAwait(false);

                        // Block header at current height not found, go to previous height.
                        if (provenBlockHeader == null)
                        {
                            continue;
                        }

                        tip = highestHeader.FindAncestorOrSelf(provenBlockHeader.GetHash());
                        if (tip != null)
                        {
                            this.TipHashHeight = new HashHeightPair(provenBlockHeader.GetHash(), height);
                            break;
                        }
                    }

                    if (tip == null)
                    {
                        this.logger.LogDebug("[TIP_NOT_FOUND]:{0}", highestHeader);
                        throw new ProvenBlockHeaderException($"{highestHeader} was not found in the store.");
                    }
                }
                else
                {
                    this.TipHashHeight = new HashHeightPair(tip.HashBlock, tip.Height);
                }
            }
            else
            {
                this.TipHashHeight = new HashHeightPair(tip.HashBlock, tip.Height);
            }

            this.logger.LogDebug("Proven block header store initialized at '{0}'.", this.TipHashHeight);

            return(tip);
        }
예제 #4
0
        public void ProvenBlockHeaderShouldSerializeAndDeserializeCorrectly()
        {
            // Setup new header to serialize with some fake properties.
            ProvenBlockHeader provenHeaderToSerialize = CreateNewProvenBlockHeaderMock();

            provenHeaderToSerialize.BlockTime = new DateTimeOffset(new DateTime(2018, 1, 1));
            provenHeaderToSerialize.Bits      = 1;
            provenHeaderToSerialize.Nonce     = 2;

            // Attempt to serialize it.
            using (var ms = new MemoryStream())
            {
                provenHeaderToSerialize.ReadWrite(new BitcoinStream(ms, true));

                byte[] bytes = ms.ToArray();
                bytes.Should().HaveCountGreaterThan(0);

                // Setup another slightly different header and try to load it from
                // serialized data from original header.
                ProvenBlockHeader provenHeaderToDeserialize = CreateNewProvenBlockHeaderMock();
                provenHeaderToDeserialize.GetHash().Should().NotBe(provenHeaderToSerialize.GetHash());

                // Attempt to deserialize it.
                provenHeaderToDeserialize.ReadWrite(bytes, this.Network.Consensus.ConsensusFactory);

                provenHeaderToDeserialize.GetHash().Should().Be(provenHeaderToSerialize.GetHash());

                // Check if merke proofs are identical.
                provenHeaderToDeserialize.MerkleProof.Hashes.Should().BeEquivalentTo(provenHeaderToSerialize.MerkleProof.Hashes);
                provenHeaderToDeserialize.MerkleProof.TransactionCount.Should().Be(provenHeaderToSerialize.MerkleProof.TransactionCount);
                for (int i = 0; i < provenHeaderToSerialize.MerkleProof.Flags.Length; i++)
                {
                    provenHeaderToDeserialize.MerkleProof.Flags[i].Should().Be(provenHeaderToSerialize.MerkleProof.Flags[i]);
                }

                // Check if coinstake properties match.
                provenHeaderToDeserialize.Coinstake.Should().BeEquivalentTo(provenHeaderToSerialize.Coinstake);

                // Check if signature properties match.
                provenHeaderToDeserialize.Signature.Signature.Should().BeEquivalentTo(provenHeaderToSerialize.Signature.Signature);

                // Check base properties.
                provenHeaderToDeserialize.BlockTime.Should().Be(provenHeaderToSerialize.BlockTime);
                provenHeaderToDeserialize.CurrentVersion.Should().Be(provenHeaderToSerialize.CurrentVersion);
                provenHeaderToDeserialize.Nonce.Should().Be(provenHeaderToSerialize.Nonce);
                provenHeaderToDeserialize.Time.Should().Be(provenHeaderToSerialize.Time);
                provenHeaderToDeserialize.Version.Should().Be(provenHeaderToSerialize.Version);
            }
        }
예제 #5
0
        public async Task PutAsync_Inserts_MultipleProvenBlockHeadersAsync()
        {
            string folder = CreateTestDir(this);

            PosBlock          posBlock = CreatePosBlockMock();
            ProvenBlockHeader header1  = CreateNewProvenBlockHeaderMock(posBlock);
            ProvenBlockHeader header2  = CreateNewProvenBlockHeaderMock(posBlock);

            var items = new List <ProvenBlockHeader> {
                header1, header2
            };

            // Put the items in the repository.
            using (IProvenBlockHeaderRepository repo = this.SetupRepository(this.Network, folder))
            {
                await repo.PutAsync(items, new HashHeightPair(header2.GetHash(), items.Count - 1));
            }

            // Check the ProvenBlockHeader exists in the database.
            using (var engine = new DBreezeEngine(folder))
            {
                DBreeze.Transactions.Transaction txn = engine.GetTransaction();
                txn.SynchronizeTables(ProvenBlockHeaderTable);
                txn.ValuesLazyLoadingIsOn = false;

                var headersOut = txn.SelectDictionary <byte[], ProvenBlockHeader>(ProvenBlockHeaderTable);

                headersOut.Keys.Count.Should().Be(2);
                headersOut.First().Value.GetHash().Should().Be(items[0].GetHash());
                headersOut.Last().Value.GetHash().Should().Be(items[1].GetHash());
            }
        }
예제 #6
0
        public async Task GetAsync_Reads_MultipleProvenBlockHeadersAsync()
        {
            string folder = CreateTestDir(this);

            PosBlock          posBlock = CreatePosBlockMock();
            ProvenBlockHeader header1  = CreateNewProvenBlockHeaderMock(posBlock);
            ProvenBlockHeader header2  = CreateNewProvenBlockHeaderMock(posBlock);

            using (var engine = new DBreezeEngine(folder))
            {
                DBreeze.Transactions.Transaction txn = engine.GetTransaction();
                txn.Insert <byte[], ProvenBlockHeader>(ProvenBlockHeaderTable, 1.ToBytes(false), header1);
                txn.Insert <byte[], ProvenBlockHeader>(ProvenBlockHeaderTable, 2.ToBytes(false), header2);
                txn.Commit();
            }

            // Query the repository for the item that was inserted in the above code.
            using (ProvenBlockHeaderRepository repo = this.SetupRepository(this.Network, folder))
            {
                List <ProvenBlockHeader> headersOut = await repo.GetAsync(1, 2).ConfigureAwait(false);

                headersOut.Count.Should().Be(2);
                headersOut.First().GetHash().Should().Be(header1.GetHash());
                headersOut.Last().GetHash().Should().Be(header2.GetHash());
            }
        }
예제 #7
0
        public async Task Add_2k_ProvenHeaders_ToPending_CacheAsync()
        {
            // Initialise store.
            await this.provenBlockHeaderStore.InitializeAsync(this.BuildProvenHeaderChain(1)).ConfigureAwait(false);

            ProvenBlockHeader inHeader = null;

            // Add to pending (add to internal cache).
            for (int i = 0; i < 2_000; i++)
            {
                var block = this.CreatePosBlock();
                if (inHeader != null)
                {
                    block.Header.HashPrevBlock = inHeader.GetHash();
                }
                inHeader = this.CreateNewProvenBlockHeaderMock(block);
                this.provenBlockHeaderStore.AddToPendingBatch(inHeader, new HashHeightPair(inHeader.GetHash(), i));
            }

            // Check Item in cache.
            var cacheCount = this.PendingBatch.Count;

            cacheCount.Should().Be(2_000);

            // Check if it has been saved to disk.  It shouldn't as the asyncProvider would not have been called yet.
            var outHeaderRepo = await this.provenBlockHeaderRepository.GetAsync(1).ConfigureAwait(false);

            outHeaderRepo.Should().BeNull();
        }
        /// <summary>
        /// Creates and store a <see cref="ProvenBlockHeader" /> generated by the signaled <see cref="ChainedHeaderBlock"/>.
        /// </summary>
        /// <param name="blockHeight">Height of the block used to generate its Proven Header.</param>
        /// <param name="chainedHeaderBlock">Block used to generate its Proven Header.</param>
        /// <param name="isIBD">Is node in IBD.</param>
        private void CreateAndStoreProvenHeader(int blockHeight, ChainedHeaderBlock chainedHeaderBlock, bool isIBD)
        {
            PosBlock block = (PosBlock)chainedHeaderBlock.Block;

            ProvenBlockHeader newProvenHeader = ((PosConsensusFactory)this.network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block);

            uint256 provenHeaderHash = newProvenHeader.GetHash();

            this.provenBlockHeaderStore.AddToPendingBatch(newProvenHeader, new HashHeightPair(provenHeaderHash, blockHeight));

            this.logger.LogDebug("Created Proven Header at height {0} with hash {1} and adding to the pending batch to be stored.", blockHeight, provenHeaderHash);

            // If our node is in IBD the block will not be announced to peers.
            // If not in IBD the signaler may expect the block header to be of type PH.
            // TODO: Memory foot print:
            // This design will cause memory to grow over time (depending on how long the node is running)
            // based on the size of the Proven Headers (a proven header can be up to 1000 bytes).
            // This is also correct for regular header (which are 80 bytes in size).
            // If we want to be able to control the size of PH we will need to change the logic
            // in ProvenHeadersBlockStoreBehavior and load the PH from the PH store instead
            if (!isIBD)
            {
                chainedHeaderBlock.SetHeader(newProvenHeader);
            }
        }
예제 #9
0
        /// <summary>
        /// Verifies header signature with the key from coinstake kernel.
        /// </summary>
        /// <param name="header">The header.</param>
        /// <exception cref="ConsensusException">
        /// Throws exception with error <see cref="ConsensusErrors.BadBlockSignature" /> if check fails.
        /// </exception>
        private void CheckHeaderSignatureWithCoinstakeKernel(ProvenBlockHeader header)
        {
            var consensusRules = (PosConsensusRuleEngine)this.Parent;

            if (!consensusRules.StakeValidator.CheckStakeSignature(header.Signature, header.GetHash(), header.Coinstake))
            {
                this.Logger.LogTrace("(-)[BAD_HEADER_SIGNATURE]");
                ConsensusErrors.BadBlockSignature.Throw();
            }
        }
예제 #10
0
        /// <summary>
        /// Creates the and store a <see cref="ProvenBlockHeader" />.
        /// </summary>
        /// <param name="blockHeight">Height of the block used to generate its Proven Header.</param>
        /// <param name="block">Block used to generate its Proven Header.</param>
        /// <returns>Created <see cref="ProvenBlockHeader"/>.</returns>
        private ProvenBlockHeader CreateAndStoreProvenHeader(int blockHeight, PosBlock block)
        {
            ProvenBlockHeader newProvenHeader = ((PosConsensusFactory)this.network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block);

            uint256 provenHeaderHash = newProvenHeader.GetHash();

            this.provenBlockHeaderStore.AddToPendingBatch(newProvenHeader, new HashHeightPair(provenHeaderHash, blockHeight));

            logger.LogTrace("Created Proven Header at height {0} with hash {1} and adding to the store (pending).", blockHeight, provenHeaderHash);

            return(newProvenHeader);
        }
        /// <summary>
        /// Creates and store a <see cref="ProvenBlockHeader" /> generated by the signaled <see cref="ChainedHeaderBlock"/>.
        /// </summary>
        /// <param name="blockHeight">Height of the block used to generate its Proven Header.</param>
        /// <param name="chainedHeaderBlock">Block used to generate its Proven Header.</param>
        private void CreateAndStoreProvenHeader(int blockHeight, ChainedHeaderBlock chainedHeaderBlock)
        {
            PosBlock block = (PosBlock)chainedHeaderBlock.Block;

            ProvenBlockHeader newProvenHeader = ((PosConsensusFactory)this.network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block);

            uint256 provenHeaderHash = newProvenHeader.GetHash();

            this.provenBlockHeaderStore.AddToPendingBatch(newProvenHeader, new HashHeightPair(provenHeaderHash, blockHeight));

            logger.LogTrace("Created Proven Header at height {0} with hash {1} and adding to the pending batch to be stored.", blockHeight, provenHeaderHash);

            chainedHeaderBlock.SetHeader(newProvenHeader);
        }
        /// <summary>
        /// Verifies header signature with the key from coinstake kernel.
        /// </summary>
        /// <param name="header">The header.</param>
        /// <exception cref="ConsensusException">
        /// Throws exception with error <see cref="ConsensusErrors.BadBlockSignature" /> if check fails.
        /// </exception>
        private void CheckHeaderSignatureWithCoinstakeKernel(ProvenBlockHeader header)
        {
            Script script = header.Coinstake.Outputs[1].ScriptPubKey;
            PubKey pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(script);

            var     signature  = new ECDSASignature(header.Signature.Signature);
            uint256 headerHash = header.GetHash();

            if (!pubKey.Verify(headerHash, signature))
            {
                this.Logger.LogTrace("(-)[BAD_HEADER_SIGNATURE]");
                ConsensusErrors.BadBlockSignature.Throw();
            }
        }
예제 #13
0
        /// <inheritdoc />
        public void AddToPendingBatch(ProvenBlockHeader provenBlockHeader, HashHeightPair newTip)
        {
            this.logger.LogDebug("({0}:'{1}',{2}:'{3}')", nameof(provenBlockHeader), provenBlockHeader, nameof(newTip),
                                 newTip);

            Guard.Assert(provenBlockHeader.GetHash() == newTip.Hash);

            // Stop the consensus loop.
            if (this.saveAsyncLoopException != null)
            {
                throw this.saveAsyncLoopException;
            }

            lock (this.lockObject)
            {
                if (this.pendingTipHashHeight != null &&
                    provenBlockHeader.HashPrevBlock != this.pendingTipHashHeight.Hash)
                {
                    // The latest proven header to be added is the most recent from consensus
                    // and is always assumed to be the consensus tip.
                    // If a non-consecutive item is added then there may have been a reorg in the chain
                    // this can happen after the node rewind its consensus.

                    // Walk back the batch and remove all the blocks that are on the fork.
                    var lastItem = this.pendingBatch.Last();
                    while (provenBlockHeader.HashPrevBlock != lastItem.Value.GetHash())
                    {
                        this.pendingBatch.Remove(lastItem.Key);
                        this.Cache.Remove(lastItem.Key);

                        if (this.pendingBatch.Count == 0)
                        {
                            break;
                        }

                        lastItem = this.pendingBatch.Last();
                    }
                }

                // If an item is already there this means a reorg happened.
                // We always assume the latest header belongs to the longest chain so just overwrite the previous values.
                this.pendingBatch.AddOrReplace(newTip.Height, provenBlockHeader);

                this.pendingTipHashHeight = newTip;
            }

            this.Cache.AddOrUpdate(newTip.Height, provenBlockHeader, provenBlockHeader.HeaderSize);
        }
        /// <inheritdoc />
        /// <remarks>When a block is signaled, we check if its header is a Proven Header, if not, we need to generate and store it.</remarks>
        protected override void AddBlockToQueue(ChainedHeaderBlock blockPair, bool isIBD)
        {
            int blockHeight = blockPair.ChainedHeader.Height;

            if (blockPair.ChainedHeader.ProvenBlockHeader != null)
            {
                this.logger.LogDebug("Current header is already a Proven Header.");

                // Add to the store, to be sure we actually store it anyway.
                // It's ProvenBlockHeaderStore responsibility to prevent us to store it twice.
                this.provenBlockHeaderStore.AddToPendingBatch(blockPair.ChainedHeader.ProvenBlockHeader, new HashHeightPair(blockPair.ChainedHeader.HashBlock, blockHeight));
            }
            else
            {
                // Ensure we doesn't have already the ProvenHeader in the store.
                ProvenBlockHeader provenHeader = this.provenBlockHeaderStore.GetAsync(blockPair.ChainedHeader.Height).GetAwaiter().GetResult();

                // Proven Header not found? create it now.
                if (provenHeader == null)
                {
                    this.logger.LogDebug("Proven Header at height {0} NOT found.", blockHeight);

                    this.CreateAndStoreProvenHeader(blockHeight, blockPair, isIBD);
                }
                else
                {
                    uint256 signaledHeaderHash = blockPair.Block.Header.GetHash();

                    // If the Proven Header is the right one, then it's OK and we can return without doing anything.
                    uint256 provenHeaderHash = provenHeader.GetHash();
                    if (provenHeaderHash == signaledHeaderHash)
                    {
                        this.logger.LogDebug("Proven Header {0} found.", signaledHeaderHash);
                    }
                    else
                    {
                        this.logger.LogDebug("Found a proven header with a different hash, recreating PH. Expected Hash: {0}, found Hash: {1}.", signaledHeaderHash, provenHeaderHash);

                        // A reorg happened so we recreate a new Proven Header to replace the wrong one.
                        this.CreateAndStoreProvenHeader(blockHeight, blockPair, isIBD);
                    }
                }
            }

            // At the end, if no exception happened, control is passed back to base AddBlockToQueue.
            base.AddBlockToQueue(blockPair, isIBD);
        }
예제 #15
0
        public async Task Add_2k_ProvenHeaders_To_PendingBatch_Then_Save_Then_PendingBatch_Should_Be_EmptyAsync()
        {
            // Initialise store.
            await this.provenBlockHeaderStore.InitializeAsync(this.BuildProvenHeaderChain(1)).ConfigureAwait(false);

            ProvenBlockHeader inHeader = null;

            // Add to pending (add to internal cache).
            for (int i = 0; i < 2_000; i++)
            {
                var block = this.CreatePosBlock();
                if (inHeader != null)
                {
                    block.Header.HashPrevBlock = inHeader.GetHash();
                }
                inHeader = this.CreateNewProvenBlockHeaderMock(block);
                this.provenBlockHeaderStore.AddToPendingBatch(inHeader, new HashHeightPair(inHeader.GetHash(), i));
            }

            // Check Item in cache.
            var cacheCount = this.PendingBatch.Count;

            cacheCount.Should().Be(2_000);

            // Call the internal save method to save cached item to disk.
            this.provenBlockHeaderStore.InvokeMethod("SaveAsync");

            // when pendingTipHashHeight is null we can safely say the items were saved to the repository, based on the above SaveAsync.
            WaitLoop(() =>
            {
                var pendingTipHashHeight = this.provenBlockHeaderStore.GetMemberValue("pendingTipHashHeight");
                return(pendingTipHashHeight == null);
            });

            WaitLoop(() =>
            {
                // Check if it has been saved to disk.
                var outHeaderRepo = this.provenBlockHeaderRepository.GetAsync(1999).ConfigureAwait(false).GetAwaiter().GetResult();
                return(outHeaderRepo != null);
            });

            // Check items in cache - should now be empty.
            cacheCount = this.PendingBatch.Count;
            cacheCount.Should().Be(0);
        }
        /// <summary>
        /// Verifies header signature with the key from coinstake kernel.
        /// </summary>
        /// <param name="header">The header.</param>
        /// <param name="stakingCoins">The staking coins.</param>
        /// <exception cref="ConsensusException">
        /// Throws exception with error <see cref="ConsensusErrors.BadBlockSignature" /> if check fails.
        /// </exception>
        private void CheckHeaderSignatureWithCoinstakeKernel(ProvenBlockHeader header, UnspentOutputs stakingCoins)
        {
            OutPoint prevOut = this.GetPreviousOut(header);

            Script scriptPubKey = stakingCoins.Outputs[prevOut.N].ScriptPubKey;
            PubKey pubKey       = scriptPubKey.GetDestinationPublicKeys(this.PosParent.Network)[0];

            var     signature  = new ECDSASignature(header.Signature.Signature);
            uint256 headerHash = header.GetHash();

            if (pubKey.Verify(headerHash, signature))
            {
                return;
            }

            this.Logger.LogTrace("(-)[BAD_HEADER_SIGNATURE]");
            ConsensusErrors.BadBlockSignature.Throw();
        }
예제 #17
0
        /// <inheritdoc />
        protected override void AddBlockToQueue(ChainedHeaderBlock blockPair)
        {
            base.AddBlockToQueue(blockPair);

            int blockHeight = blockPair.ChainedHeader.Height;

            if (this.AreProvenHeadersActivated(blockHeight))
            {
                if (blockPair.ChainedHeader.Header is ProvenBlockHeader phHeader)
                {
                    logger.LogTrace("Current header is already a Proven Header.");
                    return;
                }

                ProvenBlockHeader provenHeader = this.provenBlockHeaderStore.GetAsync(blockPair.ChainedHeader.Height).GetAwaiter().GetResult();
                // Proven Header not found? create it now.
                if (provenHeader == null)
                {
                    logger.LogTrace("Proven Header at height {0} NOT found.", blockHeight);

                    var createdProvenHeader = CreateAndStoreProvenHeader(blockHeight, (PosBlock)blockPair.Block);

                    // setters aren't accessible, not sure setting them to public is a nice idea.
                    //blockPair.Block.Header = blockPair.ChainedHeader.Header = createdProvenHeader;
                }
                else
                {
                    uint256 blockHash = blockPair.Block.Header.GetHash();

                    // If the Proven Header is the right one, then it's OK and we can return without doing anything.
                    uint256 provenHeaderHash = provenHeader.GetHash();
                    if (provenHeaderHash == blockHash)
                    {
                        logger.LogTrace("Proven Header {0} found.", blockHash);
                        return;
                    }
                    else
                    {
                        throw new BlockStoreException("Found a proven header with a different hash.");
                    }
                }
            }
        }
예제 #18
0
        public ChainedHeader BuildProvenHeaderChainFromBlocks(List <Block> posBlocks)
        {
            ChainedHeader currentHeader = ChainedHeadersHelper.CreateGenesisChainedHeader(this.Network);

            foreach (PosBlock posBlock in posBlocks)
            {
                ProvenBlockHeader header = ((PosConsensusFactory)this.Network.Consensus.ConsensusFactory).CreateProvenBlockHeader(posBlock);

                header.Nonce         = RandomUtils.GetUInt32();
                header.HashPrevBlock = currentHeader.HashBlock;
                header.Bits          = Target.Difficulty1;

                ChainedHeader prevHeader = currentHeader;
                currentHeader = new ChainedHeader(header, header.GetHash(), prevHeader);

                prevHeader.Next.Add(currentHeader);
            }

            return(currentHeader);
        }
예제 #19
0
        public async Task PutAsync_Inserts_MultipleProvenBlockHeadersAsync()
        {
            string folder = CreateTestDir(this);

            PosBlock          posBlock = CreatePosBlock();
            ProvenBlockHeader header1  = CreateNewProvenBlockHeaderMock(posBlock);
            ProvenBlockHeader header2  = CreateNewProvenBlockHeaderMock(posBlock);

            var items = new SortedDictionary <int, ProvenBlockHeader>()
            {
                { 0, header1 }, { 1, header2 }
            };

            // Put the items in the repository.
            using (IProvenBlockHeaderRepository repo = this.SetupRepository(this.Network, folder))
            {
                await repo.PutAsync(items, new HashHeightPair(header2.GetHash(), items.Count - 1));
            }

            // Check the ProvenBlockHeader exists in the database.
            using (var engine = new DB(new Options()
            {
                CreateIfMissing = true
            }, folder))
            {
                var headersOut = new Dictionary <byte[], byte[]>();
                var enumeator  = engine.GetEnumerator();
                while (enumeator.MoveNext())
                {
                    if (enumeator.Current.Key[0] == ProvenBlockHeaderTable)
                    {
                        headersOut.Add(enumeator.Current.Key, enumeator.Current.Value);
                    }
                }

                headersOut.Keys.Count.Should().Be(2);
                this.dataStoreSerializer.Deserialize <ProvenBlockHeader>(headersOut.First().Value).GetHash().Should().Be(items[0].GetHash());
                this.dataStoreSerializer.Deserialize <ProvenBlockHeader>(headersOut.Last().Value).GetHash().Should().Be(items[1].GetHash());
            }
        }
예제 #20
0
        /// <returns>Tip of a created chain of headers.</returns>
        public ChainedHeader BuildChainWithProvenHeaders(int blockCount)
        {
            ChainedHeader currentHeader = ChainedHeadersHelper.CreateGenesisChainedHeader(this.Network);

            for (int i = 1; i < blockCount; i++)
            {
                PosBlock          block  = this.CreatePosBlockMock();
                ProvenBlockHeader header = ((PosConsensusFactory)this.Network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block);

                header.Nonce         = RandomUtils.GetUInt32();
                header.HashPrevBlock = currentHeader.HashBlock;
                header.Bits          = Target.Difficulty1;

                ChainedHeader prevHeader = currentHeader;
                currentHeader = new ChainedHeader(header, header.GetHash(), i);

                currentHeader.SetPrivatePropertyValue("Previous", prevHeader);
                prevHeader.Next.Add(currentHeader);
            }

            return(currentHeader);
        }
예제 #21
0
        /// <summary>
        /// Builds a chain of proven headers.
        /// </summary>
        /// <param name="blockCount">The amount of blocks to chain.</param>
        /// <param name="startingHeader">Build the chain from this header, if not start from genesis.</param>
        /// <returns>Tip of a created chain of headers.</returns>
        public ChainedHeader BuildProvenHeaderChain(int blockCount, ChainedHeader startingHeader = null)
        {
            startingHeader = startingHeader ?? ChainedHeadersHelper.CreateGenesisChainedHeader(this.Network);

            for (int i = 1; i < blockCount; i++)
            {
                PosBlock          block  = this.CreatePosBlock();
                ProvenBlockHeader header = ((PosConsensusFactory)this.Network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block);

                header.Nonce         = RandomUtils.GetUInt32();
                header.HashPrevBlock = startingHeader.HashBlock;
                header.Bits          = Target.Difficulty1;

                ChainedHeader prevHeader = startingHeader;
                startingHeader = new ChainedHeader(header, header.GetHash(), prevHeader.Height + 1);

                startingHeader.SetPrivatePropertyValue("Previous", prevHeader);
                prevHeader.Next.Add(startingHeader);
            }

            return(startingHeader);
        }
예제 #22
0
        public (ChainedHeader chainedHeader, List <ProvenBlockHeader> provenBlockHeaders) BuildChainWithProvenHeaders(int blockCount, Network network, bool addNext = false)
        {
            Guard.Assert(blockCount > 0);

            var provenBlockHeaders = new List <ProvenBlockHeader>();

            ChainedHeader chainedHeader       = null;
            ChainedHeader previousChainHeader = null;

            for (int i = 0; i < blockCount; i++)
            {
                PosBlock          block  = CreatePosBlockMock();
                ProvenBlockHeader header = ((PosConsensusFactory)this.Network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block);

                header.Nonce         = RandomUtils.GetUInt32();
                header.HashPrevBlock = i > 0 ? chainedHeader.HashBlock : null;
                header.Bits          = Target.Difficulty1;

                chainedHeader = new ChainedHeader(header, header.GetHash(), i);

                if (previousChainHeader != null)
                {
                    chainedHeader.SetPrivatePropertyValue("Previous", previousChainHeader);

                    if (addNext)
                    {
                        chainedHeader.Previous.Next.Add(chainedHeader);
                    }
                }

                previousChainHeader = chainedHeader;

                provenBlockHeaders.Add(header);
            }

            return(chainedHeader, provenBlockHeaders);
        }
        /// <inheritdoc />
        public async Task InitializeAsync(ChainedHeader chainedHeader)
        {
            await this.provenBlockHeaderRepository.InitializeAsync().ConfigureAwait(false);

            if (chainedHeader.Height > 0)
            {
                ProvenBlockHeader repoHeader =
                    await this.provenBlockHeaderRepository.GetAsync(chainedHeader.Height);

                if (repoHeader == null)
                {
                    throw new ProvenBlockHeaderException("Unable to find proven block header in the repository.");
                }

                if (repoHeader.GetHash() != chainedHeader.HashBlock)
                {
                    throw new ProvenBlockHeaderException("Chain header tip hash does not match the latest proven block header hash saved to disk.");
                }
            }

            this.storeTip = chainedHeader;

            this.TipHashHeight = new HashHeightPair(chainedHeader.HashBlock, chainedHeader.Height);

            this.logger.LogDebug("Proven block header store tip at block '{0}'.", this.TipHashHeight);

            this.asyncLoop = this.asyncLoopFactory.Run("ProvenBlockHeaders job", async token =>
            {
                await this.SaveAsync().ConfigureAwait(false);

                this.logger.LogTrace("(-)[IN_ASYNC_LOOP]");
            },
                                                       this.nodeLifetime.ApplicationStopping,
                                                       repeatEvery: TimeSpan.FromMinutes(1),
                                                       startAfter: TimeSpan.FromMinutes(1));
        }
        /// <inheritdoc />
        protected override Payload ConstructHeadersPayload(GetHeadersPayload getHeadersPayload, out ChainedHeader lastHeader)
        {
            // If getHeadersPayload isn't a GetProvenHeadersPayload, return base implementation result
            if (!(getHeadersPayload is GetProvenHeadersPayload))
            {
                if (!(base.ConstructHeadersPayload(getHeadersPayload, out lastHeader) is HeadersPayload headersPayload))
                {
                    this.logger.LogTrace("(-)[INVALID_LOCATOR]:null");
                    return(null);
                }

                for (int i = 0; i < headersPayload.Headers.Count; i++)
                {
                    if (headersPayload.Headers[i] is ProvenBlockHeader phHeader)
                    {
                        BlockHeader newHeader = this.ChainIndexer.Network.Consensus.ConsensusFactory.CreateBlockHeader();
                        newHeader.Bits           = phHeader.Bits;
                        newHeader.Time           = phHeader.Time;
                        newHeader.Nonce          = phHeader.Nonce;
                        newHeader.Version        = phHeader.Version;
                        newHeader.HashMerkleRoot = phHeader.HashMerkleRoot;
                        newHeader.HashPrevBlock  = phHeader.HashPrevBlock;

                        headersPayload.Headers[i] = newHeader;
                    }
                }

                return(headersPayload);
            }

            ChainedHeader fork = this.ChainIndexer.FindFork(getHeadersPayload.BlockLocator);

            lastHeader = null;

            if (fork == null)
            {
                this.logger.LogTrace("(-)[INVALID_LOCATOR]:null");
                return(null);
            }

            var provenHeadersPayload = new ProvenHeadersPayload();

            ChainedHeader header = this.GetLastHeaderToSend(fork, getHeadersPayload.HashStop);

            this.logger.LogDebug("Last header that will be sent in headers payload is '{0}'.", header);

            for (int heightIndex = header.Height; heightIndex > fork.Height; heightIndex--)
            {
                ProvenBlockHeader provenBlockHeader = header.ProvenBlockHeader;
                if (provenBlockHeader == null)
                {
                    provenBlockHeader = this.provenBlockHeaderStore.GetAsync(header.Height).GetAwaiter().GetResult();

                    if (provenBlockHeader == null)
                    {
                        // Proven header is not available yet for this header.
                        // This can happen in case headers were requested by the peer right after we advanced consensus tip
                        // So at this moment proven header is not created or not yet saved to headers store for the block connected.
                        this.logger.LogDebug("No PH available for header '{0}'.", header);
                        this.logger.LogTrace("(-)[NO_PH_AVAILABLE]");
                        break;
                    }
                    else if (provenBlockHeader.GetHash() != header.HashBlock)
                    {
                        // Proven header is in the store, but with a wrong hash.
                        // This can happen in case of reorgs, when the store has not yet been updated.
                        // Without this check, we may send headers that aren't consecutive because are built from different branches, and the other peer may ban us.
                        this.logger.LogDebug("Stored PH hash is wrong. Expected: {0}, Found: {1}", header.Header.GetHash(), provenBlockHeader.GetHash());
                        this.logger.LogTrace("(-)[WRONG STORED PH]");
                        break;
                    }
                }

                lastHeader = header;

                provenHeadersPayload.Headers.Add(provenBlockHeader);

                header = header.Previous;
            }

            provenHeadersPayload.Headers.Reverse();

            return(provenHeadersPayload);
        }
예제 #25
0
        public async Task Add_2k_ProvenHeaders_ToPending_CacheAsync()
        {
            // Initialise store.
            await this.provenBlockHeaderStore.InitializeAsync(BuildProvenHeaderChain(1)).ConfigureAwait(false);

            ProvenBlockHeader inHeader = null;

            // Add to pending (add to internal cache).
            for (int i = 0; i < 2_000; i++)
            {
                inHeader = CreateNewProvenBlockHeaderMock();
                this.provenBlockHeaderStore.AddToPendingBatch(inHeader, new HashHeightPair(inHeader.GetHash(), i));
            }

            // Check Item in cache.
            var cacheCount = this.provenBlockHeaderStore.PendingBatch.GetMemberValue("Count");

            cacheCount.Should().Be(2_000);

            // Check if it has been saved to disk.  It shouldn't as the asyncLoopFactory() would not have been called yet.
            var outHeaderRepo = await this.provenBlockHeaderRepository.GetAsync(1).ConfigureAwait(false);

            outHeaderRepo.Should().BeNull();
        }