Ejemplo n.º 1
0
        private AddBlockResult InsertHeader(BlockHeader header)
        {
            if (header.IsGenesis)
            {
                return(AddBlockResult.AlreadyKnown);
            }

            AddBlockResult insertOutcome = _blockTree.Insert(header);

            if (insertOutcome == AddBlockResult.Added || insertOutcome == AddBlockResult.AlreadyKnown)
            {
                ulong nextHeaderDiff = 0;
                _nextHeaderHash = header.ParentHash;
                if (_expectedDifficultyOverride?.TryGetValue(header.Number, out nextHeaderDiff) == true)
                {
                    _nextHeaderDiff = nextHeaderDiff;
                }
                else
                {
                    _nextHeaderDiff = (header.TotalDifficulty ?? 0) - header.Difficulty;
                }
            }

            return(insertOutcome);
        }
            public ProcessingTestContext Suggested(Block block)
            {
                AddBlockResult result = _blockTree.SuggestBlock(block);

                if (result != AddBlockResult.Added)
                {
                    Console.WriteLine($"Finished waiting for {block.ToString(Block.Format.Short)} as block was ignored");
                    _resetEvent.Set();
                }

                return(this);
            }
Ejemplo n.º 3
0
        private async Task ProcessBlock(Block block)
        {
            try
            {
                if (!_blockValidator.ValidateSuggestedBlock(block))
                {
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Invalid block {block}");
                    }
                    return;
                }

                AddBlockResult result = await _blockTree.SuggestBlockAsync(block);

                if (result != AddBlockResult.Added && result != AddBlockResult.AlreadyKnown)
                {
                    if (_logger.IsError)
                    {
                        _logger.Error($"Cannot add block {block} to the blockTree, add result {result}");
                    }
                    return;
                }

                try
                {
                    if (_tracer.Trace(block, NullBlockTracer.Instance) is null)
                    {
                        return;
                    }
                }
                catch (Exception ex)
                {
                    if (_logger.IsError)
                    {
                        _logger.Error($"Failed to process block {block}", ex);
                    }
                    return;
                }

                if (_logger.IsInfo)
                {
                    _logger.Info(
                        $"HIVE suggested {block.ToString(Block.Format.Short)}, now best suggested header {_blockTree.BestSuggestedHeader}, head {_blockTree.Head?.Header?.ToString(BlockHeader.Format.Short)}");
                }

                await WaitForBlockProcessing(_resetEvent);
            }
            catch (Exception e)
            {
                _logger.Error($"HIVE Invalid block: {block.Hash}, ignoring. ", e);
            }
        }
Ejemplo n.º 4
0
            public ProcessingTestContext Suggested(BlockHeader block)
            {
                AddBlockResult result = _blockTree.SuggestHeader(block);

                if (result != AddBlockResult.Added)
                {
                    _logger.Info($"Finished waiting for {block.ToString(BlockHeader.Format.Short)} as block was ignored");
                    _resetEvent.Set();
                }

                return(this);
            }
Ejemplo n.º 5
0
        private bool HandleAddResult(BlockHeader block, bool isFirstInBatch, AddBlockResult addResult)
        {
            switch (addResult)
            {
            // this generally should not happen as there is a consistency check before
            case AddBlockResult.UnknownParent:
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Block/header {block.Number} ignored (unknown parent)");
                }
                if (isFirstInBatch)
                {
                    const string message = "Peer sent orphaned blocks/headers inside the batch";
                    _logger.Error(message);
                    throw new EthSynchronizationException(message);
                }
                else
                {
                    const string message = "Peer sent an inconsistent batch of blocks/headers";
                    _logger.Error(message);
                    throw new EthSynchronizationException(message);
                }
            }

            case AddBlockResult.CannotAccept:
                throw new EthSynchronizationException("Block tree rejected block/header");

            case AddBlockResult.InvalidBlock:
                throw new EthSynchronizationException("Peer sent an invalid block/header");

            case AddBlockResult.Added:
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Block/header {block.Number} suggested for processing");
                }
                return(true);

            case AddBlockResult.AlreadyKnown:
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Block/header {block.Number} skipped - already known");
                }
                return(false);

            default:
                throw new NotImplementedException($"Unknown {nameof(AddBlockResult)} {addResult}");
            }
        }
Ejemplo n.º 6
0
    public void Can_start_insert_pivot_block_with_correct_pointers()
    {
        (BlockTree notSyncedTree, BlockTree syncedTree) = BuildBlockTrees(10, 20);
        Block?beaconBlock = syncedTree.FindBlock(14, BlockTreeLookupOptions.None);
        BlockTreeInsertHeaderOptions insertHeaderOption = BlockTreeInsertHeaderOptions.BeaconBlockInsert;
        AddBlockResult insertResult = notSyncedTree.Insert(beaconBlock, BlockTreeInsertBlockOptions.SaveHeader, insertHeaderOption);

        Assert.AreEqual(AddBlockResult.Added, insertResult);
        Assert.AreEqual(9, notSyncedTree.BestKnownNumber);
        Assert.AreEqual(9, notSyncedTree.BestSuggestedHeader !.Number);
        Assert.AreEqual(9, notSyncedTree.Head !.Number);
        Assert.AreEqual(9, notSyncedTree.BestSuggestedBody !.Number);
        Assert.AreEqual(14, notSyncedTree.BestKnownBeaconNumber);
        Assert.AreEqual(14, notSyncedTree.BestSuggestedBeaconHeader !.Number);
    }
