Esempio n. 1
0
        public static void processPendingTransactions()
        {
            // TODO TODO improve to include failed transactions
            ulong last_block_height = IxianHandler.getLastBlockHeight();

            lock (PendingTransactions.pendingTransactions)
            {
                long cur_time = Clock.getTimestamp();
                List <PendingTransaction> tmp_pending_transactions = new List <PendingTransaction>(PendingTransactions.pendingTransactions);
                int idx = 0;
                foreach (var entry in tmp_pending_transactions)
                {
                    Transaction t       = entry.transaction;
                    long        tx_time = entry.addedTimestamp;

                    if (t.applied != 0)
                    {
                        PendingTransactions.pendingTransactions.RemoveAll(x => x.transaction.id.SequenceEqual(t.id));
                        continue;
                    }

                    // if transaction expired, remove it from pending transactions
                    if (last_block_height > ConsensusConfig.getRedactedWindowSize() && t.blockHeight < last_block_height - ConsensusConfig.getRedactedWindowSize())
                    {
                        ActivityStorage.updateStatus(t.id, ActivityStatus.Error, 0);
                        PendingTransactions.pendingTransactions.RemoveAll(x => x.transaction.id.SequenceEqual(t.id));
                        continue;
                    }

                    if (cur_time - tx_time > 40) // if the transaction is pending for over 40 seconds, resend
                    {
                        CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'H' }, ProtocolMessageCode.transactionData, t.getBytes(), null);
                        entry.addedTimestamp = cur_time;
                        entry.confirmedNodeList.Clear();
                    }

                    if (entry.confirmedNodeList.Count() >= 3) // if we get transaction from 3 nodes, we can consider it as confirmed
                    {
                        if (entry.messageId != null)
                        {
                            StreamProcessor.confirmMessage(entry.messageId);
                        }
                        continue;
                    }

                    if (cur_time - tx_time > 20) // if the transaction is pending for over 20 seconds, send inquiry
                    {
                        CoreProtocolMessage.broadcastGetTransaction(t.id, 0, null, false);
                    }

                    idx++;
                }
            }
        }
Esempio n. 2
0
        public static void processPendingTransactions()
        {
            // TODO TODO improve to include failed transactions
            ulong last_block_height = IxianHandler.getLastBlockHeight();

            lock (PendingTransactions.pendingTransactions)
            {
                long cur_time = Clock.getTimestamp();
                List <PendingTransaction> tmp_pending_transactions = new List <PendingTransaction>(PendingTransactions.pendingTransactions);
                int idx = 0;
                foreach (var entry in tmp_pending_transactions)
                {
                    Transaction t       = entry.transaction;
                    long        tx_time = entry.addedTimestamp;

                    if (t.applied != 0)
                    {
                        PendingTransactions.pendingTransactions.RemoveAll(x => x.transaction.id.SequenceEqual(t.id));
                        continue;
                    }

                    // if transaction expired, remove it from pending transactions
                    if (last_block_height > ConsensusConfig.getRedactedWindowSize() && t.blockHeight < last_block_height - ConsensusConfig.getRedactedWindowSize())
                    {
                        Console.WriteLine("Error sending the transaction {0}", Transaction.txIdV8ToLegacy(t.id));
                        PendingTransactions.pendingTransactions.RemoveAll(x => x.transaction.id.SequenceEqual(t.id));
                        continue;
                    }

                    if (cur_time - tx_time > 40) // if the transaction is pending for over 40 seconds, resend
                    {
                        CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'H' }, ProtocolMessageCode.transactionData, t.getBytes(), null);
                        entry.addedTimestamp = cur_time;
                        entry.confirmedNodeList.Clear();
                    }

                    if (entry.confirmedNodeList.Count() > 3) // already received 3+ feedback
                    {
                        continue;
                    }

                    if (cur_time - tx_time > 20) // if the transaction is pending for over 20 seconds, send inquiry
                    {
                        CoreProtocolMessage.broadcastGetTransaction(Transaction.txIdV8ToLegacy(t.id), 0);
                    }

                    idx++;
                }
            }
        }
        override protected bool sendInventoryRequest(InventoryItem item, RemoteEndpoint endpoint)
        {
            switch (item.type)
            {
            case InventoryItemTypes.block:
                return(handleBlock(item, endpoint));

            case InventoryItemTypes.blockSignature:
                return(handleSignature(item, endpoint));

            case InventoryItemTypes.keepAlive:
                return(handleKeepAlive(item, endpoint));

            case InventoryItemTypes.transaction:
                CoreProtocolMessage.broadcastGetTransaction(item.hash, 0, endpoint);
                return(true);
            }
            return(false);
        }
