Example #1
0
        private bool requestBlockAgain(ulong blockNum)
        {
            lock (pendingBlocks)
            {
                if (lastProcessedBlockNum == blockNum)
                {
                    lastProcessedBlockTime = Clock.getTimestamp();
                }

                if (missingBlocks != null)
                {
                    if (!missingBlocks.Contains(blockNum))
                    {
                        Logging.info(String.Format("Requesting missing block #{0} again.", blockNum));
                        missingBlocks.Add(blockNum);
                        missingBlocks.Sort();

                        requestedBlockTimes.Add(blockNum, Clock.getTimestamp() - 10);

                        receivedAllMissingBlocks = false;
                        return(true);
                    }
                }
            }
            return(false);
        }
Example #2
0
        public static string getRandomMasterNodeAddress()
        {
            List <Peer> connectableList = null;

            lock (peerList)
            {
                long curTime = Clock.getTimestamp();
                connectableList = peerList.FindAll(x => curTime - x.lastConnectAttempt > 30);
                if (connectableList != null && connectableList.Count > 0)
                {
                    Random rnd = new Random();
                    Peer   p   = connectableList[rnd.Next(connectableList.Count)];
                    p.lastConnectAttempt = curTime;
                    return(p.hostname);
                }
            }
            return("");
        }
Example #3
0
        // Internal function that sends data through the socket
        protected void sendDataInternal(ProtocolMessageCode code, byte[] data, byte[] checksum)
        {
            byte[] ba = CoreProtocolMessage.prepareProtocolMessage(code, data, checksum);
            NetDump.Instance.appendSent(clientSocket, ba, ba.Length);
            try
            {
                for (int sentBytes = 0; sentBytes < ba.Length && running;)
                {
                    int bytesToSendCount = ba.Length - sentBytes;
                    if (bytesToSendCount > 8000)
                    {
                        bytesToSendCount = 8000;
                    }


                    int curSentBytes = clientSocket.Send(ba, sentBytes, bytesToSendCount, SocketFlags.None);

                    lastDataSentTime = Clock.getTimestamp();


                    // Sleep a bit to allow other threads to do their thing
                    Thread.Yield();

                    sentBytes += curSentBytes;
                    // TODO TODO TODO timeout
                }
                if (clientSocket.Connected == false)
                {
                    if (running)
                    {
                        Logging.warn(String.Format("sendRE: Failed senddata to remote endpoint {0}, Closing.", getFullAddress()));
                    }
                    state = RemoteEndpointState.Closed;
                }
            }
            catch (Exception e)
            {
                if (running)
                {
                    Logging.warn(String.Format("sendRE: Socket exception for {0}, closing. {1}", getFullAddress(), e));
                }
                state = RemoteEndpointState.Closed;
            }
        }
Example #4
0
 public long getTimeSinceLastBLock()
 {
     return(Clock.getTimestamp() - lastBlockReceivedTime);
 }
