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()); } }
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()); } }
/// <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); }
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); } }
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()); } }
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()); } }
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); } }
/// <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(); } }
/// <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(); } }
/// <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); }
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(); }
/// <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."); } } } }
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); }
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()); } }
/// <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); }
/// <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); }
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); }
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(); }