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.
This commit is contained in:
CalDescent 2021-09-28 09:28:47 +01:00
parent e2a62f88a6
commit de8e96cd75

View File

@ -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();