public void ProcessMessage(PeerConnection peer, MineralMessage message)
        {
            ChainInventoryMessage chain_inventory_message = (ChainInventoryMessage)message;

            Check(peer, chain_inventory_message);

            peer.IsNeedSyncFromPeer = true;
            peer.SyncChainRequest   = default(KeyValuePair <DequeNet.Deque <Capsule.BlockCapsule.BlockId>, long>);

            Deque <BlockId> block_id = new Deque <BlockId>(chain_inventory_message.Ids);

            if (block_id.Count == 1 && Manager.Instance.NetDelegate.ContainBlock(block_id.FirstOrDefault()))
            {
                peer.IsNeedSyncFromPeer = false;
                return;
            }

            while (!peer.SyncBlockFetch.IsEmpty)
            {
                if (peer.SyncBlockFetch.Last().Equals(block_id.First()))
                {
                    break;
                }
                peer.SyncBlockFetch.TryPopRight(out _);
            }

            block_id.PopLeft();

            peer.RemainNum = chain_inventory_message.RemainNum;

            foreach (BlockId id in block_id)
            {
                peer.SyncBlockFetch.PushRight(id);
            }

            lock (Manager.Instance.NetDelegate.LockBlock)
            {
                while (!peer.SyncBlockFetch.IsEmpty &&
                       Manager.Instance.NetDelegate.ContainBlock(peer.SyncBlockFetch.First()))
                {
                    peer.SyncBlockFetch.TryPopLeft(out BlockId id);
                    peer.BlockBothHave = id;
                    Logger.Info(
                        string.Format("Block {0} from {1} is processed", id.GetString(), peer.Node.Host));
                }
            }

            if ((chain_inventory_message.RemainNum == 0 && !peer.SyncBlockFetch.IsEmpty) ||
                (chain_inventory_message.RemainNum != 0 && peer.SyncBlockFetch.Count > Parameter.NodeParameters.SYNC_FETCH_BATCH_NUM))
            {
                Manager.Instance.SyncService.IsFetch = true;
            }
            else
            {
                Manager.Instance.SyncService.SyncNext(peer);
            }
        }
        private void Check(PeerConnection peer, ChainInventoryMessage message)
        {
            if (peer.SyncChainRequest.Equals(default(KeyValuePair <Deque <BlockId>, long>)))
            {
                throw new P2pException(
                          P2pException.ErrorType.BAD_MESSAGE, "not send syncBlockChainMsg");
            }

            List <BlockId> ids = message.Ids;

            if (ids.IsNullOrEmpty())
            {
                throw new P2pException(
                          P2pException.ErrorType.BAD_MESSAGE, "blockIds is empty");
            }

            if (ids.Count > Parameter.NodeParameters.SYNC_FETCH_BATCH_NUM + 1)
            {
                throw new P2pException(
                          P2pException.ErrorType.BAD_MESSAGE, "big blockIds size: " + ids.Count);
            }

            if (message.RemainNum != 0 && ids.Count < Parameter.NodeParameters.SYNC_FETCH_BATCH_NUM)
            {
                throw new P2pException(
                          P2pException.ErrorType.BAD_MESSAGE, "remain: " + message.RemainNum + ", blockIds size: " + ids.Count);
            }

            long num = ids[0].Num;

            foreach (BlockId id in message.Ids)
            {
                if (id.Num != num++)
                {
                    throw new P2pException(
                              P2pException.ErrorType.BAD_MESSAGE, "not continuous block");
                }
            }

            if (!peer.SyncChainRequest.Key.Contains(ids[0]))
            {
                throw new P2pException(
                          P2pException.ErrorType.BAD_MESSAGE,
                          "unlinked block, my head: "
                          + peer.SyncChainRequest.Key.LastOrDefault().GetString()
                          + ", peer: " + ids[0].GetString());
            }

            if (Manager.Instance.NetDelegate.HeadBlockId.Num > 0)
            {
                long max_remain_time = Parameter.ChainParameters.CLOCK_MAX_DELAY
                                       + Helper.CurrentTimeMillis()
                                       - Manager.Instance.NetDelegate.GetBlockTime(Manager.Instance.NetDelegate.SolidBlockId);
                long max_future_num =
                    max_remain_time / Parameter.ChainParameters.BLOCK_PRODUCED_INTERVAL + Manager.Instance.NetDelegate.SolidBlockId.Num;
                long last_num = ids[ids.Count - 1].Num;

                if (last_num + message.RemainNum > max_future_num)
                {
                    throw new P2pException(P2pException.ErrorType.BAD_MESSAGE, "lastNum: " + last_num + " + remainNum: "
                                           + message.RemainNum + " > futureMaxNum: " + max_future_num);
                }
            }
        }