From 534c00eba6517a6bfb796f3d2ae56205eefaa3ea Mon Sep 17 00:00:00 2001 From: dangershony Date: Mon, 22 Feb 2021 19:47:56 +0000 Subject: [PATCH 1/2] Move difficulty from ChaindHeader to IConsensus --- .../Consensus/BlockInfo/BlockHeader.cs | 1 + .../Consensus/Chain/ChainedHeader.cs | 193 ------------------ src/Blockcore/Consensus/Consensus.cs | 73 +++++++ src/Blockcore/Consensus/IConsensus.cs | 8 + .../Utilities/Extensions/BlockExtensions.cs | 11 + .../ConsensusQuery.cs | 1 + .../CommonRules/CheckDifficultyPosRule.cs | 2 - .../CommonRules/CheckDifficultyPowRule.cs | 1 + .../Networks/Consensus/x42Consensus.cs | 73 +++++++ .../EnvironmentMockUpHelpers/CoreNode.cs | 51 ++++- .../Blockcore.Tests/NBitcoin/ChainTests.cs | 13 +- .../Blockcore.Tests/NBitcoin/pow_tests.cs | 33 ++- 12 files changed, 250 insertions(+), 210 deletions(-) diff --git a/src/Blockcore/Consensus/BlockInfo/BlockHeader.cs b/src/Blockcore/Consensus/BlockInfo/BlockHeader.cs index 6435e93aa..382c4d66d 100644 --- a/src/Blockcore/Consensus/BlockInfo/BlockHeader.cs +++ b/src/Blockcore/Consensus/BlockInfo/BlockHeader.cs @@ -1,6 +1,7 @@ using System; using Blockcore.Consensus.Chain; using Blockcore.Networks; +using Blockcore.Utilities.Extensions; using NBitcoin; using NBitcoin.BouncyCastle.Math; using NBitcoin.Crypto; diff --git a/src/Blockcore/Consensus/Chain/ChainedHeader.cs b/src/Blockcore/Consensus/Chain/ChainedHeader.cs index c9466d21b..c774cc856 100644 --- a/src/Blockcore/Consensus/Chain/ChainedHeader.cs +++ b/src/Blockcore/Consensus/Chain/ChainedHeader.cs @@ -408,141 +408,6 @@ public ChainedHeader FindAncestorOrSelf(uint256 blockHash, int height = 0) return (currentBlock?.HashBlock == blockHash) ? currentBlock : null; } - /// - /// Gets the proof of work target for a potential new block after this entry on the chain. - /// - /// The network to get target for. - /// The target proof of work. - public Target GetNextWorkRequired(Network network) - { - return this.GetNextWorkRequired(network.Consensus); - } - - /// - /// Gets the proof of work target for a potential new block after this entry on the chain. - /// - /// Consensus rules to use for this computation. - /// The target proof of work. - public Target GetNextWorkRequired(IConsensus consensus) - { - BlockHeader dummy = consensus.ConsensusFactory.CreateBlockHeader(); - dummy.HashPrevBlock = this.HashBlock; - dummy.BlockTime = DateTimeOffset.UtcNow; - return this.GetNextWorkRequired(dummy, consensus); - } - - /// - /// Gets the proof of work target for the new block specified. - /// - /// The new block to get proof of work for. - /// The network to get target for. - /// The target proof of work. - public Target GetNextWorkRequired(BlockHeader block, Network network) - { - return this.GetNextWorkRequired(block, network.Consensus); - } - - /// - /// Gets the proof of work target for the new block specified. - /// - /// The new block to get proof of work for. - /// Consensus rules to use for this computation. - /// The target proof of work. - public Target GetNextWorkRequired(BlockHeader block, IConsensus consensus) - { - return new ChainedHeader(block, block.GetHash(), this).GetWorkRequired(consensus); - } - - /// - /// Gets the proof of work target for this entry in the chain. - /// - /// The network to get target for. - /// The target proof of work. - public Target GetWorkRequired(Network network) - { - return this.GetWorkRequired(network.Consensus); - } - - /// - /// Gets the proof of work target for this entry in the chain. - /// - /// Consensus rules to use for this computation. - /// The target proof of work. - public Target GetWorkRequired(IConsensus consensus) - { - // Genesis block. - if (this.Height == 0) - return consensus.PowLimit; - - Target proofOfWorkLimit = consensus.PowLimit; - ChainedHeader lastBlock = this.Previous; - int height = this.Height; - - if (lastBlock == null) - return proofOfWorkLimit; - - long difficultyAdjustmentInterval = this.GetDifficultyAdjustmentInterval(consensus); - - // Only change once per interval. - if ((height) % difficultyAdjustmentInterval != 0) - { - if (consensus.PowAllowMinDifficultyBlocks) - { - // Special difficulty rule for testnet: - // If the new block's timestamp is more than 2* 10 minutes - // then allow mining of a min-difficulty block. - if (this.Header.BlockTime > (lastBlock.Header.BlockTime + TimeSpan.FromTicks(consensus.TargetSpacing.Ticks * 2))) - return proofOfWorkLimit; - - // Return the last non-special-min-difficulty-rules-block. - ChainedHeader chainedHeader = lastBlock; - while ((chainedHeader.Previous != null) && ((chainedHeader.Height % difficultyAdjustmentInterval) != 0) && (chainedHeader.Header.Bits == proofOfWorkLimit)) - chainedHeader = chainedHeader.Previous; - - return chainedHeader.Header.Bits; - } - - return lastBlock.Header.Bits; - } - - // Go back by what we want to be 14 days worth of blocks. - long pastHeight = lastBlock.Height - (difficultyAdjustmentInterval - 1); - - ChainedHeader firstChainedHeader = this.GetAncestor((int)pastHeight); - if (firstChainedHeader == null) - throw new NotSupportedException("Can only calculate work of a full chain"); - - if (consensus.PowNoRetargeting) - return lastBlock.Header.Bits; - - // Limit adjustment step. - TimeSpan actualTimespan = lastBlock.Header.BlockTime - firstChainedHeader.Header.BlockTime; - if (actualTimespan < TimeSpan.FromTicks(consensus.TargetTimespan.Ticks / 4)) - actualTimespan = TimeSpan.FromTicks(consensus.TargetTimespan.Ticks / 4); - if (actualTimespan > TimeSpan.FromTicks(consensus.TargetTimespan.Ticks * 4)) - actualTimespan = TimeSpan.FromTicks(consensus.TargetTimespan.Ticks * 4); - - // Retarget. - BigInteger newTarget = lastBlock.Header.Bits.ToBigInteger(); - newTarget = newTarget.Multiply(BigInteger.ValueOf((long)actualTimespan.TotalSeconds)); - newTarget = newTarget.Divide(BigInteger.ValueOf((long)consensus.TargetTimespan.TotalSeconds)); - - var finalTarget = new Target(newTarget); - if (finalTarget > proofOfWorkLimit) - finalTarget = proofOfWorkLimit; - - return finalTarget; - } - - /// - /// Calculate the difficulty adjustment interval in blocks based on settings defined in . - /// - /// The difficulty adjustment interval in blocks. - private long GetDifficultyAdjustmentInterval(IConsensus consensus) - { - return (long)consensus.TargetTimespan.TotalSeconds / (long)consensus.TargetSpacing.TotalSeconds; - } - /// /// Calculate the median block time over window from this entry in the chain. /// @@ -561,64 +426,6 @@ public DateTimeOffset GetMedianTimePast() return median[begin + ((end - begin) / 2)]; } - /// - /// Check that the header is a valid block header including the work done for PoW blocks. - /// - /// The network to verify against. - /// true if the header is a valid block header, false otherwise. - public bool Validate(Network network) - { - if (network == null) - throw new ArgumentNullException("network"); - - if (network.Consensus.IsProofOfStake) - return BlockStake.Validate(network, this); - - bool genesisCorrect = (this.Height != 0) || this.HashBlock == network.GetGenesis().GetHash(); - return genesisCorrect && this.Validate(network.Consensus); - } - - /// - /// Check PoW against consensus and that the blocks connect correctly. - /// - /// The consensus rules being used. - /// true if the header is a valid block header, false otherwise. - public bool Validate(IConsensus consensus) - { - if (consensus == null) - throw new ArgumentNullException("consensus"); - - if ((this.Height != 0) && (this.Previous == null)) - return false; - - bool heightCorrect = (this.Height == 0) || (this.Height == this.Previous.Height + 1); - bool hashPrevCorrect = (this.Height == 0) || (this.Header.HashPrevBlock == this.Previous.HashBlock); - bool hashCorrect = this.HashBlock == this.Header.GetHash(); - bool workCorrect = this.CheckProofOfWorkAndTarget(consensus); - - return heightCorrect && hashPrevCorrect && hashCorrect && workCorrect; - } - - /// - /// Verify proof of work of the header of this chain using consensus. - /// - /// The network to verify proof of work on. - /// Whether proof of work is valid. - public bool CheckProofOfWorkAndTarget(Network network) - { - return this.CheckProofOfWorkAndTarget(network.Consensus); - } - - /// - /// Verify proof of work of the header of this chain using consensus. - /// - /// Consensus rules to use for this validation. - /// Whether proof of work is valid. - public bool CheckProofOfWorkAndTarget(IConsensus consensus) - { - return (this.Height == 0) || (this.Header.CheckProofOfWork() && (this.Header.Bits == this.GetWorkRequired(consensus))); - } - /// /// Find first common block between two chains. /// diff --git a/src/Blockcore/Consensus/Consensus.cs b/src/Blockcore/Consensus/Consensus.cs index 36aa63b0b..e787c2890 100644 --- a/src/Blockcore/Consensus/Consensus.cs +++ b/src/Blockcore/Consensus/Consensus.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Blockcore.Base.Deployments; +using Blockcore.Consensus.Chain; using NBitcoin; using NBitcoin.BouncyCastle.Math; @@ -169,5 +170,77 @@ public Consensus( this.MempoolRules = new List(); this.ProofOfStakeTimestampMask = proofOfStakeTimestampMask; } + + /// + /// Gets the proof of work target for a given entry in the chain. + /// + /// The header for which to calculate the required work. + /// The target proof of work. + public Target GetWorkRequired(ChainedHeader chainedHeader) + { + // Genesis block. + if (chainedHeader.Height == 0) + return this.PowLimit; + + Target proofOfWorkLimit = this.PowLimit; + ChainedHeader lastBlock = chainedHeader.Previous; + int height = chainedHeader.Height; + + if (lastBlock == null) + return proofOfWorkLimit; + + // Calculate the difficulty adjustment interval in blocks + long difficultyAdjustmentInterval = (long)this.TargetTimespan.TotalSeconds / (long)this.TargetSpacing.TotalSeconds; + + // Only change once per interval. + if ((height) % difficultyAdjustmentInterval != 0) + { + if (this.PowAllowMinDifficultyBlocks) + { + // Special difficulty rule for testnet: + // If the new block's timestamp is more than 2* 10 minutes + // then allow mining of a min-difficulty block. + if (chainedHeader.Header.BlockTime > (lastBlock.Header.BlockTime + TimeSpan.FromTicks(this.TargetSpacing.Ticks * 2))) + return proofOfWorkLimit; + + // Return the last non-special-min-difficulty-rules-block. + ChainedHeader lastChainedHeader = lastBlock; + while ((lastChainedHeader.Previous != null) && ((lastChainedHeader.Height % difficultyAdjustmentInterval) != 0) && (lastChainedHeader.Header.Bits == proofOfWorkLimit)) + lastChainedHeader = lastChainedHeader.Previous; + + return lastChainedHeader.Header.Bits; + } + + return lastBlock.Header.Bits; + } + + // Go back by what we want to be 14 days worth of blocks. + long pastHeight = lastBlock.Height - (difficultyAdjustmentInterval - 1); + + ChainedHeader firstChainedHeader = chainedHeader.GetAncestor((int)pastHeight); + if (firstChainedHeader == null) + throw new NotSupportedException("Can only calculate work of a full chain"); + + if (this.PowNoRetargeting) + return lastBlock.Header.Bits; + + // Limit adjustment step. + TimeSpan actualTimespan = lastBlock.Header.BlockTime - firstChainedHeader.Header.BlockTime; + if (actualTimespan < TimeSpan.FromTicks(this.TargetTimespan.Ticks / 4)) + actualTimespan = TimeSpan.FromTicks(this.TargetTimespan.Ticks / 4); + if (actualTimespan > TimeSpan.FromTicks(this.TargetTimespan.Ticks * 4)) + actualTimespan = TimeSpan.FromTicks(this.TargetTimespan.Ticks * 4); + + // Retarget. + BigInteger newTarget = lastBlock.Header.Bits.ToBigInteger(); + newTarget = newTarget.Multiply(BigInteger.ValueOf((long)actualTimespan.TotalSeconds)); + newTarget = newTarget.Divide(BigInteger.ValueOf((long)this.TargetTimespan.TotalSeconds)); + + var finalTarget = new Target(newTarget); + if (finalTarget > proofOfWorkLimit) + finalTarget = proofOfWorkLimit; + + return finalTarget; + } } } \ No newline at end of file diff --git a/src/Blockcore/Consensus/IConsensus.cs b/src/Blockcore/Consensus/IConsensus.cs index 188e66173..8c1ed75b9 100644 --- a/src/Blockcore/Consensus/IConsensus.cs +++ b/src/Blockcore/Consensus/IConsensus.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Blockcore.Base.Deployments; +using Blockcore.Consensus.Chain; using NBitcoin; using NBitcoin.BouncyCastle.Math; @@ -147,5 +148,12 @@ public interface IConsensus /// Group of mempool validation rules used by the given network. List MempoolRules { get; set; } + + /// + /// Gets the proof of work target for a given entry in the chain. + /// + /// The header for which to calculate the required work. + /// The target proof of work. + Target GetWorkRequired(ChainedHeader chainedHeader); } } \ No newline at end of file diff --git a/src/Blockcore/Utilities/Extensions/BlockExtensions.cs b/src/Blockcore/Utilities/Extensions/BlockExtensions.cs index 670368b93..2238d3ce1 100644 --- a/src/Blockcore/Utilities/Extensions/BlockExtensions.cs +++ b/src/Blockcore/Utilities/Extensions/BlockExtensions.cs @@ -1,6 +1,7 @@ using System.IO; using Blockcore.Consensus; using Blockcore.Consensus.BlockInfo; +using Blockcore.Consensus.Chain; using Blockcore.Consensus.TransactionInfo; using NBitcoin; @@ -42,5 +43,15 @@ public static int GetSize(this IBitcoinSerializable data, TransactionOptions opt return (int)bms.Counter.WrittenBytes; } + + /// + /// Gets the proof of work target for this entry in the chain. + /// + /// Consensus rules to use for this computation. + /// The target proof of work. + public static Target GetWorkRequired(this ChainedHeader chainedHeader, IConsensus consensus) + { + return consensus.GetWorkRequired(chainedHeader); + } } } \ No newline at end of file diff --git a/src/Features/Blockcore.Features.Consensus/ConsensusQuery.cs b/src/Features/Blockcore.Features.Consensus/ConsensusQuery.cs index 5b1ee1610..686c902f6 100644 --- a/src/Features/Blockcore.Features.Consensus/ConsensusQuery.cs +++ b/src/Features/Blockcore.Features.Consensus/ConsensusQuery.cs @@ -6,6 +6,7 @@ using Blockcore.Interfaces; using Blockcore.Networks; using Blockcore.Utilities; +using Blockcore.Utilities.Extensions; using Microsoft.Extensions.Logging; using NBitcoin; diff --git a/src/Features/Blockcore.Features.Consensus/Rules/CommonRules/CheckDifficultyPosRule.cs b/src/Features/Blockcore.Features.Consensus/Rules/CommonRules/CheckDifficultyPosRule.cs index 1e4f8bf3c..0ad85064c 100644 --- a/src/Features/Blockcore.Features.Consensus/Rules/CommonRules/CheckDifficultyPosRule.cs +++ b/src/Features/Blockcore.Features.Consensus/Rules/CommonRules/CheckDifficultyPosRule.cs @@ -35,8 +35,6 @@ public override void Run(RuleContext context) return; } - // TODO: In the future once we migrated to fully C# network it might be good to consider signaling in the block header the network type. - ChainedHeader chainedHeader = context.ValidationContext.ChainedHeaderToValidate; // In order to calculate difficulty we need to know the if the block type is POW/POS. diff --git a/src/Features/Blockcore.Features.Consensus/Rules/CommonRules/CheckDifficultyPowRule.cs b/src/Features/Blockcore.Features.Consensus/Rules/CommonRules/CheckDifficultyPowRule.cs index 7889ac255..a255fc89d 100644 --- a/src/Features/Blockcore.Features.Consensus/Rules/CommonRules/CheckDifficultyPowRule.cs +++ b/src/Features/Blockcore.Features.Consensus/Rules/CommonRules/CheckDifficultyPowRule.cs @@ -1,6 +1,7 @@ using Blockcore.Consensus; using Blockcore.Consensus.BlockInfo; using Blockcore.Consensus.Rules; +using Blockcore.Utilities.Extensions; using Microsoft.Extensions.Logging; using NBitcoin; diff --git a/src/Networks/Blockcore.Networks.x42/Networks/Consensus/x42Consensus.cs b/src/Networks/Blockcore.Networks.x42/Networks/Consensus/x42Consensus.cs index 94bafb677..1f05b3959 100644 --- a/src/Networks/Blockcore.Networks.x42/Networks/Consensus/x42Consensus.cs +++ b/src/Networks/Blockcore.Networks.x42/Networks/Consensus/x42Consensus.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Blockcore.Base.Deployments; using Blockcore.Consensus; +using Blockcore.Consensus.Chain; using NBitcoin; using NBitcoin.BouncyCastle.Math; @@ -236,5 +237,77 @@ uint proofOfStakeTimestampMask this.PosEmptyCoinbase = posEmptyCoinbase; this.ProofOfStakeTimestampMask = proofOfStakeTimestampMask; } + + /// + /// Gets the proof of work target for a given entry in the chain. + /// + /// The header for which to calculate the required work. + /// The target proof of work. + public Target GetWorkRequired(ChainedHeader chainedHeader) + { + // Genesis block. + if (chainedHeader.Height == 0) + return this.PowLimit; + + Target proofOfWorkLimit = this.PowLimit; + ChainedHeader lastBlock = chainedHeader.Previous; + int height = chainedHeader.Height; + + if (lastBlock == null) + return proofOfWorkLimit; + + // Calculate the difficulty adjustment interval in blocks + long difficultyAdjustmentInterval = (long)this.TargetTimespan.TotalSeconds / (long)this.TargetSpacing.TotalSeconds; + + // Only change once per interval. + if ((height) % difficultyAdjustmentInterval != 0) + { + if (this.PowAllowMinDifficultyBlocks) + { + // Special difficulty rule for testnet: + // If the new block's timestamp is more than 2* 10 minutes + // then allow mining of a min-difficulty block. + if (chainedHeader.Header.BlockTime > (lastBlock.Header.BlockTime + TimeSpan.FromTicks(this.TargetSpacing.Ticks * 2))) + return proofOfWorkLimit; + + // Return the last non-special-min-difficulty-rules-block. + ChainedHeader lastChainedHeader = lastBlock; + while ((lastChainedHeader.Previous != null) && ((lastChainedHeader.Height % difficultyAdjustmentInterval) != 0) && (lastChainedHeader.Header.Bits == proofOfWorkLimit)) + lastChainedHeader = lastChainedHeader.Previous; + + return lastChainedHeader.Header.Bits; + } + + return lastBlock.Header.Bits; + } + + // Go back by what we want to be 14 days worth of blocks. + long pastHeight = lastBlock.Height - (difficultyAdjustmentInterval - 1); + + ChainedHeader firstChainedHeader = chainedHeader.GetAncestor((int)pastHeight); + if (firstChainedHeader == null) + throw new NotSupportedException("Can only calculate work of a full chain"); + + if (this.PowNoRetargeting) + return lastBlock.Header.Bits; + + // Limit adjustment step. + TimeSpan actualTimespan = lastBlock.Header.BlockTime - firstChainedHeader.Header.BlockTime; + if (actualTimespan < TimeSpan.FromTicks(this.TargetTimespan.Ticks / 4)) + actualTimespan = TimeSpan.FromTicks(this.TargetTimespan.Ticks / 4); + if (actualTimespan > TimeSpan.FromTicks(this.TargetTimespan.Ticks * 4)) + actualTimespan = TimeSpan.FromTicks(this.TargetTimespan.Ticks * 4); + + // Retarget. + BigInteger newTarget = lastBlock.Header.Bits.ToBigInteger(); + newTarget = newTarget.Multiply(BigInteger.ValueOf((long)actualTimespan.TotalSeconds)); + newTarget = newTarget.Divide(BigInteger.ValueOf((long)this.TargetTimespan.TotalSeconds)); + + var finalTarget = new Target(newTarget); + if (finalTarget > proofOfWorkLimit) + finalTarget = proofOfWorkLimit; + + return finalTarget; + } } } \ No newline at end of file diff --git a/src/Tests/Blockcore.IntegrationTests.Common/EnvironmentMockUpHelpers/CoreNode.cs b/src/Tests/Blockcore.IntegrationTests.Common/EnvironmentMockUpHelpers/CoreNode.cs index 3acdcda0f..1ab99f181 100644 --- a/src/Tests/Blockcore.IntegrationTests.Common/EnvironmentMockUpHelpers/CoreNode.cs +++ b/src/Tests/Blockcore.IntegrationTests.Common/EnvironmentMockUpHelpers/CoreNode.cs @@ -31,6 +31,7 @@ using Blockcore.Signals; using Blockcore.Tests.Common; using Blockcore.Utilities; +using Blockcore.Utilities.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; @@ -617,7 +618,7 @@ public async Task GenerateAsync(int blockCount, bool includeUnbroadcast foreach (ChainedHeader header in headers) { - if (!header.Validate(peer.Network)) + if (!this.Validate(peer.Network, header)) { throw new ProtocolException("A header which does not pass proof of work verification has been received"); } @@ -628,6 +629,54 @@ public async Task GenerateAsync(int blockCount, bool includeUnbroadcast return headers; } + /// + /// Check that the header is a valid block header including the work done for PoW blocks. + /// + /// The network to verify against. + /// true if the header is a valid block header, false otherwise. + public bool Validate(Network network, ChainedHeader header) + { + if (network == null) + throw new ArgumentNullException("network"); + + if (network.Consensus.IsProofOfStake) + return BlockStake.Validate(network, header); + + bool genesisCorrect = (header.Height != 0) || header.HashBlock == network.GetGenesis().GetHash(); + return genesisCorrect && this.Validate(network.Consensus, header); + } + + /// + /// Check PoW against consensus and that the blocks connect correctly. + /// + /// The consensus rules being used. + /// true if the header is a valid block header, false otherwise. + public bool Validate(IConsensus consensus, ChainedHeader header) + { + if (consensus == null) + throw new ArgumentNullException("consensus"); + + if ((header.Height != 0) && (header.Previous == null)) + return false; + + bool heightCorrect = (header.Height == 0) || (header.Height == header.Previous.Height + 1); + bool hashPrevCorrect = (header.Height == 0) || (header.Header.HashPrevBlock == header.Previous.HashBlock); + bool hashCorrect = header.HashBlock == header.Header.GetHash(); + bool workCorrect = this.CheckProofOfWorkAndTarget(consensus, header); + + return heightCorrect && hashPrevCorrect && hashCorrect && workCorrect; + } + + /// + /// Verify proof of work of the header of this chain using consensus. + /// + /// Consensus rules to use for this validation. + /// Whether proof of work is valid. + public bool CheckProofOfWorkAndTarget(IConsensus consensus, ChainedHeader header) + { + return (header.Height == 0) || (header.Header.CheckProofOfWork() && (header.Header.Bits == header.GetWorkRequired(consensus))); + } + private async Task AssertStateAsync(INetworkPeer peer, NetworkPeerState peerState, CancellationToken cancellationToken = default(CancellationToken)) { if ((peerState == NetworkPeerState.HandShaked) && (peer.State == NetworkPeerState.Connected)) diff --git a/src/Tests/Blockcore.Tests/NBitcoin/ChainTests.cs b/src/Tests/Blockcore.Tests/NBitcoin/ChainTests.cs index 0f30465ea..b260c56a1 100644 --- a/src/Tests/Blockcore.Tests/NBitcoin/ChainTests.cs +++ b/src/Tests/Blockcore.Tests/NBitcoin/ChainTests.cs @@ -8,6 +8,7 @@ using Blockcore.Consensus.ScriptInfo; using Blockcore.Networks; using Blockcore.Tests.Common; +using Blockcore.Utilities.Extensions; using Xunit; namespace NBitcoin.Tests @@ -158,21 +159,11 @@ public void CanCalculateDifficulty() BlockHeader block = main.GetHeader(height).Header; Assert.Equal(expectedTarget, block.Bits); - Target target = main.GetHeader(height).GetWorkRequired(network); + Target target = main.GetHeader(height).GetWorkRequired(this.network.Consensus); Assert.Equal(expectedTarget, target); } } - [Fact] - public void CanValidateChain() - { - var main = new ChainIndexer(this.network).Load(this.LoadMainChain()); - foreach (ChainedHeader h in main.EnumerateToTip(main.Genesis)) - { - Assert.True(h.Validate(this.network)); - } - } - private byte[] LoadMainChain() { if (!File.Exists("MainChain1.dat")) diff --git a/src/Tests/Blockcore.Tests/NBitcoin/pow_tests.cs b/src/Tests/Blockcore.Tests/NBitcoin/pow_tests.cs index 4a254cc0b..c3a168098 100644 --- a/src/Tests/Blockcore.Tests/NBitcoin/pow_tests.cs +++ b/src/Tests/Blockcore.Tests/NBitcoin/pow_tests.cs @@ -1,8 +1,11 @@ using System; using System.IO; using System.Net.Http; +using Blockcore.Consensus; +using Blockcore.Consensus.BlockInfo; using Blockcore.Consensus.Chain; using Blockcore.Tests.Common; +using Blockcore.Utilities.Extensions; using Xunit; namespace NBitcoin.Tests @@ -17,13 +20,23 @@ public void CanCalculatePowCorrectly() chain.Load(File.ReadAllBytes("MainChain.dat")); foreach (ChainedHeader block in chain.EnumerateAfter(chain.Genesis)) { - Target thisWork = block.GetWorkRequired(KnownNetworks.Main); - Target thisWork2 = block.Previous.GetNextWorkRequired(KnownNetworks.Main); + Target thisWork = this.GetNextWorkRequired(KnownNetworks.Main.Consensus, block); + Target thisWork2 = this.GetNextWorkRequired(KnownNetworks.Main.Consensus, block.Previous); Assert.Equal(thisWork, thisWork2); - Assert.True(block.CheckProofOfWorkAndTarget(KnownNetworks.Main)); + Assert.True(this.CheckProofOfWorkAndTarget(KnownNetworks.Main.Consensus, block)); } } + /// + /// Verify proof of work of the header of this chain using consensus. + /// + /// Consensus rules to use for this validation. + /// Whether proof of work is valid. + public bool CheckProofOfWorkAndTarget(IConsensus consensus, ChainedHeader header) + { + return (header.Height == 0) || (header.Header.CheckProofOfWork() && (header.Header.Bits == header.GetWorkRequired(consensus))); + } + private static void EnsureDownloaded(string file, string url) { if (File.Exists(file)) @@ -33,5 +46,19 @@ private static void EnsureDownloaded(string file, string url) byte[] data = client.GetByteArrayAsync(url).GetAwaiter().GetResult(); File.WriteAllBytes(file, data); } + + /// + /// Gets the proof of work target for a potential new block after this entry on the chain. + /// + /// Consensus rules to use for this computation. + /// The target proof of work. + public Target GetNextWorkRequired(IConsensus consensus, ChainedHeader chainedHeader) + { + BlockHeader dummy = consensus.ConsensusFactory.CreateBlockHeader(); + dummy.HashPrevBlock = chainedHeader.HashBlock; + dummy.BlockTime = DateTimeOffset.UtcNow; + + return new ChainedHeader(dummy, dummy.GetHash(), chainedHeader).GetWorkRequired(consensus); + } } } \ No newline at end of file From 14ad520d3c5ef28ce5f8c1f0b5afd6d151200bd0 Mon Sep 17 00:00:00 2001 From: dangershony Date: Tue, 23 Feb 2021 19:28:42 +0000 Subject: [PATCH 2/2] Fix failing test --- src/Tests/Blockcore.Tests/NBitcoin/pow_tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tests/Blockcore.Tests/NBitcoin/pow_tests.cs b/src/Tests/Blockcore.Tests/NBitcoin/pow_tests.cs index c3a168098..833f31a51 100644 --- a/src/Tests/Blockcore.Tests/NBitcoin/pow_tests.cs +++ b/src/Tests/Blockcore.Tests/NBitcoin/pow_tests.cs @@ -20,7 +20,7 @@ public void CanCalculatePowCorrectly() chain.Load(File.ReadAllBytes("MainChain.dat")); foreach (ChainedHeader block in chain.EnumerateAfter(chain.Genesis)) { - Target thisWork = this.GetNextWorkRequired(KnownNetworks.Main.Consensus, block); + Target thisWork = block.GetWorkRequired(KnownNetworks.Main.Consensus); Target thisWork2 = this.GetNextWorkRequired(KnownNetworks.Main.Consensus, block.Previous); Assert.Equal(thisWork, thisWork2); Assert.True(this.CheckProofOfWorkAndTarget(KnownNetworks.Main.Consensus, block));