Esempio n. 4
0
        public void insertMessage(FriendMessage message)
        {
            if (friend.approved == false)
            {
                if (message.type == FriendMessageType.requestAdd)
                {
                    // Call webview methods on the main UI thread only
                    Utils.sendUiCommand(webView, "showContactRequest", "1");
                    message.read = true;
                    return;
                }
            }
            else
            {
                // Don't show if the friend is already approved
                if (message.type == FriendMessageType.requestAdd)
                {
                    return;
                }
            }

            string prefix  = "addMe";
            string avatar  = "";
            string address = "";
            string nick    = "";

            if (!message.localSender)
            {
                if (friend.bot)
                {
                    if (message.senderAddress != null)
                    {
                        address = Base58Check.Base58CheckEncoding.EncodePlain(message.senderAddress);
                    }

                    nick = message.senderNick;
                    if (nick == "")
                    {
                        if (message.senderAddress != null && friend.contacts.ContainsKey(message.senderAddress))
                        {
                            nick = friend.contacts[message.senderAddress].nick;
                        }
                    }

                    if (nick == "")
                    {
                        nick = address;
                    }
                }

                prefix = "addThem";
                if (message.senderAddress != null)
                {
                    avatar = Node.localStorage.getAvatarPath(Base58Check.Base58CheckEncoding.EncodePlain(message.senderAddress));
                }
                else
                {
                    avatar = Node.localStorage.getAvatarPath(Base58Check.Base58CheckEncoding.EncodePlain(friend.walletAddress));
                }
                if (avatar == null)
                {
                    avatar = "img/spixiavatar.png";
                }
            }

            if (message.type == FriendMessageType.requestFunds)
            {
                string status      = "WAITING CONFIRMATION";
                string status_icon = "fa-clock";

                string amount = message.message;

                string txid = "";

                bool enableView = false;

                if (!message.localSender)
                {
                    enableView = true;
                }

                if (message.message.StartsWith("::"))
                {
                    status      = "DECLINED";
                    status_icon = "fa-exclamation-circle";
                    amount      = message.message.Substring(2);
                    txid        = Crypto.hashToString(message.id);
                    enableView  = false;
                }
                else if (message.message.StartsWith(":"))
                {
                    status = "PENDING";
                    txid   = message.message.Substring(1);

                    bool        confirmed   = true;
                    Transaction transaction = TransactionCache.getTransaction(txid);
                    if (transaction == null)
                    {
                        transaction = TransactionCache.getUnconfirmedTransaction(txid);
                        confirmed   = false;
                    }

                    amount = "?";

                    if (transaction != null)
                    {
                        amount = transaction.amount.ToString();

                        if (confirmed)
                        {
                            status      = "CONFIRMED";
                            status_icon = "fa-check-circle";
                        }
                    }
                    else
                    {
                        // TODO think about how to make this more private
                        CoreProtocolMessage.broadcastGetTransaction(txid, 0, null);
                    }
                    enableView = true;
                }


                if (message.localSender)
                {
                    Utils.sendUiCommand(webView, "addPaymentRequest", Crypto.hashToString(message.id), txid, address, nick, avatar, "Payment request SENT", amount, status, status_icon, message.timestamp.ToString(), message.localSender.ToString(), message.confirmed.ToString(), message.read.ToString(), enableView.ToString());
                }
                else
                {
                    Utils.sendUiCommand(webView, "addPaymentRequest", Crypto.hashToString(message.id), txid, address, nick, avatar, "Payment request RECEIVED", amount, status, status_icon, message.timestamp.ToString(), "", message.confirmed.ToString(), message.read.ToString(), enableView.ToString());
                }
            }

            if (message.type == FriendMessageType.sentFunds)
            {
                bool        confirmed   = true;
                Transaction transaction = TransactionCache.getTransaction(message.message);
                if (transaction == null)
                {
                    transaction = TransactionCache.getUnconfirmedTransaction(message.message);
                    confirmed   = false;
                }

                string status      = "PENDING";
                string status_icon = "fa-clock";

                string amount = "?";

                if (transaction != null)
                {
                    if (confirmed)
                    {
                        status      = "CONFIRMED";
                        status_icon = "fa-check-circle";
                    }
                    if (message.localSender)
                    {
                        amount = transaction.amount.ToString();
                    }
                    else
                    {
                        amount = HomePage.calculateReceivedAmount(transaction).ToString();
                    }
                }
                else
                {
                    // TODO think about how to make this more private
                    CoreProtocolMessage.broadcastGetTransaction(message.message, 0, null);
                }

                // Call webview methods on the main UI thread only
                if (message.localSender)
                {
                    Utils.sendUiCommand(webView, "addPaymentRequest", Crypto.hashToString(message.id), message.message, address, nick, avatar, "Payment SENT", amount, status, status_icon, message.timestamp.ToString(), message.localSender.ToString(), message.confirmed.ToString(), message.read.ToString(), "True");
                }
                else
                {
                    Utils.sendUiCommand(webView, "addPaymentRequest", Crypto.hashToString(message.id), message.message, address, nick, avatar, "Payment RECEIVED", amount, status, status_icon, message.timestamp.ToString(), "", message.confirmed.ToString(), message.read.ToString(), "True");
                }
            }


            if (message.type == FriendMessageType.fileHeader)
            {
                string[] split = message.message.Split(new string[] { ":" }, StringSplitOptions.None);
                if (split != null && split.Length > 1)
                {
                    string uid  = split[0];
                    string name = split[1];

                    string progress = "0";
                    if (message.completed)
                    {
                        progress = "100";
                    }
                    Utils.sendUiCommand(webView, "addFile", Crypto.hashToString(message.id), address, nick, avatar, uid, name, message.timestamp.ToString(), message.localSender.ToString(), message.confirmed.ToString(), message.read.ToString(), progress, message.completed.ToString());
                }
            }

            if (message.type == FriendMessageType.standard)
            {
                // Normal chat message
                // Call webview methods on the main UI thread only
                Utils.sendUiCommand(webView, prefix, Crypto.hashToString(message.id), address, nick, avatar, message.message, message.timestamp.ToString(), message.confirmed.ToString(), message.read.ToString());
            }
            updateMessageReadStatus(message);
        }