Example #5
0
        public bool appendBlock(Block b, bool add_to_storage = true)
        {
            lock (blocks)
            {
                if (blocks.Count > 0)
                {
                    Block prev_block = blocks[blocks.Count - 1];
                    // check for invalid block appending
                    if (b.blockNum != prev_block.blockNum + 1)
                    {
                        Logging.warn(String.Format("Attempting to add non-sequential block #{0} after block #{1}.",
                                                   b.blockNum,
                                                   prev_block.blockNum));
                        return(false);
                    }
                    if (!b.lastBlockChecksum.SequenceEqual(prev_block.blockChecksum))
                    {
                        Logging.error(String.Format("Attempting to add a block #{0} with invalid lastBlockChecksum!", b.blockNum));
                        return(false);
                    }
                    if (b.signatureFreezeChecksum != null && blocks.Count > 5 && !blocks[blocks.Count - 5].calculateSignatureChecksum().SequenceEqual(b.signatureFreezeChecksum))
                    {
                        Logging.error(String.Format("Attempting to add a block #{0} with invalid sigFreezeChecksum!", b.blockNum));
                        return(false);
                    }
                }
                lastBlock    = b;
                lastBlockNum = b.blockNum;
                if (b.version != lastBlockVersion)
                {
                    lastBlockVersion = b.version;
                }

                if (b.lastSuperBlockChecksum != null || b.blockNum == 1)
                {
                    pendingSuperBlocks.Remove(b.blockNum);

                    lastSuperBlockNum      = b.blockNum;
                    lastSuperBlockChecksum = b.blockChecksum;
                }

                // special case when we are starting up and have an empty chain
                if (blocks.Count == 0)
                {
                    blocks.Add(b);
                    lock (blocksDictionary)
                    {
                        blocksDictionary.Add(b.blockNum, b);
                    }
                    Node.storage.insertBlock(b);
                    return(true);
                }

                blocks.Add(b);
                lock (blocksDictionary)
                {
                    blocksDictionary.Add(b.blockNum, b);
                }

                if (reorgBlockStart <= b.blockNum)
                {
                    reorgBlockStart = 0;
                }
            }

            if (add_to_storage)
            {
                // Add block to storage
                Node.storage.insertBlock(b);
            }

            ConsensusConfig.redactedWindowSize    = ConsensusConfig.getRedactedWindowSize(b.version);
            ConsensusConfig.minRedactedWindowSize = ConsensusConfig.getRedactedWindowSize(b.version);

            redactChain();
            lock (blocks)
            {
                if (blocks.Count > 30)
                {
                    Block tmp_block = getBlock(b.blockNum - 30);
                    if (tmp_block != null)
                    {
                        TransactionPool.compactTransactionsForBlock(tmp_block);
                        tmp_block.compact();
                    }
                }
                compactBlockSigs(b);
            }

            // Cleanup transaction pool
            TransactionPool.performCleanup();

            lastBlockReceivedTime = Clock.getTimestamp();
            return(true);
        }
Example #6
0
        private bool requestMissingBlocks()
        {
            if (syncDone)
            {
                return(false);
            }

            if (syncTargetBlockNum == 0)
            {
                return(false);
            }

            long currentTime = Clock.getTimestamp();

            // Check if the block has already been requested
            lock (requestedBlockTimes)
            {
                Dictionary <ulong, long> tmpRequestedBlockTimes = new Dictionary <ulong, long>(requestedBlockTimes);
                foreach (var entry in tmpRequestedBlockTimes)
                {
                    ulong blockNum = entry.Key;
                    // Check if the request expired (after 10 seconds)
                    if (currentTime - requestedBlockTimes[blockNum] > 10)
                    {
                        // Re-request block
                        if (ProtocolMessage.broadcastGetBlock(blockNum, null, null, 1, true) == false)
                        {
                            if (watchDogBlockNum > 0 && (blockNum == watchDogBlockNum - 4 || blockNum == watchDogBlockNum + 1))
                            {
                                watchDogTime = DateTime.UtcNow;
                            }
                            Logging.warn(string.Format("Failed to rebroadcast getBlock request for {0}", blockNum));
                            Thread.Sleep(500);
                        }
                        else
                        {
                            // Re-set the block request time
                            requestedBlockTimes[blockNum] = currentTime;
                        }
                    }
                }
            }


            ulong syncToBlock = syncTargetBlockNum;

            ulong firstBlock = getLowestBlockNum();


            lock (pendingBlocks)
            {
                ulong lastBlock = syncToBlock;
                if (missingBlocks == null)
                {
                    missingBlocks = new List <ulong>(Enumerable.Range(0, (int)(lastBlock - firstBlock + 1)).Select(x => (ulong)x + firstBlock));
                    missingBlocks.Sort();
                }

                int total_count     = 0;
                int requested_count = 0;

                // whatever is left in missingBlocks is what we need to request
                if (missingBlocks.Count() == 0)
                {
                    receivedAllMissingBlocks = true;
                    return(false);
                }

                List <ulong> tmpMissingBlocks = new List <ulong>(missingBlocks.Take(maxBlockRequests * 2));

                foreach (ulong blockNum in tmpMissingBlocks)
                {
                    total_count++;
                    lock (requestedBlockTimes)
                    {
                        if (requestedBlockTimes.ContainsKey(blockNum))
                        {
                            requested_count++;
                            continue;
                        }
                    }

                    bool readFromStorage = false;
                    if (blockNum <= lastBlockToReadFromStorage)
                    {
                        readFromStorage = true;
                    }

                    ulong last_block_height = IxianHandler.getLastBlockHeight();
                    if (blockNum > last_block_height + (ulong)maxBlockRequests)
                    {
                        if (last_block_height > 0 || (last_block_height == 0 && total_count > 10))
                        {
                            break;
                        }
                    }

                    // First check if the missing block can be found in storage
                    Block block = Node.blockChain.getBlock(blockNum, readFromStorage);
                    if (block != null)
                    {
                        Node.blockSync.onBlockReceived(block, null);
                    }
                    else
                    {
                        if (readFromStorage)
                        {
                            Logging.warn("Expecting block {0} in storage but had to request it from network.", blockNum);
                        }
                        // Didn't find the block in storage, request it from the network
                        if (ProtocolMessage.broadcastGetBlock(blockNum, null, null, 1, true) == false)
                        {
                            if (watchDogBlockNum > 0 && (blockNum == watchDogBlockNum - 4 || blockNum == watchDogBlockNum + 1))
                            {
                                watchDogTime = DateTime.UtcNow;
                            }
                            Logging.warn(string.Format("Failed to broadcast getBlock request for {0}", blockNum));
                            Thread.Sleep(500);
                        }
                        else
                        {
                            requested_count++;
                            // Set the block request time
                            lock (requestedBlockTimes)
                            {
                                requestedBlockTimes.Add(blockNum, currentTime);
                            }
                        }
                    }
                }

                if (requested_count > 0)
                {
                    return(true);
                }
            }

            return(false);
        }
