mirror of
https://github.com/Qortal/qortal.git
synced 2025-05-05 01:07:51 +00:00
517 lines
16 KiB
Java
517 lines
16 KiB
Java
package api;
|
|
|
|
import data.block.BlockData;
|
|
import globalization.Translator;
|
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
import io.swagger.v3.oas.annotations.extensions.Extension;
|
|
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
|
|
import io.swagger.v3.oas.annotations.media.Content;
|
|
import io.swagger.v3.oas.annotations.media.Schema;
|
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
|
import java.math.BigDecimal;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
|
import javax.ws.rs.GET;
|
|
import javax.ws.rs.Path;
|
|
import javax.ws.rs.PathParam;
|
|
import javax.ws.rs.Produces;
|
|
import javax.ws.rs.core.Context;
|
|
import javax.ws.rs.core.MediaType;
|
|
import qora.block.Block;
|
|
|
|
import repository.Repository;
|
|
import repository.RepositoryManager;
|
|
import utils.Base58;
|
|
|
|
@Path("blocks")
|
|
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
|
@OpenAPIDefinition(
|
|
extensions = @Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="/Api/BlocksResource")
|
|
})
|
|
)
|
|
public class BlocksResource {
|
|
|
|
@Context
|
|
HttpServletRequest request;
|
|
|
|
private ApiErrorFactory apiErrorFactory;
|
|
|
|
public BlocksResource() {
|
|
this(new ApiErrorFactory(Translator.getInstance()));
|
|
}
|
|
|
|
public BlocksResource(ApiErrorFactory apiErrorFactory) {
|
|
this.apiErrorFactory = apiErrorFactory;
|
|
}
|
|
|
|
@GET
|
|
@Path("/{signature}")
|
|
@Operation(
|
|
description = "returns the block that matches the given signature",
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET signature"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
@Extension(properties = {
|
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true),
|
|
})
|
|
},
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the block",
|
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public BlockData getBlock(@PathParam("signature") String signature) {
|
|
Security.checkApiCallAllowed("GET blocks", request);
|
|
|
|
// decode signature
|
|
byte[] signatureBytes;
|
|
try
|
|
{
|
|
signatureBytes = Base58.decode(signature);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
|
}
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
|
|
|
// check if block exists
|
|
if(blockData == null)
|
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
|
|
|
return blockData;
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/first")
|
|
@Operation(
|
|
description = "returns the genesis block",
|
|
extensions = @Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET first"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the block",
|
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public BlockData getFirstBlock() {
|
|
Security.checkApiCallAllowed("GET blocks/first", request);
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
BlockData blockData = repository.getBlockRepository().fromHeight(1);
|
|
return blockData;
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/last")
|
|
@Operation(
|
|
description = "returns the last valid block",
|
|
extensions = @Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET last"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the block",
|
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public BlockData getLastBlock() {
|
|
Security.checkApiCallAllowed("GET blocks/last", request);
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
|
return blockData;
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/child/{signature}")
|
|
@Operation(
|
|
description = "returns the child block of the block that matches the given signature",
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET child:signature"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
@Extension(properties = {
|
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true),
|
|
})
|
|
},
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the block",
|
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public BlockData getChild(@PathParam("signature") String signature) {
|
|
Security.checkApiCallAllowed("GET blocks/child", request);
|
|
|
|
// decode signature
|
|
byte[] signatureBytes;
|
|
try
|
|
{
|
|
signatureBytes = Base58.decode(signature);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
|
}
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
|
|
|
// check if block exists
|
|
if(blockData == null)
|
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
|
|
|
int height = blockData.getHeight();
|
|
BlockData childBlockData = repository.getBlockRepository().fromHeight(height + 1);
|
|
|
|
// check if child exists
|
|
if(childBlockData == null)
|
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
|
|
|
return childBlockData;
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/generatingbalance")
|
|
@Operation(
|
|
description = "calculates the generating balance of the block that will follow the last block",
|
|
extensions = @Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET generatingbalance"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the generating balance",
|
|
content = @Content(schema = @Schema(implementation = BigDecimal.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public BigDecimal getGeneratingBalance() {
|
|
Security.checkApiCallAllowed("GET blocks/generatingbalance", request);
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
|
Block block = new Block(repository, blockData);
|
|
return block.calcNextBlockGeneratingBalance();
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/generatingbalance/{signature}")
|
|
@Operation(
|
|
description = "calculates the generating balance of the block that will follow the block that matches the signature",
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET generatingbalance:signature"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
@Extension(properties = {
|
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true),
|
|
})
|
|
},
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the block",
|
|
content = @Content(schema = @Schema(implementation = BigDecimal.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public BigDecimal getGeneratingBalance(@PathParam("signature") String signature) {
|
|
Security.checkApiCallAllowed("GET blocks/generatingbalance", request);
|
|
|
|
// decode signature
|
|
byte[] signatureBytes;
|
|
try
|
|
{
|
|
signatureBytes = Base58.decode(signature);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
|
}
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
|
|
|
// check if block exists
|
|
if(blockData == null)
|
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
|
|
|
Block block = new Block(repository, blockData);
|
|
return block.calcNextBlockGeneratingBalance();
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/time")
|
|
@Operation(
|
|
description = "calculates the time it should take for the network to generate the next block",
|
|
extensions = @Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET time"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the time in seconds", // in seconds?
|
|
content = @Content(schema = @Schema(implementation = long.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public long getTimePerBlock() {
|
|
Security.checkApiCallAllowed("GET blocks/time", request);
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
|
return Block.calcForgingDelay(blockData.getGeneratingBalance());
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/time/{generatingbalance}")
|
|
@Operation(
|
|
description = "calculates the time it should take for the network to generate blocks when the current generating balance in the network is the specified generating balance",
|
|
extensions = @Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET time:generatingbalance"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the time", // in seconds?
|
|
content = @Content(schema = @Schema(implementation = long.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public long getTimePerBlock(@PathParam("generating") BigDecimal generatingbalance) {
|
|
Security.checkApiCallAllowed("GET blocks/time", request);
|
|
|
|
return Block.calcForgingDelay(generatingbalance);
|
|
}
|
|
|
|
@GET
|
|
@Path("/height")
|
|
@Operation(
|
|
description = "returns the block height of the last block.",
|
|
extensions = @Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET height"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the height",
|
|
content = @Content(schema = @Schema(implementation = int.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public int getHeight() {
|
|
Security.checkApiCallAllowed("GET blocks/height", request);
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
return repository.getBlockRepository().getBlockchainHeight();
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/height/{signature}")
|
|
@Operation(
|
|
description = "returns the block height of the block that matches the given signature",
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET height:signature"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
@Extension(properties = {
|
|
@ExtensionProperty(name="apiErrors", value="[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true),
|
|
})
|
|
},
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the height",
|
|
content = @Content(schema = @Schema(implementation = int.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public int getHeight(@PathParam("signature") String signature) {
|
|
Security.checkApiCallAllowed("GET blocks/height", request);
|
|
|
|
// decode signature
|
|
byte[] signatureBytes;
|
|
try
|
|
{
|
|
signatureBytes = Base58.decode(signature);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
|
}
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
|
|
|
// check if block exists
|
|
if(blockData == null)
|
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
|
|
|
return blockData.getHeight();
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/byheight/{height}")
|
|
@Operation(
|
|
description = "returns the block whith given height",
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="path", value="GET byheight:height"),
|
|
@ExtensionProperty(name="description.key", value="operation:description")
|
|
}),
|
|
@Extension(properties = {
|
|
@ExtensionProperty(name="apiErrors", value="[\"BLOCK_NO_EXISTS\"]", parseValue = true),
|
|
})
|
|
},
|
|
responses = {
|
|
@ApiResponse(
|
|
description = "the block",
|
|
content = @Content(schema = @Schema(implementation = BlockData.class)),
|
|
extensions = {
|
|
@Extension(name = "translation", properties = {
|
|
@ExtensionProperty(name="description.key", value="success_response:description")
|
|
})
|
|
}
|
|
)
|
|
}
|
|
)
|
|
public BlockData getbyHeight(@PathParam("height") int height) {
|
|
Security.checkApiCallAllowed("GET blocks/byheight", request);
|
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
|
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
|
|
|
// check if block exists
|
|
if(blockData == null)
|
|
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
|
|
|
return blockData;
|
|
|
|
} catch (ApiException e) {
|
|
throw e;
|
|
} catch (Exception e) {
|
|
throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
|
|
}
|
|
}
|
|
}
|