Ejemplo n.º 7
0
    public void Can_insert_beacon_headers()
    {
        (BlockTree notSyncedTree, BlockTree syncedTree) = BuildBlockTrees(10, 20);

        Block?beaconBlock = syncedTree.FindBlock(14, BlockTreeLookupOptions.None);
        BlockTreeInsertHeaderOptions headerOptions = BlockTreeInsertHeaderOptions.BeaconBlockInsert;
        AddBlockResult insertResult = notSyncedTree.Insert(beaconBlock, BlockTreeInsertBlockOptions.SaveHeader, headerOptions);

        for (int i = 13; i > 9; --i)
        {
            BlockHeader?   beaconHeader  = syncedTree.FindHeader(i, BlockTreeLookupOptions.None);
            AddBlockResult insertOutcome = notSyncedTree.Insert(beaconHeader !, headerOptions);
            Assert.AreEqual(insertOutcome, insertResult);
        }
    }
Ejemplo n.º 8
0
        private AddBlockResult InsertHeader(BlockHeader header)
        {
            if (header.IsGenesis)
            {
                return(AddBlockResult.AlreadyKnown);
            }

            AddBlockResult insertOutcome = _blockTree.Insert(header);

            if (insertOutcome == AddBlockResult.Added || insertOutcome == AddBlockResult.AlreadyKnown)
            {
                _nextHeaderHash = header.ParentHash;
                _nextHeaderDiff = (header.TotalDifficulty ?? 0) - header.Difficulty;
            }

            return(insertOutcome);
        }
Ejemplo n.º 9
0
        private void SyncBlock(Block block, ISyncPeer syncPeer)
        {
            if (_logger.IsTrace)
            {
                _logger.Trace($"{block}");
            }

            // we do not trust total difficulty from peers
            // Parity sends invalid data here and it is equally expensive to validate and to set from null
            block.Header.TotalDifficulty = null;

            bool isKnownParent = _blockTree.IsKnownBlock(block.Number - 1, block.ParentHash);

            if (isKnownParent)
            {
                if (!_blockValidator.ValidateSuggestedBlock(block))
                {
                    string message = $"Peer {syncPeer?.Node:c} sent an invalid block";
                    if (_logger.IsDebug)
                    {
                        _logger.Debug(message);
                    }
                    lock (_recentlySuggested)
                    {
                        _recentlySuggested.Delete(block.Hash);
                    }

                    throw new EthSyncException(message);
                }

                AddBlockResult result = _blockTree.SuggestBlock(block, true);
                if (_logger.IsTrace)
                {
                    _logger.Trace($"{block.Hash} ({block.Number}) adding result is {result}");
                }
            }

            // TODO: now it should be done by sync peer pool?
            // // do not change to if..else
            // // there are some rare cases when it did not work...
            // // do not remember why
            // if (result == AddBlockResult.UnknownParent)
            // {
            //     _synchronizer.RequestSynchronization(SyncTriggerType.NewBlock);
            // }
        }
Ejemplo n.º 10
0
        public void After_removing_invalid_block_will_not_accept_it_again()
        {
            MemDb     blocksDb     = new MemDb();
            MemDb     blockInfosDb = new MemDb();
            BlockTree tree         = new BlockTree(blocksDb, blockInfosDb, MainNetSpecProvider.Instance, NullTransactionPool.Instance, LimboLogs.Instance);
            Block     block0       = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject;
            Block     block1       = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject;
            Block     block2       = Build.A.Block.WithNumber(2).WithDifficulty(3).WithParent(block1).TestObject;
            Block     block3       = Build.A.Block.WithNumber(3).WithDifficulty(4).WithParent(block2).TestObject;

            tree.SuggestBlock(block0);
            tree.SuggestBlock(block1);
            tree.SuggestBlock(block2);
            tree.SuggestBlock(block3);

            tree.DeleteInvalidBlock(block1);
            AddBlockResult result = tree.SuggestBlock(block1);

            Assert.AreEqual(AddBlockResult.InvalidBlock, result);
        }
Ejemplo n.º 11
0
        public BlockTreeBuilder OfChainLength(out Block headBlock, int chainLength, int splitVariant = 0, int splitFrom = 0, params Address[] blockBeneficiaries)
        {
            Block current = _genesisBlock;

            headBlock = _genesisBlock;

            bool skipGenesis = TestObjectInternal.Genesis != null;

            for (int i = 0; i < chainLength; i++)
            {
                Address beneficiary = blockBeneficiaries.Length == 0 ? Address.Zero : blockBeneficiaries[i % blockBeneficiaries.Length];
                headBlock = current;
                if (_onlyHeaders)
                {
                    if (!(current.IsGenesis && skipGenesis))
                    {
                        TestObjectInternal.SuggestHeader(current.Header);
                    }

                    Block parent = current;
                    current = CreateBlock(splitVariant, splitFrom, i, parent, beneficiary);
                }
                else
                {
                    if (!(current.IsGenesis && skipGenesis))
                    {
                        AddBlockResult result = TestObjectInternal.SuggestBlock(current);
                        Assert.AreEqual(AddBlockResult.Added, result, $"Adding {current.ToString(Block.Format.Short)} at split variant {splitVariant}");

                        TestObjectInternal.UpdateMainChain(current);
                    }

                    Block parent = current;

                    current = CreateBlock(splitVariant, splitFrom, i, parent, beneficiary);
                }
            }

            return(this);
        }