Example #7
0
        public void start(Socket socket = null)
        {
            if (fullyStopped)
            {
                Logging.error("Can't start a fully stopped RemoteEndpoint");
                return;
            }

            if (running)
            {
                return;
            }

            if (socket != null)
            {
                clientSocket = socket;
            }
            if (clientSocket == null)
            {
                Logging.error("Could not start NetworkRemoteEndpoint, socket is null");
                return;
            }

            prepareSocket(clientSocket);

            remoteIP        = (IPEndPoint)clientSocket.RemoteEndPoint;
            address         = remoteIP.Address.ToString();
            fullAddress     = address + ":" + remoteIP.Port;
            presence        = null;
            presenceAddress = null;

            lock (subscribedAddresses)
            {
                subscribedAddresses.Clear();
            }

            lastDataReceivedTime = Clock.getTimestamp();
            lastDataSentTime     = Clock.getTimestamp();

            state = RemoteEndpointState.Established;

            timeDifference   = 0;
            timeSyncComplete = false;
            timeSyncs.Clear();

            running = true;

            // Abort all related threads
            if (recvThread != null)
            {
                recvThread.Abort();
                recvThread = null;
            }
            if (sendThread != null)
            {
                sendThread.Abort();
                sendThread = null;
            }
            if (parseThread != null)
            {
                parseThread.Abort();
                parseThread = null;
            }

            try
            {
                TLC = new ThreadLiveCheck();
                // Start receive thread
                recvThread      = new Thread(new ThreadStart(recvLoop));
                recvThread.Name = "Network_Remote_Endpoint_Receive_Thread";
                recvThread.Start();

                // Start send thread
                sendThread      = new Thread(new ThreadStart(sendLoop));
                sendThread.Name = "Network_Remote_Endpoint_Send_Thread";
                sendThread.Start();

                // Start parse thread
                parseThread      = new Thread(new ThreadStart(parseLoop));
                parseThread.Name = "Network_Remote_Endpoint_Parse_Thread";
                parseThread.Start();
            }
            catch (Exception e)
            {
                Logging.error("Exception start remote endpoint: {0}", e.Message);
            }
        }
