Пример #1
0
        private bool handleSignature(InventoryItem item, RemoteEndpoint endpoint)
        {
            InventoryItemSignature iis = (InventoryItemSignature)item;
            ulong last_block_height    = IxianHandler.getLastBlockHeight();

            byte[] address   = iis.address;
            ulong  block_num = iis.blockNum;

            if (block_num + 5 > last_block_height && block_num <= last_block_height + 1)
            {
                if (block_num == last_block_height + 1)
                {
                    lock (Node.blockProcessor.localBlockLock)
                    {
                        Block local_block = Node.blockProcessor.localNewBlock;
                        if (local_block == null || local_block.blockNum != block_num)
                        {
                            return(false);
                        }
                        if (!local_block.blockChecksum.SequenceEqual(iis.blockHash) ||
                            local_block.hasNodeSignature(address))
                        {
                            return(false);
                        }
                    }
                }
                else
                {
                    Block sf_block = Node.blockChain.getBlock(block_num);
                    if (!sf_block.blockChecksum.SequenceEqual(iis.blockHash) ||
                        sf_block.hasNodeSignature(address))
                    {
                        return(false);
                    }
                }
                byte[] block_num_bytes = block_num.GetIxiVarIntBytes();
                byte[] addr_len_bytes  = ((ulong)address.Length).GetIxiVarIntBytes();
                byte[] data            = new byte[block_num_bytes.Length + 1 + addr_len_bytes.Length + address.Length];
                Array.Copy(block_num_bytes, data, block_num_bytes.Length);
                data[block_num_bytes.Length] = 1;
                Array.Copy(addr_len_bytes, 0, data, block_num_bytes.Length + 1, addr_len_bytes.Length);
                Array.Copy(address, 0, data, block_num_bytes.Length + 1 + addr_len_bytes.Length, address.Length);
                if (endpoint == null)
                {
                    CoreProtocolMessage.broadcastProtocolMessageToSingleRandomNode(new char[] { 'M', 'H' }, ProtocolMessageCode.getSignatures, data, block_num);
                }
                else
                {
                    endpoint.sendData(ProtocolMessageCode.getSignatures, data, null);
                }
                return(true);
            }
            return(false);
        }
Пример #2
0
            public static void broadcastGetSignatures(ulong block_num, List <InventoryItemSignature> sig_list, RemoteEndpoint endpoint)
            {
                int sig_count         = sig_list.Count;
                int max_sig_per_chunk = ConsensusConfig.maximumBlockSigners;

                for (int i = 0; i < sig_count;)
                {
                    using (MemoryStream mOut = new MemoryStream(max_sig_per_chunk * 570))
                    {
                        using (BinaryWriter writer = new BinaryWriter(mOut))
                        {
                            writer.WriteIxiVarInt(block_num);

                            int next_sig_count;
                            if (sig_count - i > max_sig_per_chunk)
                            {
                                next_sig_count = max_sig_per_chunk;
                            }
                            else
                            {
                                next_sig_count = sig_count - i;
                            }

                            writer.WriteIxiVarInt(next_sig_count);

                            for (int j = 0; j < next_sig_count && i < sig_count; j++)
                            {
                                InventoryItemSignature sig = sig_list[i];
                                i++;

                                long out_rollback_len = mOut.Length;

                                writer.WriteIxiVarInt(sig.address.Length);
                                writer.Write(sig.address);

                                if (mOut.Length > CoreConfig.maxMessageSize)
                                {
                                    mOut.SetLength(out_rollback_len);
                                    i--;
                                    break;
                                }
                            }
                        }
                        endpoint.sendData(ProtocolMessageCode.getSignatures, mOut.ToArray(), null);
                    }
                }
            }