Ejemplo n.º 12
0
        private AddBlockResult InsertHeader(BlockHeader header)
        {
            if (header.IsGenesis)
            {
                return(AddBlockResult.AlreadyKnown);
            }

            AddBlockResult insertOutcome = _blockTree.Insert(header);

            if (insertOutcome == AddBlockResult.Added || insertOutcome == AddBlockResult.AlreadyKnown)
            {
                _nextHeaderHash = header.ParentHash;
                _nextHeaderDiff = (header.TotalDifficulty ?? 0) - header.Difficulty;

                long parentNumber = header.Number - 1;
                if (_dependencies.TryRemove(parentNumber, out HeadersSyncBatch batch))
                {
                    InsertHeaders(batch);
                }
            }

            return(insertOutcome);
        }
Ejemplo n.º 13
0
        private void SyncBlock(Block block, ISyncPeer syncPeer)
        {
            if (_logger.IsTrace)
            {
                _logger.Trace($"{block}");
            }

            // we do not trust total difficulty from peers
            // Parity sends invalid data here and it is equally expensive to validate and to set from null
            block.Header.TotalDifficulty = null;

            bool isKnownParent = _blockTree.IsKnownBlock(block.Number - 1, block.ParentHash);

            if (isKnownParent)
            {
                if (!_blockValidator.ValidateSuggestedBlock(block))
                {
                    string message = $"Peer {syncPeer?.Node:c} sent an invalid block";
                    if (_logger.IsDebug)
                    {
                        _logger.Debug(message);
                    }
                    lock (_recentlySuggested)
                    {
                        _recentlySuggested.Delete(block.Hash);
                    }

                    throw new EthSyncException(message);
                }

                AddBlockResult result = _blockTree.SuggestBlock(block);
                if (_logger.IsTrace)
                {
                    _logger.Trace($"{block.Hash} ({block.Number}) adding result is {result}");
                }
            }
        }
Ejemplo n.º 14
0
        public BlockTreeBuilder OfChainLength(out Block headBlock, int chainLength, int splitVariant = 0)
        {
            Block current = _genesisBlock;

            headBlock = _genesisBlock;

            bool skipGenesis = TestObjectInternal.Head != null;

            for (int i = 0; i < chainLength; i++)
            {
                headBlock = current;
                if (_onlyHeaders)
                {
                    if (!(current.IsGenesis && skipGenesis))
                    {
                        TestObjectInternal.SuggestHeader(current.Header);
                    }

                    current = Build.A.Block.WithNumber(i + 1).WithParent(current).WithDifficulty(BlockHeaderBuilder.DefaultDifficulty - (ulong)splitVariant).TestObject;
                }
                else
                {
                    if (!(current.IsGenesis && skipGenesis))
                    {
                        AddBlockResult result = TestObjectInternal.SuggestBlock(current);
                        Assert.AreEqual(AddBlockResult.Added, result, $"Adding {current.ToString(Block.Format.Short)} at split variant {splitVariant}");

                        TestObjectInternal.UpdateMainChain(current);
                    }

                    current = Build.A.Block.WithNumber(i + 1).WithParent(current).WithDifficulty(BlockHeaderBuilder.DefaultDifficulty - (ulong)splitVariant).TestObject;
                }
            }

            return(this);
        }
Ejemplo n.º 15
0
    public void Can_fill_beacon_headers_gap()
    {
        (BlockTree notSyncedTree, BlockTree syncedTree) = BuildBlockTrees(10, 20);

        Block?beaconBlock = syncedTree.FindBlock(14, BlockTreeLookupOptions.None);
        BlockTreeInsertHeaderOptions headerOptions = BlockTreeInsertHeaderOptions.BeaconBlockInsert;
        AddBlockResult insertResult = notSyncedTree.Insert(beaconBlock, BlockTreeInsertBlockOptions.SaveHeader, headerOptions);

        for (int i = 13; i > 9; --i)
        {
            BlockHeader?   beaconHeader  = syncedTree.FindHeader(i, BlockTreeLookupOptions.None);
            AddBlockResult insertOutcome = notSyncedTree.Insert(beaconHeader !, headerOptions);
            Assert.AreEqual(AddBlockResult.Added, insertOutcome);
        }

        for (int i = 10; i < 14; ++i)
        {
            Block?         block         = syncedTree.FindBlock(i, BlockTreeLookupOptions.None);
            AddBlockResult insertOutcome = notSyncedTree.SuggestBlock(block !);
            Assert.AreEqual(AddBlockResult.Added, insertOutcome);
        }

        Assert.AreEqual(13, notSyncedTree.BestSuggestedBody !.Number);
    }