Example #8
0
        // Reads data from a socket and returns a byte array
        protected byte[] readSocketData()
        {
            Socket socket = clientSocket;

            byte[] data = null;

            // Check for socket availability
            if (socket.Connected == false)
            {
                throw new Exception("Socket already disconnected at other end");
            }

            if (socket.Available < 1)
            {
                // Sleep a while to prevent cpu cycle waste
                Thread.Sleep(10);
                return(data);
            }

            // Read multi-packet messages
            // TODO: optimize this as it's not very efficient
            List <byte> big_buffer = new List <byte>();

            bool message_found = false;

            try
            {
                int data_length   = 0;
                int header_length = 43; // start byte + int32 (4 bytes) + int32 (4 bytes) + checksum (32 bytes) + header checksum (1 byte) + end byte
                int bytesToRead   = 1;
                while (message_found == false && socket.Connected && running)
                {
                    //int pos = bytesToRead > NetworkProtocol.recvByteHist.Length ? NetworkProtocol.recvByteHist.Length - 1 : bytesToRead;

                    /*lock (NetworkProtocol.recvByteHist)
                     * {
                     *  NetworkProtocol.recvByteHist[pos]++;
                     * }*/
                    int byteCounter = socket.Receive(socketReadBuffer, bytesToRead, SocketFlags.None);
                    NetDump.Instance.appendReceived(socket, socketReadBuffer, byteCounter);
                    if (byteCounter > 0)
                    {
                        lastDataReceivedTime = Clock.getTimestamp();
                        if (big_buffer.Count > 0)
                        {
                            big_buffer.AddRange(socketReadBuffer.Take(byteCounter));
                            if (big_buffer.Count == header_length)
                            {
                                data_length = getDataLengthFromMessageHeader(big_buffer);
                                if (data_length <= 0)
                                {
                                    data_length = 0;
                                    big_buffer.Clear();
                                    bytesToRead = 1;
                                }
                            }
                            else if (big_buffer.Count == data_length + header_length)
                            {
                                // we have everything that we need, save the last byte and break
                                message_found = true;
                            }
                            else if (big_buffer.Count < header_length)
                            {
                                bytesToRead = header_length - big_buffer.Count;
                            }
                            else if (big_buffer.Count > data_length + header_length)
                            {
                                Logging.error(String.Format("Unhandled edge case occured in RemoteEndPoint:readSocketData for node {0}", getFullAddress()));
                                return(null);
                            }
                            if (data_length > 0)
                            {
                                bytesToRead = data_length + header_length - big_buffer.Count;
                                if (bytesToRead > 8000)
                                {
                                    bytesToRead = 8000;
                                }
                            }
                        }
                        else
                        {
                            if (socketReadBuffer[0] == 'X') // X is the message start byte
                            {
                                big_buffer.Add(socketReadBuffer[0]);
                                bytesToRead = header_length - 1; // header length - start byte
                            }
                            else if (helloReceived == false)
                            {
                                if (socketReadBuffer[0] == 2)
                                {
                                    readTimeSyncData();
                                }
                            }
                        }
                        Thread.Yield();
                    }
                    else
                    {
                        // sleep a litte while waiting for bytes
                        Thread.Sleep(10);
                        // TODO TODO TODO, should reset the big_buffer if a timeout occurs
                    }
                }
            }
            catch (Exception e)
            {
                Logging.error(String.Format("NET: endpoint {0} disconnected {1}", getFullAddress(), e));
                throw;
            }
            if (message_found)
            {
                data = big_buffer.ToArray();
            }
            return(data);
        }