Пример #3
0
        public bool refreshSignatures(Block b, bool forceRefresh = false, RemoteEndpoint endpoint = null)
        {
            if (!forceRefresh)
            {
                // we refuse to change sig numbers older than 4 blocks
                ulong sigLockHeight = getLastBlockNum() > 5 ? getLastBlockNum() - 3 : 1;
                if (b.blockNum <= sigLockHeight)
                {
                    return(false);
                }
            }
            Block updatestorage_block = null;
            int   beforeSigs          = 0;
            int   afterSigs           = 0;

            lock (blocks)
            {
                int idx = blocks.FindIndex(x => x.blockNum == b.blockNum && x.blockChecksum.SequenceEqual(b.blockChecksum));
                if (idx > 0)
                {
                    if (blocks[idx].compacted)
                    {
                        Logging.error("Trying to refresh signatures on compacted block {0}", blocks[idx].blockNum);
                        return(false);
                    }

                    byte[] beforeSigsChecksum = blocks[idx].calculateSignatureChecksum();
                    beforeSigs = blocks[idx].getFrozenSignatureCount();

                    var added_sigs = blocks[idx].addSignaturesFrom(b);

                    if (added_sigs != null && Node.isMasterNode())
                    {
                        foreach (var sig in added_sigs)
                        {
                            Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(sig[1], b.blockChecksum), true);
                            SignatureProtocolMessages.broadcastBlockSignature(sig[0], sig[1], b.blockNum, b.blockChecksum, endpoint, null);
                        }
                    }

                    if (forceRefresh)
                    {
                        if (!b.verifyBlockProposer())
                        {
                            Logging.error("Error verifying block proposer while force refreshing signatures on block {0} ({1})", b.blockNum, Crypto.hashToString(b.blockChecksum));
                            return(false);
                        }
                        blocks[idx].setFrozenSignatures(b.signatures);
                        afterSigs = b.signatures.Count;
                    }
                    else
                    {
                        afterSigs = blocks[idx].signatures.Count;
                    }

                    byte[] afterSigsChecksum = blocks[idx].calculateSignatureChecksum();
                    if (!beforeSigsChecksum.SequenceEqual(afterSigsChecksum))
                    {
                        updatestorage_block = blocks[idx];
                    }
                }
            }

            // Check if the block needs to be refreshed
            if (updatestorage_block != null)
            {
                updateBlock(updatestorage_block);

                Logging.info("Refreshed block #{0}: Updated signatures {1} -> {2}", b.blockNum, beforeSigs, afterSigs);
                return(true);
            }

            return(false);
        }
Пример #4
0
            // Removes event subscriptions for the provided endpoint
            public static void handleBlockSignature(byte[] data, RemoteEndpoint endpoint)
            {
                if (Node.blockSync.synchronizing)
                {
                    return;
                }

                if (data == null)
                {
                    Logging.warn(string.Format("Invalid protocol message signature data"));
                    return;
                }

                using (MemoryStream m = new MemoryStream(data))
                {
                    using (BinaryReader reader = new BinaryReader(m))
                    {
                        ulong block_num = reader.ReadUInt64();

                        int    checksum_len = reader.ReadInt32();
                        byte[] checksum     = reader.ReadBytes(checksum_len);

                        int    sig_len = reader.ReadInt32();
                        byte[] sig     = reader.ReadBytes(sig_len);

                        int    sig_addr_len = reader.ReadInt32();
                        byte[] sig_addr     = reader.ReadBytes(sig_addr_len);

                        ulong last_bh = IxianHandler.getLastBlockHeight();

                        lock (Node.blockProcessor.localBlockLock)
                        {
                            if (last_bh + 1 < block_num || (last_bh + 1 == block_num && Node.blockProcessor.getLocalBlock() == null))
                            {
                                Logging.info("Received signature for block {0} which is missing", block_num);
                                // future block, request the next block
                                BlockProtocolMessages.broadcastGetBlock(last_bh + 1, null, endpoint);
                                return;
                            }
                        }

                        if (PresenceList.getPresenceByAddress(sig_addr) == null)
                        {
                            Logging.info("Received signature for block {0} whose signer isn't in the PL", block_num);
                            return;
                        }

                        Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(sig_addr, checksum), true);

                        if (Node.blockProcessor.addSignatureToBlock(block_num, checksum, sig, sig_addr, endpoint))
                        {
                            Node.blockProcessor.acceptLocalNewBlock();
                            if (Node.isMasterNode())
                            {
                                broadcastBlockSignature(sig, sig_addr, block_num, checksum, endpoint);
                            }
                        }
                        else
                        {
                            // discard - it might have already been applied
                        }
                    }
                }
            }
