mirror of
https://github.com/Qortal/qortal.git
synced 2025-05-03 08:17:50 +00:00
API: wholesale conversion back to Base58
XmlJavaTypeAdapter api.Base58TypeAdapter converts byte[] to Base58. This XmlAdapter is applied at package-level to all packages inside data and api.models. So no need to annotate every byte[] property! Added package-info-maven-plugin to pom.xml to do this. block-explorer.html fixed to show/use base58 again Some data objects (e.g. TransactionData) have added XmlElements that convert public keys to addresses, for convenience. Several API calls updated to return specifically text/plain instead of ambiguous application/json and/or text/plain. (Typically API calls that return a single value, e.g. an integer, like /blocks/height).
This commit is contained in:
parent
034cf5dee3
commit
3ec3c69447
@ -6,6 +6,7 @@
|
|||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" path="target/generated-sources/package-info"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
@ -24,17 +25,17 @@
|
|||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="src" path="target/generated-sources/annotations">
|
<classpathentry kind="src" path="target/generated-sources/annotations">
|
||||||
<attributes>
|
<attributes>
|
||||||
|
<attribute name="ignore_optional_problems" value="true"/>
|
||||||
<attribute name="optional" value="true"/>
|
<attribute name="optional" value="true"/>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
<attribute name="ignore_optional_problems" value="true"/>
|
|
||||||
<attribute name="m2e-apt" value="true"/>
|
<attribute name="m2e-apt" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
|
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
|
||||||
<attributes>
|
<attributes>
|
||||||
|
<attribute name="ignore_optional_problems" value="true"/>
|
||||||
<attribute name="optional" value="true"/>
|
<attribute name="optional" value="true"/>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
<attribute name="ignore_optional_problems" value="true"/>
|
|
||||||
<attribute name="m2e-apt" value="true"/>
|
<attribute name="m2e-apt" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
|
@ -14,7 +14,7 @@ org.eclipse.jdt.core.compiler.source=1.8
|
|||||||
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
|
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
|
||||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=53
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
|
||||||
@ -82,7 +82,7 @@ org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
|
|||||||
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
|
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
|
||||||
org.eclipse.jdt.core.formatter.compact_else_if=true
|
org.eclipse.jdt.core.formatter.compact_else_if=true
|
||||||
org.eclipse.jdt.core.formatter.continuation_indentation=2
|
org.eclipse.jdt.core.formatter.continuation_indentation=2
|
||||||
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
|
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1
|
||||||
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
|
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
|
||||||
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
||||||
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
|
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
|
||||||
@ -106,16 +106,16 @@ org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=inser
|
|||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
|
||||||
@ -282,10 +282,10 @@ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_
|
|||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
|
org.eclipse.jdt.core.formatter.join_lines_in_comments=false
|
||||||
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
|
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
|
||||||
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
|
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
|
||||||
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
|
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=true
|
||||||
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
|
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
|
||||||
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
||||||
org.eclipse.jdt.core.formatter.lineSplit=160
|
org.eclipse.jdt.core.formatter.lineSplit=160
|
||||||
@ -293,7 +293,7 @@ org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
|
|||||||
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
|
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
|
||||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
||||||
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
|
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
|
||||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
|
org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=separate_lines_if_wrapped
|
||||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
|
org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
|
||||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
|
org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
|
||||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
|
org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
|
||||||
|
@ -51,14 +51,13 @@
|
|||||||
for (var i=0; i<transactions.length; ++i) {
|
for (var i=0; i<transactions.length; ++i) {
|
||||||
var tx = transactions[i];
|
var tx = transactions[i];
|
||||||
var txTimestamp = new Date(tx.timestamp).toUTCString();
|
var txTimestamp = new Date(tx.timestamp).toUTCString();
|
||||||
var txCreatorAddress = publicKeyToAddress(base64ToArray(tx.creatorPublicKey)); // currently base64 but likely to be base58 in the future
|
|
||||||
|
|
||||||
var row = '<tr><td>' + tx.type + '</td>' +
|
var row = '<tr><td>' + tx.type + '</td>' +
|
||||||
'<td>' + txTimestamp + '</td>' +
|
'<td>' + txTimestamp + '</td>' +
|
||||||
'<td class="addr">' + addressAsLink(txCreatorAddress) + '</td>' +
|
'<td class="addr">' + addressAsLink(tx.creatorAddress) + '</td>' +
|
||||||
'<td>' + tx.fee + ' QORA</td>' +
|
'<td>' + tx.fee + ' QORA</td>' +
|
||||||
'<td class="sig">' + Base58.encode(base64ToArray(tx.signature)) + '</td>' +
|
'<td class="sig">' + tx.signature + '</td>' +
|
||||||
'<td class="ref">' + Base58.encode(base64ToArray(tx.reference)) + '</td></tr>';
|
'<td class="ref">' + tx.reference + '</td></tr>';
|
||||||
|
|
||||||
html += row;
|
html += row;
|
||||||
}
|
}
|
||||||
@ -119,14 +118,13 @@
|
|||||||
for (var i=0; i<transactions.length; ++i) {
|
for (var i=0; i<transactions.length; ++i) {
|
||||||
var tx = transactions[i];
|
var tx = transactions[i];
|
||||||
var txTimestamp = new Date(tx.timestamp).toUTCString();
|
var txTimestamp = new Date(tx.timestamp).toUTCString();
|
||||||
var txCreatorAddress = publicKeyToAddress(base64ToArray(tx.creatorPublicKey)); // currently base64 but likely to be base58 in the future
|
|
||||||
|
|
||||||
var row = '<tr><td>' + tx.type + '</td>' +
|
var row = '<tr><td>' + tx.type + '</td>' +
|
||||||
'<td>' + txTimestamp + '</td>' +
|
'<td>' + txTimestamp + '</td>' +
|
||||||
'<td class="addr">' + addressAsLink(txCreatorAddress) + '</td>' +
|
'<td class="addr">' + addressAsLink(tx.creatorAddress) + '</td>' +
|
||||||
'<td>' + tx.fee + ' QORA</td>' +
|
'<td>' + tx.fee + ' QORA</td>' +
|
||||||
'<td class="sig">' + Base58.encode(base64ToArray(tx.signature)) + '</td>' +
|
'<td class="sig">' + tx.signature + '</td>' +
|
||||||
'<td class="ref">' + Base58.encode(base64ToArray(tx.reference)) + '</td></tr>';
|
'<td class="ref">' + tx.reference + '</td></tr>';
|
||||||
|
|
||||||
html += row;
|
html += row;
|
||||||
}
|
}
|
||||||
@ -139,16 +137,6 @@
|
|||||||
function renderBlockInfo(e) {
|
function renderBlockInfo(e) {
|
||||||
var blockData = e.target.response.block;
|
var blockData = e.target.response.block;
|
||||||
|
|
||||||
// These properties are currently emitted as base64 by API but likely to be base58 in the future, so convert them
|
|
||||||
var props = [ "signature", "reference", "transactionsSignature", "generatorPublicKey", "generatorSignature" ];
|
|
||||||
for (var i=0; i<props.length; ++i) {
|
|
||||||
var p = props[i];
|
|
||||||
blockData[p] = Base58.encode(base64ToArray(blockData[p]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert generator public key into address
|
|
||||||
blockData.generator = publicKeyToAddress(base64ToArray(blockData.generatorPublicKey)); // currently base64 but likely to be base58 in the future
|
|
||||||
|
|
||||||
var html = '<h1>Block ' + blockData.height + '</h1>';
|
var html = '<h1>Block ' + blockData.height + '</h1>';
|
||||||
html += '<table id="block"><tr><th>Property</th><th>Value</th></tr>';
|
html += '<table id="block"><tr><th>Property</th><th>Value</th></tr>';
|
||||||
|
|
||||||
@ -163,7 +151,7 @@
|
|||||||
html += '<td>';
|
html += '<td>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p == "generator") {
|
if (p == "generatorAddress") {
|
||||||
html += addressAsLink(blockData[p]);
|
html += addressAsLink(blockData[p]);
|
||||||
} else {
|
} else {
|
||||||
html += blockData[p];
|
html += blockData[p];
|
||||||
@ -200,12 +188,11 @@
|
|||||||
|
|
||||||
var ourHeight = blockData.height;
|
var ourHeight = blockData.height;
|
||||||
var blockTimestamp = new Date(blockData.timestamp).toUTCString();
|
var blockTimestamp = new Date(blockData.timestamp).toUTCString();
|
||||||
var blockGeneratorAddress = publicKeyToAddress(base64ToArray(blockData.generatorPublicKey)); // currently base64 but likely to be base58 in the future
|
|
||||||
|
|
||||||
var ourRow = document.createElement('TR');
|
var ourRow = document.createElement('TR');
|
||||||
ourRow.innerHTML = '<td><a href="#" onclick="fetchBlockInfo(' + ourHeight + ')">' + ourHeight + '</a></td>' +
|
ourRow.innerHTML = '<td><a href="#" onclick="fetchBlockInfo(' + ourHeight + ')">' + ourHeight + '</a></td>' +
|
||||||
'<td>' + blockTimestamp + '</td>' +
|
'<td>' + blockTimestamp + '</td>' +
|
||||||
'<td class="addr">' + addressAsLink(blockGeneratorAddress) + '</td>' +
|
'<td class="addr">' + addressAsLink(blockData.generatorAddress) + '</td>' +
|
||||||
'<td>' + blockData.generatingBalance + '</td>' +
|
'<td>' + blockData.generatingBalance + '</td>' +
|
||||||
'<td>' + blockData.transactionCount + '</td>' +
|
'<td>' + blockData.transactionCount + '</td>' +
|
||||||
'<td>' + blockData.totalFees + ' QORA</td>';
|
'<td>' + blockData.totalFees + ' QORA</td>';
|
||||||
|
40
pom.xml
40
pom.xml
@ -80,7 +80,8 @@
|
|||||||
<value>
|
<value>
|
||||||
deepLinking: true,
|
deepLinking: true,
|
||||||
tagsSorter: "alpha",
|
tagsSorter: "alpha",
|
||||||
operationsSorter: "alpha",
|
operationsSorter:
|
||||||
|
"alpha",
|
||||||
</value>
|
</value>
|
||||||
</replacement>
|
</replacement>
|
||||||
</replacements>
|
</replacements>
|
||||||
@ -108,6 +109,34 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!-- Generate package-info.java files for JAXB, to convert byte[] to/from
|
||||||
|
Base58 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.github.bohnman</groupId>
|
||||||
|
<artifactId>package-info-maven-plugin</artifactId>
|
||||||
|
<version>1.0.1</version>
|
||||||
|
<configuration>
|
||||||
|
<packages>
|
||||||
|
<package>
|
||||||
|
<pattern>data.**</pattern>
|
||||||
|
<template>${project.basedir}/src/data/package-info.java</template>
|
||||||
|
</package>
|
||||||
|
<package>
|
||||||
|
<pattern>api.models**</pattern>
|
||||||
|
<template>${project.basedir}/src/data/package-info.java</template>
|
||||||
|
</package>
|
||||||
|
</packages>
|
||||||
|
<sourceDirectory>${project.basedir}/src</sourceDirectory>
|
||||||
|
<outputDirectory>${project.build.directory}/generated-sources/package-info</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>generate</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
@ -128,7 +157,8 @@
|
|||||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||||
<artifactSet>
|
<artifactSet>
|
||||||
<excludes>
|
<excludes>
|
||||||
<!-- Don't include original swagger-UI as we're including our own modified version -->
|
<!-- Don't include original swagger-UI as we're including our own
|
||||||
|
modified version -->
|
||||||
<exclude>org.webjars:swagger-ui</exclude>
|
<exclude>org.webjars:swagger-ui</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</artifactSet>
|
</artifactSet>
|
||||||
@ -175,6 +205,12 @@
|
|||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.github.bohnman/package-info-maven-plugin -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.bohnman</groupId>
|
||||||
|
<artifactId>package-info-maven-plugin</artifactId>
|
||||||
|
<version>1.0.1</version>
|
||||||
|
</dependency>
|
||||||
<!-- HSQLDB for repository -->
|
<!-- HSQLDB for repository -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hsqldb</groupId>
|
<groupId>org.hsqldb</groupId>
|
||||||
|
@ -11,7 +11,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@ -31,6 +30,7 @@ import repository.DataException;
|
|||||||
import repository.Repository;
|
import repository.Repository;
|
||||||
import repository.RepositoryManager;
|
import repository.RepositoryManager;
|
||||||
import transform.Transformer;
|
import transform.Transformer;
|
||||||
|
import utils.Base58;
|
||||||
|
|
||||||
@Path("addresses")
|
@Path("addresses")
|
||||||
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
||||||
@ -48,7 +48,7 @@ public class AddressesResource {
|
|||||||
@Path("/lastreference/{address}")
|
@Path("/lastreference/{address}")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Fetch reference for next transaction to be created by address",
|
summary = "Fetch reference for next transaction to be created by address",
|
||||||
description = "Returns the base64-encoded signature of the last confirmed transaction created by address, failing that: the first incoming transaction to address. Returns \"false\" if there is no transactions.",
|
description = "Returns the base58-encoded signature of the last confirmed transaction created by address, failing that: the first incoming transaction to address. Returns \"false\" if there is no transactions.",
|
||||||
extensions = {
|
extensions = {
|
||||||
@Extension(name = "translation", properties = {
|
@Extension(name = "translation", properties = {
|
||||||
@ExtensionProperty(name="path", value="GET lastreference:address"),
|
@ExtensionProperty(name="path", value="GET lastreference:address"),
|
||||||
@ -60,7 +60,7 @@ public class AddressesResource {
|
|||||||
},
|
},
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "the base64-encoded transaction signature or \"false\"",
|
description = "the base58-encoded transaction signature or \"false\"",
|
||||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string")),
|
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string")),
|
||||||
extensions = {
|
extensions = {
|
||||||
@Extension(name = "translation", properties = {
|
@Extension(name = "translation", properties = {
|
||||||
@ -87,7 +87,7 @@ public class AddressesResource {
|
|||||||
if(lastReference == null || lastReference.length == 0) {
|
if(lastReference == null || lastReference.length == 0) {
|
||||||
return "false";
|
return "false";
|
||||||
} else {
|
} else {
|
||||||
return Base64.getEncoder().encodeToString(lastReference);
|
return Base58.encode(lastReference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ public class AddressesResource {
|
|||||||
@Path("/lastreference/{address}/unconfirmed")
|
@Path("/lastreference/{address}/unconfirmed")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Fetch reference for next transaction to be created by address, considering unconfirmed transactions",
|
summary = "Fetch reference for next transaction to be created by address, considering unconfirmed transactions",
|
||||||
description = "Returns the base64-encoded signature of the last confirmed/unconfirmed transaction created by address, failing that: the first incoming transaction. Returns \\\"false\\\" if there is no transactions.",
|
description = "Returns the base58-encoded signature of the last confirmed/unconfirmed transaction created by address, failing that: the first incoming transaction. Returns \\\"false\\\" if there is no transactions.",
|
||||||
extensions = {
|
extensions = {
|
||||||
@Extension(name = "translation", properties = {
|
@Extension(name = "translation", properties = {
|
||||||
@ExtensionProperty(name="path", value="GET lastreference:address:unconfirmed"),
|
@ExtensionProperty(name="path", value="GET lastreference:address:unconfirmed"),
|
||||||
@ -107,7 +107,7 @@ public class AddressesResource {
|
|||||||
},
|
},
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "the base64-encoded transaction signature",
|
description = "the base58-encoded transaction signature",
|
||||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string")),
|
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string")),
|
||||||
extensions = {
|
extensions = {
|
||||||
@Extension(name = "translation", properties = {
|
@Extension(name = "translation", properties = {
|
||||||
@ -134,7 +134,7 @@ public class AddressesResource {
|
|||||||
if(lastReference == null || lastReference.length == 0) {
|
if(lastReference == null || lastReference.length == 0) {
|
||||||
return "false";
|
return "false";
|
||||||
} else {
|
} else {
|
||||||
return Base64.getEncoder().encodeToString(lastReference);
|
return Base58.encode(lastReference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +356,7 @@ public class AddressesResource {
|
|||||||
@Path("/publickey/{address}")
|
@Path("/publickey/{address}")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Get public key of address",
|
summary = "Get public key of address",
|
||||||
description = "Returns the base64-encoded account public key of the given address, or \"false\" if address not known or has no public key.",
|
description = "Returns the base58-encoded account public key of the given address, or \"false\" if address not known or has no public key.",
|
||||||
extensions = {
|
extensions = {
|
||||||
@Extension(name = "translation", properties = {
|
@Extension(name = "translation", properties = {
|
||||||
@ExtensionProperty(name="path", value="GET publickey:address"),
|
@ExtensionProperty(name="path", value="GET publickey:address"),
|
||||||
@ -392,7 +392,7 @@ public class AddressesResource {
|
|||||||
if (publicKey == null)
|
if (publicKey == null)
|
||||||
return "false";
|
return "false";
|
||||||
|
|
||||||
return Base64.getEncoder().encodeToString(publicKey);
|
return Base58.encode(publicKey);
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
@ -404,7 +404,7 @@ public class AddressesResource {
|
|||||||
@Path("/convert/{publickey}")
|
@Path("/convert/{publickey}")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Convert public key into address",
|
summary = "Convert public key into address",
|
||||||
description = "Returns account address based on supplied public key. Expects base64-encoded, 32-byte public key.",
|
description = "Returns account address based on supplied public key. Expects base58-encoded, 32-byte public key.",
|
||||||
extensions = {
|
extensions = {
|
||||||
@Extension(name = "translation", properties = {
|
@Extension(name = "translation", properties = {
|
||||||
@ExtensionProperty(name="path", value="GET publickey:address"),
|
@ExtensionProperty(name="path", value="GET publickey:address"),
|
||||||
@ -426,21 +426,21 @@ public class AddressesResource {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public String fromPublicKey(@PathParam("publickey") String publicKey) {
|
public String fromPublicKey(@PathParam("publickey") String publicKey58) {
|
||||||
// Decode public key
|
// Decode public key
|
||||||
byte[] publicKeyBytes;
|
byte[] publicKey;
|
||||||
try {
|
try {
|
||||||
publicKeyBytes = Base64.getDecoder().decode(publicKey);
|
publicKey = Base58.decode(publicKey58);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_PUBLIC_KEY, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_PUBLIC_KEY, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct size for public key?
|
// Correct size for public key?
|
||||||
if (publicKeyBytes.length != Transformer.PUBLIC_KEY_LENGTH)
|
if (publicKey.length != Transformer.PUBLIC_KEY_LENGTH)
|
||||||
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_PUBLIC_KEY);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_PUBLIC_KEY);
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
return Crypto.toAddress(publicKeyBytes);
|
return Crypto.toAddress(publicKey);
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
|
@ -7,7 +7,7 @@ import io.swagger.v3.oas.annotations.info.Info;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@OpenAPIDefinition(
|
@OpenAPIDefinition(
|
||||||
info = @Info( title = "Qora API", description = "NOTE: byte-arrays are encoded in Base64" ),
|
info = @Info( title = "Qora API", description = "NOTE: byte-arrays are encoded in Base58" ),
|
||||||
tags = {
|
tags = {
|
||||||
@Tag(name = "Addresses"),
|
@Tag(name = "Addresses"),
|
||||||
@Tag(name = "Admin"),
|
@Tag(name = "Admin"),
|
||||||
|
@ -11,9 +11,9 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import repository.DataException;
|
import repository.DataException;
|
||||||
import repository.Repository;
|
import repository.Repository;
|
||||||
import repository.RepositoryManager;
|
import repository.RepositoryManager;
|
||||||
|
import utils.Base58;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@ -196,11 +196,11 @@ public class AssetsResource {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public OrderWithTrades getAssetOrder(@PathParam("orderId") String orderId64) {
|
public OrderWithTrades getAssetOrder(@PathParam("orderId") String orderId58) {
|
||||||
// Decode orderID
|
// Decode orderID
|
||||||
byte[] orderId;
|
byte[] orderId;
|
||||||
try {
|
try {
|
||||||
orderId = Base64.getDecoder().decode(orderId64);
|
orderId = Base58.decode(orderId58);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_ORDER_ID, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_ORDER_ID, e);
|
||||||
}
|
}
|
||||||
|
25
src/api/Base58TypeAdapter.java
Normal file
25
src/api/Base58TypeAdapter.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package api;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Base58;
|
||||||
|
|
||||||
|
public class Base58TypeAdapter extends XmlAdapter<String, byte[]> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] unmarshal(String input) throws Exception {
|
||||||
|
if (input == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return Base58.decode(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String marshal(byte[] input) throws Exception {
|
||||||
|
if (input == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return Base58.encode(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,7 +2,6 @@ package api;
|
|||||||
|
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
import data.transaction.TransactionData;
|
import data.transaction.TransactionData;
|
||||||
import globalization.Translator;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.extensions.Extension;
|
import io.swagger.v3.oas.annotations.extensions.Extension;
|
||||||
@ -14,7 +13,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -33,63 +31,96 @@ import qora.block.Block;
|
|||||||
import repository.DataException;
|
import repository.DataException;
|
||||||
import repository.Repository;
|
import repository.Repository;
|
||||||
import repository.RepositoryManager;
|
import repository.RepositoryManager;
|
||||||
|
import utils.Base58;
|
||||||
|
|
||||||
@Path("blocks")
|
@Path("blocks")
|
||||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN })
|
@Produces({
|
||||||
@Extension(name = "translation", properties = { @ExtensionProperty(name = "path", value = "/Api/BlocksResource") })
|
MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN
|
||||||
@Tag(name = "Blocks")
|
})
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "path",
|
||||||
|
value = "/Api/BlocksResource"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@Tag(
|
||||||
|
name = "Blocks"
|
||||||
|
)
|
||||||
public class BlocksResource {
|
public class BlocksResource {
|
||||||
|
|
||||||
@Context
|
@Context
|
||||||
HttpServletRequest request;
|
HttpServletRequest request;
|
||||||
|
|
||||||
private ApiErrorFactory apiErrorFactory;
|
|
||||||
|
|
||||||
public BlocksResource() {
|
|
||||||
this(new ApiErrorFactory(Translator.getInstance()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlocksResource(ApiErrorFactory apiErrorFactory) {
|
|
||||||
this.apiErrorFactory = apiErrorFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/signature/{signature}")
|
@Path("/signature/{signature}")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Fetch block using base64 signature",
|
summary = "Fetch block using base58 signature",
|
||||||
description = "Returns the block that matches the given signature",
|
description = "Returns the block that matches the given signature",
|
||||||
extensions =
|
extensions = {
|
||||||
{ @Extension(
|
@Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET signature"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
), @Extension(properties =
|
name = "path",
|
||||||
{ @ExtensionProperty(name = "apiErrors", value = "[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true), }) },
|
value = "GET signature"
|
||||||
responses =
|
), @ExtensionProperty(
|
||||||
{ @ApiResponse(
|
name = "description.key",
|
||||||
description = "the block",
|
value = "operation:description"
|
||||||
content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
|
|
||||||
extensions =
|
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
|
||||||
) }
|
|
||||||
)
|
)
|
||||||
public BlockWithTransactions getBlock(@PathParam("signature") String signature,
|
}
|
||||||
@Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
), @Extension(
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "apiErrors",
|
||||||
|
value = "[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]",
|
||||||
|
parseValue = true
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the block",
|
||||||
|
content = @Content(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = BlockWithTransactions.class
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public BlockWithTransactions getBlock(@PathParam("signature") String signature58, @Parameter(
|
||||||
|
ref = "includeTransactions"
|
||||||
|
) @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||||
// Decode signature
|
// Decode signature
|
||||||
byte[] signatureBytes;
|
byte[] signature;
|
||||||
try {
|
try {
|
||||||
signatureBytes = Base64.getDecoder().decode(signature);
|
signature = Base58.decode(signature58);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
|
||||||
return packageBlockData(repository, blockData, includeTransactions);
|
return packageBlockData(repository, blockData, includeTransactions);
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,25 +131,48 @@ public class BlocksResource {
|
|||||||
description = "Returns the genesis block",
|
description = "Returns the genesis block",
|
||||||
extensions = @Extension(
|
extensions = @Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET first"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
),
|
name = "path",
|
||||||
responses =
|
value = "GET first"
|
||||||
{ @ApiResponse(
|
), @ExtensionProperty(
|
||||||
description = "the block",
|
name = "description.key",
|
||||||
content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
|
value = "operation:description"
|
||||||
extensions =
|
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
|
||||||
) }
|
|
||||||
)
|
)
|
||||||
public BlockWithTransactions getFirstBlock(@Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
}
|
||||||
|
),
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the block",
|
||||||
|
content = @Content(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = BlockWithTransactions.class
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public BlockWithTransactions getFirstBlock(@Parameter(
|
||||||
|
ref = "includeTransactions"
|
||||||
|
) @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
BlockData blockData = repository.getBlockRepository().fromHeight(1);
|
BlockData blockData = repository.getBlockRepository().fromHeight(1);
|
||||||
return packageBlockData(repository, blockData, includeTransactions);
|
return packageBlockData(repository, blockData, includeTransactions);
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,73 +183,126 @@ public class BlocksResource {
|
|||||||
description = "Returns the last valid block",
|
description = "Returns the last valid block",
|
||||||
extensions = @Extension(
|
extensions = @Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET last"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
),
|
name = "path",
|
||||||
responses =
|
value = "GET last"
|
||||||
{ @ApiResponse(
|
), @ExtensionProperty(
|
||||||
description = "the block",
|
name = "description.key",
|
||||||
content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
|
value = "operation:description"
|
||||||
extensions =
|
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
|
||||||
) }
|
|
||||||
)
|
)
|
||||||
public BlockWithTransactions getLastBlock(@Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
}
|
||||||
|
),
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the block",
|
||||||
|
content = @Content(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = BlockWithTransactions.class
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public BlockWithTransactions getLastBlock(@Parameter(
|
||||||
|
ref = "includeTransactions"
|
||||||
|
) @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
||||||
return packageBlockData(repository, blockData, includeTransactions);
|
return packageBlockData(repository, blockData, includeTransactions);
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/child/{signature}")
|
@Path("/child/{signature}")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Fetch child block using base64 signature of parent block",
|
summary = "Fetch child block using base58 signature of parent block",
|
||||||
description = "Returns the child block of the block that matches the given signature",
|
description = "Returns the child block of the block that matches the given signature",
|
||||||
extensions =
|
extensions = {
|
||||||
{ @Extension(
|
@Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET child:signature"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
), @Extension(properties =
|
name = "path",
|
||||||
{ @ExtensionProperty(name = "apiErrors", value = "[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true), }) },
|
value = "GET child:signature"
|
||||||
responses =
|
), @ExtensionProperty(
|
||||||
{ @ApiResponse(
|
name = "description.key",
|
||||||
description = "the block",
|
value = "operation:description"
|
||||||
content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
|
|
||||||
extensions =
|
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
|
||||||
) }
|
|
||||||
)
|
)
|
||||||
public BlockWithTransactions getChild(@PathParam("signature") String signature,
|
}
|
||||||
@Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
), @Extension(
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "apiErrors",
|
||||||
|
value = "[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]",
|
||||||
|
parseValue = true
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the block",
|
||||||
|
content = @Content(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = BlockWithTransactions.class
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public BlockWithTransactions getChild(@PathParam("signature") String signature58, @Parameter(
|
||||||
|
ref = "includeTransactions"
|
||||||
|
) @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||||
// Decode signature
|
// Decode signature
|
||||||
byte[] signatureBytes;
|
byte[] signature;
|
||||||
try {
|
try {
|
||||||
signatureBytes = Base64.getDecoder().decode(signature);
|
signature = Base58.decode(signature58);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
|
||||||
|
|
||||||
// Check block exists
|
// Check block exists
|
||||||
if (blockData == null)
|
if (blockData == null)
|
||||||
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
throw ApiErrorFactory.getInstance().createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
BlockData childBlockData = repository.getBlockRepository().fromReference(signatureBytes);
|
BlockData childBlockData = repository.getBlockRepository().fromReference(signature);
|
||||||
|
|
||||||
// Checking child exists is handled by packageBlockData()
|
// Checking child exists is handled by packageBlockData()
|
||||||
return packageBlockData(repository, childBlockData, includeTransactions);
|
return packageBlockData(repository, childBlockData, includeTransactions);
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,16 +313,38 @@ public class BlocksResource {
|
|||||||
description = "Calculates the generating balance of the block that will follow the last block",
|
description = "Calculates the generating balance of the block that will follow the last block",
|
||||||
extensions = @Extension(
|
extensions = @Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET generatingbalance"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
|
name = "path",
|
||||||
|
value = "GET generatingbalance"
|
||||||
|
), @ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "operation:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
),
|
),
|
||||||
responses =
|
responses = {
|
||||||
{ @ApiResponse(
|
@ApiResponse(
|
||||||
description = "the generating balance",
|
description = "the generating balance",
|
||||||
content = @Content(schema = @Schema(implementation = BigDecimal.class)),
|
content = @Content(
|
||||||
extensions =
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
schema = @Schema(
|
||||||
) }
|
implementation = BigDecimal.class
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
public BigDecimal getGeneratingBalance() {
|
public BigDecimal getGeneratingBalance() {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
@ -225,7 +354,7 @@ public class BlocksResource {
|
|||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,44 +363,73 @@ public class BlocksResource {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Generating balance of block after specific block",
|
summary = "Generating balance of block after specific block",
|
||||||
description = "Calculates the generating balance of the block that will follow the block that matches the signature",
|
description = "Calculates the generating balance of the block that will follow the block that matches the signature",
|
||||||
extensions =
|
extensions = {
|
||||||
{ @Extension(
|
@Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET generatingbalance:signature"),
|
@ExtensionProperty(
|
||||||
@ExtensionProperty(name = "description.key", value = "operation:description") }
|
name = "path",
|
||||||
), @Extension(properties =
|
value = "GET generatingbalance:signature"
|
||||||
{ @ExtensionProperty(name = "apiErrors", value = "[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true), }) },
|
), @ExtensionProperty(
|
||||||
responses =
|
name = "description.key",
|
||||||
{ @ApiResponse(
|
value = "operation:description"
|
||||||
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) {
|
}
|
||||||
|
), @Extension(
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "apiErrors",
|
||||||
|
value = "[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]",
|
||||||
|
parseValue = true
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the block",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = BigDecimal.class
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public BigDecimal getGeneratingBalance(@PathParam("signature") String signature58) {
|
||||||
// Decode signature
|
// Decode signature
|
||||||
byte[] signatureBytes;
|
byte[] signature;
|
||||||
try {
|
try {
|
||||||
signatureBytes = Base64.getDecoder().decode(signature);
|
signature = Base58.decode(signature58);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
|
||||||
|
|
||||||
// Check block exists
|
// Check block exists
|
||||||
if (blockData == null)
|
if (blockData == null)
|
||||||
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
throw ApiErrorFactory.getInstance().createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
Block block = new Block(repository, blockData);
|
Block block = new Block(repository, blockData);
|
||||||
return block.calcNextBlockGeneratingBalance();
|
return block.calcNextBlockGeneratingBalance();
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,16 +440,39 @@ public class BlocksResource {
|
|||||||
description = "Calculates the time it should take for the network to generate the next block",
|
description = "Calculates the time it should take for the network to generate the next block",
|
||||||
extensions = @Extension(
|
extensions = @Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET time"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
|
name = "path",
|
||||||
|
value = "GET time"
|
||||||
|
), @ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "operation:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
),
|
),
|
||||||
responses =
|
responses = {
|
||||||
{ @ApiResponse(
|
@ApiResponse(
|
||||||
description = "the time in seconds", // in seconds?
|
description = "the time in seconds", // in
|
||||||
content = @Content(schema = @Schema(implementation = long.class)),
|
// seconds?
|
||||||
extensions =
|
content = @Content(
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
) }
|
schema = @Schema(
|
||||||
|
type = "number"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
public long getTimePerBlock() {
|
public long getTimePerBlock() {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
@ -300,7 +481,7 @@ public class BlocksResource {
|
|||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,18 +492,40 @@ public class BlocksResource {
|
|||||||
description = "Calculates the time it should take for the network to generate blocks based on specified generating balance",
|
description = "Calculates the time it should take for the network to generate blocks based on specified generating balance",
|
||||||
extensions = @Extension(
|
extensions = @Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET time:generatingbalance"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
),
|
name = "path",
|
||||||
responses =
|
value = "GET time:generatingbalance"
|
||||||
{ @ApiResponse(
|
), @ExtensionProperty(
|
||||||
description = "the time", // in seconds?
|
name = "description.key",
|
||||||
content = @Content(schema = @Schema(implementation = long.class)),
|
value = "operation:description"
|
||||||
extensions =
|
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
|
||||||
) }
|
|
||||||
)
|
)
|
||||||
public long getTimePerBlock(@PathParam("generating") BigDecimal generatingbalance) {
|
}
|
||||||
|
),
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the time", // in seconds?
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
type = "number"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public long getTimePerBlock(@PathParam("generatingbalance") BigDecimal generatingbalance) {
|
||||||
return Block.calcForgingDelay(generatingbalance);
|
return Block.calcForgingDelay(generatingbalance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,16 +536,38 @@ public class BlocksResource {
|
|||||||
description = "Returns the block height of the last block.",
|
description = "Returns the block height of the last block.",
|
||||||
extensions = @Extension(
|
extensions = @Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET height"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
|
name = "path",
|
||||||
|
value = "GET height"
|
||||||
|
), @ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "operation:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
),
|
),
|
||||||
responses =
|
responses = {
|
||||||
{ @ApiResponse(
|
@ApiResponse(
|
||||||
description = "the height",
|
description = "the height",
|
||||||
content = @Content(schema = @Schema(implementation = int.class)),
|
content = @Content(
|
||||||
extensions =
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
schema = @Schema(
|
||||||
) }
|
type = "number"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
public int getHeight() {
|
public int getHeight() {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
@ -350,7 +575,7 @@ public class BlocksResource {
|
|||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,42 +584,72 @@ public class BlocksResource {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Height of specific block",
|
summary = "Height of specific block",
|
||||||
description = "Returns the block height of the block that matches the given signature",
|
description = "Returns the block height of the block that matches the given signature",
|
||||||
extensions =
|
extensions = {
|
||||||
{ @Extension(
|
@Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET height:signature"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
), @Extension(properties =
|
name = "path",
|
||||||
{ @ExtensionProperty(name = "apiErrors", value = "[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]", parseValue = true), }) },
|
value = "GET height:signature"
|
||||||
responses =
|
), @ExtensionProperty(
|
||||||
{ @ApiResponse(
|
name = "description.key",
|
||||||
description = "the height",
|
value = "operation:description"
|
||||||
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) {
|
}
|
||||||
|
), @Extension(
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "apiErrors",
|
||||||
|
value = "[\"INVALID_SIGNATURE\", \"BLOCK_NO_EXISTS\"]",
|
||||||
|
parseValue = true
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the height",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
type = "number"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public int getHeight(@PathParam("signature") String signature58) {
|
||||||
// Decode signature
|
// Decode signature
|
||||||
byte[] signatureBytes;
|
byte[] signature;
|
||||||
try {
|
try {
|
||||||
signatureBytes = Base64.getDecoder().decode(signature);
|
signature = Base58.decode(signature58);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
|
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
|
||||||
|
|
||||||
// Check block exists
|
// Check block exists
|
||||||
if (blockData == null)
|
if (blockData == null)
|
||||||
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
throw ApiErrorFactory.getInstance().createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
return blockData.getHeight();
|
return blockData.getHeight();
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,30 +658,60 @@ public class BlocksResource {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Fetch block using block height",
|
summary = "Fetch block using block height",
|
||||||
description = "Returns the block with given height",
|
description = "Returns the block with given height",
|
||||||
extensions =
|
extensions = {
|
||||||
{ @Extension(
|
@Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET byheight:height"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
), @Extension(properties =
|
name = "path",
|
||||||
{ @ExtensionProperty(name = "apiErrors", value = "[\"BLOCK_NO_EXISTS\"]", parseValue = true), }) },
|
value = "GET byheight:height"
|
||||||
responses =
|
), @ExtensionProperty(
|
||||||
{ @ApiResponse(
|
name = "description.key",
|
||||||
description = "the block",
|
value = "operation:description"
|
||||||
content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
|
|
||||||
extensions =
|
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
|
||||||
) }
|
|
||||||
)
|
)
|
||||||
public BlockWithTransactions getByHeight(@PathParam("height") int height,
|
}
|
||||||
@Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
), @Extension(
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "apiErrors",
|
||||||
|
value = "[\"BLOCK_NO_EXISTS\"]",
|
||||||
|
parseValue = true
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the block",
|
||||||
|
content = @Content(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = BlockWithTransactions.class
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public BlockWithTransactions getByHeight(@PathParam("height") int height, @Parameter(
|
||||||
|
ref = "includeTransactions"
|
||||||
|
) @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
||||||
return packageBlockData(repository, blockData, includeTransactions);
|
return packageBlockData(repository, blockData, includeTransactions);
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,22 +720,53 @@ public class BlocksResource {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Fetch blocks starting with given height",
|
summary = "Fetch blocks starting with given height",
|
||||||
description = "Returns blocks starting with given height.",
|
description = "Returns blocks starting with given height.",
|
||||||
extensions =
|
extensions = {
|
||||||
{ @Extension(
|
@Extension(
|
||||||
name = "translation",
|
name = "translation",
|
||||||
properties =
|
properties = {
|
||||||
{ @ExtensionProperty(name = "path", value = "GET byheight:height"), @ExtensionProperty(name = "description.key", value = "operation:description") }
|
@ExtensionProperty(
|
||||||
), @Extension(properties =
|
name = "path",
|
||||||
{ @ExtensionProperty(name = "apiErrors", value = "[\"BLOCK_NO_EXISTS\"]", parseValue = true), }) },
|
value = "GET byheight:height"
|
||||||
responses =
|
), @ExtensionProperty(
|
||||||
{ @ApiResponse(
|
name = "description.key",
|
||||||
description = "blocks",
|
value = "operation:description"
|
||||||
content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
|
|
||||||
extensions =
|
|
||||||
{ @Extension(name = "translation", properties = { @ExtensionProperty(name = "description.key", value = "success_response:description") }) }
|
|
||||||
) }
|
|
||||||
)
|
)
|
||||||
public List<BlockWithTransactions> getBlockRange(@PathParam("height") int height, @Parameter(ref = "count") @QueryParam("count") int count) {
|
}
|
||||||
|
), @Extension(
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "apiErrors",
|
||||||
|
value = "[\"BLOCK_NO_EXISTS\"]",
|
||||||
|
parseValue = true
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "blocks",
|
||||||
|
content = @Content(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = BlockWithTransactions.class
|
||||||
|
)
|
||||||
|
),
|
||||||
|
extensions = {
|
||||||
|
@Extension(
|
||||||
|
name = "translation",
|
||||||
|
properties = {
|
||||||
|
@ExtensionProperty(
|
||||||
|
name = "description.key",
|
||||||
|
value = "success_response:description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public List<BlockWithTransactions> getBlockRange(@PathParam("height") int height, @Parameter(
|
||||||
|
ref = "count"
|
||||||
|
) @QueryParam("count") int count) {
|
||||||
boolean includeTransactions = false;
|
boolean includeTransactions = false;
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
@ -469,13 +785,13 @@ public class BlocksResource {
|
|||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockWithTransactions packageBlockData(Repository repository, BlockData blockData, boolean includeTransactions) throws DataException {
|
private BlockWithTransactions packageBlockData(Repository repository, BlockData blockData, boolean includeTransactions) throws DataException {
|
||||||
if (blockData == null)
|
if (blockData == null)
|
||||||
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
throw ApiErrorFactory.getInstance().createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
List<TransactionData> transactions = null;
|
List<TransactionData> transactions = null;
|
||||||
if (includeTransactions) {
|
if (includeTransactions) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package api.models;
|
package api;
|
||||||
|
|
||||||
import org.eclipse.persistence.descriptors.ClassExtractor;
|
import org.eclipse.persistence.descriptors.ClassExtractor;
|
||||||
import org.eclipse.persistence.sessions.Record;
|
import org.eclipse.persistence.sessions.Record;
|
@ -1,6 +1,5 @@
|
|||||||
package api;
|
package api;
|
||||||
|
|
||||||
import globalization.Translator;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.extensions.Extension;
|
import io.swagger.v3.oas.annotations.extensions.Extension;
|
||||||
@ -13,7 +12,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import qora.transaction.Transaction.TransactionType;
|
import qora.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@ -45,16 +43,6 @@ public class TransactionsResource {
|
|||||||
@Context
|
@Context
|
||||||
HttpServletRequest request;
|
HttpServletRequest request;
|
||||||
|
|
||||||
private ApiErrorFactory apiErrorFactory;
|
|
||||||
|
|
||||||
public TransactionsResource() {
|
|
||||||
this(new ApiErrorFactory(Translator.getInstance()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransactionsResource(ApiErrorFactory apiErrorFactory) {
|
|
||||||
this.apiErrorFactory = apiErrorFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/signature/{signature}")
|
@Path("/signature/{signature}")
|
||||||
@Operation(
|
@Operation(
|
||||||
@ -77,25 +65,24 @@ public class TransactionsResource {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public TransactionData getTransactions(@PathParam("signature") String signature) {
|
public TransactionData getTransactions(@PathParam("signature") String signature58) {
|
||||||
// Decode signature
|
byte[] signature;
|
||||||
byte[] signatureBytes;
|
|
||||||
try {
|
try {
|
||||||
signatureBytes = Base64.getDecoder().decode(signature);
|
signature = Base58.decode(signature58);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
TransactionData transactionData = repository.getTransactionRepository().fromSignature(signatureBytes);
|
TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature);
|
||||||
if (transactionData == null)
|
if (transactionData == null)
|
||||||
throw this.apiErrorFactory.createError(ApiError.TRANSACTION_NO_EXISTS);
|
throw ApiErrorFactory.getInstance().createError(ApiError.TRANSACTION_NO_EXISTS);
|
||||||
|
|
||||||
return transactionData;
|
return transactionData;
|
||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,21 +114,20 @@ public class TransactionsResource {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public List<TransactionData> getBlockTransactions(@PathParam("signature") String signature, @Parameter(ref = "limit") @QueryParam("limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
|
public List<TransactionData> getBlockTransactions(@PathParam("signature") String signature58, @Parameter(ref = "limit") @QueryParam("limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
|
||||||
// decode signature
|
byte[] signature;
|
||||||
byte[] signatureBytes;
|
|
||||||
try {
|
try {
|
||||||
signatureBytes = Base58.decode(signature);
|
signature = Base58.decode(signature58);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_SIGNATURE, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
List<TransactionData> transactions = repository.getBlockRepository().getTransactionsFromSignature(signatureBytes);
|
List<TransactionData> transactions = repository.getBlockRepository().getTransactionsFromSignature(signature);
|
||||||
|
|
||||||
// check if block exists
|
// check if block exists
|
||||||
if(transactions == null)
|
if(transactions == null)
|
||||||
throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
|
throw ApiErrorFactory.getInstance().createError(ApiError.BLOCK_NO_EXISTS);
|
||||||
|
|
||||||
// Pagination would take effect here (or as part of the repository access)
|
// Pagination would take effect here (or as part of the repository access)
|
||||||
int fromIndex = Integer.min(offset, transactions.size());
|
int fromIndex = Integer.min(offset, transactions.size());
|
||||||
@ -152,7 +138,7 @@ public class TransactionsResource {
|
|||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +165,7 @@ public class TransactionsResource {
|
|||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,13 +197,13 @@ public class TransactionsResource {
|
|||||||
public List<TransactionData> searchTransactions(@QueryParam("startBlock") Integer startBlock, @QueryParam("blockLimit") Integer blockLimit,
|
public List<TransactionData> searchTransactions(@QueryParam("startBlock") Integer startBlock, @QueryParam("blockLimit") Integer blockLimit,
|
||||||
@QueryParam("txType") Integer txTypeNum, @QueryParam("address") String address, @Parameter(ref = "limit") @QueryParam("limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
|
@QueryParam("txType") Integer txTypeNum, @QueryParam("address") String address, @Parameter(ref = "limit") @QueryParam("limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
|
||||||
if ((txTypeNum == null || txTypeNum == 0) && (address == null || address.isEmpty()))
|
if ((txTypeNum == null || txTypeNum == 0) && (address == null || address.isEmpty()))
|
||||||
throw this.apiErrorFactory.createError(ApiError.INVALID_CRITERIA);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_CRITERIA);
|
||||||
|
|
||||||
TransactionType txType = null;
|
TransactionType txType = null;
|
||||||
if (txTypeNum != null) {
|
if (txTypeNum != null) {
|
||||||
txType = TransactionType.valueOf(txTypeNum);
|
txType = TransactionType.valueOf(txTypeNum);
|
||||||
if (txType == null)
|
if (txType == null)
|
||||||
throw this.apiErrorFactory.createError(ApiError.INVALID_CRITERIA);
|
throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_CRITERIA);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
@ -237,7 +223,7 @@ public class TransactionsResource {
|
|||||||
} catch (ApiException e) {
|
} catch (ApiException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
|
throw ApiErrorFactory.getInstance().createError(ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,24 +3,36 @@ package api;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import qora.crypto.Crypto;
|
||||||
import utils.Base58;
|
import utils.Base58;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
import com.google.common.primitives.Longs;
|
||||||
|
|
||||||
@Path("/utils")
|
@Path("/utils")
|
||||||
@Produces({MediaType.TEXT_PLAIN})
|
@Produces({
|
||||||
@Tag(name = "Utilities")
|
MediaType.TEXT_PLAIN
|
||||||
|
})
|
||||||
|
@Tag(
|
||||||
|
name = "Utilities"
|
||||||
|
)
|
||||||
public class UtilsResource {
|
public class UtilsResource {
|
||||||
|
|
||||||
@Context
|
@Context
|
||||||
@ -33,7 +45,11 @@ public class UtilsResource {
|
|||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "base58 data",
|
description = "base58 data",
|
||||||
content = @Content(schema = @Schema(implementation = String.class))
|
content = @Content(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = String.class
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -52,7 +68,11 @@ public class UtilsResource {
|
|||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "base64 data",
|
description = "base64 data",
|
||||||
content = @Content(schema = @Schema(implementation = String.class))
|
content = @Content(
|
||||||
|
schema = @Schema(
|
||||||
|
implementation = String.class
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -70,15 +90,103 @@ public class UtilsResource {
|
|||||||
summary = "Generate random 32-byte seed",
|
summary = "Generate random 32-byte seed",
|
||||||
responses = {
|
responses = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
description = "base64 data",
|
description = "base58 data",
|
||||||
content = @Content(schema = @Schema(implementation = String.class))
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
type = "string"
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public String seed() {
|
public String seed() {
|
||||||
byte[] seed = new byte[32];
|
byte[] seed = new byte[32];
|
||||||
new SecureRandom().nextBytes(seed);
|
new SecureRandom().nextBytes(seed);
|
||||||
return Base64.getEncoder().encodeToString(seed);
|
return Base58.encode(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/seedPhrase")
|
||||||
|
@Operation(
|
||||||
|
summary = "Generate random 12-word BIP39 seed phrase",
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "seed phrase",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
type = "string"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public String seedPhrase() {
|
||||||
|
/*
|
||||||
|
* BIP39 word lists have 2048 entries so can be represented by 11 bits.
|
||||||
|
* UUID (128bits) and another 4 bits gives 132 bits.
|
||||||
|
* 132 bits, divided by 11, gives 12 words.
|
||||||
|
*/
|
||||||
|
final int WORD_MASK = 2048 - 1;
|
||||||
|
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
System.out.println("UUID: " + uuid.toString());
|
||||||
|
|
||||||
|
byte[] uuidMSB = Longs.toByteArray(uuid.getMostSignificantBits());
|
||||||
|
byte[] uuidLSB = Longs.toByteArray(uuid.getLeastSignificantBits());
|
||||||
|
byte[] message = Bytes.concat(uuidMSB, uuidLSB);
|
||||||
|
|
||||||
|
// Use SHA256 to generate more bits
|
||||||
|
byte[] hash = Crypto.digest(message);
|
||||||
|
|
||||||
|
// Append last 4 bits from hash to end. (Actually 8 bits but we only use 4).
|
||||||
|
message = Bytes.concat(message, new byte[] {
|
||||||
|
hash[hash.length - 1]
|
||||||
|
});
|
||||||
|
|
||||||
|
BigInteger wordBits = new BigInteger(message);
|
||||||
|
|
||||||
|
String[] phraseWords = new String[12];
|
||||||
|
for (int i = phraseWords.length; i >= 0; --i) {
|
||||||
|
int wordListIndex = wordBits.intValue() & WORD_MASK;
|
||||||
|
wordBits = wordBits.shiftRight(11);
|
||||||
|
// phraseWords[i] = wordList.get(wordListIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.join(" ", phraseWords);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/privateKey")
|
||||||
|
@Operation(
|
||||||
|
summary = "Calculate private key from 12-word BIP39 seed phrase",
|
||||||
|
description = "Returns the base58-encoded private key, or \"false\" if phrase is invalid.",
|
||||||
|
requestBody = @RequestBody(
|
||||||
|
required = true,
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
type = "string"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "the private key",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
type = "string"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public String getPublicKey(String seedPhrase) {
|
||||||
|
// TODO: convert BIP39 seed phrase to private key
|
||||||
|
return seedPhrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,14 @@ package data.block;
|
|||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
|
import qora.crypto.Crypto;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
|
||||||
// All properties to be converted to JSON via JAX-RS
|
// All properties to be converted to JSON via JAX-RS
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
@ -14,6 +17,8 @@ public class BlockData implements Serializable {
|
|||||||
|
|
||||||
private static final long serialVersionUID = -7678329659124664620L;
|
private static final long serialVersionUID = -7678329659124664620L;
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
|
||||||
private byte[] signature;
|
private byte[] signature;
|
||||||
private int version;
|
private int version;
|
||||||
private byte[] reference;
|
private byte[] reference;
|
||||||
@ -28,6 +33,8 @@ public class BlockData implements Serializable {
|
|||||||
private int atCount;
|
private int atCount;
|
||||||
private BigDecimal atFees;
|
private BigDecimal atFees;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
// necessary for JAX-RS serialization
|
// necessary for JAX-RS serialization
|
||||||
protected BlockData() {
|
protected BlockData() {
|
||||||
}
|
}
|
||||||
@ -53,6 +60,8 @@ public class BlockData implements Serializable {
|
|||||||
this.signature = null;
|
this.signature = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Getters/setters
|
||||||
|
|
||||||
public byte[] getSignature() {
|
public byte[] getSignature() {
|
||||||
return this.signature;
|
return this.signature;
|
||||||
}
|
}
|
||||||
@ -141,4 +150,11 @@ public class BlockData implements Serializable {
|
|||||||
this.atFees = atFees;
|
this.atFees = atFees;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JAXB special
|
||||||
|
|
||||||
|
@XmlElement(name = "generatorAddress")
|
||||||
|
protected String getGeneratorAddress() {
|
||||||
|
return Crypto.toAddress(this.generatorPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
6
src/data/package-info.java
Normal file
6
src/data/package-info.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// This file (data/package-info.java) is used as a template!
|
||||||
|
|
||||||
|
@XmlJavaTypeAdapter(type = byte[].class, value = api.Base58TypeAdapter.class)
|
||||||
|
package data;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
@ -6,11 +6,14 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||||
|
import javax.xml.bind.annotation.XmlTransient;
|
||||||
|
|
||||||
import org.eclipse.persistence.oxm.annotations.XmlClassExtractor;
|
import org.eclipse.persistence.oxm.annotations.XmlClassExtractor;
|
||||||
|
|
||||||
import api.models.TransactionClassExtractor;
|
import api.TransactionClassExtractor;
|
||||||
|
import qora.crypto.Crypto;
|
||||||
import qora.transaction.Transaction.TransactionType;
|
import qora.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -32,6 +35,7 @@ public abstract class TransactionData {
|
|||||||
|
|
||||||
// Properties shared with all transaction types
|
// Properties shared with all transaction types
|
||||||
protected TransactionType type;
|
protected TransactionType type;
|
||||||
|
@XmlTransient // represented in transaction-specific properties
|
||||||
protected byte[] creatorPublicKey;
|
protected byte[] creatorPublicKey;
|
||||||
protected long timestamp;
|
protected long timestamp;
|
||||||
protected byte[] reference;
|
protected byte[] reference;
|
||||||
@ -87,6 +91,13 @@ public abstract class TransactionData {
|
|||||||
this.signature = signature;
|
this.signature = signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JAXB special
|
||||||
|
|
||||||
|
@XmlElement(name = "creatorAddress")
|
||||||
|
protected String getCreatorAddress() {
|
||||||
|
return Crypto.toAddress(this.creatorPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
// Comparison
|
// Comparison
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user