Example #9
0
        // Send thread
        protected void sendLoop()
        {
            // Prepare an special message object to use while sending, without locking up the queue messages
            QueueMessage active_message = new QueueMessage();

            if (enableSendTimeSyncMessages)
            {
                sendTimeSyncMessages();
            }

            int messageCount = 0;

            while (running)
            {
                TLC.Report();
                long curTime = Clock.getTimestamp();
                if (curTime - lastDataReceivedTime > CoreConfig.pingTimeout)
                {
                    // haven't received any data for 10 seconds, stop running
                    Logging.warn(String.Format("Node {0} hasn't received any data from remote endpoint for over {1} seconds, disconnecting.", getFullAddress(), CoreConfig.pingTimeout));
                    state   = RemoteEndpointState.Closed;
                    running = false;
                    break;
                }
                if (curTime - lastDataSentTime > CoreConfig.pongInterval)
                {
                    try
                    {
                        clientSocket.Send(new byte[1] {
                            1
                        }, SocketFlags.None);
                        lastDataSentTime = curTime;
                        continue;
                    }
                    catch (Exception)
                    {
                        state   = RemoteEndpointState.Closed;
                        running = false;
                        break;
                    }
                }

                bool message_found = false;
                lock (sendQueueMessagesHighPriority)
                {
                    lock (sendQueueMessagesNormalPriority)
                    {
                        if ((messageCount > 0 && messageCount % 5 == 0) || (sendQueueMessagesNormalPriority.Count == 0 && sendQueueMessagesHighPriority.Count == 0))
                        {
                            lock (sendQueueMessagesLowPriority)
                            {
                                if (sendQueueMessagesLowPriority.Count > 0)
                                {
                                    // Pick the oldest message
                                    QueueMessage candidate = sendQueueMessagesLowPriority[0];
                                    active_message.code         = candidate.code;
                                    active_message.data         = candidate.data;
                                    active_message.checksum     = candidate.checksum;
                                    active_message.skipEndpoint = candidate.skipEndpoint;
                                    active_message.helperData   = candidate.helperData;
                                    // Remove it from the queue
                                    sendQueueMessagesLowPriority.Remove(candidate);
                                    message_found = true;
                                }
                            }
                            messageCount = 0;
                        }

                        if (message_found == false && ((messageCount > 0 && messageCount % 3 == 0) || sendQueueMessagesHighPriority.Count == 0))
                        {
                            if (sendQueueMessagesNormalPriority.Count > 0)
                            {
                                // Pick the oldest message
                                QueueMessage candidate = sendQueueMessagesNormalPriority[0];
                                active_message.code         = candidate.code;
                                active_message.data         = candidate.data;
                                active_message.checksum     = candidate.checksum;
                                active_message.skipEndpoint = candidate.skipEndpoint;
                                active_message.helperData   = candidate.helperData;
                                // Remove it from the queue
                                sendQueueMessagesNormalPriority.Remove(candidate);
                                message_found = true;
                            }
                        }

                        if (message_found == false && sendQueueMessagesHighPriority.Count > 0)
                        {
                            // Pick the oldest message
                            QueueMessage candidate = sendQueueMessagesHighPriority[0];
                            active_message.code         = candidate.code;
                            active_message.data         = candidate.data;
                            active_message.checksum     = candidate.checksum;
                            active_message.skipEndpoint = candidate.skipEndpoint;
                            active_message.helperData   = candidate.helperData;
                            // Remove it from the queue
                            sendQueueMessagesHighPriority.Remove(candidate);
                            message_found = true;
                        }
                    }
                }

                if (message_found)
                {
                    messageCount++;
                    // Active message set, attempt to send it
                    sendDataInternal(active_message.code, active_message.data, active_message.checksum);
                    if (active_message.code == ProtocolMessageCode.bye)
                    {
                        Thread.Sleep(500); // grace sleep to get the message through
                        running      = false;
                        fullyStopped = true;
                    }
                    Thread.Sleep(1);
                }
                else
                {
                    // Sleep for 10ms to prevent cpu waste
                    Thread.Sleep(10);
                }
            }
        }
Example #10
0
        // Receive thread
        protected virtual void recvLoop()
        {
            socketReadBuffer    = new byte[8192];
            lastMessageStatTime = DateTime.UtcNow;
            while (running)
            {
                TLC.Report();
                // Let the protocol handler receive and handle messages
                try
                {
                    byte[] data = readSocketData();
                    if (data != null)
                    {
                        parseDataInternal(data, this);
                        messagesPerSecond++;
                    }
                }
                catch (Exception e)
                {
                    if (running)
                    {
                        Logging.warn(string.Format("recvRE: Disconnected client {0} with exception {1}", getFullAddress(), e.ToString()));
                    }
                    state = RemoteEndpointState.Closed;
                }

                // Check if the client disconnected
                if (state == RemoteEndpointState.Closed)
                {
                    running = false;
                    break;
                }

                TimeSpan timeSinceLastStat = DateTime.UtcNow - lastMessageStatTime;
                if (timeSinceLastStat.TotalSeconds < 0 || timeSinceLastStat.TotalSeconds > 2)
                {
                    lastMessageStatTime   = DateTime.UtcNow;
                    lastMessagesPerSecond = (int)(messagesPerSecond / timeSinceLastStat.TotalSeconds);
                    messagesPerSecond     = 0;
                }

                if (lastMessagesPerSecond < 1)
                {
                    lastMessagesPerSecond = 1;
                }

                // Sleep a while to throttle the client
                // Check if there are too many messages
                // TODO TODO TODO this can be handled way better
                int total_message_count = NetworkQueue.getQueuedMessageCount() + NetworkQueue.getTxQueuedMessageCount();
                if (total_message_count > 500)
                {
                    Thread.Sleep(100 * lastMessagesPerSecond);
                    if (messagesPerSecond == 0)
                    {
                        lastMessageStatTime = DateTime.UtcNow;
                    }
                    lastDataReceivedTime = Clock.getTimestamp();
                }
                else if (total_message_count > 100)
                {
                    Thread.Sleep(total_message_count / 10);
                }
                else
                {
                    Thread.Sleep(1);
                }
            }
        }