Ejemplo n.º 16
0
        private int InsertHeaders(HeadersSyncBatch batch)
        {
            if (batch.Response.Length > batch.RequestSize)
            {
                if (_logger.IsDebug)
                {
                    _logger.Debug($"Peer sent too long response ({batch.Response.Length}) to {batch}");
                }
                _syncPeerPool.ReportBreachOfProtocol(batch.ResponseSourcePeer, $"response too long ({batch.Response.Length})");
                _pending.Enqueue(batch);
                return(0);
            }

            long addedLast       = batch.StartNumber - 1;
            long addedEarliest   = batch.EndNumber + 1;
            int  skippedAtTheEnd = 0;

            for (int i = batch.Response.Length - 1; i >= 0; i--)
            {
                BlockHeader header = batch.Response[i];
                if (header == null)
                {
                    skippedAtTheEnd++;
                    continue;
                }

                if (header.Number != batch.StartNumber + i)
                {
                    _syncPeerPool.ReportBreachOfProtocol(batch.ResponseSourcePeer, "inconsistent headers batch");
                    break;
                }

                bool isFirst = i == batch.Response.Length - 1 - skippedAtTheEnd;
                if (isFirst)
                {
                    BlockHeader lowestInserted = _blockTree.LowestInsertedHeader;
                    // response does not carry expected data
                    if (header.Number == lowestInserted?.Number && header.Hash != lowestInserted?.Hash)
                    {
                        if (batch.ResponseSourcePeer != null)
                        {
                            if (_logger.IsDebug)
                            {
                                _logger.Debug($"{batch} - reporting INVALID hash");
                            }
                            _syncPeerPool.ReportBreachOfProtocol(batch.ResponseSourcePeer, "first hash inconsistent with request");
                        }

                        break;
                    }

                    // response needs to be cached until predecessors arrive
                    if (header.Hash != _nextHeaderHash)
                    {
                        if (header.Number == (_blockTree.LowestInsertedHeader?.Number ?? _pivotNumber + 1) - 1)
                        {
                            if (_logger.IsDebug)
                            {
                                _logger.Debug($"{batch} - ended up IGNORED - different branch - number {header.Number} was {header.Hash} while expected {_nextHeaderHash}");
                            }
                            _syncPeerPool.ReportBreachOfProtocol(batch.ResponseSourcePeer, "headers - different branch");
                            break;
                        }

                        if (header.Number == _blockTree.LowestInsertedHeader?.Number)
                        {
                            if (_logger.IsDebug)
                            {
                                _logger.Debug($"{batch} - ended up IGNORED - different branch");
                            }
                            _syncPeerPool.ReportBreachOfProtocol(batch.ResponseSourcePeer, "headers - different branch");
                            break;
                        }

                        if (_dependencies.ContainsKey(header.Number))
                        {
                            _pending.Enqueue(batch);
                            throw new InvalidOperationException($"Only one header dependency expected ({batch})");
                        }

                        for (int j = 0; j < batch.Response.Length; j++)
                        {
                            BlockHeader current = batch.Response[j];
                            if (batch.Response[j] != null)
                            {
                                addedEarliest = Math.Min(addedEarliest, current.Number);
                                addedLast     = Math.Max(addedLast, current.Number);
                            }
                            else
                            {
                                break;
                            }
                        }

                        HeadersSyncBatch dependentBatch = BuildDependentBatch(batch, addedLast, addedEarliest);
                        _dependencies[header.Number] = dependentBatch;
                        if (_logger.IsDebug)
                        {
                            _logger.Debug($"{batch} -> DEPENDENCY {dependentBatch}");
                        }

                        // but we cannot do anything with it yet
                        break;
                    }
                }
                else
                {
                    if (header.Hash != batch.Response[i + 1]?.ParentHash)
                    {
                        if (batch.ResponseSourcePeer != null)
                        {
                            if (_logger.IsDebug)
                            {
                                _logger.Debug($"{batch} - reporting INVALID inconsistent");
                            }
                            _syncPeerPool.ReportBreachOfProtocol(batch.ResponseSourcePeer, "headers - response not matching request");
                        }

                        break;
                    }
                }

                header.TotalDifficulty = _nextHeaderDiff;
                AddBlockResult addBlockResult = InsertHeader(header);
                if (addBlockResult == AddBlockResult.InvalidBlock)
                {
                    if (batch.ResponseSourcePeer != null)
                    {
                        if (_logger.IsDebug)
                        {
                            _logger.Debug($"{batch} - reporting INVALID bad block");
                        }
                        _syncPeerPool.ReportBreachOfProtocol(batch.ResponseSourcePeer, $"invalid header {header.ToString(BlockHeader.Format.Short)}");
                    }

                    break;
                }

                addedEarliest = Math.Min(addedEarliest, header.Number);
                addedLast     = Math.Max(addedLast, header.Number);
            }

            int added           = (int)(addedLast - addedEarliest + 1);
            int leftFillerSize  = (int)(addedEarliest - batch.StartNumber);
            int rightFillerSize = (int)(batch.EndNumber - addedLast);

            if (added + leftFillerSize + rightFillerSize != batch.RequestSize)
            {
                throw new Exception($"Added {added} + left {leftFillerSize} + right {rightFillerSize} != request size {batch.RequestSize} in {batch}");
            }

            added = Math.Max(0, added);

            if (added < batch.RequestSize)
            {
                if (added <= 0)
                {
                    batch.Response = null;
                    _pending.Enqueue(batch);
                }
                else
                {
                    if (leftFillerSize > 0)
                    {
                        HeadersSyncBatch leftFiller = BuildLeftFiller(batch, leftFillerSize);
                        _pending.Enqueue(leftFiller);
                        if (_logger.IsDebug)
                        {
                            _logger.Debug($"{batch} -> FILLER {leftFiller}");
                        }
                    }

                    if (rightFillerSize > 0)
                    {
                        HeadersSyncBatch rightFiller = BuildRightFiller(batch, rightFillerSize);
                        _pending.Enqueue(rightFiller);
                        if (_logger.IsDebug)
                        {
                            _logger.Debug($"{batch} -> FILLER {rightFiller}");
                        }
                    }
                }
            }

            if (added == 0)
            {
                if (batch.ResponseSourcePeer != null)
                {
                    if (_logger.IsDebug)
                    {
                        _logger.Debug($"{batch} - reporting no progress");
                    }
                    _syncPeerPool.ReportNoSyncProgress(batch.ResponseSourcePeer, AllocationContexts.Headers);
                }
            }

            if (_blockTree.LowestInsertedHeader != null)
            {
                _syncReport.FastBlocksHeaders.Update(_pivotNumber - _blockTree.LowestInsertedHeader.Number + 1);
            }

            if (_logger.IsDebug)
            {
                _logger.Debug($"LOWEST_INSERTED {_blockTree.LowestInsertedHeader?.Number} | HANDLED {batch}");
            }

            _syncReport.HeadersInQueue.Update(HeadersInQueue);
            return(added);
        }