Пример #5
0
            public static void handleSignaturesChunk(byte[] data, RemoteEndpoint endpoint)
            {
                using (MemoryStream m = new MemoryStream(data))
                {
                    using (BinaryReader reader = new BinaryReader(m))
                    {
                        ulong block_num = reader.ReadIxiVarUInt();

                        int    checksum_len = (int)reader.ReadIxiVarUInt();
                        byte[] checksum     = reader.ReadBytes(checksum_len);

                        ulong last_block_height = IxianHandler.getLastBlockHeight() + 1;

                        if (block_num > last_block_height)
                        {
                            return;
                        }

                        Block block = null;
                        if (block_num == last_block_height)
                        {
                            bool haveLock = false;
                            try
                            {
                                Monitor.TryEnter(Node.blockProcessor.localBlockLock, 1000, ref haveLock);
                                if (!haveLock)
                                {
                                    throw new TimeoutException();
                                }

                                Block tmp = Node.blockProcessor.getLocalBlock();
                                if (tmp != null && tmp.blockNum == last_block_height)
                                {
                                    block = tmp;
                                }
                            }
                            finally
                            {
                                if (haveLock)
                                {
                                    Monitor.Exit(Node.blockProcessor.localBlockLock);
                                }
                            }
                        }
                        else
                        {
                            block = Node.blockChain.getBlock(block_num, false, false);
                        }


                        if (block == null)
                        {
                            // target block missing
                            Logging.warn("Target block {0} for adding sigs is missing", block_num);
                            return;
                        }
                        else if (!block.blockChecksum.SequenceEqual(checksum))
                        {
                            // incorrect target block
                            Logging.warn("Incorrect target block {0} - {1}, possibly forked", block_num, Crypto.hashToString(checksum));
                            return;
                        }


                        if (block_num + 5 < last_block_height)
                        {
                            // block already sigfreezed, do nothing
                            return;
                        }

                        int sig_count = (int)reader.ReadIxiVarUInt();

                        if (sig_count > ConsensusConfig.maximumBlockSigners)
                        {
                            sig_count = ConsensusConfig.maximumBlockSigners;
                        }

                        if (block_num + 5 == last_block_height)
                        {
                            // handle currently sigfreezing block differently

                            Block dummy_block = new Block();
                            dummy_block.blockNum      = block_num;
                            dummy_block.blockChecksum = checksum;
                            dummy_block.blockProposer = block.blockProposer;

                            for (int i = 0; i < sig_count; i++)
                            {
                                if (m.Position == m.Length)
                                {
                                    break;
                                }

                                int    sig_len = (int)reader.ReadIxiVarUInt();
                                byte[] sig     = reader.ReadBytes(sig_len);

                                int    addr_len = (int)reader.ReadIxiVarUInt();
                                byte[] addr     = reader.ReadBytes(addr_len);

                                Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(addr, checksum), true);

                                dummy_block.addSignature(sig, addr);
                            }

                            Node.blockProcessor.handleSigFreezedBlock(dummy_block, endpoint);
                        }
                        else
                        {
                            for (int i = 0; i < sig_count; i++)
                            {
                                if (m.Position == m.Length)
                                {
                                    break;
                                }

                                int    sig_len = (int)reader.ReadIxiVarUInt();
                                byte[] sig     = reader.ReadBytes(sig_len);

                                int    addr_len = (int)reader.ReadIxiVarUInt();
                                byte[] addr     = reader.ReadBytes(addr_len);

                                Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(addr, checksum), true);

                                if (PresenceList.getPresenceByAddress(addr) == null)
                                {
                                    Logging.info("Received signature for block {0} whose signer isn't in the PL", block_num);
                                    continue;
                                }

                                if (Node.blockProcessor.addSignatureToBlock(block_num, checksum, sig, addr, endpoint))
                                {
                                    if (Node.isMasterNode())
                                    {
                                        broadcastBlockSignature(sig, addr, block_num, checksum, endpoint);
                                    }
                                }
                            }
                        }
                        Node.blockProcessor.acceptLocalNewBlock();
                    }
                }
            }
