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..833f31a51 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 = block.GetWorkRequired(KnownNetworks.Main.Consensus);
+ 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