Ejemplo n.º 17
0
        public void AddNewBlock(Block block, NodeId receivedFrom)
        {
            lock (_isSyncingLock)
            {
                if (_isSyncing)
                {
                    _logger.Info($"Ignoring new block {block.Hash} while syncing");
                    return;
                }
            }

            // TODO: validation

            if (_logger.IsInfoEnabled)
            {
                _logger.Info($"Adding new block {block.Hash} ({block.Number}) from {receivedFrom}");
            }

            bool getValueResult = _peers.TryGetValue(receivedFrom, out PeerInfo peerInfo);

            if (!getValueResult)
            {
                _logger.Error($"Try get value failed on {nameof(PeerInfo)} {receivedFrom}");
            }

            if (peerInfo == null && _logger.IsErrorEnabled)
            {
                _logger.Error($"{receivedFrom} peer info is null");
            }

            if (_logger.IsDebugEnabled)
            {
                _logger.Debug($"Using {peerInfo}");
            }

            if (peerInfo == null)
            {
                string errorMessage = $"unknown synchronization peer {receivedFrom}";
                _logger.Error(errorMessage);
                throw new InvalidOperationException(errorMessage);
            }

            peerInfo.NumberAvailable = BigInteger.Max(block.Number, peerInfo.NumberAvailable);

            if (block.Number <= BlockTree.BestSuggested.Number + 1)
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"Suggesting a block {block.Hash} ({block.Number}) from {receivedFrom} with {block.Transactions.Length} transactions");
                }

                if (_logger.IsTraceEnabled)
                {
                    _logger.Trace("SUGGESTING BLOCK:");
                    _logger.Trace($"{block}");
                }

                AddBlockResult result = BlockTree.SuggestBlock(block);
                if (result == AddBlockResult.UnknownParent)
                {
                    RunSync();
                }
                else
                {
                    peerInfo.NumberReceived = block.Number;
                }

                if (_logger.IsInfoEnabled)
                {
                    _logger.Info($"{block.Hash} ({block.Number}) adding result is {result}");
                }
            }
            else if (block.Number > BlockTree.BestSuggested.Number + 1)
            {
                if (_logger.IsInfoEnabled)
                {
                    _logger.Info($"Received a block {block.Hash} ({block.Number}) from {receivedFrom} - need to resync");
                }

                RunSync();
            }
            else
            {
                Debug.Fail("above should be covering everything");
            }
        }