Esempio n. 5
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
                {
                    handleWatchDog();

                    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 (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)
                        {
                            if (tb.blockChecksum.SequenceEqual(Node.blockChain.getBlock(tb.blockNum).blockChecksum) && Node.blockProcessor.verifyBlockBasic(tb) == BlockVerifyStatus.Valid)
                            {
                                if (Node.blockProcessor.verifyBlockSignatures(tb))
                                {
                                    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(String.Format("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)
                        {
                            bool missing = false;
                            foreach (string txid in b.transactions)
                            {
                                if (!running)
                                {
                                    break;
                                }

                                Transaction t = TransactionPool.getTransaction(txid, b.blockNum, true);
                                if (t != null)
                                {
                                    t.applied = 0;
                                    if (!TransactionPool.addTransaction(t, true, null, Config.fullStorageDataVerification))
                                    {
                                        Logging.error("Error adding a transaction {0} from storage", txid);
                                    }
                                }
                                else
                                {
                                    CoreProtocolMessage.broadcastGetTransaction(txid, b.blockNum);
                                    missing = true;
                                }
                            }
                            if (missing)
                            {
                                sleep = true;
                                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)
                        {
                            Logging.info(String.Format("Waiting for missing transactions from block #{0}...", b.blockNum));
                            Thread.Sleep(100);
                            return;
                        }
                        if (b_status != BlockVerifyStatus.Valid)
                        {
                            Logging.warn(String.Format("Block #{0} {1} is invalid. Discarding and requesting a new one.", b.blockNum, Crypto.hashToString(b.blockChecksum)));
                            pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                            requestBlockAgain(b.blockNum);
                            return;
                        }

                        if (!b.fromLocalStorage && !Node.blockProcessor.verifyBlockSignatures(b) && Node.blockChain.Count > 16)
                        {
                            Logging.warn(String.Format("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.blockProcessor.applyAcceptedBlock(b);

                                if (b.version >= BlockVer.v5 && b.lastSuperBlockChecksum == null)
                                {
                                    // skip WS checksum check
                                }
                                else
                                {
                                    if (b.lastSuperBlockChecksum != null || b.blockNum % Config.saveWalletStateEveryBlock == 0)
                                    {
                                        byte[] wsChecksum = Node.walletState.calculateWalletStateChecksum();
                                        if (wsChecksum == null || !wsChecksum.SequenceEqual(b.walletStateChecksum))
                                        {
                                            Logging.error(String.Format("After applying block #{0}, walletStateChecksum is incorrect!. Block's WS: {1}, actual WS: {2}", b.blockNum, Crypto.hashToString(b.walletStateChecksum), Crypto.hashToString(wsChecksum)));
                                            handleWatchDog(true);
                                            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(String.Format("Block #{0} is last and has an invalid WSChecksum. Discarding and requesting a new one.", b.blockNum));
                                        pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                                        requestBlockAgain(b.blockNum);
                                        handleWatchDog(true);
                                        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))
                                {
                                    pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                                    requestBlockAgain(b.blockNum);
                                    return;
                                }
                            }

                            if (b.blockNum > 12 && b.blockNum + 5 >= IxianHandler.getHighestKnownNetworkBlockHeight())
                            {
                                if (Node.isMasterNode())
                                {
                                    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)
                                    {
                                        // ProtocolMessage.broadcastNewBlock(localNewBlock);
                                        ProtocolMessage.broadcastNewBlockSignature(b.blockNum, b.blockChecksum, signature_data[0], signature_data[1]);
                                    }
                                }
                            }

                            Node.blockChain.appendBlock(b, !b.fromLocalStorage);
                            resetWatchDog(b.blockNum);
                        }
                        else if (Node.blockChain.Count > 5 && !sigFreezeCheck)
                        {
                            // invalid sigfreeze, waiting for the correct block
                            pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                            return;
                        }
                    }
                    catch (Exception e)
                    {
                        Logging.error(String.Format("Exception occured while syncing block #{0}: {1}", b.blockNum, e));
                    }

                    pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum);
                } while (pendingBlocks.Count > 0 && running);
            }

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

            if (sleep)
            {
                Thread.Sleep(500);
            }
        }
Esempio n. 6
0
        public static void onBotAction(byte[] action_data, RemoteEndpoint endpoint, int channel = 0)
        {
            SpixiBotAction sba = new SpixiBotAction(action_data);

            switch (sba.action)
            {
            case SpixiBotActionCode.getChannels:
                sendChannels(endpoint);
                break;

            case SpixiBotActionCode.getInfo:
                Node.users.setPubKey(endpoint.presence.wallet, endpoint.serverPubKey, false);
                sendInfo(endpoint.presence.wallet);
                break;

            case SpixiBotActionCode.getUsers:
                sendUsers(endpoint);
                break;

            case SpixiBotActionCode.getUser:
                sendUser(endpoint.presence.wallet, Node.users.getUser(sba.data));
                break;

            case SpixiBotActionCode.payment:
                StreamTransaction stream_tx = new StreamTransaction(sba.data);

                if (!stream_tx.transaction.toList.Keys.First().SequenceEqual(IxianHandler.getWalletStorage().getPrimaryAddress()))
                {
                    Logging.warn("Received transaction txid " + stream_tx.transaction.id + " from " + Base58Check.Base58CheckEncoding.EncodePlain(endpoint.presence.wallet) + " that's not for this node.");
                    return;
                }

                StreamMessage sm = pendingMessages.Find(x => x.id.SequenceEqual(stream_tx.messageID));
                if (sm == null)
                {
                    // TODO TODO TODO send get message request to the client
                    Logging.warn("Received transaction txid " + stream_tx.transaction.id + " from " + Base58Check.Base58CheckEncoding.EncodePlain(endpoint.presence.wallet) + " but have no message for this transaction.");
                    return;
                }

                IxiNumber price = getMessagePrice(sm.sender, sm.data.Length);
                if (stream_tx.transaction.amount < price)
                {
                    Logging.warn("Received transaction txid " + stream_tx.transaction.id + " from " + Base58Check.Base58CheckEncoding.EncodePlain(endpoint.presence.wallet) + " that has lower than expected amount.");
                    return;
                }

                CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'H' }, ProtocolMessageCode.transactionData, stream_tx.transaction.getBytes(), null);
                CoreProtocolMessage.broadcastGetTransaction(stream_tx.transaction.id, 0, null, false);
                PendingTransactions.addPendingLocalTransaction(stream_tx.transaction, stream_tx.messageID);
                break;

            case SpixiBotActionCode.enableNotifications:
                bool send_notifications = false;
                if (sba.data[0] == 1)
                {
                    send_notifications = true;
                }
                Node.users.getUser(endpoint.presence.wallet).sendNotification = send_notifications;
                Node.users.writeContactsToFile();
                break;
            }
        }
Esempio n. 7
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);
            }
        }