From de8e96cd75789939fa22fa03e5af7e3e7787910b Mon Sep 17 00:00:00 2001 From: CalDescent Date: Tue, 28 Sep 2021 09:28:47 +0100 Subject: [PATCH] Added Blockchain.validateAllBlocks() to check every block back to genesis. This is extremely slow and shouldn't be needed in normal use cases. It currently checks that each block references the one before, but can ultimately be expanded to check more information about each block and its derived data. --- .../java/org/qortal/block/BlockChain.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index 15801193..aee85131 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -556,6 +556,46 @@ public class BlockChain { } } + /** + * More thorough blockchain validation method. Useful for validating bootstraps. + * A DataException is thrown if anything is invalid. + * + * @throws DataException + */ + public static void validateAllBlocks() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + BlockData chainTip = repository.getBlockRepository().getLastBlock(); + final int chainTipHeight = chainTip.getHeight(); + final int oldestBlock = 1; // TODO: increase if in pruning mode + byte[] lastReference = null; + + for (int height = chainTipHeight; height > oldestBlock; height--) { + BlockData blockData = repository.getBlockRepository().fromHeight(height); + if (blockData == null) { + blockData = repository.getBlockArchiveRepository().fromHeight(height); + } + + if (blockData == null) { + String error = String.format("Missing block at height %d", height); + LOGGER.error(error); + throw new DataException(error); + } + + if (height != chainTipHeight) { + // Check reference + if (!Arrays.equals(blockData.getSignature(), lastReference)) { + String error = String.format("Invalid reference for block at height %d: %s (should be %s)", + height, Base58.encode(blockData.getReference()), Base58.encode(lastReference)); + LOGGER.error(error); + throw new DataException(error); + } + } + + lastReference = blockData.getReference(); + } + } + } + private static boolean isGenesisBlockValid() { try (final Repository repository = RepositoryManager.getRepository()) { BlockRepository blockRepository = repository.getBlockRepository();