Exemplo n.º 1
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
                                {
                                    ProtocolMessage.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 (!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
                                {
                                    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);
                            if (missingBlocks != null)
                            {
                                missingBlocks.RemoveAll(x => x <= 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);
            }
        }
Exemplo n.º 2
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);
            }
        }