Exemple #1
0
        private void ValidateSeals(CancellationToken cancellation, BlockHeader[] headers)
        {
            if (_logger.IsTrace)
            {
                _logger.Trace("Starting seal validation");
            }
            var exceptions = new ConcurrentQueue <Exception>();

            Parallel.For(0, headers.Length, (i, state) =>
            {
                if (cancellation.IsCancellationRequested)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace("Returning fom seal validation");
                    }
                    state.Stop();
                    return;
                }

                BlockHeader header = headers[i];
                if (header == null)
                {
                    return;
                }

                try
                {
                    if (!_sealValidator.ValidateSeal(headers[i]))
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace("One of the seals is invalid");
                        }
                        throw new EthSynchronizationException("Peer sent a block with an invalid seal");
                    }
                }
                catch (Exception e)
                {
                    exceptions.Enqueue(e);
                    state.Stop();
                }
            });

            if (_logger.IsTrace)
            {
                _logger.Trace("Seal validation complete");
            }

            if (exceptions.Count > 0)
            {
                if (_logger.IsDebug)
                {
                    _logger.Debug("Seal validation failure");
                }
                throw new AggregateException(exceptions);
            }
        }
Exemple #2
0
        private void ValidateHeaders(CancellationToken cancellation, FastBlocksBatch batch)
        {
            batch.MarkValidation();
            try
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace("Starting block validation");
                }

                BlockHeader[] headers = batch.Headers.Response;
                for (int i = 0; i < headers.Length; i++)
                {
                    if (cancellation.IsCancellationRequested)
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace("Returning fom seal validation");
                        }
                        return;
                    }

                    BlockHeader header = headers[i];
                    if (header == null)
                    {
                        continue;
                    }

                    bool isHashValid = _blockValidator.ValidateHash(header);
                    bool isSealValid = _sealValidator.ValidateSeal(header);
                    if (!(isHashValid && isSealValid))
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Trace("One of the blocks is invalid");
                        }
                        _syncPeerPool.ReportInvalid(batch.Allocation?.Current);
                        batch.Headers.Response = null;
                    }
                }
            }
            catch (Exception ex)
            {
                if (_logger.IsError)
                {
                    _logger.Error($"Error when validating headers of {batch}", ex);
                }
                batch.Headers.Response = null;
            }
        }
Exemple #3
0
        private void ValidateSeal(Block block, ISyncPeer syncPeer)
        {
            if (_logger.IsTrace)
            {
                _logger.Trace($"Validating seal of {block.ToString(Block.Format.Short)}) from {syncPeer:c}");
            }

            // We hint validation range mostly to help ethash to cache epochs.
            // It is important that we only do that here, after we ensured that the block is
            // in the range of [Head - MaxReorganizationLength, Head].
            // Otherwise we could hint incorrect ranges and cause expensive cache recalculations.
            _sealValidator.HintValidationRange(_sealValidatorUserGuid, block.Number - 128, block.Number + 1024);
            if (!_sealValidator.ValidateSeal(block.Header, true))
            {
                string message = $"Peer {syncPeer?.Node:c} sent a block with an invalid seal";
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Peer {syncPeer?.Node:c} sent a block with an invalid seal");
                }
                throw new EthSyncException(message);
            }
        }
Exemple #4
0
 public bool ValidateSeal(BlockHeader header, bool force) =>
 _sealValidator.ValidateSeal(header, force);
