public void PreValidateBlock(Chain newChain)
{
var chainedHeader = newChain.LastBlock;
// calculate the next required target
var requiredBits = GetRequiredNextBits(newChain);
// validate required target
var blockTarget = chainedHeader.BlockHeader.CalculateTarget();
if (blockTarget > ChainParams.HighestTarget)
throw new ValidationException(chainedHeader.Hash);
// validate block's target against the required target
if (chainedHeader.Bits != requiredBits)
{
throw new ValidationException(chainedHeader.Hash,
$"Failing block {chainedHeader.Hash} at height {chainedHeader.Height}: Block bits {chainedHeader.Bits} did not match required bits of {requiredBits}");
}
// validate block's proof of work against its stated target
if (chainedHeader.Hash > blockTarget)
{
throw new ValidationException(chainedHeader.Hash,
$"Failing block {chainedHeader.Hash} at height {chainedHeader.Height}: Block did not match its own target of {blockTarget}");
}
//TODO
// calculate adjusted time
var adjustedTime = DateTimeOffset.Now;
// calculate max block time
var maxBlockTime = adjustedTime + TimeSpan.FromHours(2);
// verify max block time
var blockTime = chainedHeader.Time;
if (blockTime > maxBlockTime)
throw new ValidationException(chainedHeader.Hash);
// ensure timestamp is greater than the median timestamp of the previous 11 blocks
if (chainedHeader.Height > 0)
{
var medianTimeSpan = Math.Min(11, newChain.Blocks.Count - 1);
var prevHeaderTimes = newChain.Blocks.GetRange(newChain.Blocks.Count - 1 - medianTimeSpan, medianTimeSpan)
//TODO pull tester doesn't fail if the sort step is missed
.OrderBy(x => x.Time).ToList();
var medianPrevHeaderTime = GetMedianPrevHeaderTime(newChain, chainedHeader.Height);
if (blockTime <= medianPrevHeaderTime)
{
throw new ValidationException(chainedHeader.Hash,
$"Failing block {chainedHeader.Hash} at height {chainedHeader.Height}: Block's time of {blockTime} must be greater than past median time of {medianPrevHeaderTime}");
}
}
//TODO: ContextualCheckBlockHeader nVersion checks
}