Ejemplo n.º 18
0
        private async Task PeerSyncAsync(CancellationToken token, PeerInfo peerInfo)
        {
            bool wasCancelled = false;

            ISynchronizationPeer peer       = peerInfo.Peer;
            BigInteger           bestNumber = BlockTree.BestSuggested.Number;

            const int maxLookup             = 64;
            int       ancestorLookupLevel   = 0;
            bool      isCommonAncestorKnown = false;

            while (peerInfo.NumberAvailable > bestNumber && peerInfo.NumberReceived <= peerInfo.NumberAvailable)
            {
                if (ancestorLookupLevel > maxLookup)
                {
                    throw new InvalidOperationException("Cannot find ancestor"); // TODO: remodel this after full sync test is added
                }

                if (token.IsCancellationRequested)
                {
                    token.ThrowIfCancellationRequested();
                }

                if (!isCommonAncestorKnown)
                {
                    // TODO: cases when many peers used for sync and one peer finished sync and then we need resync - we should start from common point and not NumberReceived that may be far in the past
                    _logger.Info($"Finding common ancestor for {peerInfo.Peer.NodeId}");
                    isCommonAncestorKnown = true;
                }

                BigInteger blocksLeft = peerInfo.NumberAvailable - peerInfo.NumberReceived;
                // TODO: fault handling on tasks

                int blocksToRequest = (int)BigInteger.Min(blocksLeft + 1, BatchSize);
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"Sync request to peer with {peerInfo.NumberAvailable} blocks. Got {peerInfo.NumberReceived} and asking for {blocksToRequest} more.");
                }
                Task <BlockHeader[]> headersTask = peer.GetBlockHeaders(peerInfo.NumberReceived, blocksToRequest, 0, token);
                _currentSyncTask = headersTask;
                BlockHeader[] headers = await headersTask;
                if (_currentSyncTask.IsCanceled)
                {
                    wasCancelled = true;
                    break;
                }

                if (_currentSyncTask.IsFaulted)
                {
                    _logger.Error("Failed to retrieve headers when synchronizing", _currentSyncTask.Exception);
                    throw _currentSyncTask.Exception;
                }

                if (token.IsCancellationRequested)
                {
                    token.ThrowIfCancellationRequested();
                }

                List <Keccak> hashes = new List <Keccak>();
                Dictionary <Keccak, BlockHeader> headersByHash = new Dictionary <Keccak, BlockHeader>();
                for (int i = 1; i < headers.Length; i++)
                {
                    hashes.Add(headers[i].Hash);
                    headersByHash[headers[i].Hash] = headers[i];
                }

                Task <Block[]> bodiesTask = peer.GetBlocks(hashes.ToArray(), token);
                _currentSyncTask = bodiesTask;
                Block[] blocks = await bodiesTask;
                if (_currentSyncTask.IsCanceled)
                {
                    wasCancelled = true;
                    break;
                }

                if (_currentSyncTask.IsFaulted)
                {
                    _logger.Error("Failed to retrieve bodies when synchronizing", _currentSyncTask.Exception);
                    throw _currentSyncTask.Exception;
                }

                ancestorLookupLevel = 0;

                for (int i = 0; i < blocks.Length; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        token.ThrowIfCancellationRequested();
                    }

                    blocks[i].Header = headersByHash[hashes[i]];
                }

                if (blocks.Length > 0)
                {
                    Block parent = BlockTree.FindParent(blocks[0]);
                    if (parent == null)
                    {
                        ancestorLookupLevel     += BatchSize;
                        peerInfo.NumberReceived -= BatchSize;
                        continue;
                    }
                }

                // Parity 1.11 non canonical blocks when testing on 27/06
                for (int i = 0; i < blocks.Length; i++)
                {
                    if (i != 0 && blocks[i].ParentHash != blocks[i - 1].Hash)
                    {
                        throw new EthSynchronizationException("Peer send an inconsistent block list");
                    }
                }

                for (int i = 0; i < blocks.Length; i++)
                {
                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info($"Received {blocks[i]} from {peer.NodeId}");
                    }

                    if (_blockValidator.ValidateSuggestedBlock(blocks[i]))
                    {
                        AddBlockResult addResult = BlockTree.SuggestBlock(blocks[i]);
                        if (addResult == AddBlockResult.UnknownParent)
                        {
                            if (_logger.IsInfoEnabled)
                            {
                                _logger.Info($"Block {blocks[i].Number} ignored (unknown parent)");
                            }
                            if (i == 0)
                            {
                                if (_logger.IsWarnEnabled)
                                {
                                    _logger.Warn("Resyncing split");
                                }
                                peerInfo.NumberReceived -= 1;
                                var   syncTask = Task.Run(() => PeerSyncAsync(_syncCancellationTokenSource.Token, peerInfo), _syncCancellationTokenSource.Token);
                                await syncTask;
                            }
                            else
                            {
                                const string message = "Peer sent an inconsistent batch of block headers";
                                _logger.Error(message);
                                throw new EthSynchronizationException(message);
                            }
                        }

                        if (_logger.IsDebugEnabled)
                        {
                            _logger.Debug($"Block {blocks[i].Number} suggested for processing");
                        }
                    }
                    else
                    {
                        if (_logger.IsWarnEnabled)
                        {
                            _logger.Warn($"Block {blocks[i].Number} skipped (validation failed)");
                        }
                    }
                }

                peerInfo.NumberReceived = blocks[blocks.Length - 1].Number;

                bestNumber = BlockTree.BestSuggested.Number;
            }

            if (_logger.IsInfoEnabled)
            {
                _logger.Info($"Stopping sync processes with Node: {peerInfo.Peer.NodeId}, wasCancelled: {wasCancelled}");
            }

            if (!wasCancelled)
            {
                peerInfo.IsSynced = true;
                Synced?.Invoke(this, new SyncEventArgs(peerInfo.Peer));
            }
        }
Ejemplo n.º 19
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);
            }
        }