Пример #6
0
            public static void handleSigfreezedBlockSignatures(byte[] data, RemoteEndpoint endpoint)
            {
                using (MemoryStream m = new MemoryStream(data))
                {
                    using (BinaryReader reader = new BinaryReader(m))
                    {
                        ulong block_num = reader.ReadUInt64();

                        int    checksum_len = reader.ReadInt32();
                        byte[] checksum     = reader.ReadBytes(checksum_len);

                        ulong last_block_height = IxianHandler.getLastBlockHeight();

                        Block target_block = Node.blockChain.getBlock(block_num, true);
                        if (target_block == null)
                        {
                            if (block_num == last_block_height + 1)
                            {
                                // target block missing, request the next block
                                Logging.warn("Target block {0} missing, requesting...", block_num);
                                BlockProtocolMessages.broadcastGetBlock(block_num, null, endpoint);
                            }
                            else
                            {
                                // target block missing
                                Logging.warn("Target block {0} missing", block_num);
                            }
                            return;
                        }
                        else if (!target_block.blockChecksum.SequenceEqual(checksum))
                        {
                            // incorrect target block
                            Logging.warn("Incorrect target block {0} - {1}, possibly forked", block_num, Crypto.hashToString(checksum));
                            return;
                        }


                        Block sf_block = null;
                        if (block_num + 4 == last_block_height)
                        {
                            sf_block = Node.blockProcessor.getLocalBlock();
                        }
                        else if (block_num + 4 > last_block_height)
                        {
                            Logging.warn("Sigfreezing block {0} missing", block_num + 5);
                            return;
                        }
                        else
                        {
                            // block already sigfreezed, do nothing
                            return;
                        }

                        lock (target_block)
                        {
                            if (sf_block != null)
                            {
                                if (target_block.calculateSignatureChecksum().SequenceEqual(sf_block.signatureFreezeChecksum))
                                {
                                    // we already have the correct sigfreeze
                                    return;
                                }
                            }
                            else
                            {
                                // sf_block missing
                                Logging.warn("Sigfreezing block {0} missing", block_num + 5);
                                return;
                            }


                            int sig_count = reader.ReadInt32();

                            if (sig_count > ConsensusConfig.maximumBlockSigners)
                            {
                                sig_count = ConsensusConfig.maximumBlockSigners;
                            }

                            Block dummy_block = new Block();
                            dummy_block.blockNum      = block_num;
                            dummy_block.blockChecksum = checksum;
                            dummy_block.blockProposer = sf_block.blockProposer;

                            for (int i = 0; i < sig_count; i++)
                            {
                                if (m.Position == m.Length)
                                {
                                    break;
                                }

                                int    sig_len = reader.ReadInt32();
                                byte[] sig     = reader.ReadBytes(sig_len);

                                int    addr_len = reader.ReadInt32();
                                byte[] addr     = reader.ReadBytes(addr_len);

                                Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(addr, checksum), true);

                                dummy_block.addSignature(sig, addr);
                            }

                            Node.blockProcessor.handleSigFreezedBlock(dummy_block, endpoint);
                        }
                    }
                }
            }
Пример #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);
            }
        }