From fb2c2b1d09dbf577876ef0c5e496ae2ce87c0c58 Mon Sep 17 00:00:00 2001 From: catbref Date: Mon, 20 Jul 2020 13:05:43 +0100 Subject: [PATCH] Added API call GET /blocks/summaries Returns summary info about a range of blocks. (Not to be confused with network-related BlockSummaries) --- .../java/org/qortal/api/model/BlockInfo.java | 41 ++++++++++ .../qortal/api/resource/BlocksResource.java | 41 ++++++++++ .../qortal/repository/BlockRepository.java | 6 ++ .../hsqldb/HSQLDBBlockRepository.java | 76 +++++++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 src/main/java/org/qortal/api/model/BlockInfo.java diff --git a/src/main/java/org/qortal/api/model/BlockInfo.java b/src/main/java/org/qortal/api/model/BlockInfo.java new file mode 100644 index 00000000..6076cf43 --- /dev/null +++ b/src/main/java/org/qortal/api/model/BlockInfo.java @@ -0,0 +1,41 @@ +package org.qortal.api.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; + +@XmlAccessorType(XmlAccessType.FIELD) +public class BlockInfo { + + private byte[] signature; + private int height; + private long timestamp; + private int transactionCount; + + protected BlockInfo() { + /* For JAXB */ + } + + public BlockInfo(byte[] signature, int height, long timestamp, int transactionCount) { + this.signature = signature; + this.height = height; + this.timestamp = timestamp; + this.transactionCount = transactionCount; + } + + public byte[] getSignature() { + return this.signature; + } + + public int getHeight() { + return this.height; + } + + public long getTimestamp() { + return this.timestamp; + } + + public int getTransactionCount() { + return this.transactionCount; + } + +} diff --git a/src/main/java/org/qortal/api/resource/BlocksResource.java b/src/main/java/org/qortal/api/resource/BlocksResource.java index 6686268e..3e1e2cfa 100644 --- a/src/main/java/org/qortal/api/resource/BlocksResource.java +++ b/src/main/java/org/qortal/api/resource/BlocksResource.java @@ -23,6 +23,7 @@ import javax.ws.rs.core.MediaType; import org.qortal.api.ApiError; import org.qortal.api.ApiErrors; import org.qortal.api.ApiExceptionFactory; +import org.qortal.api.model.BlockInfo; import org.qortal.api.model.BlockSignerSummary; import org.qortal.crypto.Crypto; import org.qortal.data.account.AccountData; @@ -480,4 +481,44 @@ public class BlocksResource { } } + @GET + @Path("/summaries") + @Operation( + summary = "Fetch only summary info about a range of blocks", + description = "Specify up to 2 out 3 of: start, end and count. If neither start nor end are specified, then end is assumed to be latest block. Where necessary, count is assumed to be 50.", + responses = { + @ApiResponse( + description = "blocks", + content = @Content( + array = @ArraySchema( + schema = @Schema( + implementation = BlockInfo.class + ) + ) + ) + ) + } + ) + @ApiErrors({ + ApiError.REPOSITORY_ISSUE + }) + public List getBlockRange( + @QueryParam("start") Integer startHeight, + @QueryParam("end") Integer endHeight, + @Parameter(ref = "count") @QueryParam("count") Integer count) { + // Check up to 2 out of 3 params + if (startHeight != null && endHeight != null && count != null) + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA); + + // Check values + if ((startHeight != null && startHeight < 1) || (endHeight != null && endHeight < 1) || (count != null && count < 1)) + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA); + + try (final Repository repository = RepositoryManager.getRepository()) { + return repository.getBlockRepository().getBlockInfos(startHeight, endHeight, count); + } catch (DataException e) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); + } + } + } diff --git a/src/main/java/org/qortal/repository/BlockRepository.java b/src/main/java/org/qortal/repository/BlockRepository.java index 24372212..8104edef 100644 --- a/src/main/java/org/qortal/repository/BlockRepository.java +++ b/src/main/java/org/qortal/repository/BlockRepository.java @@ -2,6 +2,7 @@ package org.qortal.repository; import java.util.List; +import org.qortal.api.model.BlockInfo; import org.qortal.api.model.BlockSignerSummary; import org.qortal.data.block.BlockData; import org.qortal.data.block.BlockSummaryData; @@ -128,6 +129,11 @@ public interface BlockRepository { */ public List getBlockSummaries(int firstBlockHeight, int lastBlockHeight) throws DataException; + /** + * Returns block infos for the passed height range, for API use. + */ + public List getBlockInfos(Integer startHeight, Integer endHeight, Integer count) throws DataException; + /** * Trim online accounts signatures from blocks older than passed timestamp. * diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java index 0860e1b1..39de4cc7 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.qortal.api.model.BlockInfo; import org.qortal.api.model.BlockSignerSummary; import org.qortal.data.block.BlockData; import org.qortal.data.block.BlockSummaryData; @@ -360,6 +361,81 @@ public class HSQLDBBlockRepository implements BlockRepository { } } + @Override + public List getBlockInfos(Integer startHeight, Integer endHeight, Integer count) throws DataException { + StringBuilder sql = new StringBuilder(512); + sql.append("SELECT signature, height, minted_when, transaction_count "); + + /* + * start end count result + * 10 40 null blocks 10 to 39 (excludes end block, ignore count) + * + * null null null blocks 1 to 50 (assume count=50, maybe start=1) + * 30 null null blocks 30 to 79 (assume count=50) + * 30 null 10 blocks 30 to 39 + * + * null null 50 last 50 blocks? so if max(blocks.height) is 200, then blocks 151 to 200 + * null 200 null blocks 150 to 199 (excludes end block, assume count=50) + * null 200 10 blocks 190 to 199 (excludes end block) + */ + + if (startHeight != null && endHeight != null) { + sql.append("FROM Blocks WHERE height BETWEEN "); + sql.append(startHeight); + sql.append(" AND "); + sql.append(endHeight - 1); + } else if (endHeight != null || (startHeight == null && count != null)) { + // we are going to return blocks from the end of the chain + if (count == null) + count = 50; + + if (endHeight == null) { + sql.append("FROM (SELECT height FROM Blocks ORDER BY height DESC LIMIT 1) AS MaxHeights (max_height) "); + sql.append("JOIN Blocks ON height BETWEEN (max_height - "); + sql.append(count); + sql.append(" + 1) AND max_height"); + } else { + sql.append("FROM Blocks WHERE height BETWEEN "); + sql.append(endHeight - count); + sql.append(" AND "); + sql.append(endHeight - 1); + } + } else { + // we are going to return blocks from the start of the chain + if (startHeight == null) + startHeight = 1; + + if (count == null) + count = 50; + + sql.append("FROM Blocks WHERE height BETWEEN "); + sql.append(startHeight); + sql.append(" AND "); + sql.append(startHeight + count - 1); + } + + List blockInfos = new ArrayList<>(); + + try (ResultSet resultSet = this.repository.checkedExecute(sql.toString())) { + if (resultSet == null) + return blockInfos; + + do { + byte[] signature = resultSet.getBytes(1); + int height = resultSet.getInt(2); + long timestamp = resultSet.getLong(3); + int transactionCount = resultSet.getInt(4); + + BlockInfo blockInfo = new BlockInfo(signature, height, timestamp, transactionCount); + blockInfos.add(blockInfo); + } while (resultSet.next()); + + return blockInfos; + } catch (SQLException e) { + throw new DataException("Unable to fetch height-ranged block infos from repository", e); + } + } + @Override public int trimOldOnlineAccountsSignatures(long timestamp) throws DataException { String sql = "UPDATE Blocks set online_accounts_signatures = NULL WHERE minted_when < ? AND online_accounts_signatures IS NOT NULL";