Ejemplo n.º 20
0
        public void AddNewBlock(Block block, NodeId receivedFrom)
        {
            // TODO: validation

            _peers.TryGetValue(receivedFrom, out PeerInfo peerInfo);
            if (peerInfo == null)
            {
                string errorMessage = $"Received a new block from an unknown peer {receivedFrom}";
                _logger.Error(errorMessage);
                return;
            }

            peerInfo.NumberAvailable = UInt256.Max(block.Number, peerInfo.NumberAvailable);
//            peerInfo.Difficulty = UInt256.Max(block.Difficulty, peerInfo.Difficulty);

            lock (_isSyncingLock)
            {
                if (_isSyncing)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Ignoring new block {block.Hash} while syncing");
                    }
                    return;
                }
            }

            if (_logger.IsTrace)
            {
                _logger.Trace($"Adding new block {block.Hash} ({block.Number}) from {receivedFrom}");
            }

            if (block.Number <= _blockTree.BestSuggested.Number + 1)
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Suggesting a block {block.Hash} ({block.Number}) from {receivedFrom} with {block.Transactions.Length} transactions");
                }
                if (_logger.IsTrace)
                {
                    _logger.Trace($"{block}");
                }

                AddBlockResult result = _blockTree.SuggestBlock(block);
                if (_logger.IsInfo)
                {
                    _logger.Info($"{block.Hash} ({block.Number}) adding result is {result}");
                }
                if (result == AddBlockResult.UnknownParent)
                {
                    /* here we want to cover scenario when our peer is reorganizing and sends us a head block
                     * from a new branch and we need to sync previous blocks as we do not know this block's parent */
                    RequestSync();
                }
                else
                {
                    peerInfo.NumberReceived = block.Number;
                }
            }
            else
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Received a block {block.Hash} ({block.Number}) from {receivedFrom} - need to resync");
                }
                RequestSync();
            }
        }
Ejemplo n.º 21
0
        private async Task SynchronizeWithPeerAsync(PeerInfo peerInfo, CancellationToken peerSyncToken)
        {
            bool wasCanceled = false;

            ISynchronizationPeer peer       = peerInfo.Peer;
            BigInteger           bestNumber = _blockTree.BestSuggested.Number;
//            UInt256 bestDifficulty = _blockTree.BestSuggested.Difficulty;

            const int maxLookup             = 64;
            int       ancestorLookupLevel   = 0;
            bool      isCommonAncestorKnown = false;

            while (peerInfo.NumberAvailable > bestNumber && peerInfo.NumberReceived <= peerInfo.NumberAvailable)
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Continue syncing with {peerInfo} (our best {bestNumber})");
                }

                if (ancestorLookupLevel > maxLookup)
                {
                    if (_logger.IsWarn)
                    {
                        _logger.Warn($"Could not find common ancestor with {peerInfo.Peer.NodeId}");
                    }
                    throw new EthSynchronizationException("Peer with inconsistent chain in sync");
                }

                if (peerSyncToken.IsCancellationRequested)
                {
                    peerSyncToken.ThrowIfCancellationRequested();
                }

                if (!isCommonAncestorKnown)
                {
                    // TODO: cases when many peers used for sync and one peer finished sync and then we need resync - we should start from common point and not NumberReceived that may be far in the past
                    _logger.Trace($"Finding common ancestor for {peerInfo.Peer.NodeId}");
                    isCommonAncestorKnown = true;
                }

                BigInteger blocksLeft      = peerInfo.NumberAvailable - peerInfo.NumberReceived;
                int        blocksToRequest = (int)BigInteger.Min(blocksLeft + 1, BatchSize);
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Sync request to peer with {peerInfo.NumberAvailable} blocks. Got {peerInfo.NumberReceived} and asking for {blocksToRequest} more.");
                }

                Task <BlockHeader[]> headersTask = peer.GetBlockHeaders(peerInfo.NumberReceived, blocksToRequest, 0, peerSyncToken);
                _currentSyncTask = headersTask;
                BlockHeader[] headers = await headersTask;
                if (_currentSyncTask.IsCanceled)
                {
                    wasCanceled = true;
                    break;
                }

                if (_currentSyncTask.IsFaulted)
                {
                    if (_currentSyncTask.Exception.InnerExceptions.Any(x => x.InnerException is TimeoutException))
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Error("Failed to retrieve headers when synchronizing (Timeout)", _currentSyncTask.Exception);
                        }
                    }
                    else
                    {
                        if (_logger.IsError)
                        {
                            _logger.Error("Failed to retrieve headers when synchronizing", _currentSyncTask.Exception);
                        }
                    }
                    throw _currentSyncTask.Exception;
                }

                if (peerSyncToken.IsCancellationRequested)
                {
                    peerSyncToken.ThrowIfCancellationRequested();
                }

                List <Keccak> hashes = new List <Keccak>();
                Dictionary <Keccak, BlockHeader> headersByHash = new Dictionary <Keccak, BlockHeader>();
                for (int i = 1; i < headers.Length; i++)
                {
                    hashes.Add(headers[i].Hash);
                    headersByHash[headers[i].Hash] = headers[i];
                }

                Task <Block[]> bodiesTask = peer.GetBlocks(hashes.ToArray(), peerSyncToken);
                _currentSyncTask = bodiesTask;
                Block[] blocks = await bodiesTask;
                if (_currentSyncTask.IsCanceled)
                {
                    wasCanceled = true;
                    break;
                }

                if (_currentSyncTask.IsFaulted)
                {
                    if (_currentSyncTask.Exception.InnerExceptions.Any(x => x.InnerException is TimeoutException))
                    {
                        if (_logger.IsTrace)
                        {
                            _logger.Error("Failed to retrieve bodies when synchronizing (Timeout)", _currentSyncTask.Exception);
                        }
                    }
                    else
                    {
                        if (_logger.IsError)
                        {
                            _logger.Error("Failed to retrieve bodies when synchronizing", _currentSyncTask.Exception);
                        }
                    }
                    throw _currentSyncTask.Exception;
                }

                for (int i = 0; i < blocks.Length; i++)
                {
                    if (peerSyncToken.IsCancellationRequested)
                    {
                        peerSyncToken.ThrowIfCancellationRequested();
                    }

                    blocks[i].Header = headersByHash[hashes[i]];
                }

                if (blocks.Length > 0)
                {
                    Block parent = _blockTree.FindParent(blocks[0]);
                    if (parent == null)
                    {
                        ancestorLookupLevel    += BatchSize;
                        peerInfo.NumberReceived = peerInfo.NumberReceived >= BatchSize ? peerInfo.NumberReceived - BatchSize : 0;
                        continue;
                    }
                }

                // Parity 1.11 non canonical blocks when testing on 27/06
                for (int i = 0; i < blocks.Length; i++)
                {
                    if (i != 0 && blocks[i].ParentHash != blocks[i - 1].Hash)
                    {
                        throw new EthSynchronizationException("Peer sent an inconsistent block list");
                    }
                }

                for (int i = 0; i < blocks.Length; i++)
                {
                    if (_logger.IsTrace)
                    {
                        _logger.Trace($"Received {blocks[i]} from {peer.NodeId}");
                    }

                    if (_blockValidator.ValidateSuggestedBlock(blocks[i]))
                    {
                        AddBlockResult addResult = _blockTree.SuggestBlock(blocks[i]);
                        if (addResult == AddBlockResult.UnknownParent)
                        {
                            if (_logger.IsTrace)
                            {
                                _logger.Trace($"Block {blocks[i].Number} ignored (unknown parent)");
                            }
                            if (i == 0)
                            {
                                const string message = "Peer sent orphaned blocks";
                                _logger.Error(message);
                                throw new EthSynchronizationException(message);
//                                if (_logger.IsTrace) _logger.Trace("Resyncing split");
//                                peerInfo.NumberReceived -= 1;
//                                var syncTask =
//                                    Task.Run(() => SynchronizeWithPeerAsync(peerInfo, _peerSyncCancellationTokenSource.Token),
//                                        _peerSyncCancellationTokenSource.Token);
//                                await syncTask;
                            }
                            else
                            {
                                const string message = "Peer sent an inconsistent batch of block headers";
                                _logger.Error(message);
                                throw new EthSynchronizationException(message);
                            }
                        }

                        if (_logger.IsTrace)
                        {
                            _logger.Trace($"Block {blocks[i].Number} suggested for processing");
                        }
                    }
                    else
                    {
                        if (_logger.IsWarn)
                        {
                            _logger.Warn($"Block {blocks[i].Number} skipped (validation failed)");
                        }
                    }
                }

                peerInfo.NumberReceived = blocks[blocks.Length - 1].Number;
                bestNumber = _blockTree.BestSuggested.Number;
            }

            if (_logger.IsTrace)
            {
                _logger.Trace($"Stopping sync processes with Node: {peerInfo.Peer.NodeId}, wasCancelled: {wasCanceled}");
            }
        }