Example #11
0
        private void rollForward()
        {
            bool sleep = false;

            ulong lowestBlockNum = getLowestBlockNum();

            ulong syncToBlock = syncTargetBlockNum;

            if (Node.blockChain.Count > 5)
            {
                lock (pendingBlocks)
                {
                    pendingBlocks.RemoveAll(x => x.blockNum < Node.blockChain.getLastBlockNum() - 5);
                }
            }

            lock (pendingBlocks)
            {
                // Loop until we have no more pending blocks
                do
                {
                    ulong next_to_apply = lowestBlockNum;
                    if (Node.blockChain.Count > 0)
                    {
                        next_to_apply = Node.blockChain.getLastBlockNum() + 1;
                    }

                    if (next_to_apply > syncToBlock)
                    {
                        // we have everything, clear pending blocks and break
                        pendingBlocks.Clear();
                        lock (requestedBlockTimes)
                        {
                            requestedBlockTimes.Clear();
                        }
                        break;
                    }
                    Block b = pendingBlocks.Find(x => x.blockNum == next_to_apply);
                    if (b == null)
                    {
                        lock (requestedBlockTimes)
                        {
                            if (requestBlockAgain(next_to_apply))
                            {
                                // the node isn't connected yet, wait a while
                                sleep = true;
                            }
                        }
                        break;
                    }
                    b = new Block(b);
                    if (Node.blockChain.Count == 0 && b.blockNum > 1)
                    {
                        Block tmp_b = Node.blockChain.getBlock(b.blockNum - 1, true, true);
                        if (tmp_b != null)
                        {
                            Node.blockChain.setLastBlockVersion(tmp_b.version);
                        }
                        else
                        {
                            Node.blockChain.setLastBlockVersion(b.version);
                        }
                    }

                    if (b.version > Block.maxVersion)
                    {
                        Logging.error("Received block {0} with a version higher than this node can handle, discarding the block.", b.blockNum);
                        pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                        Node.blockProcessor.networkUpgraded = true;
                        sleep = true;
                        break;
                    }
                    else
                    {
                        Node.blockProcessor.networkUpgraded = false;
                    }


                    if (next_to_apply > 5)
                    {
                        ulong targetBlock = next_to_apply - 5;

                        Block tb = pendingBlocks.Find(x => x.blockNum == targetBlock);
                        if (tb != null)
                        {
                            Block local_block = Node.blockChain.getBlock(tb.blockNum);
                            if (local_block != null && tb.blockChecksum.SequenceEqual(local_block.blockChecksum) && Node.blockProcessor.verifyBlockBasic(tb) == BlockVerifyStatus.Valid)
                            {
                                if (Node.blockProcessor.verifyBlockSignatures(tb, null))
                                {
                                    Node.blockChain.refreshSignatures(tb, true);
                                }
                                else
                                {
                                    Logging.warn("Target block " + tb.blockNum + " does not have the required consensus.");
                                }
                            }
                            pendingBlocks.RemoveAll(x => x.blockNum == tb.blockNum);
                        }
                    }

                    try
                    {
                        Logging.info("Sync: Applying block #{0}/{1}.",
                                     b.blockNum, syncToBlock);

                        bool ignoreWalletState = true;

                        if (b.blockNum > wsSyncConfirmedBlockNum || Config.fullStorageDataVerification)
                        {
                            ignoreWalletState = false;
                        }

                        b.powField = null;

                        // wallet state is correct as of wsConfirmedBlockNumber, so before that we call
                        // verify with a parameter to ignore WS tests, but do all the others
                        BlockVerifyStatus b_status = BlockVerifyStatus.Valid;

                        if (b.fromLocalStorage)
                        {
                            // TODO TODO improve this section with NodeStorage.getTransactionsInBlock once rocksdb switch happens
                            bool missing = false;
                            foreach (byte[] txid in b.transactions)
                            {
                                if (!running)
                                {
                                    break;
                                }

                                Transaction t = TransactionPool.getUnappliedTransaction(txid);
                                if (t == null)
                                {
                                    t = Node.storage.getTransaction(txid, b.blockNum);
                                    if (t != null)
                                    {
                                        t.applied = 0;
                                        TransactionPool.addTransaction(t, true, null, Config.fullStorageDataVerification);
                                    }
                                    else
                                    {
                                        CoreProtocolMessage.broadcastGetTransaction(txid, b.blockNum);
                                        missing = true;
                                    }
                                }
                            }
                            if (missing)
                            {
                                Logging.info("Requesting missing transactions for block {0}", b.blockNum);
                                Thread.Sleep(100);
                                break;
                            }
                        }

                        if (b.blockNum > wsSyncConfirmedBlockNum || b.fromLocalStorage == false || Config.fullStorageDataVerification)
                        {
                            b_status = Node.blockProcessor.verifyBlock(b, ignoreWalletState);
                        }
                        else
                        {
                            b_status = Node.blockProcessor.verifyBlockBasic(b, false);
                        }

                        if (b_status == BlockVerifyStatus.Indeterminate)
                        {
                            if (lastProcessedBlockNum < b.blockNum)
                            {
                                lastProcessedBlockNum  = b.blockNum;
                                lastProcessedBlockTime = Clock.getTimestamp();
                            }


                            if (Clock.getTimestamp() - lastProcessedBlockTime > ConsensusConfig.blockGenerationInterval * 2)
                            {
                                Logging.info("Sync: Discarding indeterminate block #{0}, due to timeout...", b.blockNum);
                                Node.blockProcessor.blacklistBlock(b);
                                pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                                requestBlockAgain(b.blockNum);
                            }
                            else
                            {
                                Logging.info("Sync: Waiting for missing transactions from block #{0}...", b.blockNum);
                            }

                            Thread.Sleep(100);
                            return;
                        }
                        if (b_status != BlockVerifyStatus.Valid)
                        {
                            Logging.warn("Block #{0} {1} is invalid. Discarding and requesting a new one.", b.blockNum, Crypto.hashToString(b.blockChecksum));
                            Node.blockProcessor.blacklistBlock(b);
                            pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                            requestBlockAgain(b.blockNum);
                            if (b_status == BlockVerifyStatus.PotentiallyForkedBlock && b.blockNum + 7 > lastBlockToReadFromStorage)
                            {
                                Node.blockProcessor.handleForkedFlag();
                            }
                            return;
                        }

                        if (!b.fromLocalStorage && !Node.blockProcessor.verifyBlockSignatures(b, null) && Node.blockChain.Count > 16)
                        {
                            Logging.warn("Block #{0} {1} doesn't have the required consensus. Discarding and requesting a new one.", b.blockNum, Crypto.hashToString(b.blockChecksum));
                            pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                            requestBlockAgain(b.blockNum);
                            return;
                        }

                        bool sigFreezeCheck = Node.blockProcessor.verifySignatureFreezeChecksum(b, null);

                        // Apply transactions when rolling forward from a recover file without a synced WS
                        if (b.blockNum > wsSyncConfirmedBlockNum)
                        {
                            if (Node.blockChain.Count <= 5 || sigFreezeCheck)
                            {
                                Node.walletState.beginTransaction(b.blockNum, false);
                                bool applied = false;
                                try
                                {
                                    applied = Node.blockProcessor.applyAcceptedBlock(b);

                                    if (applied)
                                    {
                                        Node.walletState.commitTransaction(b.blockNum);
                                    }
                                }catch (Exception e)
                                {
                                    Logging.error("Error occured during block sync, while applying/commiting transactions: " + e);
                                }
                                if (!applied)
                                {
                                    Logging.warn("Error applying Block #{0} {1}. Reverting, discarding and requesting a new one.", b.blockNum, Crypto.hashToString(b.blockChecksum));
                                    Node.walletState.revertTransaction(b.blockNum);
                                    Node.blockChain.revertBlockTransactions(b);
                                    Node.blockProcessor.blacklistBlock(b);
                                    pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                                    requestBlockAgain(b.blockNum);
                                    return;
                                }
                                else
                                {
                                    if (b.version >= BlockVer.v5 && b.lastSuperBlockChecksum == null)
                                    {
                                        // skip WS checksum check
                                    }
                                    else
                                    {
                                        if (b.lastSuperBlockChecksum != null)
                                        {
                                            byte[] wsChecksum = Node.walletState.calculateWalletStateChecksum();
                                            if (wsChecksum == null || !wsChecksum.SequenceEqual(b.walletStateChecksum))
                                            {
                                                Logging.error("After applying block #{0}, walletStateChecksum is incorrect!. Block's WS: {1}, actual WS: {2}", b.blockNum, Crypto.hashToString(b.walletStateChecksum), Crypto.hashToString(wsChecksum));
                                                Node.walletState.revertTransaction(b.blockNum);
                                                Node.blockChain.revertBlockTransactions(b);
                                                Node.blockProcessor.blacklistBlock(b);
                                                pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                                                requestBlockAgain(b.blockNum);
                                                return;
                                            }
                                        }
                                    }
                                    if (b.blockNum % Config.saveWalletStateEveryBlock == 0)
                                    {
                                        DLT.Meta.WalletStateStorage.saveWalletState(b.blockNum);
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (syncToBlock == b.blockNum)
                            {
                                if (b.version >= BlockVer.v5 && b.lastSuperBlockChecksum == null)
                                {
                                    // skip WS checksum check
                                }
                                else
                                {
                                    byte[] wsChecksum = Node.walletState.calculateWalletStateChecksum();
                                    if (wsChecksum == null || !wsChecksum.SequenceEqual(b.walletStateChecksum))
                                    {
                                        Logging.warn("Block #{0} is last and has an invalid WSChecksum. Discarding and requesting a new one.", b.blockNum);
                                        Node.blockProcessor.blacklistBlock(b);
                                        pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                                        requestBlockAgain(b.blockNum);
                                        return;
                                    }
                                }
                            }
                        }

                        if (Node.blockChain.Count <= 5 || sigFreezeCheck)
                        {
                            //Logging.info(String.Format("Appending block #{0} to blockChain.", b.blockNum));
                            if (b.blockNum <= wsSyncConfirmedBlockNum)
                            {
                                if (!TransactionPool.setAppliedFlagToTransactionsFromBlock(b))
                                {
                                    Node.blockChain.revertBlockTransactions(b);
                                    Node.blockProcessor.blacklistBlock(b);
                                    pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                                    requestBlockAgain(b.blockNum);
                                    return;
                                }
                            }

                            if (b.blockNum > 12 && b.blockNum + 5 >= IxianHandler.getHighestKnownNetworkBlockHeight())
                            {
                                if (Node.isMasterNode() && b.blockNum > 7)
                                {
                                    byte[][] signature_data = b.applySignature(); // applySignature() will return signature_data, if signature was applied and null, if signature was already present from before
                                    if (signature_data != null)
                                    {
                                        Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(signature_data[1], b.blockChecksum), true);
                                        // ProtocolMessage.broadcastNewBlock(localNewBlock);
                                        SignatureProtocolMessages.broadcastBlockSignature(signature_data[0], signature_data[1], b.blockNum, b.blockChecksum, null, null);
                                    }
                                }
                            }

                            Node.blockChain.appendBlock(b, !b.fromLocalStorage);
                        }
                        else if (Node.blockChain.Count > 5 && !sigFreezeCheck)
                        {
                            if (CoreConfig.preventNetworkOperations || Config.recoverFromFile)
                            {
                                var last_block = lastBlocks.Find(x => x.blockNum == b.blockNum - 5);
                                if (last_block != null)
                                {
                                    pendingBlocks.Add(last_block);
                                }
                                return;
                            }
                            // invalid sigfreeze, waiting for the correct block
                            Logging.warn("Block #{0} {1} doesn't have the correct sigfreezed block. Discarding and requesting a new one.", b.blockNum, Crypto.hashToString(b.blockChecksum));
                            pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                            requestBlockAgain(b.blockNum);
                            return;
                        }
                    }
                    catch (Exception e)
                    {
                        Logging.error("Exception occured while syncing block #{0}: {1}", b.blockNum, e);
                    }
                    if (Config.enableChainReorgTest)
                    {
                        if (!Node.blockProcessor.chainReorgTest(b.blockNum))
                        {
                            pendingBlocks.RemoveAll(x => x.blockNum + 6 < b.blockNum);
                        }
                    }
                    else
                    {
                        pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                    }
                    Node.blockProcessor.cleanupBlockBlacklist();
                } while (pendingBlocks.Count > 0 && running);
            }

            if (!sleep && Node.blockChain.getLastBlockNum() >= syncToBlock)
            {
                if (verifyLastBlock())
                {
                    sleep = false;
                }
                else
                {
                    sleep = true;
                }
            }

            if (sleep)
            {
                Thread.Sleep(500);
            }
        }