Exemple #5
0
        public void AddNewBlock(Block block, Node nodeWhoSentTheBlock)
        {
            if (block.TotalDifficulty == null)
            {
                throw new InvalidOperationException("Cannot add a block with unknown total difficulty");
            }

            _pool.TryFind(nodeWhoSentTheBlock.Id, out PeerInfo peerInfo);
            if (peerInfo == null)
            {
                string errorMessage = $"Received a new block from an unknown peer {nodeWhoSentTheBlock:c} {nodeWhoSentTheBlock.Id} {_pool.PeerCount}";
                if (_logger.IsDebug)
                {
                    _logger.Debug(errorMessage);
                }
                return;
            }

            if ((block.TotalDifficulty ?? 0) > peerInfo.TotalDifficulty)
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"ADD NEW BLOCK Updating header of {peerInfo} from {peerInfo.HeadNumber} {peerInfo.TotalDifficulty} to {block.Number} {block.TotalDifficulty}");
                }
                peerInfo.HeadNumber      = block.Number;
                peerInfo.HeadHash        = block.Hash;
                peerInfo.TotalDifficulty = block.TotalDifficulty ?? peerInfo.TotalDifficulty;
            }

            if ((block.TotalDifficulty ?? 0) < _blockTree.BestSuggestedHeader.TotalDifficulty)
            {
                return;
            }

            lock (_recentlySuggested)
            {
                if (_recentlySuggested.Get(block.Hash) != null)
                {
                    return;
                }
                _recentlySuggested.Set(block.Hash, _dummyValue);
            }

            if (block.Number > _blockTree.BestKnownNumber + 8)
            {
                // ignore blocks when syncing in a simple non-locking way
                _synchronizer.RequestSynchronization(SyncTriggerType.NewDistantBlock);
                return;
            }

            if (_logger.IsTrace)
            {
                _logger.Trace($"Adding new block {block.ToString(Block.Format.Short)}) from {nodeWhoSentTheBlock:c}");
            }

            if (!_sealValidator.ValidateSeal(block.Header))
            {
                throw new EthSynchronizationException("Peer sent a block with an invalid seal");
            }

            if (block.Number <= _blockTree.BestKnownNumber + 1)
            {
                if (_logger.IsInfo)
                {
                    string authorString = block.Author == null ? string.Empty : "sealed by " + (KnownAddresses.GoerliValidators.ContainsKey(block.Author) ? KnownAddresses.GoerliValidators[block.Author] : block.Author?.ToString());
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Discovered a new block {string.Empty.PadLeft(9 - block.Number.ToString().Length, ' ')}{block.ToString(Block.Format.HashNumberAndTx)} {authorString}, sent by {nodeWhoSentTheBlock:s}");
                    }
                }

                if (_logger.IsTrace)
                {
                    _logger.Trace($"{block}");
                }

                if (_synchronizer.SyncMode == SyncMode.Full)
                {
                    AddBlockResult result = _blockTree.SuggestBlock(block);
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"{block.Hash} ({block.Number}) adding result is {result}");
                    }
                    if (result == AddBlockResult.UnknownParent)
                    {
                        _synchronizer.RequestSynchronization(SyncTriggerType.Reorganization);
                    }
                }
            }
            else
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Received a block {block.Hash} ({block.Number}) from {nodeWhoSentTheBlock} - need to resync");
                }
                _synchronizer.RequestSynchronization(SyncTriggerType.NewNearBlock);
            }
        }
Exemple #6
0
        public bool Validate(BlockHeader header, bool isOmmer = false)
        {
            bool areNonceValidAndMixHashValid = header.Number == 0 || header.SealEngineType == SealEngineType.None || _sealValidator.ValidateSeal(header);

            if (!areNonceValidAndMixHashValid)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - invalid mix hash / nonce");
            }

            bool hashAsExpected = header.Hash == BlockHeader.CalculateHash(header);

            if (!hashAsExpected)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - invalid block hash");
            }

            Block parent = _blockTree.FindBlock(header.ParentHash, false);

            if (parent == null)
            {
                if (header.Number == 0)
                {
                    var isGenesisValid = IsGenesisHeaderValid(header);;
                    if (!isGenesisValid)
                    {
                        _logger.Warn($"Invalid genesis block header ({header.Hash})");
                    }

                    return(isGenesisValid);
                }

                _logger.Warn($"Orphan block, could not find parent ({header.Hash})");
                return(false);
            }

            bool sealParamsCorrect = _sealValidator.ValidateParams(parent, header);

            if (!sealParamsCorrect)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - seal parameters incorrect");
            }

            // difficulty check
            bool gasUsedBelowLimit = header.GasUsed <= header.GasLimit;

            if (!gasUsedBelowLimit)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - gas used above gas limit");
            }

            bool gasLimitNotTooHigh = header.GasLimit < parent.Header.GasLimit + BigInteger.Divide(parent.Header.GasLimit, 1024);

            if (!gasLimitNotTooHigh)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - gas limit too high");
            }

            bool gasLimitNotTooLow = header.GasLimit > parent.Header.GasLimit - BigInteger.Divide(parent.Header.GasLimit, 1024);

            if (!gasLimitNotTooLow)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - invalid mix hash / nonce");
            }