Ejemplo n.º 22
0
    protected override AddBlockResult InsertToBlockTree(BlockHeader header)
    {
        if (_chainMerged)
        {
            if (_logger.IsTrace)
            {
                _logger.Trace(
                    "Chain already merged, skipping header insert");
            }
            return(AddBlockResult.AlreadyKnown);
        }

        if (_logger.IsTrace)
        {
            _logger.Trace(
                $"Adding new header in beacon headers sync {header.ToString(BlockHeader.Format.FullHashAndNumber)}");
        }
        BlockTreeInsertHeaderOptions headerOptions = BlockTreeInsertHeaderOptions.BeaconHeaderInsert;

        if (_nextHeaderDiff is null)
        {
            headerOptions |= BlockTreeInsertHeaderOptions.TotalDifficultyNotNeeded;
        }

        // Found existing block in the block tree
        if (_blockTree.IsKnownBlock(header.Number, header.GetOrCalculateHash()))
        {
            _chainMerged = true;
            if (_logger.IsTrace)
            {
                _logger.Trace(
                    $"Found header to join dangling beacon chain {header.ToString(BlockHeader.Format.FullHashAndNumber)}");
            }
            return(AddBlockResult.AlreadyKnown);
        }

        _invalidChainTracker.SetChildParent(header.Hash !, header.ParentHash !);

        AddBlockResult insertOutcome = _blockTree.Insert(header, headerOptions);

        if (insertOutcome == AddBlockResult.Added || insertOutcome == AddBlockResult.AlreadyKnown)
        {
            _nextHeaderHash = header.ParentHash !;
            if (_expectedDifficultyOverride?.TryGetValue(header.Number, out ulong nextHeaderDiff) == true)
            {
                _nextHeaderDiff = nextHeaderDiff;
            }
            else
            {
                _nextHeaderDiff = header.TotalDifficulty != null && header.TotalDifficulty >= header.Difficulty
                    ? header.TotalDifficulty - header.Difficulty
                    : null;
            }
        }

        if (_logger.IsTrace)
        {
            _logger.Trace(
                $"New header {header.ToString(BlockHeader.Format.FullHashAndNumber)} in beacon headers sync. InsertOutcome: {insertOutcome}");
        }
        return(insertOutcome);
    }