Browse Source

Added importFromArchive() feature

This allows archived blocks to be imported back into HSQLDB in order to make them SQL-compatible again.
block-archive
CalDescent 3 years ago
parent
commit
1d8351f921
  1. 15
      src/main/java/org/qortal/repository/BlockArchiveReader.java
  2. 12
      src/main/java/org/qortal/repository/BlockArchiveRepository.java
  3. 15
      src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockArchiveRepository.java
  4. 78
      src/main/java/org/qortal/utils/BlockArchiveUtils.java

15
src/main/java/org/qortal/repository/BlockArchiveReader.java

@ -105,6 +105,21 @@ public class BlockArchiveReader {
return null;
}
public List<Triple<BlockData, List<TransactionData>, List<ATStateData>>> fetchBlocksFromRange(
int startHeight, int endHeight) {
List<Triple<BlockData, List<TransactionData>, List<ATStateData>>> blockInfoList = new ArrayList<>();
for (int height = startHeight; height <= endHeight; height++) {
Triple<BlockData, List<TransactionData>, List<ATStateData>> blockInfo = this.fetchBlockAtHeight(height);
if (blockInfo == null) {
return blockInfoList;
}
blockInfoList.add(blockInfo);
}
return blockInfoList;
}
public Integer fetchHeightForSignature(byte[] signature, Repository repository) {
// Lookup the height for the requested signature
try {

12
src/main/java/org/qortal/repository/BlockArchiveRepository.java

@ -36,6 +36,18 @@ public interface BlockArchiveRepository {
*/
public BlockData fromHeight(int height) throws DataException;
/**
* Returns a list of BlockData objects from archive using
* block height range.
*
* @param startHeight
* @return a list of BlockData objects, or an empty list if
* not found in blockchain. It is not guaranteed that all
* requested blocks will be returned.
* @throws DataException
*/
public List<BlockData> fromRange(int startHeight, int endHeight) throws DataException;
/**
* Returns BlockData from archive using block reference.
* Currently relies on a child block being the one block

15
src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockArchiveRepository.java

@ -3,6 +3,7 @@ package org.qortal.repository.hsqldb;
import org.qortal.api.ApiError;
import org.qortal.api.ApiExceptionFactory;
import org.qortal.api.model.BlockSignerSummary;
import org.qortal.block.Block;
import org.qortal.data.block.BlockArchiveData;
import org.qortal.data.block.BlockData;
import org.qortal.data.block.BlockSummaryData;
@ -53,6 +54,20 @@ public class HSQLDBBlockArchiveRepository implements BlockArchiveRepository {
return null;
}
@Override
public List<BlockData> fromRange(int startHeight, int endHeight) throws DataException {
List<BlockData> blocks = new ArrayList<>();
for (int height = startHeight; height < endHeight; height++) {
BlockData blockData = this.fromHeight(height);
if (blockData == null) {
return blocks;
}
blocks.add(blockData);
}
return blocks;
}
@Override
public BlockData fromReference(byte[] reference) throws DataException {
BlockData referenceBlock = this.repository.getBlockArchiveRepository().fromSignature(reference);

78
src/main/java/org/qortal/utils/BlockArchiveUtils.java

@ -0,0 +1,78 @@
package org.qortal.utils;
import org.qortal.data.at.ATStateData;
import org.qortal.data.block.BlockData;
import org.qortal.data.transaction.TransactionData;
import org.qortal.repository.BlockArchiveReader;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import java.util.List;
public class BlockArchiveUtils {
/**
* importFromArchive
* <p>
* Reads the requested block range from the archive
* and imports the BlockData and AT state data hashes
* This can be used to convert a block archive back
* into the HSQLDB, in order to make it SQL-compatible
* again.
* <p>
* Note: calls discardChanges() and saveChanges(), so
* make sure that you commit any existing repository
* changes before calling this method.
*
* @param startHeight The earliest block to import
* @param endHeight The latest block to import
* @param repository A clean repository session
* @throws DataException
*/
public static void importFromArchive(int startHeight, int endHeight, Repository repository) throws DataException {
repository.discardChanges();
final int requestedRange = endHeight+1-startHeight;
List<Triple<BlockData, List<TransactionData>, List<ATStateData>>> blockInfoList =
BlockArchiveReader.getInstance().fetchBlocksFromRange(startHeight, endHeight);
// Ensure that we have received all of the requested blocks
if (blockInfoList == null || blockInfoList.isEmpty()) {
throw new IllegalStateException("No blocks found when importing from archive");
}
if (blockInfoList.size() != requestedRange) {
throw new IllegalStateException("Non matching block count when importing from archive");
}
Triple<BlockData, List<TransactionData>, List<ATStateData>> firstBlock = blockInfoList.get(0);
if (firstBlock == null || firstBlock.getA().getHeight() != startHeight) {
throw new IllegalStateException("Non matching first block when importing from archive");
}
if (blockInfoList.size() > 0) {
Triple<BlockData, List<TransactionData>, List<ATStateData>> lastBlock =
blockInfoList.get(blockInfoList.size() - 1);
if (lastBlock == null || lastBlock.getA().getHeight() != endHeight) {
throw new IllegalStateException("Non matching last block when importing from archive");
}
}
// Everything seems okay, so go ahead with the import
for (Triple<BlockData, List<TransactionData>, List<ATStateData>> blockInfo : blockInfoList) {
try {
// Save block
repository.getBlockRepository().save(blockInfo.getA());
// Save AT state data hashes
for (ATStateData atStateData : blockInfo.getC()) {
atStateData.setHeight(blockInfo.getA().getHeight());
repository.getATRepository().save(atStateData);
}
} catch (DataException e) {
repository.discardChanges();
throw new IllegalStateException("Unable to import blocks from archive");
}
}
repository.saveChanges();
}
}
Loading…
Cancel
Save