//            bool gasLimitAboveAbsoluteMinimum = header.GasLimit >= 125000; // TODO: tests are consistently not following this rule
            bool timestampMoreThanAtParent = header.Timestamp > parent.Header.Timestamp;

            if (!timestampMoreThanAtParent)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - timestamp before parent");
            }

            bool numberIsParentPlusOne = header.Number == parent.Header.Number + 1;

            if (!numberIsParentPlusOne)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - block number is not parent + 1");
            }

            if (_logger.IsTrace)
            {
                _logger.Trace($"Validating block {header.Hash} ({header.Number}) - DAO block {_daoBlockNumber}, extraData {header.ExtraData.ToHexString(true)}");
            }

            bool extraDataValid = isOmmer ||
                                  _daoBlockNumber == null ||
                                  header.Number < _daoBlockNumber ||
                                  header.Number >= _daoBlockNumber + 10 ||
                                  Bytes.AreEqual(header.ExtraData, DaoExtraData);

            if (!extraDataValid)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - DAO extra data not valid");
            }

            return
                (areNonceValidAndMixHashValid &&
                 gasUsedBelowLimit &&
                 gasLimitNotTooLow &&
                 gasLimitNotTooHigh &&
                 sealParamsCorrect &&
//                   gasLimitAboveAbsoluteMinimum && // TODO: tests are consistently not following this rule
                 timestampMoreThanAtParent &&
                 numberIsParentPlusOne &&
                 hashAsExpected &&
                 extraDataValid);
        }
 public bool ValidateSeal(BlockHeader header, bool force) =>
 _poSSwitcher.GetBlockConsensusInfo(header, true).IsPostMerge || _preMergeSealValidator.ValidateSeal(header, force);
        /// <summary>
        /// Validates all the header elements (usually in relation to parent). Difficulty calculation is validated in <see cref="ISealValidator"/>
        /// </summary>
        /// <param name="header">Block header to validate</param>
        /// <param name="isOmmer"><value>True</value> if the <paramref name="header"/> is an ommer, otherwise <value>False</value></param>
        /// <returns><value>True</value> if <paramref name="header"/> is valid, otherwise <value>False</value></returns>
        public bool Validate(BlockHeader header, bool isOmmer = false)
        {
            // the rule here is to validate the seal first (avoid any cheap attacks on validation logic)
            // then validate whatever does not need to load parent from disk (the most expensive operation)

            bool areNonceValidAndMixHashValid = header.Number == 0 || header.SealEngineType == SealEngineType.None || _sealValidator.ValidateSeal(header);

            if (!areNonceValidAndMixHashValid)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Invalid block header ({header.Hash}) - invalid mix hash / nonce");
                }
            }

            bool hashAsExpected = header.Hash == BlockHeader.CalculateHash(header);

            if (!hashAsExpected)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Invalid block header ({header.Hash}) - invalid block hash");
                }
            }

            bool extraDataValid = isOmmer ||
                                  _daoBlockNumber == null ||
                                  header.Number < _daoBlockNumber ||
                                  header.Number >= _daoBlockNumber + 10 ||
                                  Bytes.AreEqual(header.ExtraData, DaoExtraData);

            if (!extraDataValid)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - DAO extra data not valid");
            }

            BlockHeader parent = _blockTree.FindHeader(header.ParentHash, false);

            if (parent == null)
            {
                if (header.Number == 0)
                {
                    var isGenesisValid = ValidateGenesis(header);;
                    if (!isGenesisValid)
                    {
                        if (_logger.IsWarn)
                        {
                            _logger.Warn($"Invalid genesis block header ({header.Hash})");
                        }
                    }

                    return(isGenesisValid);
                }

                if (_logger.IsWarn)
                {
                    _logger.Warn($"Orphan block, could not find parent ({header.Hash})");
                }
                return(false);
            }

            // seal is validated when synchronizing so we can remove it from here - review and test
            bool sealParamsCorrect = _sealValidator.ValidateParams(parent, header);

            if (!sealParamsCorrect)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - seal parameters incorrect");
            }

            bool gasUsedBelowLimit = header.GasUsed <= header.GasLimit;

            if (!gasUsedBelowLimit)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - gas used above gas limit");
            }

            long maxGasLimitDifference = parent.GasLimit / 1024;
            bool gasLimitNotTooHigh    = header.GasLimit < parent.GasLimit + maxGasLimitDifference;

            if (!gasLimitNotTooHigh)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - gas limit too high");
            }

            bool gasLimitNotTooLow = header.GasLimit > parent.GasLimit - maxGasLimitDifference;

            if (!gasLimitNotTooLow)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - invalid mix hash / nonce");
            }

            // bool gasLimitAboveAbsoluteMinimum = header.GasLimit >= 125000; // described in the YellowPaper but not followed
            bool timestampMoreThanAtParent = header.Timestamp > parent.Timestamp;

            if (!timestampMoreThanAtParent)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - timestamp before parent");
            }

            bool numberIsParentPlusOne = header.Number == parent.Number + 1;

            if (!numberIsParentPlusOne)
            {
                _logger.Warn($"Invalid block header ({header.Hash}) - block number is not parent + 1");
            }

            if (_logger.IsTrace)
            {
                _logger.Trace($"Validating block {header.ToString(BlockHeader.Format.Short)}, extraData {header.ExtraData.ToHexString(true)}");
            }

            return
                (areNonceValidAndMixHashValid &&
                 gasUsedBelowLimit &&
                 gasLimitNotTooLow &&
                 gasLimitNotTooHigh &&
                 sealParamsCorrect &&
                 // gasLimitAboveAbsoluteMinimum && // described in the YellowPaper but not followed
                 timestampMoreThanAtParent &&
                 numberIsParentPlusOne &&
                 hashAsExpected &&
                 extraDataValid);
        }