mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-23 04:36:50 +00:00
Refactoring, new translations, cleaning up warnings.
Refactored to standard Maven layout: src/main/java src/main/resources src/test/java etc. New translation code that uses locale-specific ResourceBundles to load translations on demand. Reworked API error/exceptions code to a shorter, simpler @ApiErrors annotation. Processing of @ApiErrors annotations produces an example for each possible API error and includes API error string in HTTP response code, e.g. 400 INVALID_SIGNATURE Missing API error cases added to each API call. Translation of openAPI.json removed (for now). block-explorer.html and BIP39 wordlists now read as resources instead of direct from disk. Java compile warnings fixed. Some runtime warnings remain: WARNING: A provider api.resource.ApiDefinition registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. WARNING: A provider api.resource.AnnotationPostProcessor registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. WARN org.reflections.Reflections - given scan urls are empty. set urls in the configuration
This commit is contained in:
140
src/main/java/api/ApiError.java
Normal file
140
src/main/java/api/ApiError.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package api;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public enum ApiError {
|
||||
// COMMON
|
||||
UNKNOWN(0, 500),
|
||||
JSON(1, 400),
|
||||
NO_BALANCE(2, 422),
|
||||
NOT_YET_RELEASED(3, 422),
|
||||
UNAUTHORIZED(4, 403),
|
||||
REPOSITORY_ISSUE(5, 500),
|
||||
|
||||
// VALIDATION
|
||||
INVALID_SIGNATURE(101, 400),
|
||||
INVALID_ADDRESS(102, 400),
|
||||
INVALID_SEED(103, 400),
|
||||
INVALID_AMOUNT(104, 400),
|
||||
INVALID_FEE(105, 400),
|
||||
INVALID_SENDER(106, 400),
|
||||
INVALID_RECIPIENT(107, 400),
|
||||
INVALID_NAME_LENGTH(108, 400),
|
||||
INVALID_VALUE_LENGTH(109, 400),
|
||||
INVALID_NAME_OWNER(110, 400),
|
||||
INVALID_BUYER(111, 400),
|
||||
INVALID_PUBLIC_KEY(112, 400),
|
||||
INVALID_OPTIONS_LENGTH(113, 400),
|
||||
INVALID_OPTION_LENGTH(114, 400),
|
||||
INVALID_DATA(115, 400),
|
||||
INVALID_DATA_LENGTH(116, 400),
|
||||
INVALID_UPDATE_VALUE(117, 400),
|
||||
KEY_ALREADY_EXISTS(118, 422),
|
||||
KEY_NOT_EXISTS(119, 404),
|
||||
LAST_KEY_IS_DEFAULT_KEY_ERROR(120, 422),
|
||||
FEE_LESS_REQUIRED(121, 422),
|
||||
WALLET_NOT_IN_SYNC(122, 422),
|
||||
INVALID_NETWORK_ADDRESS(123, 404),
|
||||
ADDRESS_NO_EXISTS(124, 404),
|
||||
INVALID_CRITERIA(125, 400),
|
||||
INVALID_REFERENCE(126, 400),
|
||||
TRANSFORMATION_ERROR(127, 400),
|
||||
|
||||
// WALLET
|
||||
WALLET_NO_EXISTS(201, 404),
|
||||
WALLET_ADDRESS_NO_EXISTS(202, 404),
|
||||
WALLET_LOCKED(203, 422),
|
||||
WALLET_ALREADY_EXISTS(204, 422),
|
||||
WALLET_API_CALL_FORBIDDEN_BY_USER(205, 403),
|
||||
|
||||
// BLOCKS
|
||||
BLOCK_NO_EXISTS(301, 404),
|
||||
|
||||
// TRANSACTIONS
|
||||
TRANSACTION_NO_EXISTS(311, 404),
|
||||
PUBLIC_KEY_NOT_FOUND(304, 404),
|
||||
TRANSACTION_INVALID(312, 400),
|
||||
|
||||
// NAMING
|
||||
NAME_NO_EXISTS(401, 404),
|
||||
NAME_ALREADY_EXISTS(402, 422),
|
||||
NAME_ALREADY_FOR_SALE(403, 422),
|
||||
NAME_NOT_LOWER_CASE(404, 422),
|
||||
NAME_SALE_NO_EXISTS(410, 404),
|
||||
BUYER_ALREADY_OWNER(411, 422),
|
||||
|
||||
// POLLS
|
||||
POLL_NO_EXISTS(501, 404),
|
||||
POLL_ALREADY_EXISTS(502, 422),
|
||||
DUPLICATE_OPTION(503, 422),
|
||||
POLL_OPTION_NO_EXISTS(504, 404),
|
||||
ALREADY_VOTED_FOR_THAT_OPTION(505, 422),
|
||||
|
||||
// ASSET
|
||||
INVALID_ASSET_ID(601, 400),
|
||||
INVALID_ORDER_ID(602, 400),
|
||||
ORDER_NO_EXISTS(603, 404),
|
||||
|
||||
// NAME PAYMENTS
|
||||
NAME_NOT_REGISTERED(701, 422),
|
||||
NAME_FOR_SALE(702, 422),
|
||||
NAME_WITH_SPACE(703, 422),
|
||||
|
||||
// ATs
|
||||
INVALID_DESC_LENGTH(801, 400),
|
||||
EMPTY_CODE(802, 400),
|
||||
DATA_SIZE(803, 400),
|
||||
NULL_PAGES(804, 400),
|
||||
INVALID_TYPE_LENGTH(805, 400),
|
||||
INVALID_TAGS_LENGTH(806, 400),
|
||||
INVALID_CREATION_BYTES(809, 400),
|
||||
|
||||
// BLOG/Namestorage
|
||||
BODY_EMPTY(901, 400),
|
||||
BLOG_DISABLED(902, 403),
|
||||
NAME_NOT_OWNER(903, 422),
|
||||
TX_AMOUNT(904, 400),
|
||||
BLOG_ENTRY_NO_EXISTS(905, 404),
|
||||
BLOG_EMPTY(906, 404),
|
||||
POSTID_EMPTY(907, 400),
|
||||
POST_NOT_EXISTING(908, 404),
|
||||
COMMENTING_DISABLED(909, 403),
|
||||
COMMENT_NOT_EXISTING(910, 404),
|
||||
INVALID_COMMENT_OWNER(911, 422),
|
||||
|
||||
// Messages
|
||||
MESSAGE_FORMAT_NOT_HEX(1001, 400),
|
||||
MESSAGE_BLANK(1002, 400),
|
||||
NO_PUBLIC_KEY(1003, 422),
|
||||
MESSAGESIZE_EXCEEDED(1004, 400);
|
||||
|
||||
private final static Map<Integer, ApiError> map = stream(ApiError.values()).collect(toMap(apiError -> apiError.code, apiError -> apiError));
|
||||
|
||||
private final int code; // API error code
|
||||
private final int status; // HTTP status code
|
||||
|
||||
private ApiError(int code) {
|
||||
this(code, 400); // defaults to "400 - BAD REQUEST"
|
||||
}
|
||||
|
||||
private ApiError(int code, int status) {
|
||||
this.code = code;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public static ApiError fromCode(int code) {
|
||||
return map.get(code);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
}
|
22
src/main/java/api/ApiErrorMessage.java
Normal file
22
src/main/java/api/ApiErrorMessage.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package api;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class ApiErrorMessage {
|
||||
|
||||
protected int error;
|
||||
|
||||
protected String message;
|
||||
|
||||
protected ApiErrorMessage() {
|
||||
}
|
||||
|
||||
public ApiErrorMessage(int errorCode, String message) {
|
||||
this.error = errorCode;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
18
src/main/java/api/ApiErrors.java
Normal file
18
src/main/java/api/ApiErrors.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package api;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation lists potential ApiErrors that may be returned, or thrown, during the execution of this method.
|
||||
* <p>
|
||||
* Value is expected to be an array of ApiError enum instances.
|
||||
*
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ApiErrors {
|
||||
ApiError[] value() default {};
|
||||
}
|
38
src/main/java/api/ApiException.java
Normal file
38
src/main/java/api/ApiException.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package api;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
public class ApiException extends WebApplicationException {
|
||||
|
||||
private static final long serialVersionUID = 4619299036312089050L;
|
||||
|
||||
// HTTP status code
|
||||
public int status;
|
||||
|
||||
// API error code
|
||||
public int error;
|
||||
|
||||
public String message;
|
||||
|
||||
public ApiException(int status, int error, String message) {
|
||||
this(status, error, message, null);
|
||||
}
|
||||
|
||||
public ApiException(int status, int error, String message, Throwable throwable) {
|
||||
super(
|
||||
message,
|
||||
throwable,
|
||||
Response.status(Status.fromStatusCode(status))
|
||||
.entity(new ApiErrorMessage(error, message))
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.build()
|
||||
);
|
||||
|
||||
this.status = status;
|
||||
this.error = error;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
20
src/main/java/api/ApiExceptionFactory.java
Normal file
20
src/main/java/api/ApiExceptionFactory.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package api;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import globalization.Translator;
|
||||
|
||||
public enum ApiExceptionFactory {
|
||||
INSTANCE;
|
||||
|
||||
public ApiException createException(HttpServletRequest request, ApiError apiError, Throwable throwable, Object... args) {
|
||||
String template = Translator.INSTANCE.translate("ApiError", request.getLocale().getLanguage(), apiError.name());
|
||||
String message = String.format(template, args);
|
||||
return new ApiException(apiError.getStatus(), apiError.getCode(), message, throwable);
|
||||
}
|
||||
|
||||
public ApiException createException(HttpServletRequest request, ApiError apiError) {
|
||||
return createException(request, apiError, null);
|
||||
}
|
||||
|
||||
}
|
109
src/main/java/api/ApiService.java
Normal file
109
src/main/java/api/ApiService.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package api;
|
||||
|
||||
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
|
||||
|
||||
import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
|
||||
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.InetAccessHandler;
|
||||
import org.eclipse.jetty.servlet.DefaultServlet;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
import org.glassfish.jersey.servlet.ServletContainer;
|
||||
|
||||
import api.resource.AnnotationPostProcessor;
|
||||
import api.resource.ApiDefinition;
|
||||
import settings.Settings;
|
||||
|
||||
public class ApiService {
|
||||
|
||||
private final Server server;
|
||||
private final ResourceConfig config;
|
||||
|
||||
public ApiService() {
|
||||
config = new ResourceConfig();
|
||||
config.packages("api.resource");
|
||||
config.register(OpenApiResource.class);
|
||||
config.register(ApiDefinition.class);
|
||||
config.register(AnnotationPostProcessor.class);
|
||||
|
||||
// Create RPC server
|
||||
this.server = new Server(Settings.getInstance().getRpcPort());
|
||||
|
||||
// IP address based access control
|
||||
InetAccessHandler accessHandler = new InetAccessHandler();
|
||||
for (String pattern : Settings.getInstance().getRpcAllowed()) {
|
||||
accessHandler.include(pattern);
|
||||
}
|
||||
this.server.setHandler(accessHandler);
|
||||
|
||||
// URL rewriting
|
||||
RewriteHandler rewriteHandler = new RewriteHandler();
|
||||
accessHandler.setHandler(rewriteHandler);
|
||||
|
||||
// Context
|
||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
||||
context.setContextPath("/");
|
||||
rewriteHandler.setHandler(context);
|
||||
|
||||
FilterHolder filterHolder = new FilterHolder(CrossOriginFilter.class);
|
||||
filterHolder.setInitParameter("allowedOrigins", "*");
|
||||
filterHolder.setInitParameter("allowedMethods", "GET, POST");
|
||||
context.addFilter(filterHolder, "/*", null);
|
||||
|
||||
// API servlet
|
||||
ServletContainer container = new ServletContainer(config);
|
||||
ServletHolder apiServlet = new ServletHolder(container);
|
||||
apiServlet.setInitOrder(1);
|
||||
context.addServlet(apiServlet, "/*");
|
||||
|
||||
// Swagger-UI static content
|
||||
ClassLoader loader = this.getClass().getClassLoader();
|
||||
ServletHolder swaggerUIServlet = new ServletHolder("static-swagger-ui", DefaultServlet.class);
|
||||
swaggerUIServlet.setInitParameter("resourceBase", loader.getResource("resources/swagger-ui/").toString());
|
||||
swaggerUIServlet.setInitParameter("dirAllowed", "true");
|
||||
swaggerUIServlet.setInitParameter("pathInfoOnly", "true");
|
||||
context.addServlet(swaggerUIServlet, "/api-documentation/*");
|
||||
|
||||
rewriteHandler.addRule(new RedirectPatternRule("/api-documentation", "/api-documentation/index.html")); // redirect to swagger ui start page
|
||||
}
|
||||
|
||||
// XXX: replace singleton pattern by dependency injection?
|
||||
private static ApiService instance;
|
||||
|
||||
public static ApiService getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ApiService();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Iterable<Class<?>> getResources() {
|
||||
// return resources;
|
||||
return config.getClasses();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
// Start server
|
||||
server.start();
|
||||
} catch (Exception e) {
|
||||
// Failed to start
|
||||
throw new RuntimeException("Failed to start API", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
try {
|
||||
// Stop server
|
||||
server.stop();
|
||||
} catch (Exception e) {
|
||||
// Failed to stop
|
||||
}
|
||||
}
|
||||
}
|
25
src/main/java/api/Base58TypeAdapter.java
Normal file
25
src/main/java/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[] output) throws Exception {
|
||||
if (output == null)
|
||||
return null;
|
||||
|
||||
return Base58.encode(output);
|
||||
}
|
||||
|
||||
}
|
25
src/main/java/api/BigDecimalTypeAdapter.java
Normal file
25
src/main/java/api/BigDecimalTypeAdapter.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package api;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
public class BigDecimalTypeAdapter extends XmlAdapter<String, BigDecimal> {
|
||||
|
||||
@Override
|
||||
public BigDecimal unmarshal(String input) throws Exception {
|
||||
if (input == null)
|
||||
return null;
|
||||
|
||||
return new BigDecimal(input).setScale(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String marshal(BigDecimal output) throws Exception {
|
||||
if (output == null)
|
||||
return null;
|
||||
|
||||
return output.toPlainString();
|
||||
}
|
||||
|
||||
}
|
76
src/main/java/api/Constants.java
Normal file
76
src/main/java/api/Constants.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package api;
|
||||
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.PathItem;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||
import static java.util.Arrays.asList;
|
||||
import java.util.List;
|
||||
|
||||
class Constants {
|
||||
public static final String APIERROR_CONTEXT_PATH = "/Api";
|
||||
public static final String APIERROR_KEY = "ApiError/%s";
|
||||
|
||||
public static final String TRANSLATION_EXTENSION_NAME = "translation";
|
||||
public static final String TRANSLATION_PATH_EXTENSION_NAME = "path";
|
||||
|
||||
public static final String TRANSLATION_ANNOTATION_DESCRIPTION_KEY = "description.key";
|
||||
public static final String TRANSLATION_ANNOTATION_SUMMARY_KEY = "summary.key";
|
||||
public static final String TRANSLATION_ANNOTATION_TITLE_KEY = "title.key";
|
||||
public static final String TRANSLATION_ANNOTATION_TERMS_OF_SERVICE_KEY = "termsOfService.key";
|
||||
|
||||
public static final String API_ERRORS_EXTENSION_NAME = "apiErrors";
|
||||
public static final String API_ERROR_CODE_EXTENSION_NAME = "apiErrorCode";
|
||||
|
||||
public static final List<TranslatableProperty<Info>> TRANSLATABLE_INFO_PROPERTIES = asList(
|
||||
new TranslatableProperty<Info>() {
|
||||
@Override public String keyName() { return TRANSLATION_ANNOTATION_DESCRIPTION_KEY; }
|
||||
@Override public void setValue(Info item, String translation) { item.setDescription(translation); }
|
||||
@Override public String getValue(Info item) { return item.getDescription(); }
|
||||
},
|
||||
new TranslatableProperty<Info>() {
|
||||
@Override public String keyName() { return TRANSLATION_ANNOTATION_TITLE_KEY; }
|
||||
@Override public void setValue(Info item, String translation) { item.setTitle(translation); }
|
||||
@Override public String getValue(Info item) { return item.getTitle(); }
|
||||
},
|
||||
new TranslatableProperty<Info>() {
|
||||
@Override public String keyName() { return TRANSLATION_ANNOTATION_TERMS_OF_SERVICE_KEY; }
|
||||
@Override public void setValue(Info item, String translation) { item.setTermsOfService(translation); }
|
||||
@Override public String getValue(Info item) { return item.getTermsOfService(); }
|
||||
}
|
||||
);
|
||||
|
||||
public static final List<TranslatableProperty<PathItem>> TRANSLATABLE_PATH_ITEM_PROPERTIES = asList(
|
||||
new TranslatableProperty<PathItem>() {
|
||||
@Override public String keyName() { return TRANSLATION_ANNOTATION_DESCRIPTION_KEY; }
|
||||
@Override public void setValue(PathItem item, String translation) { item.setDescription(translation); }
|
||||
@Override public String getValue(PathItem item) { return item.getDescription(); }
|
||||
},
|
||||
new TranslatableProperty<PathItem>() {
|
||||
@Override public String keyName() { return TRANSLATION_ANNOTATION_SUMMARY_KEY; }
|
||||
@Override public void setValue(PathItem item, String translation) { item.setSummary(translation); }
|
||||
@Override public String getValue(PathItem item) { return item.getSummary(); }
|
||||
}
|
||||
);
|
||||
|
||||
public static final List<TranslatableProperty<Operation>> TRANSLATABLE_OPERATION_PROPERTIES = asList(
|
||||
new TranslatableProperty<Operation>() {
|
||||
@Override public String keyName() { return TRANSLATION_ANNOTATION_DESCRIPTION_KEY; }
|
||||
@Override public void setValue(Operation item, String translation) { item.setDescription(translation); }
|
||||
@Override public String getValue(Operation item) { return item.getDescription(); }
|
||||
},
|
||||
new TranslatableProperty<Operation>() {
|
||||
@Override public String keyName() { return TRANSLATION_ANNOTATION_SUMMARY_KEY; }
|
||||
@Override public void setValue(Operation item, String translation) { item.setSummary(translation); }
|
||||
@Override public String getValue(Operation item) { return item.getSummary(); }
|
||||
}
|
||||
);
|
||||
|
||||
public static final List<TranslatableProperty<ApiResponse>> TRANSLATABLE_API_RESPONSE_PROPERTIES = asList(
|
||||
new TranslatableProperty<ApiResponse>() {
|
||||
@Override public String keyName() { return TRANSLATION_ANNOTATION_DESCRIPTION_KEY; }
|
||||
@Override public void setValue(ApiResponse item, String translation) { item.setDescription(translation); }
|
||||
@Override public String getValue(ApiResponse item) { return item.getDescription(); }
|
||||
}
|
||||
);
|
||||
}
|
22
src/main/java/api/Security.java
Normal file
22
src/main/java/api/Security.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package api;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public class Security {
|
||||
|
||||
// TODO: replace with proper authentication
|
||||
public static void checkApiCallAllowed(HttpServletRequest request) {
|
||||
InetAddress remoteAddr;
|
||||
try {
|
||||
remoteAddr = InetAddress.getByName(request.getRemoteAddr());
|
||||
} catch (UnknownHostException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if (!remoteAddr.isLoopbackAddress())
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.UNAUTHORIZED);
|
||||
}
|
||||
}
|
16
src/main/java/api/TransactionClassExtractor.java
Normal file
16
src/main/java/api/TransactionClassExtractor.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package api;
|
||||
|
||||
import org.eclipse.persistence.descriptors.ClassExtractor;
|
||||
import org.eclipse.persistence.sessions.Record;
|
||||
import org.eclipse.persistence.sessions.Session;
|
||||
|
||||
public class TransactionClassExtractor extends ClassExtractor {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public Class extractClassFromRow(Record record, Session session) {
|
||||
// Never called anyway?
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
7
src/main/java/api/TranslatableProperty.java
Normal file
7
src/main/java/api/TranslatableProperty.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package api;
|
||||
|
||||
interface TranslatableProperty<T> {
|
||||
public String keyName();
|
||||
public void setValue(T item, String translation);
|
||||
public String getValue(T item);
|
||||
}
|
33
src/main/java/api/models/AssetWithHolders.java
Normal file
33
src/main/java/api/models/AssetWithHolders.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package api.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
|
||||
import data.account.AccountBalanceData;
|
||||
import data.assets.AssetData;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "Asset info, maybe including asset holders")
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class AssetWithHolders {
|
||||
|
||||
@Schema(implementation = AssetData.class, name = "asset", title = "asset data")
|
||||
@XmlElement(name = "asset")
|
||||
public AssetData assetData;
|
||||
|
||||
public List<AccountBalanceData> holders;
|
||||
|
||||
// For JAX-RS
|
||||
protected AssetWithHolders() {
|
||||
}
|
||||
|
||||
public AssetWithHolders(AssetData assetData, List<AccountBalanceData> holders) {
|
||||
this.assetData = assetData;
|
||||
this.holders = holders;
|
||||
}
|
||||
|
||||
}
|
33
src/main/java/api/models/BlockWithTransactions.java
Normal file
33
src/main/java/api/models/BlockWithTransactions.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package api.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
|
||||
import data.block.BlockData;
|
||||
import data.transaction.TransactionData;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "Block info, maybe including transactions")
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class BlockWithTransactions {
|
||||
|
||||
@Schema(implementation = BlockData.class, name = "block", title = "block data")
|
||||
@XmlElement(name = "block")
|
||||
public BlockData blockData;
|
||||
|
||||
public List<TransactionData> transactions;
|
||||
|
||||
// For JAX-RS
|
||||
protected BlockWithTransactions() {
|
||||
}
|
||||
|
||||
public BlockWithTransactions(BlockData blockData, List<TransactionData> transactions) {
|
||||
this.blockData = blockData;
|
||||
this.transactions = transactions;
|
||||
}
|
||||
|
||||
}
|
33
src/main/java/api/models/OrderWithTrades.java
Normal file
33
src/main/java/api/models/OrderWithTrades.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package api.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
|
||||
import data.assets.OrderData;
|
||||
import data.assets.TradeData;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "Asset order info, maybe including trades")
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class OrderWithTrades {
|
||||
|
||||
@Schema(implementation = OrderData.class, name = "order", title = "order data")
|
||||
@XmlElement(name = "order")
|
||||
public OrderData orderData;
|
||||
|
||||
List<TradeData> trades;
|
||||
|
||||
// For JAX-RS
|
||||
protected OrderWithTrades() {
|
||||
}
|
||||
|
||||
public OrderWithTrades(OrderData orderData, List<TradeData> trades) {
|
||||
this.orderData = orderData;
|
||||
this.trades = trades;
|
||||
}
|
||||
|
||||
}
|
23
src/main/java/api/models/SimpleTransactionSignRequest.java
Normal file
23
src/main/java/api/models/SimpleTransactionSignRequest.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package api.models;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class SimpleTransactionSignRequest {
|
||||
|
||||
@Schema(
|
||||
description = "signer's private key",
|
||||
example = "A9MNsATgQgruBUjxy2rjWY36Yf19uRioKZbiLFT2P7c6"
|
||||
)
|
||||
public byte[] privateKey;
|
||||
|
||||
@Schema(
|
||||
description = "raw, unsigned transaction bytes",
|
||||
example = "base58"
|
||||
)
|
||||
public byte[] transactionBytes;
|
||||
|
||||
}
|
38
src/main/java/api/models/TradeWithOrderInfo.java
Normal file
38
src/main/java/api/models/TradeWithOrderInfo.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package api.models;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
|
||||
import data.assets.OrderData;
|
||||
import data.assets.TradeData;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "Asset trade, including order info")
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class TradeWithOrderInfo {
|
||||
|
||||
@Schema(implementation = TradeData.class, name = "trade", title = "trade data")
|
||||
@XmlElement(name = "trade")
|
||||
public TradeData tradeData;
|
||||
|
||||
@Schema(implementation = OrderData.class, name = "order", title = "order data")
|
||||
@XmlElement(name = "initiatingOrder")
|
||||
public OrderData initiatingOrderData;
|
||||
|
||||
@Schema(implementation = OrderData.class, name = "order", title = "order data")
|
||||
@XmlElement(name = "targetOrder")
|
||||
public OrderData targetOrderData;
|
||||
|
||||
// For JAX-RS
|
||||
protected TradeWithOrderInfo() {
|
||||
}
|
||||
|
||||
public TradeWithOrderInfo(TradeData tradeData, OrderData initiatingOrderData, OrderData targetOrderData) {
|
||||
this.tradeData = tradeData;
|
||||
this.initiatingOrderData = initiatingOrderData;
|
||||
this.targetOrderData = targetOrderData;
|
||||
}
|
||||
|
||||
}
|
319
src/main/java/api/resource/AddressesResource.java
Normal file
319
src/main/java/api/resource/AddressesResource.java
Normal file
@@ -0,0 +1,319 @@
|
||||
package api.resource;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import api.ApiError;
|
||||
import api.ApiErrors;
|
||||
import api.ApiException;
|
||||
import api.ApiExceptionFactory;
|
||||
import data.account.AccountBalanceData;
|
||||
import data.account.AccountData;
|
||||
import qora.account.Account;
|
||||
import qora.assets.Asset;
|
||||
import qora.crypto.Crypto;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import repository.RepositoryManager;
|
||||
import transform.Transformer;
|
||||
import utils.Base58;
|
||||
|
||||
@Path("/addresses")
|
||||
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
||||
@Tag(name = "Addresses")
|
||||
public class AddressesResource {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/lastreference/{address}")
|
||||
@Operation(
|
||||
summary = "Fetch reference for next transaction to be created by address",
|
||||
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.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the base58-encoded transaction signature or \"false\"",
|
||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string"))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public String getLastReference(@Parameter(ref = "address") @PathParam("address") String address) {
|
||||
if (!Crypto.isValidAddress(address))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||
|
||||
byte[] lastReference = null;
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Account account = new Account(repository, address);
|
||||
lastReference = account.getLastReference();
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
|
||||
if(lastReference == null || lastReference.length == 0) {
|
||||
return "false";
|
||||
} else {
|
||||
return Base58.encode(lastReference);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/lastreference/{address}/unconfirmed")
|
||||
@Operation(
|
||||
summary = "Fetch reference for next transaction to be created by address, considering unconfirmed 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.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the base58-encoded transaction signature",
|
||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string"))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public String getLastReferenceUnconfirmed(@PathParam("address") String address) {
|
||||
if (!Crypto.isValidAddress(address))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||
|
||||
byte[] lastReference = null;
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Account account = new Account(repository, address);
|
||||
lastReference = account.getUnconfirmedLastReference();
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
|
||||
if(lastReference == null || lastReference.length == 0) {
|
||||
return "false";
|
||||
} else {
|
||||
return Base58.encode(lastReference);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/validate/{address}")
|
||||
@Operation(
|
||||
summary = "Validates the given address",
|
||||
description = "Returns true/false.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "boolean"))
|
||||
)
|
||||
}
|
||||
)
|
||||
public boolean validate(@PathParam("address") String address) {
|
||||
return Crypto.isValidAddress(address);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/generatingbalance/{address}")
|
||||
@Operation(
|
||||
summary = "Return the generating balance of the given address",
|
||||
description = "Returns the effective balance of the given address, used in Proof-of-Stake calculationgs when generating a new block.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the generating balance",
|
||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string", format = "number"))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public BigDecimal getGeneratingBalanceOfAddress(@PathParam("address") String address) {
|
||||
if (!Crypto.isValidAddress(address))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Account account = new Account(repository, address);
|
||||
return account.getGeneratingBalance();
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/balance/{address}")
|
||||
@Operation(
|
||||
summary = "Returns the confirmed balance of the given address",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the balance",
|
||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string", format = "number"))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public BigDecimal getGeneratingBalance(@PathParam("address") String address) {
|
||||
if (!Crypto.isValidAddress(address))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Account account = new Account(repository, address);
|
||||
return account.getConfirmedBalance(Asset.QORA);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/assetbalance/{assetid}/{address}")
|
||||
@Operation(
|
||||
summary = "Asset-specific balance request",
|
||||
description = "Returns the confirmed balance of the given address for the given asset key.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the balance",
|
||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string", format = "number"))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public BigDecimal getAssetBalance(@PathParam("assetid") long assetid, @PathParam("address") String address) {
|
||||
if (!Crypto.isValidAddress(address))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Account account = new Account(repository, address);
|
||||
return account.getConfirmedBalance(assetid);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/assets/{address}")
|
||||
@Operation(
|
||||
summary = "All assets owned by this address",
|
||||
description = "Returns the list of assets for this address, with balances.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the list of assets",
|
||||
content = @Content(array = @ArraySchema(schema = @Schema(implementation = AccountBalanceData.class)))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public List<AccountBalanceData> getAssets(@PathParam("address") String address) {
|
||||
if (!Crypto.isValidAddress(address))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
return repository.getAccountRepository().getAllBalances(address);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/balance/{address}/{confirmations}")
|
||||
@Operation(
|
||||
summary = "Calculates the balance of the given address for the given confirmations",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the balance",
|
||||
content = @Content(schema = @Schema(type = "string", format = "number"))
|
||||
)
|
||||
}
|
||||
)
|
||||
public String getGeneratingBalance(@PathParam("address") String address, @PathParam("confirmations") int confirmations) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/publickey/{address}")
|
||||
@Operation(
|
||||
summary = "Get public key of address",
|
||||
description = "Returns the base58-encoded account public key of the given address, or \"false\" if address not known or has no public key.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the public key",
|
||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string"))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public String getPublicKey(@PathParam("address") String address) {
|
||||
if (!Crypto.isValidAddress(address))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
AccountData accountData = repository.getAccountRepository().getAccount(address);
|
||||
|
||||
if (accountData == null)
|
||||
return "false";
|
||||
|
||||
byte[] publicKey = accountData.getPublicKey();
|
||||
if (publicKey == null)
|
||||
return "false";
|
||||
|
||||
return Base58.encode(publicKey);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/convert/{publickey}")
|
||||
@Operation(
|
||||
summary = "Convert public key into address",
|
||||
description = "Returns account address based on supplied public key. Expects base58-encoded, 32-byte public key.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the address",
|
||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string"))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PUBLIC_KEY, ApiError.REPOSITORY_ISSUE})
|
||||
public String fromPublicKey(@PathParam("publickey") String publicKey58) {
|
||||
// Decode public key
|
||||
byte[] publicKey;
|
||||
try {
|
||||
publicKey = Base58.decode(publicKey58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PUBLIC_KEY, e);
|
||||
}
|
||||
|
||||
// Correct size for public key?
|
||||
if (publicKey.length != Transformer.PUBLIC_KEY_LENGTH)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PUBLIC_KEY);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
return Crypto.toAddress(publicKey);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
86
src/main/java/api/resource/AdminResource.java
Normal file
86
src/main/java/api/resource/AdminResource.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package api.resource;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import api.Security;
|
||||
import controller.Controller;
|
||||
|
||||
@Path("/admin")
|
||||
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
||||
@Tag(name = "Admin")
|
||||
public class AdminResource {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/unused")
|
||||
@Parameter(in = ParameterIn.PATH, name = "blockSignature", description = "Block signature", schema = @Schema(type = "string", format = "byte"), example = "ZZZZ==")
|
||||
@Parameter(in = ParameterIn.PATH, name = "assetId", description = "Asset ID, 0 is native coin", schema = @Schema(type = "string", format = "byte"))
|
||||
@Parameter(in = ParameterIn.PATH, name = "otherAssetId", description = "Asset ID, 0 is native coin", schema = @Schema(type = "string", format = "byte"))
|
||||
@Parameter(in = ParameterIn.PATH, name = "address", description = "an account address", example = "QRHDHASWAXarqTvB2X4SNtJCWbxGf68M2o")
|
||||
@Parameter(in = ParameterIn.QUERY, name = "count", description = "Maximum number of entries to return, 0 means none", schema = @Schema(type = "integer", defaultValue = "20"))
|
||||
@Parameter(in = ParameterIn.QUERY, name = "limit", description = "Maximum number of entries to return, 0 means unlimited", schema = @Schema(type = "integer", defaultValue = "20"))
|
||||
@Parameter(in = ParameterIn.QUERY, name = "offset", description = "Starting entry in results, 0 is first entry", schema = @Schema(type = "integer"))
|
||||
@Parameter(in = ParameterIn.QUERY, name = "includeTransactions", description = "Include associated transactions in results", schema = @Schema(type = "boolean"))
|
||||
@Parameter(in = ParameterIn.QUERY, name = "includeHolders", description = "Include asset holders in results", schema = @Schema(type = "boolean"))
|
||||
@Parameter(in = ParameterIn.QUERY, name = "queryAssetId", description = "Asset ID, 0 is native coin", schema = @Schema(type = "string", format = "byte"))
|
||||
public String globalParameters() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/uptime")
|
||||
@Operation(
|
||||
summary = "Fetch running time of server",
|
||||
description = "Returns uptime in milliseconds",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "uptime in milliseconds",
|
||||
content = @Content(schema = @Schema(type = "number"))
|
||||
)
|
||||
}
|
||||
)
|
||||
public long uptime() {
|
||||
return System.currentTimeMillis() - Controller.startTime;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stop")
|
||||
@Operation(
|
||||
summary = "Shutdown",
|
||||
description = "Shutdown",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "\"true\"",
|
||||
content = @Content(schema = @Schema(type = "string"))
|
||||
)
|
||||
}
|
||||
)
|
||||
public String shutdown() {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Controller.shutdown();
|
||||
}
|
||||
}).start();
|
||||
|
||||
return "true";
|
||||
}
|
||||
|
||||
}
|
114
src/main/java/api/resource/AnnotationPostProcessor.java
Normal file
114
src/main/java/api/resource/AnnotationPostProcessor.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package api.resource;
|
||||
|
||||
import globalization.Translator;
|
||||
import io.swagger.v3.core.converter.ModelConverters;
|
||||
import io.swagger.v3.jaxrs2.Reader;
|
||||
import io.swagger.v3.jaxrs2.ReaderListener;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.PathItem;
|
||||
import io.swagger.v3.oas.models.examples.Example;
|
||||
import io.swagger.v3.oas.models.media.Content;
|
||||
import io.swagger.v3.oas.models.media.MediaType;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
import io.swagger.v3.oas.models.parameters.Parameter;
|
||||
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import api.ApiError;
|
||||
import api.ApiErrorMessage;
|
||||
import api.ApiErrors;
|
||||
import api.ApiService;
|
||||
|
||||
public class AnnotationPostProcessor implements ReaderListener {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(AnnotationPostProcessor.class);
|
||||
|
||||
@Override
|
||||
public void beforeScan(Reader reader, OpenAPI openAPI) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterScan(Reader reader, OpenAPI openAPI) {
|
||||
// Populate Components section with reusable parameters, like "limit" and "offset"
|
||||
// We take the reusable parameters from AdminResource.globalParameters path "/admin/unused"
|
||||
Components components = openAPI.getComponents();
|
||||
|
||||
PathItem globalParametersPathItem = openAPI.getPaths().get("/admin/unused");
|
||||
|
||||
if (globalParametersPathItem != null) {
|
||||
for (Parameter parameter : globalParametersPathItem.getGet().getParameters())
|
||||
components.addParameters(parameter.getName(), parameter);
|
||||
|
||||
openAPI.getPaths().remove("/admin/unused");
|
||||
}
|
||||
|
||||
// Search all ApiService resources (classes) for @ApiErrors annotations
|
||||
// to generate corresponding openAPI operation responses.
|
||||
for (Class<?> clazz : ApiService.getInstance().getResources()) {
|
||||
Path classPath = clazz.getAnnotation(Path.class);
|
||||
if (classPath == null)
|
||||
continue;
|
||||
|
||||
String classPathString = classPath.value();
|
||||
if (classPathString.charAt(0) != '/')
|
||||
classPathString = "/" + classPathString;
|
||||
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
ApiErrors apiErrors = method.getAnnotation(ApiErrors.class);
|
||||
if (apiErrors == null)
|
||||
continue;
|
||||
|
||||
LOGGER.info("Found @ApiErrors annotation on " + clazz.getSimpleName() + "." + method.getName());
|
||||
PathItem pathItem = getPathItemFromMethod(openAPI, classPathString, method);
|
||||
|
||||
for (Operation operation : pathItem.readOperations())
|
||||
for (ApiError apiError : apiErrors.value())
|
||||
addApiErrorResponse(operation, apiError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PathItem getPathItemFromMethod(OpenAPI openAPI, String classPathString, Method method) {
|
||||
Path path = method.getAnnotation(Path.class);
|
||||
if (path == null)
|
||||
throw new RuntimeException("API method has no @Path annotation?");
|
||||
|
||||
String pathString = path.value();
|
||||
return openAPI.getPaths().get(classPathString + pathString);
|
||||
}
|
||||
|
||||
private void addApiErrorResponse(Operation operation, ApiError apiError) {
|
||||
String statusCode = Integer.toString(apiError.getStatus()) + " " + apiError.name();
|
||||
|
||||
// Create response for this HTTP response code if it doesn't already exist
|
||||
ApiResponse apiResponse = operation.getResponses().get(statusCode);
|
||||
if (apiResponse == null) {
|
||||
Schema<?> errorMessageSchema = ModelConverters.getInstance().readAllAsResolvedSchema(ApiErrorMessage.class).schema;
|
||||
MediaType mediaType = new MediaType().schema(errorMessageSchema);
|
||||
Content content = new Content().addMediaType(javax.ws.rs.core.MediaType.APPLICATION_JSON, mediaType);
|
||||
apiResponse = new ApiResponse().content(content);
|
||||
operation.getResponses().addApiResponse(statusCode, apiResponse);
|
||||
}
|
||||
|
||||
// Add this specific ApiError code as an example
|
||||
int apiErrorCode = apiError.getCode();
|
||||
String lang = Locale.getDefault().getLanguage();
|
||||
ApiErrorMessage apiErrorMessage = new ApiErrorMessage(apiErrorCode, Translator.INSTANCE.translate("ApiError", lang, apiError.name()));
|
||||
Example example = new Example().value(apiErrorMessage);
|
||||
|
||||
// XXX: addExamples(..) is not working in Swagger 2.0.4. This bug is referenced in https://github.com/swagger-api/swagger-ui/issues/2651
|
||||
// Replace the call to .setExample(..) by .addExamples(..) when the bug is fixed.
|
||||
apiResponse.getContent().get(javax.ws.rs.core.MediaType.APPLICATION_JSON).setExample(example);
|
||||
//apiResponse.getContent().get(javax.ws.rs.core.MediaType.APPLICATION_JSON).addExamples(Integer.toString(apiErrorCode), example);
|
||||
}
|
||||
|
||||
}
|
28
src/main/java/api/resource/ApiDefinition.java
Normal file
28
src/main/java/api/resource/ApiDefinition.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package api.resource;
|
||||
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||
import io.swagger.v3.oas.annotations.extensions.Extension;
|
||||
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
|
||||
import io.swagger.v3.oas.annotations.info.Info;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
@OpenAPIDefinition(
|
||||
info = @Info( title = "Qora API", description = "NOTE: byte-arrays are encoded in Base58" ),
|
||||
tags = {
|
||||
@Tag(name = "Addresses"),
|
||||
@Tag(name = "Admin"),
|
||||
@Tag(name = "Assets"),
|
||||
@Tag(name = "Blocks"),
|
||||
@Tag(name = "Names"),
|
||||
@Tag(name = "Payments"),
|
||||
@Tag(name = "Transactions"),
|
||||
@Tag(name = "Utilities")
|
||||
},
|
||||
extensions = {
|
||||
@Extension(name = "translation", properties = {
|
||||
@ExtensionProperty(name="title.key", value="info:title")
|
||||
})
|
||||
}
|
||||
)
|
||||
public class ApiDefinition {
|
||||
}
|
274
src/main/java/api/resource/AssetsResource.java
Normal file
274
src/main/java/api/resource/AssetsResource.java
Normal file
@@ -0,0 +1,274 @@
|
||||
package api.resource;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
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.tags.Tag;
|
||||
import qora.transaction.Transaction;
|
||||
import qora.transaction.Transaction.ValidationResult;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import repository.RepositoryManager;
|
||||
import transform.TransformationException;
|
||||
import transform.transaction.IssueAssetTransactionTransformer;
|
||||
import utils.Base58;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import api.ApiError;
|
||||
import api.ApiErrors;
|
||||
import api.ApiExceptionFactory;
|
||||
import api.models.AssetWithHolders;
|
||||
import api.models.OrderWithTrades;
|
||||
import api.models.TradeWithOrderInfo;
|
||||
import data.account.AccountBalanceData;
|
||||
import data.assets.AssetData;
|
||||
import data.assets.OrderData;
|
||||
import data.assets.TradeData;
|
||||
import data.transaction.IssueAssetTransactionData;
|
||||
|
||||
@Path("/assets")
|
||||
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
||||
@Tag(name = "Assets")
|
||||
public class AssetsResource {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/all")
|
||||
@Operation(
|
||||
summary = "List all known assets",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "asset info",
|
||||
content = @Content(array = @ArraySchema(schema = @Schema(implementation = AssetData.class)))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||
public List<AssetData> getAllAssets(@Parameter(ref = "limit") @QueryParam("limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
List<AssetData> assets = repository.getAssetRepository().getAllAssets();
|
||||
|
||||
// Pagination would take effect here (or as part of the repository access)
|
||||
int fromIndex = Integer.min(offset, assets.size());
|
||||
int toIndex = limit == 0 ? assets.size() : Integer.min(fromIndex + limit, assets.size());
|
||||
assets = assets.subList(fromIndex, toIndex);
|
||||
|
||||
return assets;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/info")
|
||||
@Operation(
|
||||
summary = "Info on specific asset",
|
||||
description = "Supply either assetId OR assetName. (If both supplied, assetId takes priority).",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "asset info",
|
||||
content = @Content(array = @ArraySchema(schema = @Schema(implementation = AssetWithHolders.class)))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_CRITERIA, ApiError.INVALID_ASSET_ID, ApiError.REPOSITORY_ISSUE})
|
||||
public AssetWithHolders getAssetInfo(@QueryParam("assetId") Integer assetId, @QueryParam("assetName") String assetName, @Parameter(ref = "includeHolders") @QueryParam("includeHolders") boolean includeHolders) {
|
||||
if (assetId == null && (assetName == null || assetName.isEmpty()))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
AssetData assetData = null;
|
||||
|
||||
if (assetId != null)
|
||||
assetData = repository.getAssetRepository().fromAssetId(assetId);
|
||||
else
|
||||
assetData = repository.getAssetRepository().fromAssetName(assetName);
|
||||
|
||||
if (assetData == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||
|
||||
List<AccountBalanceData> holders = null;
|
||||
if (includeHolders)
|
||||
holders = repository.getAccountRepository().getAssetBalances(assetData.getAssetId());
|
||||
|
||||
return new AssetWithHolders(assetData, holders);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/orderbook/{assetId}/{otherAssetId}")
|
||||
@Operation(
|
||||
summary = "Asset order book",
|
||||
description = "Returns open orders, offering {assetId} for {otherAssetId} in return.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "asset orders",
|
||||
content = @Content(array = @ArraySchema(schema = @Schema(implementation = OrderData.class)))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ASSET_ID, ApiError.REPOSITORY_ISSUE})
|
||||
public List<OrderData> getAssetOrders(@Parameter(ref = "assetId") @PathParam("assetId") int assetId, @Parameter(ref = "otherAssetId") @PathParam("otherAssetId") int otherAssetId,
|
||||
@Parameter(ref = "limit") @QueryParam("limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
if (!repository.getAssetRepository().assetExists(assetId))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||
|
||||
if (!repository.getAssetRepository().assetExists(otherAssetId))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||
|
||||
List<OrderData> orders = repository.getAssetRepository().getOpenOrders(assetId, otherAssetId);
|
||||
|
||||
// Pagination would take effect here (or as part of the repository access)
|
||||
int fromIndex = Integer.min(offset, orders.size());
|
||||
int toIndex = limit == 0 ? orders.size() : Integer.min(fromIndex + limit, orders.size());
|
||||
orders = orders.subList(fromIndex, toIndex);
|
||||
|
||||
return orders;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/trades/{assetId}/{otherAssetId}")
|
||||
@Operation(
|
||||
summary = "Asset trades",
|
||||
description = "Returns successful trades of {assetId} for {otherAssetId}.<br>" +
|
||||
"Does NOT include trades of {otherAssetId} for {assetId}!<br>" +
|
||||
"\"Initiating\" order is the order that caused the actual trade by matching up with the \"target\" order.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "asset trades",
|
||||
content = @Content(array = @ArraySchema(schema = @Schema(implementation = TradeWithOrderInfo.class)))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ASSET_ID, ApiError.REPOSITORY_ISSUE})
|
||||
public List<TradeWithOrderInfo> getAssetTrades(@Parameter(ref = "assetId") @PathParam("assetId") int assetId, @Parameter(ref = "otherAssetId") @PathParam("otherAssetId") int otherAssetId,
|
||||
@Parameter(ref = "limit") @QueryParam("limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
if (!repository.getAssetRepository().assetExists(assetId))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||
|
||||
if (!repository.getAssetRepository().assetExists(otherAssetId))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
|
||||
|
||||
List<TradeData> trades = repository.getAssetRepository().getTrades(assetId, otherAssetId);
|
||||
|
||||
// Pagination would take effect here (or as part of the repository access)
|
||||
int fromIndex = Integer.min(offset, trades.size());
|
||||
int toIndex = limit == 0 ? trades.size() : Integer.min(fromIndex + limit, trades.size());
|
||||
trades = trades.subList(fromIndex, toIndex);
|
||||
|
||||
// Expanding remaining entries
|
||||
List<TradeWithOrderInfo> fullTrades = new ArrayList<>();
|
||||
for (TradeData tradeData : trades) {
|
||||
OrderData initiatingOrderData = repository.getAssetRepository().fromOrderId(tradeData.getInitiator());
|
||||
OrderData targetOrderData = repository.getAssetRepository().fromOrderId(tradeData.getTarget());
|
||||
fullTrades.add(new TradeWithOrderInfo(tradeData, initiatingOrderData, targetOrderData));
|
||||
}
|
||||
|
||||
return fullTrades;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/order/{orderId}")
|
||||
@Operation(
|
||||
summary = "Fetch asset order",
|
||||
description = "Returns asset order info.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "asset order",
|
||||
content = @Content(schema = @Schema(implementation = OrderData.class))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ORDER_ID, ApiError.ORDER_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public OrderWithTrades getAssetOrder(@PathParam("orderId") String orderId58) {
|
||||
// Decode orderID
|
||||
byte[] orderId;
|
||||
try {
|
||||
orderId = Base58.decode(orderId58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ORDER_ID, e);
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
OrderData orderData = repository.getAssetRepository().fromOrderId(orderId);
|
||||
if (orderData == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.ORDER_NO_EXISTS);
|
||||
|
||||
List<TradeData> trades = repository.getAssetRepository().getOrdersTrades(orderId);
|
||||
|
||||
return new OrderWithTrades(orderData, trades);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/issue")
|
||||
@Operation(
|
||||
summary = "Issue new asset",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = IssueAssetTransactionData.class)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "raw, unsigned payment transaction encoded in Base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE, ApiError.TRANSACTION_INVALID})
|
||||
public String issueAsset(IssueAssetTransactionData transactionData) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
|
||||
ValidationResult result = transaction.isValid();
|
||||
if (result != ValidationResult.OK)
|
||||
throw TransactionsResource.createTransactionInvalidException(request, result);
|
||||
|
||||
byte[] bytes = IssueAssetTransactionTransformer.toBytes(transactionData);
|
||||
return Base58.encode(bytes);
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
37
src/main/java/api/resource/BlockExplorerResource.java
Normal file
37
src/main/java/api/resource/BlockExplorerResource.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package api.resource;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
|
||||
@Path("/")
|
||||
public class BlockExplorerResource {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/block-explorer.html")
|
||||
@Operation(hidden = true)
|
||||
public String getBlockExplorer() {
|
||||
ClassLoader loader = this.getClass().getClassLoader();
|
||||
try (InputStream inputStream = loader.getResourceAsStream("block-explorer.html")) {
|
||||
if (inputStream == null)
|
||||
return "block-explorer.html resource not found";
|
||||
|
||||
return new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n"));
|
||||
} catch (IOException e) {
|
||||
return "Error reading block-explorer.html resource";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
468
src/main/java/api/resource/BlocksResource.java
Normal file
468
src/main/java/api/resource/BlocksResource.java
Normal file
@@ -0,0 +1,468 @@
|
||||
package api.resource;
|
||||
|
||||
import data.block.BlockData;
|
||||
import data.transaction.TransactionData;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import api.ApiError;
|
||||
import api.ApiErrors;
|
||||
import api.ApiException;
|
||||
import api.ApiExceptionFactory;
|
||||
import api.models.BlockWithTransactions;
|
||||
import qora.block.Block;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import repository.RepositoryManager;
|
||||
import utils.Base58;
|
||||
|
||||
@Path("/blocks")
|
||||
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
||||
@Tag(name = "Blocks")
|
||||
public class BlocksResource {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/signature/{signature}")
|
||||
@Operation(
|
||||
summary = "Fetch block using base58 signature",
|
||||
description = "Returns the block that matches the given signature",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the block",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
implementation = BlockWithTransactions.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_SIGNATURE, ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public BlockWithTransactions getBlock(@PathParam("signature") String signature58, @Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||
// Decode signature
|
||||
byte[] signature;
|
||||
try {
|
||||
signature = Base58.decode(signature58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_SIGNATURE, e);
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
|
||||
return packageBlockData(repository, blockData, includeTransactions);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/first")
|
||||
@Operation(
|
||||
summary = "Fetch genesis block",
|
||||
description = "Returns the genesis block",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the block",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
implementation = BlockWithTransactions.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public BlockWithTransactions getFirstBlock(@Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().fromHeight(1);
|
||||
return packageBlockData(repository, blockData, includeTransactions);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/last")
|
||||
@Operation(
|
||||
summary = "Fetch last/newest block in blockchain",
|
||||
description = "Returns the last valid block",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the block",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
implementation = BlockWithTransactions.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public BlockWithTransactions getLastBlock(@Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
||||
return packageBlockData(repository, blockData, includeTransactions);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/child/{signature}")
|
||||
@Operation(
|
||||
summary = "Fetch child block using base58 signature of parent block",
|
||||
description = "Returns the child block of the block that matches the given signature",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the block",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
implementation = BlockWithTransactions.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_SIGNATURE, ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public BlockWithTransactions getChild(@PathParam("signature") String signature58, @Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||
// Decode signature
|
||||
byte[] signature;
|
||||
try {
|
||||
signature = Base58.decode(signature58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_SIGNATURE, e);
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
|
||||
|
||||
// Check block exists
|
||||
if (blockData == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.BLOCK_NO_EXISTS);
|
||||
|
||||
BlockData childBlockData = repository.getBlockRepository().fromReference(signature);
|
||||
|
||||
// Checking child exists is handled by packageBlockData()
|
||||
return packageBlockData(repository, childBlockData, includeTransactions);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/generatingbalance")
|
||||
@Operation(
|
||||
summary = "Generating balance of next block",
|
||||
description = "Calculates the generating balance of the block that will follow the last block",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the generating balance",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
implementation = BigDecimal.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||
public BigDecimal getGeneratingBalance() {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
||||
Block block = new Block(repository, blockData);
|
||||
return block.calcNextBlockGeneratingBalance();
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/generatingbalance/{signature}")
|
||||
@Operation(
|
||||
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",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the block",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
implementation = BigDecimal.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_SIGNATURE, ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public BigDecimal getGeneratingBalance(@PathParam("signature") String signature58) {
|
||||
// Decode signature
|
||||
byte[] signature;
|
||||
try {
|
||||
signature = Base58.decode(signature58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_SIGNATURE, e);
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
|
||||
|
||||
// Check block exists
|
||||
if (blockData == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.BLOCK_NO_EXISTS);
|
||||
|
||||
Block block = new Block(repository, blockData);
|
||||
return block.calcNextBlockGeneratingBalance();
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/time")
|
||||
@Operation(
|
||||
summary = "Estimated time to forge next block",
|
||||
description = "Calculates the time it should take for the network to generate the next block",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the time in seconds",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||
public long getTimePerBlock() {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
||||
return Block.calcForgingDelay(blockData.getGeneratingBalance());
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/time/{generatingbalance}")
|
||||
@Operation(
|
||||
summary = "Estimated time to forge block given generating balance",
|
||||
description = "Calculates the time it should take for the network to generate blocks based on specified generating balance",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the time", // in seconds?
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
public long getTimePerBlock(@PathParam("generatingbalance") BigDecimal generatingbalance) {
|
||||
return Block.calcForgingDelay(generatingbalance);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/height")
|
||||
@Operation(
|
||||
summary = "Current blockchain height",
|
||||
description = "Returns the block height of the last block.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the height",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||
public int getHeight() {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
return repository.getBlockRepository().getBlockchainHeight();
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/height/{signature}")
|
||||
@Operation(
|
||||
summary = "Height of specific block",
|
||||
description = "Returns the block height of the block that matches the given signature",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the height",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_SIGNATURE, ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public int getHeight(@PathParam("signature") String signature58) {
|
||||
// Decode signature
|
||||
byte[] signature;
|
||||
try {
|
||||
signature = Base58.decode(signature58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_SIGNATURE, e);
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
|
||||
|
||||
// Check block exists
|
||||
if (blockData == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.BLOCK_NO_EXISTS);
|
||||
|
||||
return blockData.getHeight();
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/byheight/{height}")
|
||||
@Operation(
|
||||
summary = "Fetch block using block height",
|
||||
description = "Returns the block with given height",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "the block",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
implementation = BlockWithTransactions.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public BlockWithTransactions getByHeight(@PathParam("height") int height, @Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
||||
return packageBlockData(repository, blockData, includeTransactions);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/range/{height}")
|
||||
@Operation(
|
||||
summary = "Fetch blocks starting with given height",
|
||||
description = "Returns blocks starting with given height.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "blocks",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
implementation = BlockWithTransactions.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public List<BlockWithTransactions> getBlockRange(@PathParam("height") int height, @Parameter(ref = "count") @QueryParam("count") int count) {
|
||||
boolean includeTransactions = false;
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
List<BlockWithTransactions> blocks = new ArrayList<BlockWithTransactions>();
|
||||
|
||||
for (/* count already set */; count > 0; --count, ++height) {
|
||||
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
||||
if (blockData == null)
|
||||
// Run out of blocks!
|
||||
break;
|
||||
|
||||
blocks.add(packageBlockData(repository, blockData, includeTransactions));
|
||||
}
|
||||
|
||||
return blocks;
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns block, optionally including transactions.
|
||||
* <p>
|
||||
* Throws ApiException using ApiError.BLOCK_NO_EXISTS if blockData is null.
|
||||
*
|
||||
* @param repository
|
||||
* @param blockData
|
||||
* @param includeTransactions
|
||||
* @return packaged block, with optional transactions
|
||||
* @throws DataException
|
||||
* @throws ApiException ApiError.BLOCK_NO_EXISTS
|
||||
*/
|
||||
private BlockWithTransactions packageBlockData(Repository repository, BlockData blockData, boolean includeTransactions) throws DataException {
|
||||
if (blockData == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.BLOCK_NO_EXISTS);
|
||||
|
||||
List<TransactionData> transactions = null;
|
||||
if (includeTransactions) {
|
||||
Block block = new Block(repository, blockData);
|
||||
transactions = block.getTransactions().stream().map(transaction -> transaction.getTransactionData()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return new BlockWithTransactions(blockData, transactions);
|
||||
}
|
||||
|
||||
}
|
81
src/main/java/api/resource/NamesResource.java
Normal file
81
src/main/java/api/resource/NamesResource.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package api.resource;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
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.tags.Tag;
|
||||
import qora.transaction.Transaction;
|
||||
import qora.transaction.Transaction.ValidationResult;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import repository.RepositoryManager;
|
||||
import transform.TransformationException;
|
||||
import transform.transaction.RegisterNameTransactionTransformer;
|
||||
import utils.Base58;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import api.ApiError;
|
||||
import api.ApiErrors;
|
||||
import api.ApiExceptionFactory;
|
||||
import data.transaction.RegisterNameTransactionData;
|
||||
|
||||
@Path("/names")
|
||||
@Produces({ MediaType.TEXT_PLAIN})
|
||||
@Tag(name = "Names")
|
||||
public class NamesResource {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@POST
|
||||
@Path("/register")
|
||||
@Operation(
|
||||
summary = "Build raw, unsigned REGISTER_NAME transaction",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = RegisterNameTransactionData.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "raw, unsigned REGISTER_NAME transaction encoded in Base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
|
||||
public String buildTransaction(RegisterNameTransactionData transactionData) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
|
||||
ValidationResult result = transaction.isValid();
|
||||
if (result != ValidationResult.OK)
|
||||
throw TransactionsResource.createTransactionInvalidException(request, result);
|
||||
|
||||
byte[] bytes = RegisterNameTransactionTransformer.toBytes(transactionData);
|
||||
return Base58.encode(bytes);
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
81
src/main/java/api/resource/PaymentsResource.java
Normal file
81
src/main/java/api/resource/PaymentsResource.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package api.resource;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
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.tags.Tag;
|
||||
import qora.transaction.Transaction;
|
||||
import qora.transaction.Transaction.ValidationResult;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import repository.RepositoryManager;
|
||||
import transform.TransformationException;
|
||||
import transform.transaction.PaymentTransactionTransformer;
|
||||
import utils.Base58;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import api.ApiError;
|
||||
import api.ApiErrors;
|
||||
import api.ApiExceptionFactory;
|
||||
import data.transaction.PaymentTransactionData;
|
||||
|
||||
@Path("/payments")
|
||||
@Produces({MediaType.TEXT_PLAIN})
|
||||
@Tag(name = "Payments")
|
||||
public class PaymentsResource {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@POST
|
||||
@Path("/pay")
|
||||
@Operation(
|
||||
summary = "Build raw, unsigned payment transaction",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = PaymentTransactionData.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "raw, unsigned payment transaction encoded in Base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
|
||||
public String buildTransaction(PaymentTransactionData transactionData) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
|
||||
ValidationResult result = transaction.isValid();
|
||||
if (result != ValidationResult.OK)
|
||||
throw TransactionsResource.createTransactionInvalidException(request, result);
|
||||
|
||||
byte[] bytes = PaymentTransactionTransformer.toBytes(transactionData);
|
||||
return Base58.encode(bytes);
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
408
src/main/java/api/resource/TransactionsResource.java
Normal file
408
src/main/java/api/resource/TransactionsResource.java
Normal file
@@ -0,0 +1,408 @@
|
||||
package api.resource;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
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.tags.Tag;
|
||||
import qora.account.PrivateKeyAccount;
|
||||
import qora.transaction.Transaction;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
import qora.transaction.Transaction.ValidationResult;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
import api.ApiError;
|
||||
import api.ApiErrors;
|
||||
import api.ApiException;
|
||||
import api.ApiExceptionFactory;
|
||||
import api.models.SimpleTransactionSignRequest;
|
||||
import data.transaction.GenesisTransactionData;
|
||||
import data.transaction.PaymentTransactionData;
|
||||
import data.transaction.TransactionData;
|
||||
import globalization.Translator;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import repository.RepositoryManager;
|
||||
import transform.TransformationException;
|
||||
import transform.transaction.TransactionTransformer;
|
||||
import utils.Base58;
|
||||
|
||||
@Path("/transactions")
|
||||
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
|
||||
@Tag(name = "Transactions")
|
||||
public class TransactionsResource {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/signature/{signature}")
|
||||
@Operation(
|
||||
summary = "Fetch transaction using transaction signature",
|
||||
description = "Returns transaction",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "a transaction",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
implementation = TransactionData.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_SIGNATURE, ApiError.TRANSACTION_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public TransactionData getTransactions(@PathParam("signature") String signature58) {
|
||||
byte[] signature;
|
||||
try {
|
||||
signature = Base58.decode(signature58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_SIGNATURE, e);
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature);
|
||||
if (transactionData == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSACTION_NO_EXISTS);
|
||||
|
||||
return transactionData;
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/block/{signature}")
|
||||
@Operation(
|
||||
summary = "Fetch transactions using block signature",
|
||||
description = "Returns list of transactions",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "list of transactions",
|
||||
content = @Content(
|
||||
array = @ArraySchema(
|
||||
schema = @Schema(
|
||||
oneOf = {
|
||||
GenesisTransactionData.class, PaymentTransactionData.class
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_SIGNATURE, ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE})
|
||||
public List<TransactionData> getBlockTransactions(@PathParam("signature") String signature58, @Parameter(ref = "limit") @QueryParam("limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
|
||||
byte[] signature;
|
||||
try {
|
||||
signature = Base58.decode(signature58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_SIGNATURE, e);
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
List<TransactionData> transactions = repository.getBlockRepository().getTransactionsFromSignature(signature);
|
||||
|
||||
// check if block exists
|
||||
if (transactions == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.BLOCK_NO_EXISTS);
|
||||
|
||||
// Pagination would take effect here (or as part of the repository access)
|
||||
int fromIndex = Integer.min(offset, transactions.size());
|
||||
int toIndex = limit == 0 ? transactions.size() : Integer.min(fromIndex + limit, transactions.size());
|
||||
transactions = transactions.subList(fromIndex, toIndex);
|
||||
|
||||
return transactions;
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/unconfirmed")
|
||||
@Operation(
|
||||
summary = "List unconfirmed transactions",
|
||||
description = "Returns transactions",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "transactions",
|
||||
content = @Content(
|
||||
array = @ArraySchema(
|
||||
schema = @Schema(
|
||||
implementation = TransactionData.class
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||
public List<TransactionData> getUnconfirmedTransactions() {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
return repository.getTransactionRepository().getAllUnconfirmedTransactions();
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/search")
|
||||
@Operation(
|
||||
summary = "Find matching transactions",
|
||||
description = "Returns transactions that match criteria. At least either txType or address must be provided.",
|
||||
/*
|
||||
* parameters = {
|
||||
*
|
||||
* @Parameter(in = ParameterIn.QUERY, name = "txType", description = "Transaction type", schema = @Schema(type = "integer")),
|
||||
*
|
||||
* @Parameter(in = ParameterIn.QUERY, name = "address", description = "Account's address", schema = @Schema(type = "string")),
|
||||
*
|
||||
* @Parameter(in = ParameterIn.QUERY, name = "startBlock", description = "Start block height", schema = @Schema(type = "integer")),
|
||||
*
|
||||
* @Parameter(in = ParameterIn.QUERY, name = "blockLimit", description = "Maximum number of blocks to search", schema = @Schema(type = "integer"))
|
||||
* },
|
||||
*/
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "transactions",
|
||||
content = @Content(
|
||||
array = @ArraySchema(
|
||||
schema = @Schema(
|
||||
implementation = TransactionData.class
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_CRITERIA, ApiError.REPOSITORY_ISSUE})
|
||||
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) {
|
||||
if ((txTypeNum == null || txTypeNum == 0) && (address == null || address.isEmpty()))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
|
||||
|
||||
TransactionType txType = null;
|
||||
if (txTypeNum != null) {
|
||||
txType = TransactionType.valueOf(txTypeNum);
|
||||
if (txType == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
List<byte[]> signatures = repository.getTransactionRepository().getAllSignaturesMatchingCriteria(startBlock, blockLimit, txType, address);
|
||||
|
||||
// Pagination would take effect here (or as part of the repository access)
|
||||
int fromIndex = Integer.min(offset, signatures.size());
|
||||
int toIndex = limit == 0 ? signatures.size() : Integer.min(fromIndex + limit, signatures.size());
|
||||
signatures = signatures.subList(fromIndex, toIndex);
|
||||
|
||||
// Expand signatures to transactions
|
||||
List<TransactionData> transactions = new ArrayList<TransactionData>(signatures.size());
|
||||
for (byte[] signature : signatures)
|
||||
transactions.add(repository.getTransactionRepository().fromSignature(signature));
|
||||
|
||||
return transactions;
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/sign")
|
||||
@Operation(
|
||||
summary = "Sign a raw, unsigned transaction",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = SimpleTransactionSignRequest.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "raw, signed transaction encoded in Base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.TRANSFORMATION_ERROR})
|
||||
public String signTransaction(SimpleTransactionSignRequest signRequest) {
|
||||
try {
|
||||
// Append null signature on the end before transformation
|
||||
byte[] rawBytes = Bytes.concat(signRequest.transactionBytes, new byte[TransactionTransformer.SIGNATURE_LENGTH]);
|
||||
|
||||
TransactionData transactionData = TransactionTransformer.fromBytes(rawBytes);
|
||||
|
||||
PrivateKeyAccount signer = new PrivateKeyAccount(null, signRequest.privateKey);
|
||||
|
||||
Transaction transaction = Transaction.fromData(null, transactionData);
|
||||
transaction.sign(signer);
|
||||
|
||||
byte[] signedBytes = TransactionTransformer.toBytes(transactionData);
|
||||
|
||||
return Base58.encode(signedBytes);
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/process")
|
||||
@Operation(
|
||||
summary = "Submit raw, signed transaction for processing and adding to blockchain",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string",
|
||||
description = "raw, signed transaction in base58 encoding",
|
||||
example = "base58"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "true if accepted, false otherwise",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_SIGNATURE, ApiError.INVALID_DATA, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
|
||||
public String processTransaction(String rawBytes58) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
byte[] rawBytes = Base58.decode(rawBytes58);
|
||||
TransactionData transactionData = TransactionTransformer.fromBytes(rawBytes);
|
||||
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
if (!transaction.isSignatureValid())
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_SIGNATURE);
|
||||
|
||||
ValidationResult result = transaction.isValid();
|
||||
if (result != ValidationResult.OK)
|
||||
throw createTransactionInvalidException(request, result);
|
||||
|
||||
repository.getTransactionRepository().save(transactionData);
|
||||
repository.getTransactionRepository().unconfirmTransaction(transactionData);
|
||||
repository.saveChanges();
|
||||
|
||||
return "true";
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/decode")
|
||||
@Operation(
|
||||
summary = "Decode a raw, signed transaction",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string",
|
||||
description = "raw, unsigned/signed transaction in base58 encoding",
|
||||
example = "base58"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "a transaction",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
implementation = TransactionData.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_SIGNATURE, ApiError.INVALID_DATA, ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
|
||||
public TransactionData decodeTransaction(String rawBytes58) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
byte[] rawBytes = Base58.decode(rawBytes58);
|
||||
boolean hasSignature = true;
|
||||
|
||||
TransactionData transactionData;
|
||||
try {
|
||||
transactionData = TransactionTransformer.fromBytes(rawBytes);
|
||||
} catch (TransformationException e) {
|
||||
// Maybe we're missing a signature, so append one and try one more time
|
||||
rawBytes = Bytes.concat(rawBytes, new byte[TransactionTransformer.SIGNATURE_LENGTH]);
|
||||
hasSignature = false;
|
||||
transactionData = TransactionTransformer.fromBytes(rawBytes);
|
||||
}
|
||||
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
if (hasSignature && !transaction.isSignatureValid())
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_SIGNATURE);
|
||||
|
||||
ValidationResult result = transaction.isValid();
|
||||
if (result != ValidationResult.OK)
|
||||
throw createTransactionInvalidException(request, result);
|
||||
|
||||
if (!hasSignature)
|
||||
transactionData.setSignature(null);
|
||||
|
||||
return transactionData;
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ApiException createTransactionInvalidException(HttpServletRequest request, ValidationResult result) {
|
||||
String translatedResult = Translator.INSTANCE.translate("TransactionValidity", request.getLocale().getLanguage(), result.name());
|
||||
return ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSACTION_INVALID, null, translatedResult);
|
||||
}
|
||||
|
||||
}
|
375
src/main/java/api/resource/UtilsResource.java
Normal file
375
src/main/java/api/resource/UtilsResource.java
Normal file
@@ -0,0 +1,375 @@
|
||||
package api.resource;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
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.tags.Tag;
|
||||
import qora.account.PrivateKeyAccount;
|
||||
import qora.crypto.Crypto;
|
||||
import utils.BIP39;
|
||||
import utils.Base58;
|
||||
import utils.NTP;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import api.ApiError;
|
||||
import api.ApiErrors;
|
||||
import api.ApiExceptionFactory;
|
||||
|
||||
@Path("/utils")
|
||||
@Produces({
|
||||
MediaType.TEXT_PLAIN
|
||||
})
|
||||
@Tag(
|
||||
name = "Utilities"
|
||||
)
|
||||
public class UtilsResource {
|
||||
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@POST
|
||||
@Path("/fromBase64")
|
||||
@Operation(
|
||||
summary = "Convert base64 data to hex",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "hex string",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_DATA})
|
||||
public String fromBase64(String base64) {
|
||||
try {
|
||||
return HashCode.fromBytes(Base64.getDecoder().decode(base64.trim())).toString();
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/fromBase58")
|
||||
@Operation(
|
||||
summary = "Convert base58 data to hex",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "hex string",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_DATA})
|
||||
public String base64from58(String base58) {
|
||||
try {
|
||||
return HashCode.fromBytes(Base58.decode(base58.trim())).toString();
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/toBase64/{hex}")
|
||||
@Operation(
|
||||
summary = "Convert hex to base64",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "base64",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
public String toBase64(@PathParam("hex") String hex) {
|
||||
return Base64.getEncoder().encodeToString(HashCode.fromString(hex).asBytes());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/toBase58/{hex}")
|
||||
@Operation(
|
||||
summary = "Convert hex to base58",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "base58",
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
public String toBase58(@PathParam("hex") String hex) {
|
||||
return Base58.encode(HashCode.fromString(hex).asBytes());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/random")
|
||||
@Operation(
|
||||
summary = "Generate random data",
|
||||
description = "Optionally pass data length, defaults to 32 bytes.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "base58 data",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
public String random(@QueryParam("length") Integer length) {
|
||||
if (length == null)
|
||||
length = 32;
|
||||
|
||||
byte[] random = new byte[length];
|
||||
new SecureRandom().nextBytes(random);
|
||||
return Base58.encode(random);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/mnemonic")
|
||||
@Operation(
|
||||
summary = "Generate 12-word BIP39 mnemonic",
|
||||
description = "Optionally pass 16-byte, base58-encoded entropy or entropy will be internally generated.<br>"
|
||||
+ "Example entropy input: YcVfxkQb6JRzqk5kF2tNLv",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "mnemonic",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_DATA})
|
||||
public String getMnemonic(@QueryParam("entropy") String suppliedEntropy) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
byte[] entropy;
|
||||
if (suppliedEntropy != null) {
|
||||
// Use caller-supplied entropy input
|
||||
try {
|
||||
entropy = Base58.decode(suppliedEntropy);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||
}
|
||||
|
||||
// Must be 16-bytes
|
||||
if (entropy.length != 16)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||
} else {
|
||||
// Generate entropy internally
|
||||
UUID uuid = UUID.randomUUID();
|
||||
|
||||
byte[] uuidMSB = Longs.toByteArray(uuid.getMostSignificantBits());
|
||||
byte[] uuidLSB = Longs.toByteArray(uuid.getLeastSignificantBits());
|
||||
entropy = Bytes.concat(uuidMSB, uuidLSB);
|
||||
}
|
||||
|
||||
// Use SHA256 to generate more bits
|
||||
byte[] hash = Crypto.digest(entropy);
|
||||
|
||||
// Append first 4 bits from hash to end. (Actually 8 bits but we only use 4).
|
||||
byte checksum = (byte) (hash[0] & 0xf0);
|
||||
entropy = Bytes.concat(entropy, new byte[] {
|
||||
checksum
|
||||
});
|
||||
|
||||
return BIP39.encode(entropy, "en");
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/mnemonic")
|
||||
@Operation(
|
||||
summary = "Calculate binary entropy from 12-word BIP39 mnemonic",
|
||||
description = "Returns the base58-encoded binary form, or \"false\" if mnemonic is invalid.",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "entropy in base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
public String fromMnemonic(String mnemonic) {
|
||||
if (mnemonic.isEmpty())
|
||||
return "false";
|
||||
|
||||
// Strip leading/trailing whitespace if any
|
||||
mnemonic = mnemonic.trim();
|
||||
|
||||
String[] phraseWords = mnemonic.split(" ");
|
||||
if (phraseWords.length != 12)
|
||||
return "false";
|
||||
|
||||
// Convert BIP39 mnemonic to binary
|
||||
byte[] binary = BIP39.decode(phraseWords, "en");
|
||||
if (binary == null)
|
||||
return "false";
|
||||
|
||||
byte[] entropy = Arrays.copyOf(binary, 16); // 132 bits is 16.5 bytes, but we're discarding checksum nybble
|
||||
|
||||
byte checksumNybble = (byte) (binary[16] & 0xf0);
|
||||
byte[] checksum = Crypto.digest(entropy);
|
||||
if (checksumNybble != (byte) (checksum[0] & 0xf0))
|
||||
return "false";
|
||||
|
||||
return Base58.encode(entropy);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/privateKey/{entropy}")
|
||||
@Operation(
|
||||
summary = "Calculate private key from supplied 16-byte entropy",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "private key in base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_DATA})
|
||||
public String privateKey(@PathParam("entropy") String entropy58) {
|
||||
byte[] entropy;
|
||||
try {
|
||||
entropy = Base58.decode(entropy58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||
}
|
||||
|
||||
if (entropy.length != 16)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||
|
||||
byte[] privateKey = Crypto.digest(entropy);
|
||||
|
||||
return Base58.encode(privateKey);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/publicKey/{privateKey}")
|
||||
@Operation(
|
||||
summary = "Calculate public key from supplied 32-byte private key",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "public key in base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_DATA})
|
||||
public String publicKey(@PathParam("privateKey") String privateKey58) {
|
||||
byte[] privateKey;
|
||||
try {
|
||||
privateKey = Base58.decode(privateKey58);
|
||||
} catch (NumberFormatException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||
}
|
||||
|
||||
if (privateKey.length != 32)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||
|
||||
byte[] publicKey = new PrivateKeyAccount(null, privateKey).getPublicKey();
|
||||
|
||||
return Base58.encode(publicKey);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/timestamp")
|
||||
@Operation(
|
||||
summary = "Returns current timestamp as milliseconds from unix epoch",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
public long getTimestamp() {
|
||||
return NTP.getTime();
|
||||
}
|
||||
|
||||
}
|
75
src/main/java/blockgenerator.java
Normal file
75
src/main/java/blockgenerator.java
Normal file
@@ -0,0 +1,75 @@
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import qora.block.BlockChain;
|
||||
import qora.block.BlockGenerator;
|
||||
import repository.DataException;
|
||||
import repository.RepositoryFactory;
|
||||
import repository.RepositoryManager;
|
||||
import repository.hsqldb.HSQLDBRepositoryFactory;
|
||||
import utils.Base58;
|
||||
|
||||
public class blockgenerator {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(blockgenerator.class);
|
||||
public static final String connectionUrl = "jdbc:hsqldb:file:db/blockchain;create=true";
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 1) {
|
||||
System.err.println("usage: blockgenerator private-key-base58 | 'RANDOM'");
|
||||
System.err.println("example: blockgenerator 7Vg53HrETZZuVySMPWJnVwQESS3dV8jCXPL5GDHMCeKS");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
byte[] privateKey;
|
||||
|
||||
if (args[0].equalsIgnoreCase("RANDOM")) {
|
||||
privateKey = new byte[32];
|
||||
new SecureRandom().nextBytes(privateKey);
|
||||
} else {
|
||||
privateKey = Base58.decode(args[0]);
|
||||
}
|
||||
|
||||
try {
|
||||
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
||||
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
||||
} catch (DataException e) {
|
||||
LOGGER.error("Couldn't connect to repository", e);
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
try {
|
||||
BlockChain.validate();
|
||||
} catch (DataException e) {
|
||||
LOGGER.error("Couldn't validate repository", e);
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
BlockGenerator blockGenerator = new BlockGenerator(privateKey);
|
||||
blockGenerator.start();
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
blockGenerator.shutdown();
|
||||
|
||||
try {
|
||||
blockGenerator.join();
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
RepositoryManager.closeRepositoryFactory();
|
||||
} catch (DataException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
21
src/main/java/brokenmd160.java
Normal file
21
src/main/java/brokenmd160.java
Normal file
@@ -0,0 +1,21 @@
|
||||
import com.google.common.hash.HashCode;
|
||||
|
||||
import qora.crypto.BrokenMD160;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class brokenmd160 {
|
||||
|
||||
public static void main(String args[]) {
|
||||
if (args.length == 0) {
|
||||
System.err.println("usage: broken-md160 <hex>\noutputs: hex");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
byte[] raw = HashCode.fromString(args[0]).asBytes();
|
||||
BrokenMD160 brokenMD160 = new BrokenMD160();
|
||||
byte[] digest = brokenMD160.digest(raw);
|
||||
|
||||
System.out.println(HashCode.fromBytes(digest).toString());
|
||||
}
|
||||
|
||||
}
|
101
src/main/java/controller/Controller.java
Normal file
101
src/main/java/controller/Controller.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package controller;
|
||||
|
||||
import api.ApiService;
|
||||
import qora.block.BlockChain;
|
||||
import qora.block.BlockGenerator;
|
||||
import repository.DataException;
|
||||
import repository.RepositoryFactory;
|
||||
import repository.RepositoryManager;
|
||||
import repository.hsqldb.HSQLDBRepositoryFactory;
|
||||
import settings.Settings;
|
||||
import utils.Base58;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class Controller {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(Controller.class);
|
||||
|
||||
public static final String connectionUrl = "jdbc:hsqldb:file:db/blockchain;create=true";
|
||||
|
||||
public static final long startTime = System.currentTimeMillis();
|
||||
private static final Object shutdownLock = new Object();
|
||||
private static boolean isStopping = false;
|
||||
|
||||
private static BlockGenerator blockGenerator;
|
||||
|
||||
public static void main(String args[]) {
|
||||
LOGGER.info("Starting up...");
|
||||
|
||||
// Load/check settings, which potentially sets up blockchain config, etc.
|
||||
Settings.getInstance();
|
||||
|
||||
LOGGER.info("Starting repository");
|
||||
try {
|
||||
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
||||
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
||||
} catch (DataException e) {
|
||||
LOGGER.error("Unable to start repository", e);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
LOGGER.info("Validating blockchain");
|
||||
try {
|
||||
BlockChain.validate();
|
||||
} catch (DataException e) {
|
||||
LOGGER.error("Couldn't validate blockchain", e);
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
LOGGER.info("Starting block generator");
|
||||
byte[] privateKey = Base58.decode("A9MNsATgQgruBUjxy2rjWY36Yf19uRioKZbiLFT2P7c6");
|
||||
blockGenerator = new BlockGenerator(privateKey);
|
||||
blockGenerator.start();
|
||||
|
||||
LOGGER.info("Starting API");
|
||||
try {
|
||||
ApiService apiService = ApiService.getInstance();
|
||||
apiService.start();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Unable to start API", e);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
Controller.shutdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
synchronized (shutdownLock) {
|
||||
if (!isStopping) {
|
||||
isStopping = true;
|
||||
|
||||
LOGGER.info("Shutting down API");
|
||||
ApiService.getInstance().stop();
|
||||
|
||||
LOGGER.info("Shutting down block generator");
|
||||
blockGenerator.shutdown();
|
||||
try {
|
||||
blockGenerator.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
LOGGER.info("Shutting down repository");
|
||||
RepositoryManager.closeRepositoryFactory();
|
||||
} catch (DataException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
LOGGER.info("Shutdown complete!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
304
src/main/java/crosschain/BTC.java
Normal file
304
src/main/java/crosschain/BTC.java
Normal file
@@ -0,0 +1,304 @@
|
||||
package crosschain;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.BlockChain;
|
||||
import org.bitcoinj.core.CheckpointManager;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.PeerGroup;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.StoredBlock;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
import org.bitcoinj.core.VerificationException;
|
||||
import org.bitcoinj.core.listeners.NewBestBlockListener;
|
||||
import org.bitcoinj.net.discovery.DnsDiscovery;
|
||||
import org.bitcoinj.params.MainNetParams;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.store.BlockStore;
|
||||
import org.bitcoinj.store.BlockStoreException;
|
||||
import org.bitcoinj.store.SPVBlockStore;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.KeyChainGroup;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
import org.bitcoinj.wallet.listeners.WalletCoinsReceivedEventListener;
|
||||
|
||||
import settings.Settings;
|
||||
|
||||
public class BTC {
|
||||
|
||||
private static class RollbackBlockChain extends BlockChain {
|
||||
|
||||
public RollbackBlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
|
||||
super(params, blockStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChainHead(StoredBlock chainHead) throws BlockStoreException {
|
||||
super.setChainHead(chainHead);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class UpdateableCheckpointManager extends CheckpointManager implements NewBestBlockListener {
|
||||
|
||||
private static final int checkpointInterval = 500;
|
||||
|
||||
private static final String minimalTestNet3TextFile = "TXT CHECKPOINTS 1\n0\n1\nAAAAAAAAB+EH4QfhAAAH4AEAAAApmwX6UCEnJcYIKTa7HO3pFkqqNhAzJVBMdEuGAAAAAPSAvVCBUypCbBW/OqU0oIF7ISF84h2spOqHrFCWN9Zw6r6/T///AB0E5oOO\n";
|
||||
private static final String minimalMainNetTextFile = "TXT CHECKPOINTS 1\n0\n1\nAAAAAAAAB+EH4QfhAAAH4AEAAABjl7tqvU/FIcDT9gcbVlA4nwtFUbxAtOawZzBpAAAAAKzkcK7NqciBjI/ldojNKncrWleVSgDfBCCn3VRrbSxXaw5/Sf//AB0z8Bkv\n";
|
||||
|
||||
public UpdateableCheckpointManager(NetworkParameters params) throws IOException {
|
||||
super(params, getMinimalTextFileStream(params));
|
||||
}
|
||||
|
||||
public UpdateableCheckpointManager(NetworkParameters params, InputStream inputStream) throws IOException {
|
||||
super(params, inputStream);
|
||||
}
|
||||
|
||||
private static ByteArrayInputStream getMinimalTextFileStream(NetworkParameters params) {
|
||||
if (params == MainNetParams.get())
|
||||
return new ByteArrayInputStream(minimalMainNetTextFile.getBytes());
|
||||
|
||||
if (params == TestNet3Params.get())
|
||||
return new ByteArrayInputStream(minimalTestNet3TextFile.getBytes());
|
||||
|
||||
throw new RuntimeException("Failed to construct empty UpdateableCheckpointManageer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
|
||||
int height = block.getHeight();
|
||||
|
||||
if (height % checkpointInterval == 0)
|
||||
checkpoints.put(block.getHeader().getTimeSeconds(), block);
|
||||
}
|
||||
|
||||
public void saveAsText(File textFile) throws FileNotFoundException {
|
||||
try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(textFile), StandardCharsets.US_ASCII))) {
|
||||
writer.println("TXT CHECKPOINTS 1");
|
||||
writer.println("0"); // Number of signatures to read. Do this later.
|
||||
writer.println(checkpoints.size());
|
||||
ByteBuffer buffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE);
|
||||
for (StoredBlock block : checkpoints.values()) {
|
||||
block.serializeCompact(buffer);
|
||||
writer.println(CheckpointManager.BASE64.encode(buffer.array()));
|
||||
buffer.position(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void saveAsBinary(File file) throws IOException {
|
||||
try (final FileOutputStream fileOutputStream = new FileOutputStream(file, false)) {
|
||||
MessageDigest digest = Sha256Hash.newDigest();
|
||||
|
||||
try (final DigestOutputStream digestOutputStream = new DigestOutputStream(fileOutputStream, digest)) {
|
||||
digestOutputStream.on(false);
|
||||
|
||||
try (final DataOutputStream dataOutputStream = new DataOutputStream(digestOutputStream)) {
|
||||
dataOutputStream.writeBytes("CHECKPOINTS 1");
|
||||
dataOutputStream.writeInt(0); // Number of signatures to read. Do this later.
|
||||
digestOutputStream.on(true);
|
||||
dataOutputStream.writeInt(checkpoints.size());
|
||||
ByteBuffer buffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE);
|
||||
for (StoredBlock block : checkpoints.values()) {
|
||||
block.serializeCompact(buffer);
|
||||
dataOutputStream.write(buffer.array());
|
||||
buffer.position(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static BTC instance;
|
||||
private static final Object instanceLock = new Object();
|
||||
|
||||
private static File directory;
|
||||
private static String chainFileName;
|
||||
private static String checkpointsFileName;
|
||||
|
||||
private static NetworkParameters params;
|
||||
private static PeerGroup peerGroup;
|
||||
private static BlockStore blockStore;
|
||||
private static RollbackBlockChain chain;
|
||||
private static UpdateableCheckpointManager manager;
|
||||
|
||||
private BTC() {
|
||||
// Start wallet
|
||||
if (Settings.getInstance().useBitcoinTestNet()) {
|
||||
params = TestNet3Params.get();
|
||||
chainFileName = "bitcoinj-testnet.spvchain";
|
||||
checkpointsFileName = "checkpoints-testnet.txt";
|
||||
} else {
|
||||
params = MainNetParams.get();
|
||||
chainFileName = "bitcoinj.spvchain";
|
||||
checkpointsFileName = "checkpoints.txt";
|
||||
}
|
||||
|
||||
directory = new File("Qora-BTC");
|
||||
if (!directory.exists())
|
||||
directory.mkdirs();
|
||||
|
||||
File chainFile = new File(directory, chainFileName);
|
||||
|
||||
try {
|
||||
blockStore = new SPVBlockStore(params, chainFile);
|
||||
} catch (BlockStoreException e) {
|
||||
throw new RuntimeException("Failed to open/create BTC SPVBlockStore", e);
|
||||
}
|
||||
|
||||
File checkpointsFile = new File(directory, checkpointsFileName);
|
||||
try (InputStream checkpointsStream = new FileInputStream(checkpointsFile)) {
|
||||
manager = new UpdateableCheckpointManager(params, checkpointsStream);
|
||||
} catch (FileNotFoundException e) {
|
||||
// Construct with no checkpoints then
|
||||
try {
|
||||
manager = new UpdateableCheckpointManager(params);
|
||||
} catch (IOException e2) {
|
||||
throw new RuntimeException("Failed to create new BTC checkpoints", e2);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load BTC checkpoints", e);
|
||||
}
|
||||
|
||||
try {
|
||||
chain = new RollbackBlockChain(params, blockStore);
|
||||
} catch (BlockStoreException e) {
|
||||
throw new RuntimeException("Failed to construct BTC blockchain", e);
|
||||
}
|
||||
|
||||
peerGroup = new PeerGroup(params, chain);
|
||||
peerGroup.setUserAgent("qqq", "1.0");
|
||||
peerGroup.addPeerDiscovery(new DnsDiscovery(params));
|
||||
peerGroup.start();
|
||||
}
|
||||
|
||||
public static BTC getInstance() {
|
||||
if (instance == null)
|
||||
synchronized (instanceLock) {
|
||||
if (instance == null)
|
||||
instance = new BTC();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
synchronized (instanceLock) {
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
instance = null;
|
||||
}
|
||||
|
||||
peerGroup.stop();
|
||||
|
||||
try {
|
||||
blockStore.close();
|
||||
} catch (BlockStoreException e) {
|
||||
// What can we do?
|
||||
}
|
||||
}
|
||||
|
||||
protected Wallet createEmptyWallet() {
|
||||
ECKey dummyKey = new ECKey();
|
||||
|
||||
KeyChainGroup keyChainGroup = new KeyChainGroup(params);
|
||||
keyChainGroup.importKeys(dummyKey);
|
||||
|
||||
Wallet wallet = new Wallet(params, keyChainGroup);
|
||||
|
||||
wallet.removeKey(dummyKey);
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
public void watch(String base58Address, long startTime) throws InterruptedException, ExecutionException, TimeoutException, BlockStoreException {
|
||||
Wallet wallet = createEmptyWallet();
|
||||
|
||||
WalletCoinsReceivedEventListener coinsReceivedListener = new WalletCoinsReceivedEventListener() {
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
System.out.println("Coins received via transaction " + tx.getHashAsString());
|
||||
}
|
||||
};
|
||||
wallet.addCoinsReceivedEventListener(coinsReceivedListener);
|
||||
|
||||
Address address = Address.fromBase58(params, base58Address);
|
||||
wallet.addWatchedAddress(address, startTime);
|
||||
|
||||
StoredBlock checkpoint = manager.getCheckpointBefore(startTime);
|
||||
blockStore.put(checkpoint);
|
||||
blockStore.setChainHead(checkpoint);
|
||||
chain.setChainHead(checkpoint);
|
||||
|
||||
chain.addWallet(wallet);
|
||||
peerGroup.addWallet(wallet);
|
||||
peerGroup.setFastCatchupTimeSecs(startTime);
|
||||
|
||||
System.out.println("Starting download...");
|
||||
peerGroup.downloadBlockChain();
|
||||
|
||||
List<TransactionOutput> outputs = wallet.getWatchedOutputs(true);
|
||||
|
||||
peerGroup.removeWallet(wallet);
|
||||
chain.removeWallet(wallet);
|
||||
|
||||
for (TransactionOutput output : outputs)
|
||||
System.out.println(output.toString());
|
||||
}
|
||||
|
||||
public void watch(Script script) {
|
||||
// wallet.addWatchedScripts(scripts);
|
||||
}
|
||||
|
||||
public void updateCheckpoints() {
|
||||
final long now = new Date().getTime() / 1000;
|
||||
|
||||
try {
|
||||
StoredBlock checkpoint = manager.getCheckpointBefore(now);
|
||||
blockStore.put(checkpoint);
|
||||
blockStore.setChainHead(checkpoint);
|
||||
chain.setChainHead(checkpoint);
|
||||
} catch (BlockStoreException e) {
|
||||
throw new RuntimeException("Failed to update BTC checkpoints", e);
|
||||
}
|
||||
|
||||
peerGroup.setFastCatchupTimeSecs(now);
|
||||
|
||||
chain.addNewBestBlockListener(Threading.SAME_THREAD, manager);
|
||||
|
||||
peerGroup.downloadBlockChain();
|
||||
|
||||
try {
|
||||
manager.saveAsText(new File(directory, checkpointsFileName));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("Failed to save updated BTC checkpoints", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
43
src/main/java/data/PaymentData.java
Normal file
43
src/main/java/data/PaymentData.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class PaymentData {
|
||||
|
||||
// Properties
|
||||
protected String recipient;
|
||||
protected long assetId;
|
||||
protected BigDecimal amount;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected PaymentData() {
|
||||
}
|
||||
|
||||
public PaymentData(String recipient, long assetId, BigDecimal amount) {
|
||||
this.recipient = recipient;
|
||||
this.assetId = assetId;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public String getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
}
|
48
src/main/java/data/account/AccountBalanceData.java
Normal file
48
src/main/java/data/account/AccountBalanceData.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package data.account;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class AccountBalanceData {
|
||||
|
||||
// Properties
|
||||
private String address;
|
||||
private long assetId;
|
||||
private BigDecimal balance;
|
||||
|
||||
// Constructors
|
||||
|
||||
// necessary for JAX-RS serialization
|
||||
@SuppressWarnings("unused")
|
||||
private AccountBalanceData() {
|
||||
}
|
||||
|
||||
public AccountBalanceData(String address, long assetId, BigDecimal balance) {
|
||||
this.address = address;
|
||||
this.assetId = assetId;
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public String getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
public long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public BigDecimal getBalance() {
|
||||
return this.balance;
|
||||
}
|
||||
|
||||
public void setBalance(BigDecimal balance) {
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
}
|
59
src/main/java/data/account/AccountData.java
Normal file
59
src/main/java/data/account/AccountData.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package data.account;
|
||||
|
||||
public class AccountData {
|
||||
|
||||
// Properties
|
||||
protected String address;
|
||||
protected byte[] reference;
|
||||
protected byte[] publicKey;
|
||||
|
||||
// Constructors
|
||||
|
||||
public AccountData(String address, byte[] reference, byte[] publicKey) {
|
||||
this.address = address;
|
||||
this.reference = reference;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public AccountData(String address) {
|
||||
this(address, null, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public String getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
public byte[] getReference() {
|
||||
return this.reference;
|
||||
}
|
||||
|
||||
public void setReference(byte[] reference) {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
public byte[] getPublicKey() {
|
||||
return this.publicKey;
|
||||
}
|
||||
|
||||
public void setPublicKey(byte[] publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
// Comparison
|
||||
|
||||
@Override
|
||||
public boolean equals(Object b) {
|
||||
if (!(b instanceof AccountData))
|
||||
return false;
|
||||
|
||||
return this.getAddress().equals(((AccountData) b).getAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.getAddress().hashCode();
|
||||
}
|
||||
|
||||
}
|
75
src/main/java/data/assets/AssetData.java
Normal file
75
src/main/java/data/assets/AssetData.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package data.assets;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class AssetData {
|
||||
|
||||
// Properties
|
||||
private Long assetId;
|
||||
private String owner;
|
||||
private String name;
|
||||
private String description;
|
||||
private long quantity;
|
||||
private boolean isDivisible;
|
||||
private byte[] reference;
|
||||
|
||||
// Constructors
|
||||
|
||||
// necessary for JAX-RS serialization
|
||||
protected AssetData() {
|
||||
}
|
||||
|
||||
// NOTE: key is Long, not long, because it can be null if asset ID/key not yet assigned.
|
||||
public AssetData(Long assetId, String owner, String name, String description, long quantity, boolean isDivisible, byte[] reference) {
|
||||
this.assetId = assetId;
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.quantity = quantity;
|
||||
this.isDivisible = isDivisible;
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
// New asset with unassigned assetId
|
||||
public AssetData(String owner, String name, String description, long quantity, boolean isDivisible, byte[] reference) {
|
||||
this(null, owner, name, description, quantity, isDivisible, reference);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public Long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public void setAssetId(Long assetId) {
|
||||
this.assetId = assetId;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public long getQuantity() {
|
||||
return this.quantity;
|
||||
}
|
||||
|
||||
public boolean getIsDivisible() {
|
||||
return this.isDivisible;
|
||||
}
|
||||
|
||||
public byte[] getReference() {
|
||||
return this.reference;
|
||||
}
|
||||
|
||||
}
|
125
src/main/java/data/assets/OrderData.java
Normal file
125
src/main/java/data/assets/OrderData.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package data.assets;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class OrderData implements Comparable<OrderData> {
|
||||
|
||||
// Properties
|
||||
private byte[] orderId;
|
||||
private byte[] creatorPublicKey;
|
||||
|
||||
@Schema(description = "asset on offer to give by order creator")
|
||||
private long haveAssetId;
|
||||
|
||||
@Schema(description = "asset wanted to receive by order creator")
|
||||
private long wantAssetId;
|
||||
|
||||
@Schema(description = "amount of \"have\" asset to trade")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "amount of \"want\" asset to receive per unit of \"have\" asset traded")
|
||||
private BigDecimal price;
|
||||
|
||||
@Schema(description = "how much \"have\" asset has traded")
|
||||
private BigDecimal fulfilled;
|
||||
|
||||
private long timestamp;
|
||||
|
||||
@Schema(description = "has this order been cancelled for further trades?")
|
||||
private boolean isClosed;
|
||||
|
||||
@Schema(description = "has this order been fully traded?")
|
||||
private boolean isFulfilled;
|
||||
|
||||
// Constructors
|
||||
|
||||
// necessary for JAX-RS serialization
|
||||
protected OrderData() {
|
||||
}
|
||||
|
||||
public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal fulfilled, BigDecimal price,
|
||||
long timestamp, boolean isClosed, boolean isFulfilled) {
|
||||
this.orderId = orderId;
|
||||
this.creatorPublicKey = creatorPublicKey;
|
||||
this.haveAssetId = haveAssetId;
|
||||
this.wantAssetId = wantAssetId;
|
||||
this.amount = amount;
|
||||
this.fulfilled = fulfilled;
|
||||
this.price = price;
|
||||
this.timestamp = timestamp;
|
||||
this.isClosed = isClosed;
|
||||
this.isFulfilled = isFulfilled;
|
||||
}
|
||||
|
||||
public OrderData(byte[] orderId, byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, long timestamp) {
|
||||
this(orderId, creatorPublicKey, haveAssetId, wantAssetId, amount, BigDecimal.ZERO.setScale(8), price, timestamp, false, false);
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public byte[] getOrderId() {
|
||||
return this.orderId;
|
||||
}
|
||||
|
||||
public byte[] getCreatorPublicKey() {
|
||||
return this.creatorPublicKey;
|
||||
}
|
||||
|
||||
public long getHaveAssetId() {
|
||||
return this.haveAssetId;
|
||||
}
|
||||
|
||||
public long getWantAssetId() {
|
||||
return this.wantAssetId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public BigDecimal getFulfilled() {
|
||||
return this.fulfilled;
|
||||
}
|
||||
|
||||
public void setFulfilled(BigDecimal fulfilled) {
|
||||
this.fulfilled = fulfilled;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return this.price;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
public boolean getIsClosed() {
|
||||
return this.isClosed;
|
||||
}
|
||||
|
||||
public void setIsClosed(boolean isClosed) {
|
||||
this.isClosed = isClosed;
|
||||
}
|
||||
|
||||
public boolean getIsFulfilled() {
|
||||
return this.isFulfilled;
|
||||
}
|
||||
|
||||
public void setIsFulfilled(boolean isFulfilled) {
|
||||
this.isFulfilled = isFulfilled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(OrderData orderData) {
|
||||
// Compare using prices
|
||||
return this.price.compareTo(orderData.getPrice());
|
||||
}
|
||||
|
||||
}
|
71
src/main/java/data/assets/TradeData.java
Normal file
71
src/main/java/data/assets/TradeData.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package data.assets;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class TradeData {
|
||||
|
||||
// Properties
|
||||
@Schema(name = "initiatingOrderId", description = "ID of order that caused trade")
|
||||
@XmlElement(name = "initiatingOrderId")
|
||||
private byte[] initiator;
|
||||
|
||||
@Schema(name = "targetOrderId", description = "ID of order that matched")
|
||||
@XmlElement(name = "targetOrderId")
|
||||
private byte[] target;
|
||||
|
||||
@Schema(name = "targetAmount", description = "amount traded from target order")
|
||||
@XmlElement(name = "targetAmount")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(name = "initiatorAmount", description = "amount traded from initiating order")
|
||||
@XmlElement(name = "initiatorAmount")
|
||||
private BigDecimal price;
|
||||
|
||||
@Schema(description = "when trade happened")
|
||||
private long timestamp;
|
||||
|
||||
// Constructors
|
||||
|
||||
// necessary for JAX-RS serialization
|
||||
protected TradeData() {
|
||||
}
|
||||
|
||||
public TradeData(byte[] initiator, byte[] target, BigDecimal amount, BigDecimal price, long timestamp) {
|
||||
this.initiator = initiator;
|
||||
this.target = target;
|
||||
this.amount = amount;
|
||||
this.price = price;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public byte[] getInitiator() {
|
||||
return this.initiator;
|
||||
}
|
||||
|
||||
public byte[] getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return this.price;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
}
|
123
src/main/java/data/at/ATData.java
Normal file
123
src/main/java/data/at/ATData.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package data.at;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ATData {
|
||||
|
||||
// Properties
|
||||
private String ATAddress;
|
||||
private byte[] creatorPublicKey;
|
||||
private long creation;
|
||||
private int version;
|
||||
private long assetId;
|
||||
private byte[] codeBytes;
|
||||
private boolean isSleeping;
|
||||
private Integer sleepUntilHeight;
|
||||
private boolean isFinished;
|
||||
private boolean hadFatalError;
|
||||
private boolean isFrozen;
|
||||
private BigDecimal frozenBalance;
|
||||
|
||||
// Constructors
|
||||
|
||||
public ATData(String ATAddress, byte[] creatorPublicKey, long creation, int version, long assetId, byte[] codeBytes, boolean isSleeping,
|
||||
Integer sleepUntilHeight, boolean isFinished, boolean hadFatalError, boolean isFrozen, BigDecimal frozenBalance) {
|
||||
this.ATAddress = ATAddress;
|
||||
this.creatorPublicKey = creatorPublicKey;
|
||||
this.creation = creation;
|
||||
this.version = version;
|
||||
this.assetId = assetId;
|
||||
this.codeBytes = codeBytes;
|
||||
this.isSleeping = isSleeping;
|
||||
this.sleepUntilHeight = sleepUntilHeight;
|
||||
this.isFinished = isFinished;
|
||||
this.hadFatalError = hadFatalError;
|
||||
this.isFrozen = isFrozen;
|
||||
this.frozenBalance = frozenBalance;
|
||||
}
|
||||
|
||||
public ATData(String ATAddress, byte[] creatorPublicKey, long creation, int version, long assetId, byte[] codeBytes, boolean isSleeping,
|
||||
Integer sleepUntilHeight, boolean isFinished, boolean hadFatalError, boolean isFrozen, Long frozenBalance) {
|
||||
this(ATAddress, creatorPublicKey, creation, version, assetId, codeBytes, isSleeping, sleepUntilHeight, isFinished, hadFatalError, isFrozen,
|
||||
(BigDecimal) null);
|
||||
|
||||
// Convert Long frozenBalance to BigDecimal
|
||||
if (frozenBalance != null)
|
||||
this.frozenBalance = BigDecimal.valueOf(frozenBalance, 8);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public String getATAddress() {
|
||||
return this.ATAddress;
|
||||
}
|
||||
|
||||
public byte[] getCreatorPublicKey() {
|
||||
return this.creatorPublicKey;
|
||||
}
|
||||
|
||||
public long getCreation() {
|
||||
return this.creation;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public byte[] getCodeBytes() {
|
||||
return this.codeBytes;
|
||||
}
|
||||
|
||||
public boolean getIsSleeping() {
|
||||
return this.isSleeping;
|
||||
}
|
||||
|
||||
public void setIsSleeping(boolean isSleeping) {
|
||||
this.isSleeping = isSleeping;
|
||||
}
|
||||
|
||||
public Integer getSleepUntilHeight() {
|
||||
return this.sleepUntilHeight;
|
||||
}
|
||||
|
||||
public void setSleepUntilHeight(Integer sleepUntilHeight) {
|
||||
this.sleepUntilHeight = sleepUntilHeight;
|
||||
}
|
||||
|
||||
public boolean getIsFinished() {
|
||||
return this.isFinished;
|
||||
}
|
||||
|
||||
public void setIsFinished(boolean isFinished) {
|
||||
this.isFinished = isFinished;
|
||||
}
|
||||
|
||||
public boolean getHadFatalError() {
|
||||
return this.hadFatalError;
|
||||
}
|
||||
|
||||
public void setHadFatalError(boolean hadFatalError) {
|
||||
this.hadFatalError = hadFatalError;
|
||||
}
|
||||
|
||||
public boolean getIsFrozen() {
|
||||
return this.isFrozen;
|
||||
}
|
||||
|
||||
public void setIsFrozen(boolean isFrozen) {
|
||||
this.isFrozen = isFrozen;
|
||||
}
|
||||
|
||||
public BigDecimal getFrozenBalance() {
|
||||
return this.frozenBalance;
|
||||
}
|
||||
|
||||
public void setFrozenBalance(BigDecimal frozenBalance) {
|
||||
this.frozenBalance = frozenBalance;
|
||||
}
|
||||
|
||||
}
|
73
src/main/java/data/at/ATStateData.java
Normal file
73
src/main/java/data/at/ATStateData.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package data.at;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ATStateData {
|
||||
|
||||
// Properties
|
||||
private String ATAddress;
|
||||
private Integer height;
|
||||
private Long creation;
|
||||
private byte[] stateData;
|
||||
private byte[] stateHash;
|
||||
private BigDecimal fees;
|
||||
|
||||
// Constructors
|
||||
|
||||
/** Create new ATStateData */
|
||||
public ATStateData(String ATAddress, Integer height, Long creation, byte[] stateData, byte[] stateHash, BigDecimal fees) {
|
||||
this.ATAddress = ATAddress;
|
||||
this.height = height;
|
||||
this.creation = creation;
|
||||
this.stateData = stateData;
|
||||
this.stateHash = stateHash;
|
||||
this.fees = fees;
|
||||
}
|
||||
|
||||
/** For recreating per-block ATStateData from repository where not all info is needed */
|
||||
public ATStateData(String ATAddress, int height, byte[] stateHash, BigDecimal fees) {
|
||||
this(ATAddress, height, null, null, stateHash, fees);
|
||||
}
|
||||
|
||||
/** For creating ATStateData from serialized bytes when we don't have all the info */
|
||||
public ATStateData(String ATAddress, byte[] stateHash) {
|
||||
this(ATAddress, null, null, null, stateHash, null);
|
||||
}
|
||||
|
||||
/** For creating ATStateData from serialized bytes when we don't have all the info */
|
||||
public ATStateData(String ATAddress, byte[] stateHash, BigDecimal fees) {
|
||||
this(ATAddress, null, null, null, stateHash, fees);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public String getATAddress() {
|
||||
return this.ATAddress;
|
||||
}
|
||||
|
||||
public Integer getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
|
||||
// Likely to be used when block received over network is attached to blockchain
|
||||
public void setHeight(Integer height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public Long getCreation() {
|
||||
return this.creation;
|
||||
}
|
||||
|
||||
public byte[] getStateData() {
|
||||
return this.stateData;
|
||||
}
|
||||
|
||||
public byte[] getStateHash() {
|
||||
return this.stateHash;
|
||||
}
|
||||
|
||||
public BigDecimal getFees() {
|
||||
return this.fees;
|
||||
}
|
||||
|
||||
}
|
160
src/main/java/data/block/BlockData.java
Normal file
160
src/main/java/data/block/BlockData.java
Normal file
@@ -0,0 +1,160 @@
|
||||
package data.block;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
import qora.crypto.Crypto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class BlockData implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -7678329659124664620L;
|
||||
|
||||
// Properties
|
||||
|
||||
private byte[] signature;
|
||||
private int version;
|
||||
private byte[] reference;
|
||||
private int transactionCount;
|
||||
private BigDecimal totalFees;
|
||||
private byte[] transactionsSignature;
|
||||
private Integer height;
|
||||
private long timestamp;
|
||||
private BigDecimal generatingBalance;
|
||||
private byte[] generatorPublicKey;
|
||||
private byte[] generatorSignature;
|
||||
private int atCount;
|
||||
private BigDecimal atFees;
|
||||
|
||||
// Constructors
|
||||
|
||||
// necessary for JAX-RS serialization
|
||||
protected BlockData() {
|
||||
}
|
||||
|
||||
public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp,
|
||||
BigDecimal generatingBalance, byte[] generatorPublicKey, byte[] generatorSignature, int atCount, BigDecimal atFees) {
|
||||
this.version = version;
|
||||
this.reference = reference;
|
||||
this.transactionCount = transactionCount;
|
||||
this.totalFees = totalFees;
|
||||
this.transactionsSignature = transactionsSignature;
|
||||
this.height = height;
|
||||
this.timestamp = timestamp;
|
||||
this.generatingBalance = generatingBalance;
|
||||
this.generatorPublicKey = generatorPublicKey;
|
||||
this.generatorSignature = generatorSignature;
|
||||
this.atCount = atCount;
|
||||
this.atFees = atFees;
|
||||
|
||||
if (this.generatorSignature != null && this.transactionsSignature != null)
|
||||
this.signature = Bytes.concat(this.generatorSignature, this.transactionsSignature);
|
||||
else
|
||||
this.signature = null;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public byte[] getSignature() {
|
||||
return this.signature;
|
||||
}
|
||||
|
||||
public void setSignature(byte[] signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public byte[] getReference() {
|
||||
return this.reference;
|
||||
}
|
||||
|
||||
public void setReference(byte[] reference) {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
public int getTransactionCount() {
|
||||
return this.transactionCount;
|
||||
}
|
||||
|
||||
public void setTransactionCount(int transactionCount) {
|
||||
this.transactionCount = transactionCount;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalFees() {
|
||||
return this.totalFees;
|
||||
}
|
||||
|
||||
public void setTotalFees(BigDecimal totalFees) {
|
||||
this.totalFees = totalFees;
|
||||
}
|
||||
|
||||
public byte[] getTransactionsSignature() {
|
||||
return this.transactionsSignature;
|
||||
}
|
||||
|
||||
public void setTransactionsSignature(byte[] transactionsSignature) {
|
||||
this.transactionsSignature = transactionsSignature;
|
||||
}
|
||||
|
||||
public Integer getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
|
||||
public void setHeight(Integer height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
public BigDecimal getGeneratingBalance() {
|
||||
return this.generatingBalance;
|
||||
}
|
||||
|
||||
public byte[] getGeneratorPublicKey() {
|
||||
return this.generatorPublicKey;
|
||||
}
|
||||
|
||||
public byte[] getGeneratorSignature() {
|
||||
return this.generatorSignature;
|
||||
}
|
||||
|
||||
public void setGeneratorSignature(byte[] generatorSignature) {
|
||||
this.generatorSignature = generatorSignature;
|
||||
}
|
||||
|
||||
public int getATCount() {
|
||||
return this.atCount;
|
||||
}
|
||||
|
||||
public void setATCount(int atCount) {
|
||||
this.atCount = atCount;
|
||||
}
|
||||
|
||||
public BigDecimal getATFees() {
|
||||
return this.atFees;
|
||||
}
|
||||
|
||||
public void setATFees(BigDecimal atFees) {
|
||||
this.atFees = atFees;
|
||||
}
|
||||
|
||||
// JAXB special
|
||||
|
||||
@XmlElement(name = "generatorAddress")
|
||||
protected String getGeneratorAddress() {
|
||||
return Crypto.toAddress(this.generatorPublicKey);
|
||||
}
|
||||
|
||||
}
|
32
src/main/java/data/block/BlockTransactionData.java
Normal file
32
src/main/java/data/block/BlockTransactionData.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package data.block;
|
||||
|
||||
public class BlockTransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] blockSignature;
|
||||
private int sequence;
|
||||
private byte[] transactionSignature;
|
||||
|
||||
// Constructors
|
||||
|
||||
public BlockTransactionData(byte[] blockSignature, int sequence, byte[] transactionSignature) {
|
||||
this.blockSignature = blockSignature;
|
||||
this.sequence = sequence;
|
||||
this.transactionSignature = transactionSignature;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public byte[] getBlockSignature() {
|
||||
return this.blockSignature;
|
||||
}
|
||||
|
||||
public int getSequence() {
|
||||
return this.sequence;
|
||||
}
|
||||
|
||||
public byte[] getTransactionSignature() {
|
||||
return this.transactionSignature;
|
||||
}
|
||||
|
||||
}
|
99
src/main/java/data/naming/NameData.java
Normal file
99
src/main/java/data/naming/NameData.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package data.naming;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class NameData {
|
||||
|
||||
// Properties
|
||||
private byte[] registrantPublicKey;
|
||||
private String owner;
|
||||
private String name;
|
||||
private String data;
|
||||
private long registered;
|
||||
private Long updated;
|
||||
private byte[] reference;
|
||||
private boolean isForSale;
|
||||
private BigDecimal salePrice;
|
||||
|
||||
// Constructors
|
||||
|
||||
public NameData(byte[] registrantPublicKey, String owner, String name, String data, long registered, Long updated, byte[] reference, boolean isForSale,
|
||||
BigDecimal salePrice) {
|
||||
this.registrantPublicKey = registrantPublicKey;
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
this.registered = registered;
|
||||
this.updated = updated;
|
||||
this.reference = reference;
|
||||
this.isForSale = isForSale;
|
||||
this.salePrice = salePrice;
|
||||
}
|
||||
|
||||
public NameData(byte[] registrantPublicKey, String owner, String name, String data, long registered, byte[] reference) {
|
||||
this(registrantPublicKey, owner, name, data, registered, null, reference, false, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getRegistrantPublicKey() {
|
||||
return this.registrantPublicKey;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public long getRegistered() {
|
||||
return this.registered;
|
||||
}
|
||||
|
||||
public Long getUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public void setUpdated(Long updated) {
|
||||
this.updated = updated;
|
||||
}
|
||||
|
||||
public byte[] getReference() {
|
||||
return this.reference;
|
||||
}
|
||||
|
||||
public void setReference(byte[] reference) {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
public boolean getIsForSale() {
|
||||
return this.isForSale;
|
||||
}
|
||||
|
||||
public void setIsForSale(boolean isForSale) {
|
||||
this.isForSale = isForSale;
|
||||
}
|
||||
|
||||
public BigDecimal getSalePrice() {
|
||||
return this.salePrice;
|
||||
}
|
||||
|
||||
public void setSalePrice(BigDecimal salePrice) {
|
||||
this.salePrice = salePrice;
|
||||
}
|
||||
|
||||
}
|
15
src/main/java/data/package-info.java
Normal file
15
src/main/java/data/package-info.java
Normal file
@@ -0,0 +1,15 @@
|
||||
// This file (data/package-info.java) is used as a template!
|
||||
|
||||
@XmlJavaTypeAdapters({
|
||||
@XmlJavaTypeAdapter(
|
||||
type = byte[].class,
|
||||
value = api.Base58TypeAdapter.class
|
||||
), @XmlJavaTypeAdapter(
|
||||
type = java.math.BigDecimal.class,
|
||||
value = api.BigDecimalTypeAdapter.class
|
||||
)
|
||||
})
|
||||
package data;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
|
68
src/main/java/data/transaction/ATTransactionData.java
Normal file
68
src/main/java/data/transaction/ATTransactionData.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.account.GenesisAccount;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class ATTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private String atAddress;
|
||||
private String recipient;
|
||||
private BigDecimal amount;
|
||||
private Long assetId;
|
||||
private byte[] message;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected ATTransactionData() {
|
||||
}
|
||||
|
||||
public ATTransactionData(String atAddress, String recipient, BigDecimal amount, Long assetId, byte[] message, BigDecimal fee, long timestamp,
|
||||
byte[] reference, byte[] signature) {
|
||||
super(TransactionType.AT, fee, GenesisAccount.PUBLIC_KEY, timestamp, reference, signature);
|
||||
|
||||
this.atAddress = atAddress;
|
||||
this.recipient = recipient;
|
||||
this.amount = amount;
|
||||
this.assetId = assetId;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public ATTransactionData(String atAddress, String recipient, BigDecimal amount, Long assetId, byte[] message, BigDecimal fee, long timestamp,
|
||||
byte[] reference) {
|
||||
this(atAddress, recipient, amount, assetId, message, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public String getATAddress() {
|
||||
return this.atAddress;
|
||||
}
|
||||
|
||||
public String getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public Long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public byte[] getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
}
|
103
src/main/java/data/transaction/ArbitraryTransactionData.java
Normal file
103
src/main/java/data/transaction/ArbitraryTransactionData.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import data.PaymentData;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class ArbitraryTransactionData extends TransactionData {
|
||||
|
||||
// "data" field types
|
||||
public enum DataType {
|
||||
RAW_DATA,
|
||||
DATA_HASH;
|
||||
}
|
||||
|
||||
// Properties
|
||||
private int version;
|
||||
private byte[] senderPublicKey;
|
||||
private int service;
|
||||
private byte[] data;
|
||||
private DataType dataType;
|
||||
private List<PaymentData> payments;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected ArbitraryTransactionData() {
|
||||
}
|
||||
|
||||
/** Reconstructing a V3 arbitrary transaction with signature */
|
||||
public ArbitraryTransactionData(int version, byte[] senderPublicKey, int service, byte[] data, DataType dataType, List<PaymentData> payments,
|
||||
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.ARBITRARY, fee, senderPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.version = version;
|
||||
this.senderPublicKey = senderPublicKey;
|
||||
this.service = service;
|
||||
this.data = data;
|
||||
this.dataType = dataType;
|
||||
this.payments = payments;
|
||||
}
|
||||
|
||||
/** Constructing a new V3 arbitrary transaction without signature */
|
||||
public ArbitraryTransactionData(int version, byte[] senderPublicKey, int service, byte[] data, DataType dataType, List<PaymentData> payments,
|
||||
BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(version, senderPublicKey, service, data, dataType, payments, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
/** Reconstructing a V1 arbitrary transaction with signature */
|
||||
public ArbitraryTransactionData(int version, byte[] senderPublicKey, int service, byte[] data, DataType dataType, BigDecimal fee, long timestamp,
|
||||
byte[] reference, byte[] signature) {
|
||||
this(version, senderPublicKey, service, data, dataType, null, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
/** Constructing a new V1 arbitrary transaction without signature */
|
||||
public ArbitraryTransactionData(int version, byte[] senderPublicKey, int service, byte[] data, DataType dataType, BigDecimal fee, long timestamp,
|
||||
byte[] reference) {
|
||||
this(version, senderPublicKey, service, data, dataType, null, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public byte[] getSenderPublicKey() {
|
||||
return this.senderPublicKey;
|
||||
}
|
||||
|
||||
public int getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public void setData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public DataType getDataType() {
|
||||
return this.dataType;
|
||||
}
|
||||
|
||||
public void setDataType(DataType dataType) {
|
||||
this.dataType = dataType;
|
||||
}
|
||||
|
||||
public List<PaymentData> getPayments() {
|
||||
return this.payments;
|
||||
}
|
||||
|
||||
}
|
80
src/main/java/data/transaction/BuyNameTransactionData.java
Normal file
80
src/main/java/data/transaction/BuyNameTransactionData.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class BuyNameTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] buyerPublicKey;
|
||||
private String name;
|
||||
private BigDecimal amount;
|
||||
private String seller;
|
||||
private byte[] nameReference;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected BuyNameTransactionData() {
|
||||
}
|
||||
|
||||
public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, byte[] nameReference, BigDecimal fee, long timestamp,
|
||||
byte[] reference, byte[] signature) {
|
||||
super(TransactionType.BUY_NAME, fee, buyerPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.buyerPublicKey = buyerPublicKey;
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
this.seller = seller;
|
||||
this.nameReference = nameReference;
|
||||
}
|
||||
|
||||
public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, BigDecimal fee, long timestamp, byte[] reference,
|
||||
byte[] signature) {
|
||||
this(buyerPublicKey, name, amount, seller, null, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, byte[] nameReference, BigDecimal fee, long timestamp,
|
||||
byte[] reference) {
|
||||
this(buyerPublicKey, name, amount, seller, nameReference, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(buyerPublicKey, name, amount, seller, null, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getBuyerPublicKey() {
|
||||
return this.buyerPublicKey;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public String getSeller() {
|
||||
return this.seller;
|
||||
}
|
||||
|
||||
public byte[] getNameReference() {
|
||||
return this.nameReference;
|
||||
}
|
||||
|
||||
public void setNameReference(byte[] nameReference) {
|
||||
this.nameReference = nameReference;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class CancelOrderTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] orderId;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected CancelOrderTransactionData() {
|
||||
}
|
||||
|
||||
public CancelOrderTransactionData(byte[] creatorPublicKey, byte[] orderId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(Transaction.TransactionType.CANCEL_ASSET_ORDER, fee, creatorPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.orderId = orderId;
|
||||
}
|
||||
|
||||
public CancelOrderTransactionData(byte[] creatorPublicKey, byte[] orderId, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(creatorPublicKey, orderId, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public byte[] getOrderId() {
|
||||
return this.orderId;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class CancelSellNameTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] ownerPublicKey;
|
||||
private String name;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected CancelSellNameTransactionData() {
|
||||
}
|
||||
|
||||
public CancelSellNameTransactionData(byte[] ownerPublicKey, String name, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.CANCEL_SELL_NAME, fee, ownerPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.ownerPublicKey = ownerPublicKey;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public CancelSellNameTransactionData(byte[] ownerPublicKey, String name, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(ownerPublicKey, name, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getOwnerPublicKey() {
|
||||
return this.ownerPublicKey;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class CreateOrderTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private long haveAssetId;
|
||||
private long wantAssetId;
|
||||
private BigDecimal amount;
|
||||
private BigDecimal price;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected CreateOrderTransactionData() {
|
||||
}
|
||||
|
||||
public CreateOrderTransactionData(byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, BigDecimal fee,
|
||||
long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.CREATE_ASSET_ORDER, fee, creatorPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.haveAssetId = haveAssetId;
|
||||
this.wantAssetId = wantAssetId;
|
||||
this.amount = amount;
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public CreateOrderTransactionData(byte[] creatorPublicKey, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price, BigDecimal fee,
|
||||
long timestamp, byte[] reference) {
|
||||
this(creatorPublicKey, haveAssetId, wantAssetId, amount, price, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public long getHaveAssetId() {
|
||||
return this.haveAssetId;
|
||||
}
|
||||
|
||||
public long getWantAssetId() {
|
||||
return this.wantAssetId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return this.price;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import data.voting.PollOptionData;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class CreatePollTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private String owner;
|
||||
private String pollName;
|
||||
private String description;
|
||||
private List<PollOptionData> pollOptions;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected CreatePollTransactionData() {
|
||||
}
|
||||
|
||||
public CreatePollTransactionData(byte[] creatorPublicKey, String owner, String pollName, String description, List<PollOptionData> pollOptions,
|
||||
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(Transaction.TransactionType.CREATE_POLL, fee, creatorPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.owner = owner;
|
||||
this.pollName = pollName;
|
||||
this.description = description;
|
||||
this.pollOptions = pollOptions;
|
||||
}
|
||||
|
||||
public CreatePollTransactionData(byte[] creatorPublicKey, String owner, String pollName, String description, List<PollOptionData> pollOptions,
|
||||
BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(creatorPublicKey, owner, pollName, description, pollOptions, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public String getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public String getPollName() {
|
||||
return this.pollName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public List<PollOptionData> getPollOptions() {
|
||||
return this.pollOptions;
|
||||
}
|
||||
|
||||
}
|
94
src/main/java/data/transaction/DeployATTransactionData.java
Normal file
94
src/main/java/data/transaction/DeployATTransactionData.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class DeployATTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private String name;
|
||||
private String description;
|
||||
private String ATType;
|
||||
private String tags;
|
||||
private byte[] creationBytes;
|
||||
private BigDecimal amount;
|
||||
private long assetId;
|
||||
private String ATAddress;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected DeployATTransactionData() {
|
||||
}
|
||||
|
||||
public DeployATTransactionData(String ATAddress, byte[] creatorPublicKey, String name, String description, String ATType, String tags, byte[] creationBytes,
|
||||
BigDecimal amount, long assetId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.DEPLOY_AT, fee, creatorPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.ATType = ATType;
|
||||
this.tags = tags;
|
||||
this.amount = amount;
|
||||
this.assetId = assetId;
|
||||
this.creationBytes = creationBytes;
|
||||
this.ATAddress = ATAddress;
|
||||
}
|
||||
|
||||
public DeployATTransactionData(byte[] creatorPublicKey, String name, String description, String ATType, String tags, byte[] creationBytes,
|
||||
BigDecimal amount, long assetId, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
this(null, creatorPublicKey, name, description, ATType, tags, creationBytes, amount, assetId, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public DeployATTransactionData(byte[] creatorPublicKey, String name, String description, String ATType, String tags, byte[] creationBytes,
|
||||
BigDecimal amount, long assetId, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(null, creatorPublicKey, name, description, ATType, tags, creationBytes, amount, assetId, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public String getATType() {
|
||||
return this.ATType;
|
||||
}
|
||||
|
||||
public String getTags() {
|
||||
return this.tags;
|
||||
}
|
||||
|
||||
public byte[] getCreationBytes() {
|
||||
return this.creationBytes;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public String getATAddress() {
|
||||
return this.ATAddress;
|
||||
}
|
||||
|
||||
public void setATAddress(String ATAddress) {
|
||||
this.ATAddress = ATAddress;
|
||||
}
|
||||
|
||||
}
|
68
src/main/java/data/transaction/GenesisTransactionData.java
Normal file
68
src/main/java/data/transaction/GenesisTransactionData.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.account.GenesisAccount;
|
||||
import qora.assets.Asset;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(
|
||||
allOf = {
|
||||
TransactionData.class
|
||||
}
|
||||
)
|
||||
public class GenesisTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private String recipient;
|
||||
private BigDecimal amount;
|
||||
private long assetId;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected GenesisTransactionData() {
|
||||
}
|
||||
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long assetId, long timestamp, byte[] signature) {
|
||||
// Zero fee
|
||||
super(TransactionType.GENESIS, BigDecimal.ZERO, GenesisAccount.PUBLIC_KEY, timestamp, null, signature);
|
||||
|
||||
this.recipient = recipient;
|
||||
this.amount = amount;
|
||||
this.assetId = assetId;
|
||||
}
|
||||
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp, byte[] signature) {
|
||||
this(recipient, amount, Asset.QORA, timestamp, signature);
|
||||
}
|
||||
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long assetId, long timestamp) {
|
||||
this(recipient, amount, assetId, timestamp, null);
|
||||
}
|
||||
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp) {
|
||||
this(recipient, amount, Asset.QORA, timestamp, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public String getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class IssueAssetTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
// assetId can be null but assigned during save() or during load from repository
|
||||
@Schema(accessMode = AccessMode.READ_ONLY)
|
||||
private Long assetId = null;
|
||||
private byte[] issuerPublicKey;
|
||||
private String owner;
|
||||
private String assetName;
|
||||
private String description;
|
||||
private long quantity;
|
||||
private boolean isDivisible;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected IssueAssetTransactionData() {
|
||||
super(TransactionType.ISSUE_ASSET);
|
||||
}
|
||||
|
||||
public IssueAssetTransactionData(Long assetId, byte[] issuerPublicKey, String owner, String assetName, String description, long quantity,
|
||||
boolean isDivisible, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.ISSUE_ASSET, fee, issuerPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.assetId = assetId;
|
||||
this.issuerPublicKey = issuerPublicKey;
|
||||
this.owner = owner;
|
||||
this.assetName = assetName;
|
||||
this.description = description;
|
||||
this.quantity = quantity;
|
||||
this.isDivisible = isDivisible;
|
||||
}
|
||||
|
||||
public IssueAssetTransactionData(byte[] issuerPublicKey, String owner, String assetName, String description, long quantity, boolean isDivisible,
|
||||
BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
this(null, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public IssueAssetTransactionData(byte[] issuerPublicKey, String owner, String assetName, String description, long quantity, boolean isDivisible,
|
||||
BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(null, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public Long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public void setAssetId(Long assetId) {
|
||||
this.assetId = assetId;
|
||||
}
|
||||
|
||||
public byte[] getIssuerPublicKey() {
|
||||
return this.issuerPublicKey;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getAssetName() {
|
||||
return this.assetName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public long getQuantity() {
|
||||
return this.quantity;
|
||||
}
|
||||
|
||||
public boolean getIsDivisible() {
|
||||
return this.isDivisible;
|
||||
}
|
||||
|
||||
}
|
91
src/main/java/data/transaction/MessageTransactionData.java
Normal file
91
src/main/java/data/transaction/MessageTransactionData.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.assets.Asset;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class MessageTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private int version;
|
||||
private byte[] senderPublicKey;
|
||||
private String recipient;
|
||||
private Long assetId;
|
||||
private BigDecimal amount;
|
||||
private byte[] data;
|
||||
private boolean isText;
|
||||
private boolean isEncrypted;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected MessageTransactionData() {
|
||||
}
|
||||
|
||||
public MessageTransactionData(int version, byte[] senderPublicKey, String recipient, Long assetId, BigDecimal amount, byte[] data, boolean isText,
|
||||
boolean isEncrypted, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.MESSAGE, fee, senderPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.version = version;
|
||||
this.senderPublicKey = senderPublicKey;
|
||||
this.recipient = recipient;
|
||||
|
||||
if (assetId != null)
|
||||
this.assetId = assetId;
|
||||
else
|
||||
this.assetId = Asset.QORA;
|
||||
|
||||
this.amount = amount;
|
||||
this.data = data;
|
||||
this.isText = isText;
|
||||
this.isEncrypted = isEncrypted;
|
||||
}
|
||||
|
||||
public MessageTransactionData(int version, byte[] senderPublicKey, String recipient, Long assetId, BigDecimal amount, byte[] data, boolean isText,
|
||||
boolean isEncrypted, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public byte[] getSenderPublicKey() {
|
||||
return this.senderPublicKey;
|
||||
}
|
||||
|
||||
public String getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public Long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public boolean getIsText() {
|
||||
return this.isText;
|
||||
}
|
||||
|
||||
public boolean getIsEncrypted() {
|
||||
return this.isEncrypted;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import data.PaymentData;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class MultiPaymentTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] senderPublicKey;
|
||||
private List<PaymentData> payments;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected MultiPaymentTransactionData() {
|
||||
}
|
||||
|
||||
public MultiPaymentTransactionData(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(Transaction.TransactionType.MULTIPAYMENT, fee, senderPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.senderPublicKey = senderPublicKey;
|
||||
this.payments = payments;
|
||||
}
|
||||
|
||||
public MultiPaymentTransactionData(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(senderPublicKey, payments, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public byte[] getSenderPublicKey() {
|
||||
return this.senderPublicKey;
|
||||
}
|
||||
|
||||
public List<PaymentData> getPayments() {
|
||||
return this.payments;
|
||||
}
|
||||
|
||||
}
|
63
src/main/java/data/transaction/PaymentTransactionData.java
Normal file
63
src/main/java/data/transaction/PaymentTransactionData.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema( allOf = { TransactionData.class } )
|
||||
public class PaymentTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
@Schema(description = "sender's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||
private byte[] senderPublicKey;
|
||||
@Schema(description = "recipient's address", example = "Qj2Stco8ziE3ZQN2AdpWCmkBFfYjuz8fGu")
|
||||
private String recipient;
|
||||
@Schema(description = "amount to send", example = "123.456")
|
||||
@XmlJavaTypeAdapter(
|
||||
type = BigDecimal.class,
|
||||
value = api.BigDecimalTypeAdapter.class
|
||||
)
|
||||
private BigDecimal amount;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected PaymentTransactionData() {
|
||||
super(TransactionType.PAYMENT);
|
||||
}
|
||||
|
||||
public PaymentTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference,
|
||||
byte[] signature) {
|
||||
super(TransactionType.PAYMENT, fee, senderPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.senderPublicKey = senderPublicKey;
|
||||
this.recipient = recipient;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public PaymentTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(senderPublicKey, recipient, amount, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
|
||||
public byte[] getSenderPublicKey() {
|
||||
return this.senderPublicKey;
|
||||
}
|
||||
|
||||
public String getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class RegisterNameTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
@Schema(description = "registrant's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||
private byte[] registrantPublicKey;
|
||||
@Schema(description = "new owner's address", example = "Qj2Stco8ziE3ZQN2AdpWCmkBFfYjuz8fGu")
|
||||
private String owner;
|
||||
@Schema(description = "requested name", example = "my-name")
|
||||
private String name;
|
||||
@Schema(description = "simple name-related info in JSON format", example = "{ \"age\": 30 }")
|
||||
private String data;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected RegisterNameTransactionData() {
|
||||
super(TransactionType.REGISTER_NAME);
|
||||
}
|
||||
|
||||
public RegisterNameTransactionData(byte[] registrantPublicKey, String owner, String name, String data, BigDecimal fee, long timestamp, byte[] reference,
|
||||
byte[] signature) {
|
||||
super(TransactionType.REGISTER_NAME, fee, registrantPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.registrantPublicKey = registrantPublicKey;
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public RegisterNameTransactionData(byte[] registrantPublicKey, String owner, String name, String data, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(registrantPublicKey, owner, name, data, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getRegistrantPublicKey() {
|
||||
return this.registrantPublicKey;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
}
|
53
src/main/java/data/transaction/SellNameTransactionData.java
Normal file
53
src/main/java/data/transaction/SellNameTransactionData.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class SellNameTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] ownerPublicKey;
|
||||
private String name;
|
||||
private BigDecimal amount;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected SellNameTransactionData() {
|
||||
}
|
||||
|
||||
public SellNameTransactionData(byte[] ownerPublicKey, String name, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.SELL_NAME, fee, ownerPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.ownerPublicKey = ownerPublicKey;
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public SellNameTransactionData(byte[] ownerPublicKey, String name, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(ownerPublicKey, name, amount, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getOwnerPublicKey() {
|
||||
return this.ownerPublicKey;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
}
|
145
src/main/java/data/transaction/TransactionData.java
Normal file
145
src/main/java/data/transaction/TransactionData.java
Normal file
@@ -0,0 +1,145 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
import org.eclipse.persistence.oxm.annotations.XmlClassExtractor;
|
||||
|
||||
import api.TransactionClassExtractor;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
|
||||
import qora.crypto.Crypto;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
/*
|
||||
* If you encounter an error like:
|
||||
*
|
||||
* MessageBodyWriter not found for <some class>
|
||||
*
|
||||
* then chances are that class is missing a no-argument constructor!
|
||||
*/
|
||||
|
||||
@XmlClassExtractor(TransactionClassExtractor.class)
|
||||
@XmlSeeAlso({ArbitraryTransactionData.class, ATTransactionData.class, BuyNameTransactionData.class, CancelOrderTransactionData.class, CancelSellNameTransactionData.class,
|
||||
CreateOrderTransactionData.class, CreatePollTransactionData.class, DeployATTransactionData.class, GenesisTransactionData.class, IssueAssetTransactionData.class,
|
||||
MessageTransactionData.class, MultiPaymentTransactionData.class, PaymentTransactionData.class, RegisterNameTransactionData.class, SellNameTransactionData.class,
|
||||
TransferAssetTransactionData.class, UpdateNameTransactionData.class, VoteOnPollTransactionData.class})
|
||||
//All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public abstract class TransactionData {
|
||||
|
||||
// Properties shared with all transaction types
|
||||
@Schema(accessMode = AccessMode.READ_ONLY, hidden = true)
|
||||
protected TransactionType type;
|
||||
@XmlTransient // represented in transaction-specific properties
|
||||
@Schema(hidden = true)
|
||||
protected byte[] creatorPublicKey;
|
||||
@Schema(description = "timestamp when transaction created, in milliseconds since unix epoch", example = "1545062012000")
|
||||
protected long timestamp;
|
||||
@Schema(description = "sender's last transaction ID", example = "47fw82McxnTQ8wtTS5A51Qojhg62b8px1rF3FhJp5a3etKeb5Y2DniL4Q6E7GbVCs6BAjHVe6sA4gTPxtYzng3AX")
|
||||
protected byte[] reference;
|
||||
@Schema(description = "fee for processing transaction", example = "1.0")
|
||||
protected BigDecimal fee;
|
||||
@Schema(accessMode = AccessMode.READ_ONLY, description = "signature for transaction's raw bytes, using sender's private key", example = "28u54WRcMfGujtQMZ9dNKFXVqucY7XfPihXAqPFsnx853NPUwfDJy1sMH5boCkahFgjUNYqc5fkduxdBhQTKgUsC")
|
||||
protected byte[] signature;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected TransactionData() {
|
||||
}
|
||||
|
||||
// For JAX-RS
|
||||
protected TransactionData(TransactionType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public TransactionData(TransactionType type, BigDecimal fee, byte[] creatorPublicKey, long timestamp, byte[] reference, byte[] signature) {
|
||||
this.fee = fee;
|
||||
this.type = type;
|
||||
this.creatorPublicKey = creatorPublicKey;
|
||||
this.timestamp = timestamp;
|
||||
this.reference = reference;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public TransactionData(TransactionType type, BigDecimal fee, byte[] creatorPublicKey, long timestamp, byte[] reference) {
|
||||
this(type, fee, creatorPublicKey, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public TransactionType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public byte[] getCreatorPublicKey() {
|
||||
return this.creatorPublicKey;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
public byte[] getReference() {
|
||||
return this.reference;
|
||||
}
|
||||
|
||||
public BigDecimal getFee() {
|
||||
return this.fee;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return this.signature;
|
||||
}
|
||||
|
||||
public void setSignature(byte[] signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
// JAXB special
|
||||
|
||||
@XmlElement(name = "creatorAddress")
|
||||
protected String getCreatorAddress() {
|
||||
return Crypto.toAddress(this.creatorPublicKey);
|
||||
}
|
||||
|
||||
// Comparison
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
byte[] bytes = this.signature;
|
||||
|
||||
// No signature? Use reference instead
|
||||
if (bytes == null)
|
||||
bytes = this.reference;
|
||||
|
||||
return new BigInteger(bytes).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
// If we don't have a signature then fail
|
||||
if (this.signature == null)
|
||||
return false;
|
||||
|
||||
if (!(other instanceof TransactionData))
|
||||
return false;
|
||||
|
||||
TransactionData otherTransactionData = (TransactionData) other;
|
||||
|
||||
// If other transactionData has no signature then fail
|
||||
if (otherTransactionData.signature == null)
|
||||
return false;
|
||||
|
||||
return Arrays.equals(this.signature, otherTransactionData.signature);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class TransferAssetTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] senderPublicKey;
|
||||
private String recipient;
|
||||
private BigDecimal amount;
|
||||
private long assetId;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected TransferAssetTransactionData() {
|
||||
}
|
||||
|
||||
public TransferAssetTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, long assetId, BigDecimal fee, long timestamp,
|
||||
byte[] reference, byte[] signature) {
|
||||
super(TransactionType.TRANSFER_ASSET, fee, senderPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.senderPublicKey = senderPublicKey;
|
||||
this.recipient = recipient;
|
||||
this.amount = amount;
|
||||
this.assetId = assetId;
|
||||
}
|
||||
|
||||
public TransferAssetTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, long assetId, BigDecimal fee, long timestamp,
|
||||
byte[] reference) {
|
||||
this(senderPublicKey, recipient, amount, assetId, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public byte[] getSenderPublicKey() {
|
||||
return this.senderPublicKey;
|
||||
}
|
||||
|
||||
public String getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public long getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class UpdateNameTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] ownerPublicKey;
|
||||
private String newOwner;
|
||||
private String name;
|
||||
private String newData;
|
||||
private byte[] nameReference;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected UpdateNameTransactionData() {
|
||||
}
|
||||
|
||||
public UpdateNameTransactionData(byte[] ownerPublicKey, String newOwner, String name, String newData, byte[] nameReference, BigDecimal fee, long timestamp,
|
||||
byte[] reference, byte[] signature) {
|
||||
super(TransactionType.UPDATE_NAME, fee, ownerPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.ownerPublicKey = ownerPublicKey;
|
||||
this.newOwner = newOwner;
|
||||
this.name = name;
|
||||
this.newData = newData;
|
||||
this.nameReference = nameReference;
|
||||
}
|
||||
|
||||
public UpdateNameTransactionData(byte[] ownerPublicKey, String newOwner, String name, String newData, BigDecimal fee, long timestamp, byte[] reference,
|
||||
byte[] signature) {
|
||||
this(ownerPublicKey, newOwner, name, newData, null, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public UpdateNameTransactionData(byte[] ownerPublicKey, String newOwner, String name, String newData, byte[] nameReference, BigDecimal fee, long timestamp,
|
||||
byte[] reference) {
|
||||
this(ownerPublicKey, newOwner, name, newData, nameReference, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getOwnerPublicKey() {
|
||||
return this.ownerPublicKey;
|
||||
}
|
||||
|
||||
public String getNewOwner() {
|
||||
return this.newOwner;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getNewData() {
|
||||
return this.newData;
|
||||
}
|
||||
|
||||
public byte[] getNameReference() {
|
||||
return this.nameReference;
|
||||
}
|
||||
|
||||
public void setNameReference(byte[] nameReference) {
|
||||
this.nameReference = nameReference;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package data.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import qora.transaction.Transaction.TransactionType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class VoteOnPollTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
private byte[] voterPublicKey;
|
||||
private String pollName;
|
||||
private int optionIndex;
|
||||
private Integer previousOptionIndex;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected VoteOnPollTransactionData() {
|
||||
}
|
||||
|
||||
public VoteOnPollTransactionData(byte[] voterPublicKey, String pollName, int optionIndex, Integer previousOptionIndex, BigDecimal fee, long timestamp,
|
||||
byte[] reference, byte[] signature) {
|
||||
super(TransactionType.VOTE_ON_POLL, fee, voterPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.voterPublicKey = voterPublicKey;
|
||||
this.pollName = pollName;
|
||||
this.optionIndex = optionIndex;
|
||||
this.previousOptionIndex = previousOptionIndex;
|
||||
}
|
||||
|
||||
public VoteOnPollTransactionData(byte[] voterPublicKey, String pollName, int optionIndex, BigDecimal fee, long timestamp, byte[] reference,
|
||||
byte[] signature) {
|
||||
this(voterPublicKey, pollName, optionIndex, null, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public VoteOnPollTransactionData(byte[] voterPublicKey, String pollName, int optionIndex, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(voterPublicKey, pollName, optionIndex, null, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getVoterPublicKey() {
|
||||
return this.voterPublicKey;
|
||||
}
|
||||
|
||||
public String getPollName() {
|
||||
return this.pollName;
|
||||
}
|
||||
|
||||
public int getOptionIndex() {
|
||||
return this.optionIndex;
|
||||
}
|
||||
|
||||
public Integer getPreviousOptionIndex() {
|
||||
return this.previousOptionIndex;
|
||||
}
|
||||
|
||||
public void setPreviousOptionIndex(Integer previousOptionIndex) {
|
||||
this.previousOptionIndex = previousOptionIndex;
|
||||
}
|
||||
|
||||
}
|
58
src/main/java/data/voting/PollData.java
Normal file
58
src/main/java/data/voting/PollData.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package data.voting;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import data.voting.PollOptionData;
|
||||
|
||||
public class PollData {
|
||||
|
||||
// Properties
|
||||
private byte[] creatorPublicKey;
|
||||
private String owner;
|
||||
private String pollName;
|
||||
private String description;
|
||||
private List<PollOptionData> pollOptions;
|
||||
private long published;
|
||||
|
||||
// Constructors
|
||||
|
||||
public PollData(byte[] creatorPublicKey, String owner, String pollName, String description, List<PollOptionData> pollOptions, long published) {
|
||||
this.creatorPublicKey = creatorPublicKey;
|
||||
this.owner = owner;
|
||||
this.pollName = pollName;
|
||||
this.description = description;
|
||||
this.pollOptions = pollOptions;
|
||||
this.published = published;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public byte[] getCreatorPublicKey() {
|
||||
return this.creatorPublicKey;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public String getPollName() {
|
||||
return this.pollName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public List<PollOptionData> getPollOptions() {
|
||||
return this.pollOptions;
|
||||
}
|
||||
|
||||
public long getPublished() {
|
||||
return this.published;
|
||||
}
|
||||
|
||||
public void setPublished(long published) {
|
||||
this.published = published;
|
||||
}
|
||||
|
||||
}
|
29
src/main/java/data/voting/PollOptionData.java
Normal file
29
src/main/java/data/voting/PollOptionData.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package data.voting;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
//All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class PollOptionData {
|
||||
|
||||
// Properties
|
||||
private String optionName;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected PollOptionData() {
|
||||
}
|
||||
|
||||
public PollOptionData(String optionName) {
|
||||
this.optionName = optionName;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public String getOptionName() {
|
||||
return this.optionName;
|
||||
}
|
||||
|
||||
}
|
32
src/main/java/data/voting/VoteOnPollData.java
Normal file
32
src/main/java/data/voting/VoteOnPollData.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package data.voting;
|
||||
|
||||
public class VoteOnPollData {
|
||||
|
||||
// Properties
|
||||
private String pollName;
|
||||
private byte[] voterPublicKey;
|
||||
private int optionIndex;
|
||||
|
||||
// Constructors
|
||||
|
||||
public VoteOnPollData(String pollName, byte[] voterPublicKey, int optionIndex) {
|
||||
this.pollName = pollName;
|
||||
this.voterPublicKey = voterPublicKey;
|
||||
this.optionIndex = optionIndex;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
public String getPollName() {
|
||||
return this.pollName;
|
||||
}
|
||||
|
||||
public byte[] getVoterPublicKey() {
|
||||
return this.voterPublicKey;
|
||||
}
|
||||
|
||||
public int getOptionIndex() {
|
||||
return this.optionIndex;
|
||||
}
|
||||
|
||||
}
|
16
src/main/java/database/NoDataFoundException.java
Normal file
16
src/main/java/database/NoDataFoundException.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package database;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Exception for use in DB-backed constructors to indicate no matching data found.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Deprecated
|
||||
public class NoDataFoundException extends SQLException {
|
||||
|
||||
public NoDataFoundException() {
|
||||
}
|
||||
|
||||
}
|
52
src/main/java/globalization/BIP39WordList.java
Normal file
52
src/main/java/globalization/BIP39WordList.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package globalization;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/** Providing multi-language BIP39 word lists, downloaded from https://github.com/bitcoin/bips/tree/master/bip-0039 */
|
||||
public enum BIP39WordList {
|
||||
INSTANCE;
|
||||
|
||||
private Logger LOGGER = LogManager.getLogger(BIP39WordList.class);
|
||||
|
||||
private Map<String, List<String>> wordListsByLang;
|
||||
|
||||
private BIP39WordList() {
|
||||
wordListsByLang = new HashMap<>();
|
||||
}
|
||||
|
||||
public synchronized List<String> getByLang(String lang) {
|
||||
List<String> wordList = wordListsByLang.get(lang);
|
||||
|
||||
if (wordList == null) {
|
||||
ClassLoader loader = this.getClass().getClassLoader();
|
||||
|
||||
try (InputStream inputStream = loader.getResourceAsStream("BIP39/wordlist_" + lang + ".txt")) {
|
||||
if (inputStream == null) {
|
||||
LOGGER.warn("Can't locate '" + lang + "' BIP39 wordlist");
|
||||
return null;
|
||||
}
|
||||
|
||||
wordList = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Error reading '" + lang + "' BIP39 wordlist", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
wordListsByLang.put(lang, wordList);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(wordList);
|
||||
}
|
||||
|
||||
}
|
52
src/main/java/globalization/Translator.java
Normal file
52
src/main/java/globalization/Translator.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package globalization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public enum Translator {
|
||||
INSTANCE;
|
||||
|
||||
private final Logger LOGGER = LogManager.getLogger(Translator.class);
|
||||
private final String DEFAULT_LANG = Locale.getDefault().getLanguage();
|
||||
|
||||
private final Map<String, ResourceBundle> resourceBundles = new HashMap<>();
|
||||
|
||||
private synchronized ResourceBundle getOrLoadResourceBundle(String className, String lang) {
|
||||
final String bundleKey = className + ":" + lang;
|
||||
|
||||
ResourceBundle resourceBundle = resourceBundles.get(bundleKey);
|
||||
if (resourceBundle != null)
|
||||
return resourceBundle;
|
||||
|
||||
try {
|
||||
resourceBundle = ResourceBundle.getBundle("i18n." + className, Locale.forLanguageTag(lang));
|
||||
} catch (MissingResourceException e) {
|
||||
LOGGER.warn("Can't locate '" + lang + "' translation resource bundle for " + className, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
resourceBundles.put(bundleKey, resourceBundle);
|
||||
|
||||
return resourceBundle;
|
||||
}
|
||||
|
||||
public String translate(final String className, final String key) {
|
||||
return this.translate(className, DEFAULT_LANG, key);
|
||||
}
|
||||
|
||||
public String translate(final String className, final String lang, final String key, final Object... args) {
|
||||
ResourceBundle resourceBundle = getOrLoadResourceBundle(className, lang);
|
||||
|
||||
if (resourceBundle == null || !resourceBundle.containsKey(key))
|
||||
return "!!" + lang + ":" + className + "." + key + "!!";
|
||||
|
||||
return String.format(resourceBundle.getString(key), args);
|
||||
}
|
||||
|
||||
}
|
16
src/main/java/org/whispersystems/curve25519/java/Arrays.java
Normal file
16
src/main/java/org/whispersystems/curve25519/java/Arrays.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class Arrays {
|
||||
/**
|
||||
* Assigns the specified byte value to each element of the specified array
|
||||
* of bytes.
|
||||
*
|
||||
* @param a the array to be filled
|
||||
* @param val the value to be stored in all elements of the array
|
||||
*/
|
||||
public static void fill(byte[] a, byte val) {
|
||||
for (int i = 0, len = a.length; i < len; i++)
|
||||
a[i] = val;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public interface Sha512 {
|
||||
|
||||
public void calculateDigest(byte[] out, byte[] in, long length);
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class crypto_verify_32 {
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static int crypto_verify_32(byte[] x, byte[] y) {
|
||||
int differentbits = 0;
|
||||
for (int count = 0; count < 32; count++) {
|
||||
differentbits |= (x[count] ^ y[count]);
|
||||
}
|
||||
return differentbits;
|
||||
}
|
||||
}
|
114
src/main/java/org/whispersystems/curve25519/java/curve_sigs.java
Normal file
114
src/main/java/org/whispersystems/curve25519/java/curve_sigs.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class curve_sigs {
|
||||
|
||||
public static void curve25519_keygen(byte[] curve25519_pubkey_out,
|
||||
byte[] curve25519_privkey_in)
|
||||
{
|
||||
ge_p3 ed = new ge_p3(); /* Ed25519 pubkey point */
|
||||
//int[] ed_y = new int[10];
|
||||
int[] ed_y_plus_one = new int[10];
|
||||
int[] one_minus_ed_y = new int[10];
|
||||
int[] inv_one_minus_ed_y = new int[10];
|
||||
int[] mont_x = new int[10];
|
||||
|
||||
/* Perform a fixed-base multiplication of the Edwards base point,
|
||||
(which is efficient due to precalculated tables), then convert
|
||||
to the Curve25519 montgomery-format public key. In particular,
|
||||
convert Curve25519's "montgomery" x-coordinate into an Ed25519
|
||||
"edwards" y-coordinate:
|
||||
|
||||
mont_x = (ed_y + 1) / (1 - ed_y)
|
||||
|
||||
with projective coordinates:
|
||||
|
||||
mont_x = (ed_y + ed_z) / (ed_z - ed_y)
|
||||
|
||||
NOTE: ed_y=1 is converted to mont_x=0 since fe_invert is mod-exp
|
||||
*/
|
||||
|
||||
ge_scalarmult_base.ge_scalarmult_base(ed, curve25519_privkey_in);
|
||||
fe_add.fe_add(ed_y_plus_one, ed.Y, ed.Z);
|
||||
fe_sub.fe_sub(one_minus_ed_y, ed.Z, ed.Y);
|
||||
fe_invert.fe_invert(inv_one_minus_ed_y, one_minus_ed_y);
|
||||
fe_mul.fe_mul(mont_x, ed_y_plus_one, inv_one_minus_ed_y);
|
||||
fe_tobytes.fe_tobytes(curve25519_pubkey_out, mont_x);
|
||||
}
|
||||
|
||||
public static int curve25519_sign(Sha512 sha512provider, byte[] signature_out,
|
||||
byte[] curve25519_privkey,
|
||||
byte[] msg, int msg_len,
|
||||
byte[] random)
|
||||
{
|
||||
ge_p3 ed_pubkey_point = new ge_p3(); /* Ed25519 pubkey point */
|
||||
byte[] ed_pubkey = new byte[32]; /* Ed25519 encoded pubkey */
|
||||
byte[] sigbuf = new byte[msg_len + 128]; /* working buffer */
|
||||
byte sign_bit = 0;
|
||||
|
||||
/* Convert the Curve25519 privkey to an Ed25519 public key */
|
||||
ge_scalarmult_base.ge_scalarmult_base(ed_pubkey_point, curve25519_privkey);
|
||||
ge_p3_tobytes.ge_p3_tobytes(ed_pubkey, ed_pubkey_point);
|
||||
sign_bit = (byte)(ed_pubkey[31] & 0x80);
|
||||
|
||||
/* Perform an Ed25519 signature with explicit private key */
|
||||
sign_modified.crypto_sign_modified(sha512provider, sigbuf, msg, msg_len, curve25519_privkey,
|
||||
ed_pubkey, random);
|
||||
System.arraycopy(sigbuf, 0, signature_out, 0, 64);
|
||||
|
||||
/* Encode the sign bit into signature (in unused high bit of S) */
|
||||
signature_out[63] &= 0x7F; /* bit should be zero already, but just in case */
|
||||
signature_out[63] |= sign_bit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int curve25519_verify(Sha512 sha512provider, byte[] signature,
|
||||
byte[] curve25519_pubkey,
|
||||
byte[] msg, int msg_len)
|
||||
{
|
||||
int[] mont_x = new int[10];
|
||||
int[] mont_x_minus_one = new int[10];
|
||||
int[] mont_x_plus_one = new int[10];
|
||||
int[] inv_mont_x_plus_one = new int[10];
|
||||
int[] one = new int[10];
|
||||
int[] ed_y = new int[10];
|
||||
byte[] ed_pubkey = new byte[32];
|
||||
long some_retval = 0;
|
||||
byte[] verifybuf = new byte[msg_len + 64]; /* working buffer */
|
||||
byte[] verifybuf2 = new byte[msg_len + 64]; /* working buffer #2 */
|
||||
|
||||
/* Convert the Curve25519 public key into an Ed25519 public key. In
|
||||
particular, convert Curve25519's "montgomery" x-coordinate into an
|
||||
Ed25519 "edwards" y-coordinate:
|
||||
|
||||
ed_y = (mont_x - 1) / (mont_x + 1)
|
||||
|
||||
NOTE: mont_x=-1 is converted to ed_y=0 since fe_invert is mod-exp
|
||||
|
||||
Then move the sign bit into the pubkey from the signature.
|
||||
*/
|
||||
|
||||
fe_frombytes.fe_frombytes(mont_x, curve25519_pubkey);
|
||||
fe_1.fe_1(one);
|
||||
fe_sub.fe_sub(mont_x_minus_one, mont_x, one);
|
||||
fe_add.fe_add(mont_x_plus_one, mont_x, one);
|
||||
fe_invert.fe_invert(inv_mont_x_plus_one, mont_x_plus_one);
|
||||
fe_mul.fe_mul(ed_y, mont_x_minus_one, inv_mont_x_plus_one);
|
||||
fe_tobytes.fe_tobytes(ed_pubkey, ed_y);
|
||||
|
||||
/* Copy the sign bit, and remove it from signature */
|
||||
ed_pubkey[31] &= 0x7F; /* bit should be zero already, but just in case */
|
||||
ed_pubkey[31] |= (signature[63] & 0x80);
|
||||
System.arraycopy(signature, 0, verifybuf, 0, 64);
|
||||
verifybuf[63] &= 0x7F;
|
||||
|
||||
System.arraycopy(msg, 0, verifybuf, 64, (int)msg_len);
|
||||
|
||||
/* Then perform a normal Ed25519 verification, return 0 on success */
|
||||
/* The below call has a strange API: */
|
||||
/* verifybuf = R || S || message */
|
||||
/* verifybuf2 = java to next call gets a copy of verifybuf, S gets
|
||||
replaced with pubkey for hashing, then the whole thing gets zeroized
|
||||
(if bad sig), or contains a copy of msg (good sig) */
|
||||
return open.crypto_sign_open(sha512provider, verifybuf2, some_retval, verifybuf, 64 + msg_len, ed_pubkey);
|
||||
}
|
||||
}
|
27
src/main/java/org/whispersystems/curve25519/java/fe_0.java
Normal file
27
src/main/java/org/whispersystems/curve25519/java/fe_0.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_0 {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
h = 0
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_0(int[] h)
|
||||
{
|
||||
h[0] = 0;
|
||||
h[1] = 0;
|
||||
h[2] = 0;
|
||||
h[3] = 0;
|
||||
h[4] = 0;
|
||||
h[5] = 0;
|
||||
h[6] = 0;
|
||||
h[7] = 0;
|
||||
h[8] = 0;
|
||||
h[9] = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
27
src/main/java/org/whispersystems/curve25519/java/fe_1.java
Normal file
27
src/main/java/org/whispersystems/curve25519/java/fe_1.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_1 {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
h = 1
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_1(int[] h)
|
||||
{
|
||||
h[0] = 1;
|
||||
h[1] = 0;
|
||||
h[2] = 0;
|
||||
h[3] = 0;
|
||||
h[4] = 0;
|
||||
h[5] = 0;
|
||||
h[6] = 0;
|
||||
h[7] = 0;
|
||||
h[8] = 0;
|
||||
h[9] = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
65
src/main/java/org/whispersystems/curve25519/java/fe_add.java
Normal file
65
src/main/java/org/whispersystems/curve25519/java/fe_add.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_add {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
h = f + g
|
||||
Can overlap h with f or g.
|
||||
|
||||
Preconditions:
|
||||
|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
|
||||
Postconditions:
|
||||
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_add(int[] h,int[] f,int[] g)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
int g0 = g[0];
|
||||
int g1 = g[1];
|
||||
int g2 = g[2];
|
||||
int g3 = g[3];
|
||||
int g4 = g[4];
|
||||
int g5 = g[5];
|
||||
int g6 = g[6];
|
||||
int g7 = g[7];
|
||||
int g8 = g[8];
|
||||
int g9 = g[9];
|
||||
int h0 = f0 + g0;
|
||||
int h1 = f1 + g1;
|
||||
int h2 = f2 + g2;
|
||||
int h3 = f3 + g3;
|
||||
int h4 = f4 + g4;
|
||||
int h5 = f5 + g5;
|
||||
int h6 = f6 + g6;
|
||||
int h7 = f7 + g7;
|
||||
int h8 = f8 + g8;
|
||||
int h9 = f9 + g9;
|
||||
h[0] = (int)h0;
|
||||
h[1] = (int)h1;
|
||||
h[2] = (int)h2;
|
||||
h[3] = (int)h3;
|
||||
h[4] = (int)h4;
|
||||
h[5] = (int)h5;
|
||||
h[6] = (int)h6;
|
||||
h[7] = (int)h7;
|
||||
h[8] = (int)h8;
|
||||
h[9] = (int)h9;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_cmov {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
Replace (f,g) with (g,g) if b == 1;
|
||||
replace (f,g) with (f,g) if b == 0.
|
||||
|
||||
Preconditions: b in {0,1}.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_cmov(int[] f,int[] g,int b)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
int g0 = g[0];
|
||||
int g1 = g[1];
|
||||
int g2 = g[2];
|
||||
int g3 = g[3];
|
||||
int g4 = g[4];
|
||||
int g5 = g[5];
|
||||
int g6 = g[6];
|
||||
int g7 = g[7];
|
||||
int g8 = g[8];
|
||||
int g9 = g[9];
|
||||
int x0 = f0 ^ g0;
|
||||
int x1 = f1 ^ g1;
|
||||
int x2 = f2 ^ g2;
|
||||
int x3 = f3 ^ g3;
|
||||
int x4 = f4 ^ g4;
|
||||
int x5 = f5 ^ g5;
|
||||
int x6 = f6 ^ g6;
|
||||
int x7 = f7 ^ g7;
|
||||
int x8 = f8 ^ g8;
|
||||
int x9 = f9 ^ g9;
|
||||
b = -b;
|
||||
x0 &= b;
|
||||
x1 &= b;
|
||||
x2 &= b;
|
||||
x3 &= b;
|
||||
x4 &= b;
|
||||
x5 &= b;
|
||||
x6 &= b;
|
||||
x7 &= b;
|
||||
x8 &= b;
|
||||
x9 &= b;
|
||||
f[0] = f0 ^ x0;
|
||||
f[1] = f1 ^ x1;
|
||||
f[2] = f2 ^ x2;
|
||||
f[3] = f3 ^ x3;
|
||||
f[4] = f4 ^ x4;
|
||||
f[5] = f5 ^ x5;
|
||||
f[6] = f6 ^ x6;
|
||||
f[7] = f7 ^ x7;
|
||||
f[8] = f8 ^ x8;
|
||||
f[9] = f9 ^ x9;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_copy {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
h = f
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_copy(int[] h,int[] f)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
h[0] = f0;
|
||||
h[1] = f1;
|
||||
h[2] = f2;
|
||||
h[3] = f3;
|
||||
h[4] = f4;
|
||||
h[5] = f5;
|
||||
h[6] = f6;
|
||||
h[7] = f7;
|
||||
h[8] = f8;
|
||||
h[9] = f9;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_cswap {
|
||||
|
||||
//CONVERT #include <stdio.h>
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
Replace (f,g) with (g,f) if b == 1;
|
||||
replace (f,g) with (f,g) if b == 0.
|
||||
|
||||
Preconditions: b in {0,1}.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_cswap(int[] f,int[] g,int b)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
int g0 = g[0];
|
||||
int g1 = g[1];
|
||||
int g2 = g[2];
|
||||
int g3 = g[3];
|
||||
int g4 = g[4];
|
||||
int g5 = g[5];
|
||||
int g6 = g[6];
|
||||
int g7 = g[7];
|
||||
int g8 = g[8];
|
||||
int g9 = g[9];
|
||||
int x0 = f0 ^ g0;
|
||||
int x1 = f1 ^ g1;
|
||||
int x2 = f2 ^ g2;
|
||||
int x3 = f3 ^ g3;
|
||||
int x4 = f4 ^ g4;
|
||||
int x5 = f5 ^ g5;
|
||||
int x6 = f6 ^ g6;
|
||||
int x7 = f7 ^ g7;
|
||||
int x8 = f8 ^ g8;
|
||||
int x9 = f9 ^ g9;
|
||||
b = -b;
|
||||
x0 &= b;
|
||||
x1 &= b;
|
||||
x2 &= b;
|
||||
x3 &= b;
|
||||
x4 &= b;
|
||||
x5 &= b;
|
||||
x6 &= b;
|
||||
x7 &= b;
|
||||
x8 &= b;
|
||||
x9 &= b;
|
||||
f[0] = f0 ^ x0;
|
||||
f[1] = f1 ^ x1;
|
||||
f[2] = f2 ^ x2;
|
||||
f[3] = f3 ^ x3;
|
||||
f[4] = f4 ^ x4;
|
||||
f[5] = f5 ^ x5;
|
||||
f[6] = f6 ^ x6;
|
||||
f[7] = f7 ^ x7;
|
||||
f[8] = f8 ^ x8;
|
||||
f[9] = f9 ^ x9;
|
||||
g[0] = g0 ^ x0;
|
||||
g[1] = g1 ^ x1;
|
||||
g[2] = g2 ^ x2;
|
||||
g[3] = g3 ^ x3;
|
||||
g[4] = g4 ^ x4;
|
||||
g[5] = g5 ^ x5;
|
||||
g[6] = g6 ^ x6;
|
||||
g[7] = g7 ^ x7;
|
||||
g[8] = g8 ^ x8;
|
||||
g[9] = g9 ^ x9;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_frombytes {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
//CONVERT #include "long.h"
|
||||
//CONVERT #include "long.h"
|
||||
|
||||
public static long load_3(byte[] in, int index)
|
||||
{
|
||||
long result;
|
||||
result = ((long) in[index + 0]) & 0xFF;
|
||||
result |= (((long) in[index + 1]) << 8) & 0xFF00;
|
||||
result |= (((long) in[index + 2]) << 16) & 0xFF0000;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long load_4(byte[] in, int index)
|
||||
{
|
||||
long result;
|
||||
result = (((long) in[index + 0]) & 0xFF);
|
||||
result |= ((((long) in[index + 1]) << 8) & 0xFF00);
|
||||
result |= ((((long) in[index + 2]) << 16) & 0xFF0000);
|
||||
result |= ((((long) in[index + 3]) << 24) & 0xFF000000L);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
Ignores top bit of h.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_frombytes(int[] h,byte[] s)
|
||||
{
|
||||
long h0 = load_4(s, 0);
|
||||
long h1 = load_3(s, 4) << 6;
|
||||
long h2 = load_3(s, 7) << 5;
|
||||
long h3 = load_3(s, 10) << 3;
|
||||
long h4 = load_3(s, 13) << 2;
|
||||
long h5 = load_4(s, 16);
|
||||
long h6 = load_3(s, 20) << 7;
|
||||
long h7 = load_3(s, 23) << 5;
|
||||
long h8 = load_3(s, 26) << 4;
|
||||
long h9 = (load_3(s, 29) & 8388607) << 2;
|
||||
long carry0;
|
||||
long carry1;
|
||||
long carry2;
|
||||
long carry3;
|
||||
long carry4;
|
||||
long carry5;
|
||||
long carry6;
|
||||
long carry7;
|
||||
long carry8;
|
||||
long carry9;
|
||||
|
||||
carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
|
||||
carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
|
||||
carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
|
||||
carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
|
||||
carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
|
||||
|
||||
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||
carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
|
||||
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||
carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
|
||||
carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
|
||||
|
||||
h[0] = (int)h0;
|
||||
h[1] = (int)h1;
|
||||
h[2] = (int)h2;
|
||||
h[3] = (int)h3;
|
||||
h[4] = (int)h4;
|
||||
h[5] = (int)h5;
|
||||
h[6] = (int)h6;
|
||||
h[7] = (int)h7;
|
||||
h[8] = (int)h8;
|
||||
h[9] = (int)h9;
|
||||
}
|
||||
|
||||
|
||||
}
|
182
src/main/java/org/whispersystems/curve25519/java/fe_invert.java
Normal file
182
src/main/java/org/whispersystems/curve25519/java/fe_invert.java
Normal file
@@ -0,0 +1,182 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_invert {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_invert(int[] out,int[] z)
|
||||
{
|
||||
int[] t0 = new int[10];
|
||||
int[] t1 = new int[10];
|
||||
int[] t2 = new int[10];
|
||||
int[] t3 = new int[10];
|
||||
int i;
|
||||
|
||||
//CONVERT #include "pow225521.h"
|
||||
|
||||
/* qhasm: fe z1 */
|
||||
|
||||
/* qhasm: fe z2 */
|
||||
|
||||
/* qhasm: fe z8 */
|
||||
|
||||
/* qhasm: fe z9 */
|
||||
|
||||
/* qhasm: fe z11 */
|
||||
|
||||
/* qhasm: fe z22 */
|
||||
|
||||
/* qhasm: fe z_5_0 */
|
||||
|
||||
/* qhasm: fe z_10_5 */
|
||||
|
||||
/* qhasm: fe z_10_0 */
|
||||
|
||||
/* qhasm: fe z_20_10 */
|
||||
|
||||
/* qhasm: fe z_20_0 */
|
||||
|
||||
/* qhasm: fe z_40_20 */
|
||||
|
||||
/* qhasm: fe z_40_0 */
|
||||
|
||||
/* qhasm: fe z_50_10 */
|
||||
|
||||
/* qhasm: fe z_50_0 */
|
||||
|
||||
/* qhasm: fe z_100_50 */
|
||||
|
||||
/* qhasm: fe z_100_0 */
|
||||
|
||||
/* qhasm: fe z_200_100 */
|
||||
|
||||
/* qhasm: fe z_200_0 */
|
||||
|
||||
/* qhasm: fe z_250_50 */
|
||||
|
||||
/* qhasm: fe z_250_0 */
|
||||
|
||||
/* qhasm: fe z_255_5 */
|
||||
|
||||
/* qhasm: fe z_255_21 */
|
||||
|
||||
/* qhasm: enter pow225521 */
|
||||
|
||||
/* qhasm: z2 = z1^2^1 */
|
||||
/* asm 1: fe_sq.fe_sq(>z2=fe#1,<z1=fe#11); for (i = 1;i < 1;++i) fe_sq.fe_sq(>z2=fe#1,>z2=fe#1); */
|
||||
/* asm 2: fe_sq.fe_sq(>z2=t0,<z1=z); for (i = 1;i < 1;++i) fe_sq.fe_sq(>z2=t0,>z2=t0); */
|
||||
fe_sq.fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq.fe_sq(t0,t0);
|
||||
|
||||
/* qhasm: z8 = z2^2^2 */
|
||||
/* asm 1: fe_sq.fe_sq(>z8=fe#2,<z2=fe#1); for (i = 1;i < 2;++i) fe_sq.fe_sq(>z8=fe#2,>z8=fe#2); */
|
||||
/* asm 2: fe_sq.fe_sq(>z8=t1,<z2=t0); for (i = 1;i < 2;++i) fe_sq.fe_sq(>z8=t1,>z8=t1); */
|
||||
fe_sq.fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq.fe_sq(t1,t1);
|
||||
|
||||
/* qhasm: z9 = z1*z8 */
|
||||
/* asm 1: fe_mul.fe_mul(>z9=fe#2,<z1=fe#11,<z8=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z9=t1,<z1=z,<z8=t1); */
|
||||
fe_mul.fe_mul(t1,z,t1);
|
||||
|
||||
/* qhasm: z11 = z2*z9 */
|
||||
/* asm 1: fe_mul.fe_mul(>z11=fe#1,<z2=fe#1,<z9=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z11=t0,<z2=t0,<z9=t1); */
|
||||
fe_mul.fe_mul(t0,t0,t1);
|
||||
|
||||
/* qhasm: z22 = z11^2^1 */
|
||||
/* asm 1: fe_sq.fe_sq(>z22=fe#3,<z11=fe#1); for (i = 1;i < 1;++i) fe_sq.fe_sq(>z22=fe#3,>z22=fe#3); */
|
||||
/* asm 2: fe_sq.fe_sq(>z22=t2,<z11=t0); for (i = 1;i < 1;++i) fe_sq.fe_sq(>z22=t2,>z22=t2); */
|
||||
fe_sq.fe_sq(t2,t0); for (i = 1;i < 1;++i) fe_sq.fe_sq(t2,t2);
|
||||
|
||||
/* qhasm: z_5_0 = z9*z22 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_5_0=fe#2,<z9=fe#2,<z22=fe#3); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_5_0=t1,<z9=t1,<z22=t2); */
|
||||
fe_mul.fe_mul(t1,t1,t2);
|
||||
|
||||
/* qhasm: z_10_5 = z_5_0^2^5 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_10_5=fe#3,<z_5_0=fe#2); for (i = 1;i < 5;++i) fe_sq.fe_sq(>z_10_5=fe#3,>z_10_5=fe#3); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_10_5=t2,<z_5_0=t1); for (i = 1;i < 5;++i) fe_sq.fe_sq(>z_10_5=t2,>z_10_5=t2); */
|
||||
fe_sq.fe_sq(t2,t1); for (i = 1;i < 5;++i) fe_sq.fe_sq(t2,t2);
|
||||
|
||||
/* qhasm: z_10_0 = z_10_5*z_5_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_10_0=fe#2,<z_10_5=fe#3,<z_5_0=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_10_0=t1,<z_10_5=t2,<z_5_0=t1); */
|
||||
fe_mul.fe_mul(t1,t2,t1);
|
||||
|
||||
/* qhasm: z_20_10 = z_10_0^2^10 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_20_10=fe#3,<z_10_0=fe#2); for (i = 1;i < 10;++i) fe_sq.fe_sq(>z_20_10=fe#3,>z_20_10=fe#3); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_20_10=t2,<z_10_0=t1); for (i = 1;i < 10;++i) fe_sq.fe_sq(>z_20_10=t2,>z_20_10=t2); */
|
||||
fe_sq.fe_sq(t2,t1); for (i = 1;i < 10;++i) fe_sq.fe_sq(t2,t2);
|
||||
|
||||
/* qhasm: z_20_0 = z_20_10*z_10_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_20_0=fe#3,<z_20_10=fe#3,<z_10_0=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_20_0=t2,<z_20_10=t2,<z_10_0=t1); */
|
||||
fe_mul.fe_mul(t2,t2,t1);
|
||||
|
||||
/* qhasm: z_40_20 = z_20_0^2^20 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_40_20=fe#4,<z_20_0=fe#3); for (i = 1;i < 20;++i) fe_sq.fe_sq(>z_40_20=fe#4,>z_40_20=fe#4); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_40_20=t3,<z_20_0=t2); for (i = 1;i < 20;++i) fe_sq.fe_sq(>z_40_20=t3,>z_40_20=t3); */
|
||||
fe_sq.fe_sq(t3,t2); for (i = 1;i < 20;++i) fe_sq.fe_sq(t3,t3);
|
||||
|
||||
/* qhasm: z_40_0 = z_40_20*z_20_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_40_0=fe#3,<z_40_20=fe#4,<z_20_0=fe#3); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_40_0=t2,<z_40_20=t3,<z_20_0=t2); */
|
||||
fe_mul.fe_mul(t2,t3,t2);
|
||||
|
||||
/* qhasm: z_50_10 = z_40_0^2^10 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_50_10=fe#3,<z_40_0=fe#3); for (i = 1;i < 10;++i) fe_sq.fe_sq(>z_50_10=fe#3,>z_50_10=fe#3); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_50_10=t2,<z_40_0=t2); for (i = 1;i < 10;++i) fe_sq.fe_sq(>z_50_10=t2,>z_50_10=t2); */
|
||||
fe_sq.fe_sq(t2,t2); for (i = 1;i < 10;++i) fe_sq.fe_sq(t2,t2);
|
||||
|
||||
/* qhasm: z_50_0 = z_50_10*z_10_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_50_0=fe#2,<z_50_10=fe#3,<z_10_0=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_50_0=t1,<z_50_10=t2,<z_10_0=t1); */
|
||||
fe_mul.fe_mul(t1,t2,t1);
|
||||
|
||||
/* qhasm: z_100_50 = z_50_0^2^50 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_100_50=fe#3,<z_50_0=fe#2); for (i = 1;i < 50;++i) fe_sq.fe_sq(>z_100_50=fe#3,>z_100_50=fe#3); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_100_50=t2,<z_50_0=t1); for (i = 1;i < 50;++i) fe_sq.fe_sq(>z_100_50=t2,>z_100_50=t2); */
|
||||
fe_sq.fe_sq(t2,t1); for (i = 1;i < 50;++i) fe_sq.fe_sq(t2,t2);
|
||||
|
||||
/* qhasm: z_100_0 = z_100_50*z_50_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_100_0=fe#3,<z_100_50=fe#3,<z_50_0=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_100_0=t2,<z_100_50=t2,<z_50_0=t1); */
|
||||
fe_mul.fe_mul(t2,t2,t1);
|
||||
|
||||
/* qhasm: z_200_100 = z_100_0^2^100 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_200_100=fe#4,<z_100_0=fe#3); for (i = 1;i < 100;++i) fe_sq.fe_sq(>z_200_100=fe#4,>z_200_100=fe#4); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_200_100=t3,<z_100_0=t2); for (i = 1;i < 100;++i) fe_sq.fe_sq(>z_200_100=t3,>z_200_100=t3); */
|
||||
fe_sq.fe_sq(t3,t2); for (i = 1;i < 100;++i) fe_sq.fe_sq(t3,t3);
|
||||
|
||||
/* qhasm: z_200_0 = z_200_100*z_100_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_200_0=fe#3,<z_200_100=fe#4,<z_100_0=fe#3); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_200_0=t2,<z_200_100=t3,<z_100_0=t2); */
|
||||
fe_mul.fe_mul(t2,t3,t2);
|
||||
|
||||
/* qhasm: z_250_50 = z_200_0^2^50 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_250_50=fe#3,<z_200_0=fe#3); for (i = 1;i < 50;++i) fe_sq.fe_sq(>z_250_50=fe#3,>z_250_50=fe#3); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_250_50=t2,<z_200_0=t2); for (i = 1;i < 50;++i) fe_sq.fe_sq(>z_250_50=t2,>z_250_50=t2); */
|
||||
fe_sq.fe_sq(t2,t2); for (i = 1;i < 50;++i) fe_sq.fe_sq(t2,t2);
|
||||
|
||||
/* qhasm: z_250_0 = z_250_50*z_50_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_250_0=fe#2,<z_250_50=fe#3,<z_50_0=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_250_0=t1,<z_250_50=t2,<z_50_0=t1); */
|
||||
fe_mul.fe_mul(t1,t2,t1);
|
||||
|
||||
/* qhasm: z_255_5 = z_250_0^2^5 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_255_5=fe#2,<z_250_0=fe#2); for (i = 1;i < 5;++i) fe_sq.fe_sq(>z_255_5=fe#2,>z_255_5=fe#2); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_255_5=t1,<z_250_0=t1); for (i = 1;i < 5;++i) fe_sq.fe_sq(>z_255_5=t1,>z_255_5=t1); */
|
||||
fe_sq.fe_sq(t1,t1); for (i = 1;i < 5;++i) fe_sq.fe_sq(t1,t1);
|
||||
|
||||
/* qhasm: z_255_21 = z_255_5*z11 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_255_21=fe#12,<z_255_5=fe#2,<z11=fe#1); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_255_21=out,<z_255_5=t1,<z11=t0); */
|
||||
fe_mul.fe_mul(out,t1,t0);
|
||||
|
||||
/* qhasm: return */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_isnegative {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
return 1 if f is in {1,3,5,...,q-2}
|
||||
return 0 if f is in {0,2,4,...,q-1}
|
||||
|
||||
Preconditions:
|
||||
|f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static int fe_isnegative(int[] f)
|
||||
{
|
||||
byte[] s = new byte[32];
|
||||
fe_tobytes.fe_tobytes(s,f);
|
||||
return s[0] & 1;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_isnonzero {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
//CONVERT #include "crypto_verify_32.crypto_verify_32.h"
|
||||
|
||||
/*
|
||||
return 1 if f == 0
|
||||
return 0 if f != 0
|
||||
|
||||
Preconditions:
|
||||
|f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
*/
|
||||
|
||||
static final byte[] zero = new byte[32];
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static int fe_isnonzero(int[] f)
|
||||
{
|
||||
byte[] s = new byte[32];
|
||||
fe_tobytes.fe_tobytes(s,f);
|
||||
return crypto_verify_32.crypto_verify_32(s,zero);
|
||||
}
|
||||
|
||||
|
||||
}
|
261
src/main/java/org/whispersystems/curve25519/java/fe_mul.java
Normal file
261
src/main/java/org/whispersystems/curve25519/java/fe_mul.java
Normal file
@@ -0,0 +1,261 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_mul {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
//CONVERT #include "long.h"
|
||||
|
||||
/*
|
||||
h = f * g
|
||||
Can overlap h with f or g.
|
||||
|
||||
Preconditions:
|
||||
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|
||||
|g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|
||||
|
||||
Postconditions:
|
||||
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
|
||||
*/
|
||||
|
||||
/*
|
||||
Notes on implementation strategy:
|
||||
|
||||
Using schoolbook multiplication.
|
||||
Karatsuba would save a little in some cost models.
|
||||
|
||||
Most multiplications by 2 and 19 are 32-bit precomputations;
|
||||
cheaper than 64-bit postcomputations.
|
||||
|
||||
There is one remaining multiplication by 19 in the carry chain;
|
||||
one *19 precomputation can be merged into this,
|
||||
but the resulting data flow is considerably less clean.
|
||||
|
||||
There are 12 carries below.
|
||||
10 of them are 2-way parallelizable and vectorizable.
|
||||
Can get away with 11 carries, but then data flow is much deeper.
|
||||
|
||||
With tighter constraints on inputs can squeeze carries into int32.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_mul(int[] h,int[] f,int[] g)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
int g0 = g[0];
|
||||
int g1 = g[1];
|
||||
int g2 = g[2];
|
||||
int g3 = g[3];
|
||||
int g4 = g[4];
|
||||
int g5 = g[5];
|
||||
int g6 = g[6];
|
||||
int g7 = g[7];
|
||||
int g8 = g[8];
|
||||
int g9 = g[9];
|
||||
int g1_19 = 19 * g1; /* 1.959375*2^29 */
|
||||
int g2_19 = 19 * g2; /* 1.959375*2^30; still ok */
|
||||
int g3_19 = 19 * g3;
|
||||
int g4_19 = 19 * g4;
|
||||
int g5_19 = 19 * g5;
|
||||
int g6_19 = 19 * g6;
|
||||
int g7_19 = 19 * g7;
|
||||
int g8_19 = 19 * g8;
|
||||
int g9_19 = 19 * g9;
|
||||
int f1_2 = 2 * f1;
|
||||
int f3_2 = 2 * f3;
|
||||
int f5_2 = 2 * f5;
|
||||
int f7_2 = 2 * f7;
|
||||
int f9_2 = 2 * f9;
|
||||
long f0g0 = f0 * (long) g0;
|
||||
long f0g1 = f0 * (long) g1;
|
||||
long f0g2 = f0 * (long) g2;
|
||||
long f0g3 = f0 * (long) g3;
|
||||
long f0g4 = f0 * (long) g4;
|
||||
long f0g5 = f0 * (long) g5;
|
||||
long f0g6 = f0 * (long) g6;
|
||||
long f0g7 = f0 * (long) g7;
|
||||
long f0g8 = f0 * (long) g8;
|
||||
long f0g9 = f0 * (long) g9;
|
||||
long f1g0 = f1 * (long) g0;
|
||||
long f1g1_2 = f1_2 * (long) g1;
|
||||
long f1g2 = f1 * (long) g2;
|
||||
long f1g3_2 = f1_2 * (long) g3;
|
||||
long f1g4 = f1 * (long) g4;
|
||||
long f1g5_2 = f1_2 * (long) g5;
|
||||
long f1g6 = f1 * (long) g6;
|
||||
long f1g7_2 = f1_2 * (long) g7;
|
||||
long f1g8 = f1 * (long) g8;
|
||||
long f1g9_38 = f1_2 * (long) g9_19;
|
||||
long f2g0 = f2 * (long) g0;
|
||||
long f2g1 = f2 * (long) g1;
|
||||
long f2g2 = f2 * (long) g2;
|
||||
long f2g3 = f2 * (long) g3;
|
||||
long f2g4 = f2 * (long) g4;
|
||||
long f2g5 = f2 * (long) g5;
|
||||
long f2g6 = f2 * (long) g6;
|
||||
long f2g7 = f2 * (long) g7;
|
||||
long f2g8_19 = f2 * (long) g8_19;
|
||||
long f2g9_19 = f2 * (long) g9_19;
|
||||
long f3g0 = f3 * (long) g0;
|
||||
long f3g1_2 = f3_2 * (long) g1;
|
||||
long f3g2 = f3 * (long) g2;
|
||||
long f3g3_2 = f3_2 * (long) g3;
|
||||
long f3g4 = f3 * (long) g4;
|
||||
long f3g5_2 = f3_2 * (long) g5;
|
||||
long f3g6 = f3 * (long) g6;
|
||||
long f3g7_38 = f3_2 * (long) g7_19;
|
||||
long f3g8_19 = f3 * (long) g8_19;
|
||||
long f3g9_38 = f3_2 * (long) g9_19;
|
||||
long f4g0 = f4 * (long) g0;
|
||||
long f4g1 = f4 * (long) g1;
|
||||
long f4g2 = f4 * (long) g2;
|
||||
long f4g3 = f4 * (long) g3;
|
||||
long f4g4 = f4 * (long) g4;
|
||||
long f4g5 = f4 * (long) g5;
|
||||
long f4g6_19 = f4 * (long) g6_19;
|
||||
long f4g7_19 = f4 * (long) g7_19;
|
||||
long f4g8_19 = f4 * (long) g8_19;
|
||||
long f4g9_19 = f4 * (long) g9_19;
|
||||
long f5g0 = f5 * (long) g0;
|
||||
long f5g1_2 = f5_2 * (long) g1;
|
||||
long f5g2 = f5 * (long) g2;
|
||||
long f5g3_2 = f5_2 * (long) g3;
|
||||
long f5g4 = f5 * (long) g4;
|
||||
long f5g5_38 = f5_2 * (long) g5_19;
|
||||
long f5g6_19 = f5 * (long) g6_19;
|
||||
long f5g7_38 = f5_2 * (long) g7_19;
|
||||
long f5g8_19 = f5 * (long) g8_19;
|
||||
long f5g9_38 = f5_2 * (long) g9_19;
|
||||
long f6g0 = f6 * (long) g0;
|
||||
long f6g1 = f6 * (long) g1;
|
||||
long f6g2 = f6 * (long) g2;
|
||||
long f6g3 = f6 * (long) g3;
|
||||
long f6g4_19 = f6 * (long) g4_19;
|
||||
long f6g5_19 = f6 * (long) g5_19;
|
||||
long f6g6_19 = f6 * (long) g6_19;
|
||||
long f6g7_19 = f6 * (long) g7_19;
|
||||
long f6g8_19 = f6 * (long) g8_19;
|
||||
long f6g9_19 = f6 * (long) g9_19;
|
||||
long f7g0 = f7 * (long) g0;
|
||||
long f7g1_2 = f7_2 * (long) g1;
|
||||
long f7g2 = f7 * (long) g2;
|
||||
long f7g3_38 = f7_2 * (long) g3_19;
|
||||
long f7g4_19 = f7 * (long) g4_19;
|
||||
long f7g5_38 = f7_2 * (long) g5_19;
|
||||
long f7g6_19 = f7 * (long) g6_19;
|
||||
long f7g7_38 = f7_2 * (long) g7_19;
|
||||
long f7g8_19 = f7 * (long) g8_19;
|
||||
long f7g9_38 = f7_2 * (long) g9_19;
|
||||
long f8g0 = f8 * (long) g0;
|
||||
long f8g1 = f8 * (long) g1;
|
||||
long f8g2_19 = f8 * (long) g2_19;
|
||||
long f8g3_19 = f8 * (long) g3_19;
|
||||
long f8g4_19 = f8 * (long) g4_19;
|
||||
long f8g5_19 = f8 * (long) g5_19;
|
||||
long f8g6_19 = f8 * (long) g6_19;
|
||||
long f8g7_19 = f8 * (long) g7_19;
|
||||
long f8g8_19 = f8 * (long) g8_19;
|
||||
long f8g9_19 = f8 * (long) g9_19;
|
||||
long f9g0 = f9 * (long) g0;
|
||||
long f9g1_38 = f9_2 * (long) g1_19;
|
||||
long f9g2_19 = f9 * (long) g2_19;
|
||||
long f9g3_38 = f9_2 * (long) g3_19;
|
||||
long f9g4_19 = f9 * (long) g4_19;
|
||||
long f9g5_38 = f9_2 * (long) g5_19;
|
||||
long f9g6_19 = f9 * (long) g6_19;
|
||||
long f9g7_38 = f9_2 * (long) g7_19;
|
||||
long f9g8_19 = f9 * (long) g8_19;
|
||||
long f9g9_38 = f9_2 * (long) g9_19;
|
||||
long h0 = f0g0+f1g9_38+f2g8_19+f3g7_38+f4g6_19+f5g5_38+f6g4_19+f7g3_38+f8g2_19+f9g1_38;
|
||||
long h1 = f0g1+f1g0 +f2g9_19+f3g8_19+f4g7_19+f5g6_19+f6g5_19+f7g4_19+f8g3_19+f9g2_19;
|
||||
long h2 = f0g2+f1g1_2 +f2g0 +f3g9_38+f4g8_19+f5g7_38+f6g6_19+f7g5_38+f8g4_19+f9g3_38;
|
||||
long h3 = f0g3+f1g2 +f2g1 +f3g0 +f4g9_19+f5g8_19+f6g7_19+f7g6_19+f8g5_19+f9g4_19;
|
||||
long h4 = f0g4+f1g3_2 +f2g2 +f3g1_2 +f4g0 +f5g9_38+f6g8_19+f7g7_38+f8g6_19+f9g5_38;
|
||||
long h5 = f0g5+f1g4 +f2g3 +f3g2 +f4g1 +f5g0 +f6g9_19+f7g8_19+f8g7_19+f9g6_19;
|
||||
long h6 = f0g6+f1g5_2 +f2g4 +f3g3_2 +f4g2 +f5g1_2 +f6g0 +f7g9_38+f8g8_19+f9g7_38;
|
||||
long h7 = f0g7+f1g6 +f2g5 +f3g4 +f4g3 +f5g2 +f6g1 +f7g0 +f8g9_19+f9g8_19;
|
||||
long h8 = f0g8+f1g7_2 +f2g6 +f3g5_2 +f4g4 +f5g3_2 +f6g2 +f7g1_2 +f8g0 +f9g9_38;
|
||||
long h9 = f0g9+f1g8 +f2g7 +f3g6 +f4g5 +f5g4 +f6g3 +f7g2 +f8g1 +f9g0 ;
|
||||
long carry0;
|
||||
long carry1;
|
||||
long carry2;
|
||||
long carry3;
|
||||
long carry4;
|
||||
long carry5;
|
||||
long carry6;
|
||||
long carry7;
|
||||
long carry8;
|
||||
long carry9;
|
||||
|
||||
/*
|
||||
|h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38))
|
||||
i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8
|
||||
|h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19))
|
||||
i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9
|
||||
*/
|
||||
|
||||
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||
/* |h0| <= 2^25 */
|
||||
/* |h4| <= 2^25 */
|
||||
/* |h1| <= 1.71*2^59 */
|
||||
/* |h5| <= 1.71*2^59 */
|
||||
|
||||
carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
|
||||
carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
|
||||
/* |h1| <= 2^24; from now on fits into int32 */
|
||||
/* |h5| <= 2^24; from now on fits into int32 */
|
||||
/* |h2| <= 1.41*2^60 */
|
||||
/* |h6| <= 1.41*2^60 */
|
||||
|
||||
carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
|
||||
carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
|
||||
/* |h2| <= 2^25; from now on fits into int32 unchanged */
|
||||
/* |h6| <= 2^25; from now on fits into int32 unchanged */
|
||||
/* |h3| <= 1.71*2^59 */
|
||||
/* |h7| <= 1.71*2^59 */
|
||||
|
||||
carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
|
||||
carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
|
||||
/* |h3| <= 2^24; from now on fits into int32 unchanged */
|
||||
/* |h7| <= 2^24; from now on fits into int32 unchanged */
|
||||
/* |h4| <= 1.72*2^34 */
|
||||
/* |h8| <= 1.41*2^60 */
|
||||
|
||||
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||
carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
|
||||
/* |h4| <= 2^25; from now on fits into int32 unchanged */
|
||||
/* |h8| <= 2^25; from now on fits into int32 unchanged */
|
||||
/* |h5| <= 1.01*2^24 */
|
||||
/* |h9| <= 1.71*2^59 */
|
||||
|
||||
carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
|
||||
/* |h9| <= 2^24; from now on fits into int32 unchanged */
|
||||
/* |h0| <= 1.1*2^39 */
|
||||
|
||||
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||
/* |h0| <= 2^25; from now on fits into int32 unchanged */
|
||||
/* |h1| <= 1.01*2^24 */
|
||||
|
||||
h[0] = (int)h0;
|
||||
h[1] = (int)h1;
|
||||
h[2] = (int)h2;
|
||||
h[3] = (int)h3;
|
||||
h[4] = (int)h4;
|
||||
h[5] = (int)h5;
|
||||
h[6] = (int)h6;
|
||||
h[7] = (int)h7;
|
||||
h[8] = (int)h8;
|
||||
h[9] = (int)h9;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_mul121666 {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
//CONVERT #include "long.h"
|
||||
|
||||
/*
|
||||
h = f * 121666
|
||||
Can overlap h with f.
|
||||
|
||||
Preconditions:
|
||||
|f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
|
||||
Postconditions:
|
||||
|h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_mul121666(int[] h,int[] f)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
long h0 = f0 * (long) 121666;
|
||||
long h1 = f1 * (long) 121666;
|
||||
long h2 = f2 * (long) 121666;
|
||||
long h3 = f3 * (long) 121666;
|
||||
long h4 = f4 * (long) 121666;
|
||||
long h5 = f5 * (long) 121666;
|
||||
long h6 = f6 * (long) 121666;
|
||||
long h7 = f7 * (long) 121666;
|
||||
long h8 = f8 * (long) 121666;
|
||||
long h9 = f9 * (long) 121666;
|
||||
long carry0;
|
||||
long carry1;
|
||||
long carry2;
|
||||
long carry3;
|
||||
long carry4;
|
||||
long carry5;
|
||||
long carry6;
|
||||
long carry7;
|
||||
long carry8;
|
||||
long carry9;
|
||||
|
||||
carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
|
||||
carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
|
||||
carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
|
||||
carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
|
||||
carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
|
||||
|
||||
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||
carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
|
||||
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||
carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
|
||||
carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
|
||||
|
||||
h[0] = (int)h0;
|
||||
h[1] = (int)h1;
|
||||
h[2] = (int)h2;
|
||||
h[3] = (int)h3;
|
||||
h[4] = (int)h4;
|
||||
h[5] = (int)h5;
|
||||
h[6] = (int)h6;
|
||||
h[7] = (int)h7;
|
||||
h[8] = (int)h8;
|
||||
h[9] = (int)h9;
|
||||
}
|
||||
|
||||
|
||||
}
|
53
src/main/java/org/whispersystems/curve25519/java/fe_neg.java
Normal file
53
src/main/java/org/whispersystems/curve25519/java/fe_neg.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_neg {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
h = -f
|
||||
|
||||
Preconditions:
|
||||
|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
|
||||
Postconditions:
|
||||
|h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_neg(int[] h,int[] f)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
int h0 = -f0;
|
||||
int h1 = -f1;
|
||||
int h2 = -f2;
|
||||
int h3 = -f3;
|
||||
int h4 = -f4;
|
||||
int h5 = -f5;
|
||||
int h6 = -f6;
|
||||
int h7 = -f7;
|
||||
int h8 = -f8;
|
||||
int h9 = -f9;
|
||||
h[0] = (int)h0;
|
||||
h[1] = (int)h1;
|
||||
h[2] = (int)h2;
|
||||
h[3] = (int)h3;
|
||||
h[4] = (int)h4;
|
||||
h[5] = (int)h5;
|
||||
h[6] = (int)h6;
|
||||
h[7] = (int)h7;
|
||||
h[8] = (int)h8;
|
||||
h[9] = (int)h9;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,181 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_pow22523 {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_pow22523(int[] out,int[] z)
|
||||
{
|
||||
int[] t0 = new int[10];
|
||||
int[] t1 = new int[10];
|
||||
int[] t2 = new int[10];
|
||||
int i;
|
||||
|
||||
//CONVERT #include "pow22523.h"
|
||||
|
||||
/* qhasm: fe z1 */
|
||||
|
||||
/* qhasm: fe z2 */
|
||||
|
||||
/* qhasm: fe z8 */
|
||||
|
||||
/* qhasm: fe z9 */
|
||||
|
||||
/* qhasm: fe z11 */
|
||||
|
||||
/* qhasm: fe z22 */
|
||||
|
||||
/* qhasm: fe z_5_0 */
|
||||
|
||||
/* qhasm: fe z_10_5 */
|
||||
|
||||
/* qhasm: fe z_10_0 */
|
||||
|
||||
/* qhasm: fe z_20_10 */
|
||||
|
||||
/* qhasm: fe z_20_0 */
|
||||
|
||||
/* qhasm: fe z_40_20 */
|
||||
|
||||
/* qhasm: fe z_40_0 */
|
||||
|
||||
/* qhasm: fe z_50_10 */
|
||||
|
||||
/* qhasm: fe z_50_0 */
|
||||
|
||||
/* qhasm: fe z_100_50 */
|
||||
|
||||
/* qhasm: fe z_100_0 */
|
||||
|
||||
/* qhasm: fe z_200_100 */
|
||||
|
||||
/* qhasm: fe z_200_0 */
|
||||
|
||||
/* qhasm: fe z_250_50 */
|
||||
|
||||
/* qhasm: fe z_250_0 */
|
||||
|
||||
/* qhasm: fe z_252_2 */
|
||||
|
||||
/* qhasm: fe z_252_3 */
|
||||
|
||||
/* qhasm: enter pow22523 */
|
||||
|
||||
/* qhasm: z2 = z1^2^1 */
|
||||
/* asm 1: fe_sq.fe_sq(>z2=fe#1,<z1=fe#11); for (i = 1;i < 1;++i) fe_sq.fe_sq(>z2=fe#1,>z2=fe#1); */
|
||||
/* asm 2: fe_sq.fe_sq(>z2=t0,<z1=z); for (i = 1;i < 1;++i) fe_sq.fe_sq(>z2=t0,>z2=t0); */
|
||||
fe_sq.fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq.fe_sq(t0,t0);
|
||||
|
||||
/* qhasm: z8 = z2^2^2 */
|
||||
/* asm 1: fe_sq.fe_sq(>z8=fe#2,<z2=fe#1); for (i = 1;i < 2;++i) fe_sq.fe_sq(>z8=fe#2,>z8=fe#2); */
|
||||
/* asm 2: fe_sq.fe_sq(>z8=t1,<z2=t0); for (i = 1;i < 2;++i) fe_sq.fe_sq(>z8=t1,>z8=t1); */
|
||||
fe_sq.fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq.fe_sq(t1,t1);
|
||||
|
||||
/* qhasm: z9 = z1*z8 */
|
||||
/* asm 1: fe_mul.fe_mul(>z9=fe#2,<z1=fe#11,<z8=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z9=t1,<z1=z,<z8=t1); */
|
||||
fe_mul.fe_mul(t1,z,t1);
|
||||
|
||||
/* qhasm: z11 = z2*z9 */
|
||||
/* asm 1: fe_mul.fe_mul(>z11=fe#1,<z2=fe#1,<z9=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z11=t0,<z2=t0,<z9=t1); */
|
||||
fe_mul.fe_mul(t0,t0,t1);
|
||||
|
||||
/* qhasm: z22 = z11^2^1 */
|
||||
/* asm 1: fe_sq.fe_sq(>z22=fe#1,<z11=fe#1); for (i = 1;i < 1;++i) fe_sq.fe_sq(>z22=fe#1,>z22=fe#1); */
|
||||
/* asm 2: fe_sq.fe_sq(>z22=t0,<z11=t0); for (i = 1;i < 1;++i) fe_sq.fe_sq(>z22=t0,>z22=t0); */
|
||||
fe_sq.fe_sq(t0,t0); for (i = 1;i < 1;++i) fe_sq.fe_sq(t0,t0);
|
||||
|
||||
/* qhasm: z_5_0 = z9*z22 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_5_0=fe#1,<z9=fe#2,<z22=fe#1); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_5_0=t0,<z9=t1,<z22=t0); */
|
||||
fe_mul.fe_mul(t0,t1,t0);
|
||||
|
||||
/* qhasm: z_10_5 = z_5_0^2^5 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_10_5=fe#2,<z_5_0=fe#1); for (i = 1;i < 5;++i) fe_sq.fe_sq(>z_10_5=fe#2,>z_10_5=fe#2); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_10_5=t1,<z_5_0=t0); for (i = 1;i < 5;++i) fe_sq.fe_sq(>z_10_5=t1,>z_10_5=t1); */
|
||||
fe_sq.fe_sq(t1,t0); for (i = 1;i < 5;++i) fe_sq.fe_sq(t1,t1);
|
||||
|
||||
/* qhasm: z_10_0 = z_10_5*z_5_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_10_0=fe#1,<z_10_5=fe#2,<z_5_0=fe#1); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_10_0=t0,<z_10_5=t1,<z_5_0=t0); */
|
||||
fe_mul.fe_mul(t0,t1,t0);
|
||||
|
||||
/* qhasm: z_20_10 = z_10_0^2^10 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_20_10=fe#2,<z_10_0=fe#1); for (i = 1;i < 10;++i) fe_sq.fe_sq(>z_20_10=fe#2,>z_20_10=fe#2); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_20_10=t1,<z_10_0=t0); for (i = 1;i < 10;++i) fe_sq.fe_sq(>z_20_10=t1,>z_20_10=t1); */
|
||||
fe_sq.fe_sq(t1,t0); for (i = 1;i < 10;++i) fe_sq.fe_sq(t1,t1);
|
||||
|
||||
/* qhasm: z_20_0 = z_20_10*z_10_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_20_0=fe#2,<z_20_10=fe#2,<z_10_0=fe#1); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_20_0=t1,<z_20_10=t1,<z_10_0=t0); */
|
||||
fe_mul.fe_mul(t1,t1,t0);
|
||||
|
||||
/* qhasm: z_40_20 = z_20_0^2^20 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_40_20=fe#3,<z_20_0=fe#2); for (i = 1;i < 20;++i) fe_sq.fe_sq(>z_40_20=fe#3,>z_40_20=fe#3); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_40_20=t2,<z_20_0=t1); for (i = 1;i < 20;++i) fe_sq.fe_sq(>z_40_20=t2,>z_40_20=t2); */
|
||||
fe_sq.fe_sq(t2,t1); for (i = 1;i < 20;++i) fe_sq.fe_sq(t2,t2);
|
||||
|
||||
/* qhasm: z_40_0 = z_40_20*z_20_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_40_0=fe#2,<z_40_20=fe#3,<z_20_0=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_40_0=t1,<z_40_20=t2,<z_20_0=t1); */
|
||||
fe_mul.fe_mul(t1,t2,t1);
|
||||
|
||||
/* qhasm: z_50_10 = z_40_0^2^10 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_50_10=fe#2,<z_40_0=fe#2); for (i = 1;i < 10;++i) fe_sq.fe_sq(>z_50_10=fe#2,>z_50_10=fe#2); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_50_10=t1,<z_40_0=t1); for (i = 1;i < 10;++i) fe_sq.fe_sq(>z_50_10=t1,>z_50_10=t1); */
|
||||
fe_sq.fe_sq(t1,t1); for (i = 1;i < 10;++i) fe_sq.fe_sq(t1,t1);
|
||||
|
||||
/* qhasm: z_50_0 = z_50_10*z_10_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_50_0=fe#1,<z_50_10=fe#2,<z_10_0=fe#1); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_50_0=t0,<z_50_10=t1,<z_10_0=t0); */
|
||||
fe_mul.fe_mul(t0,t1,t0);
|
||||
|
||||
/* qhasm: z_100_50 = z_50_0^2^50 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_100_50=fe#2,<z_50_0=fe#1); for (i = 1;i < 50;++i) fe_sq.fe_sq(>z_100_50=fe#2,>z_100_50=fe#2); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_100_50=t1,<z_50_0=t0); for (i = 1;i < 50;++i) fe_sq.fe_sq(>z_100_50=t1,>z_100_50=t1); */
|
||||
fe_sq.fe_sq(t1,t0); for (i = 1;i < 50;++i) fe_sq.fe_sq(t1,t1);
|
||||
|
||||
/* qhasm: z_100_0 = z_100_50*z_50_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_100_0=fe#2,<z_100_50=fe#2,<z_50_0=fe#1); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_100_0=t1,<z_100_50=t1,<z_50_0=t0); */
|
||||
fe_mul.fe_mul(t1,t1,t0);
|
||||
|
||||
/* qhasm: z_200_100 = z_100_0^2^100 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_200_100=fe#3,<z_100_0=fe#2); for (i = 1;i < 100;++i) fe_sq.fe_sq(>z_200_100=fe#3,>z_200_100=fe#3); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_200_100=t2,<z_100_0=t1); for (i = 1;i < 100;++i) fe_sq.fe_sq(>z_200_100=t2,>z_200_100=t2); */
|
||||
fe_sq.fe_sq(t2,t1); for (i = 1;i < 100;++i) fe_sq.fe_sq(t2,t2);
|
||||
|
||||
/* qhasm: z_200_0 = z_200_100*z_100_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_200_0=fe#2,<z_200_100=fe#3,<z_100_0=fe#2); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_200_0=t1,<z_200_100=t2,<z_100_0=t1); */
|
||||
fe_mul.fe_mul(t1,t2,t1);
|
||||
|
||||
/* qhasm: z_250_50 = z_200_0^2^50 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_250_50=fe#2,<z_200_0=fe#2); for (i = 1;i < 50;++i) fe_sq.fe_sq(>z_250_50=fe#2,>z_250_50=fe#2); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_250_50=t1,<z_200_0=t1); for (i = 1;i < 50;++i) fe_sq.fe_sq(>z_250_50=t1,>z_250_50=t1); */
|
||||
fe_sq.fe_sq(t1,t1); for (i = 1;i < 50;++i) fe_sq.fe_sq(t1,t1);
|
||||
|
||||
/* qhasm: z_250_0 = z_250_50*z_50_0 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_250_0=fe#1,<z_250_50=fe#2,<z_50_0=fe#1); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_250_0=t0,<z_250_50=t1,<z_50_0=t0); */
|
||||
fe_mul.fe_mul(t0,t1,t0);
|
||||
|
||||
/* qhasm: z_252_2 = z_250_0^2^2 */
|
||||
/* asm 1: fe_sq.fe_sq(>z_252_2=fe#1,<z_250_0=fe#1); for (i = 1;i < 2;++i) fe_sq.fe_sq(>z_252_2=fe#1,>z_252_2=fe#1); */
|
||||
/* asm 2: fe_sq.fe_sq(>z_252_2=t0,<z_250_0=t0); for (i = 1;i < 2;++i) fe_sq.fe_sq(>z_252_2=t0,>z_252_2=t0); */
|
||||
fe_sq.fe_sq(t0,t0); for (i = 1;i < 2;++i) fe_sq.fe_sq(t0,t0);
|
||||
|
||||
/* qhasm: z_252_3 = z_252_2*z1 */
|
||||
/* asm 1: fe_mul.fe_mul(>z_252_3=fe#12,<z_252_2=fe#1,<z1=fe#11); */
|
||||
/* asm 2: fe_mul.fe_mul(>z_252_3=out,<z_252_2=t0,<z1=z); */
|
||||
fe_mul.fe_mul(out,t0,z);
|
||||
|
||||
/* qhasm: return */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
157
src/main/java/org/whispersystems/curve25519/java/fe_sq.java
Normal file
157
src/main/java/org/whispersystems/curve25519/java/fe_sq.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_sq {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
//CONVERT #include "long.h"
|
||||
|
||||
/*
|
||||
h = f * f
|
||||
Can overlap h with f.
|
||||
|
||||
Preconditions:
|
||||
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|
||||
|
||||
Postconditions:
|
||||
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
|
||||
*/
|
||||
|
||||
/*
|
||||
See fe_mul.c for discussion of implementation strategy.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_sq(int[] h,int[] f)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
int f0_2 = 2 * f0;
|
||||
int f1_2 = 2 * f1;
|
||||
int f2_2 = 2 * f2;
|
||||
int f3_2 = 2 * f3;
|
||||
int f4_2 = 2 * f4;
|
||||
int f5_2 = 2 * f5;
|
||||
int f6_2 = 2 * f6;
|
||||
int f7_2 = 2 * f7;
|
||||
int f5_38 = 38 * f5; /* 1.959375*2^30 */
|
||||
int f6_19 = 19 * f6; /* 1.959375*2^30 */
|
||||
int f7_38 = 38 * f7; /* 1.959375*2^30 */
|
||||
int f8_19 = 19 * f8; /* 1.959375*2^30 */
|
||||
int f9_38 = 38 * f9; /* 1.959375*2^30 */
|
||||
long f0f0 = f0 * (long) f0;
|
||||
long f0f1_2 = f0_2 * (long) f1;
|
||||
long f0f2_2 = f0_2 * (long) f2;
|
||||
long f0f3_2 = f0_2 * (long) f3;
|
||||
long f0f4_2 = f0_2 * (long) f4;
|
||||
long f0f5_2 = f0_2 * (long) f5;
|
||||
long f0f6_2 = f0_2 * (long) f6;
|
||||
long f0f7_2 = f0_2 * (long) f7;
|
||||
long f0f8_2 = f0_2 * (long) f8;
|
||||
long f0f9_2 = f0_2 * (long) f9;
|
||||
long f1f1_2 = f1_2 * (long) f1;
|
||||
long f1f2_2 = f1_2 * (long) f2;
|
||||
long f1f3_4 = f1_2 * (long) f3_2;
|
||||
long f1f4_2 = f1_2 * (long) f4;
|
||||
long f1f5_4 = f1_2 * (long) f5_2;
|
||||
long f1f6_2 = f1_2 * (long) f6;
|
||||
long f1f7_4 = f1_2 * (long) f7_2;
|
||||
long f1f8_2 = f1_2 * (long) f8;
|
||||
long f1f9_76 = f1_2 * (long) f9_38;
|
||||
long f2f2 = f2 * (long) f2;
|
||||
long f2f3_2 = f2_2 * (long) f3;
|
||||
long f2f4_2 = f2_2 * (long) f4;
|
||||
long f2f5_2 = f2_2 * (long) f5;
|
||||
long f2f6_2 = f2_2 * (long) f6;
|
||||
long f2f7_2 = f2_2 * (long) f7;
|
||||
long f2f8_38 = f2_2 * (long) f8_19;
|
||||
long f2f9_38 = f2 * (long) f9_38;
|
||||
long f3f3_2 = f3_2 * (long) f3;
|
||||
long f3f4_2 = f3_2 * (long) f4;
|
||||
long f3f5_4 = f3_2 * (long) f5_2;
|
||||
long f3f6_2 = f3_2 * (long) f6;
|
||||
long f3f7_76 = f3_2 * (long) f7_38;
|
||||
long f3f8_38 = f3_2 * (long) f8_19;
|
||||
long f3f9_76 = f3_2 * (long) f9_38;
|
||||
long f4f4 = f4 * (long) f4;
|
||||
long f4f5_2 = f4_2 * (long) f5;
|
||||
long f4f6_38 = f4_2 * (long) f6_19;
|
||||
long f4f7_38 = f4 * (long) f7_38;
|
||||
long f4f8_38 = f4_2 * (long) f8_19;
|
||||
long f4f9_38 = f4 * (long) f9_38;
|
||||
long f5f5_38 = f5 * (long) f5_38;
|
||||
long f5f6_38 = f5_2 * (long) f6_19;
|
||||
long f5f7_76 = f5_2 * (long) f7_38;
|
||||
long f5f8_38 = f5_2 * (long) f8_19;
|
||||
long f5f9_76 = f5_2 * (long) f9_38;
|
||||
long f6f6_19 = f6 * (long) f6_19;
|
||||
long f6f7_38 = f6 * (long) f7_38;
|
||||
long f6f8_38 = f6_2 * (long) f8_19;
|
||||
long f6f9_38 = f6 * (long) f9_38;
|
||||
long f7f7_38 = f7 * (long) f7_38;
|
||||
long f7f8_38 = f7_2 * (long) f8_19;
|
||||
long f7f9_76 = f7_2 * (long) f9_38;
|
||||
long f8f8_19 = f8 * (long) f8_19;
|
||||
long f8f9_38 = f8 * (long) f9_38;
|
||||
long f9f9_38 = f9 * (long) f9_38;
|
||||
long h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
|
||||
long h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
|
||||
long h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
|
||||
long h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
|
||||
long h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38;
|
||||
long h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
|
||||
long h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
|
||||
long h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
|
||||
long h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38;
|
||||
long h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
|
||||
long carry0;
|
||||
long carry1;
|
||||
long carry2;
|
||||
long carry3;
|
||||
long carry4;
|
||||
long carry5;
|
||||
long carry6;
|
||||
long carry7;
|
||||
long carry8;
|
||||
long carry9;
|
||||
|
||||
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||
|
||||
carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
|
||||
carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
|
||||
|
||||
carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
|
||||
carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
|
||||
|
||||
carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
|
||||
carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
|
||||
|
||||
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||
carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
|
||||
|
||||
carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
|
||||
|
||||
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||
|
||||
h[0] = (int)h0;
|
||||
h[1] = (int)h1;
|
||||
h[2] = (int)h2;
|
||||
h[3] = (int)h3;
|
||||
h[4] = (int)h4;
|
||||
h[5] = (int)h5;
|
||||
h[6] = (int)h6;
|
||||
h[7] = (int)h7;
|
||||
h[8] = (int)h8;
|
||||
h[9] = (int)h9;
|
||||
}
|
||||
|
||||
|
||||
}
|
168
src/main/java/org/whispersystems/curve25519/java/fe_sq2.java
Normal file
168
src/main/java/org/whispersystems/curve25519/java/fe_sq2.java
Normal file
@@ -0,0 +1,168 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_sq2 {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
//CONVERT #include "long.h"
|
||||
|
||||
/*
|
||||
h = 2 * f * f
|
||||
Can overlap h with f.
|
||||
|
||||
Preconditions:
|
||||
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|
||||
|
||||
Postconditions:
|
||||
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
|
||||
*/
|
||||
|
||||
/*
|
||||
See fe_mul.c for discussion of implementation strategy.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_sq2(int[] h,int[] f)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
int f0_2 = 2 * f0;
|
||||
int f1_2 = 2 * f1;
|
||||
int f2_2 = 2 * f2;
|
||||
int f3_2 = 2 * f3;
|
||||
int f4_2 = 2 * f4;
|
||||
int f5_2 = 2 * f5;
|
||||
int f6_2 = 2 * f6;
|
||||
int f7_2 = 2 * f7;
|
||||
int f5_38 = 38 * f5; /* 1.959375*2^30 */
|
||||
int f6_19 = 19 * f6; /* 1.959375*2^30 */
|
||||
int f7_38 = 38 * f7; /* 1.959375*2^30 */
|
||||
int f8_19 = 19 * f8; /* 1.959375*2^30 */
|
||||
int f9_38 = 38 * f9; /* 1.959375*2^30 */
|
||||
long f0f0 = f0 * (long) f0;
|
||||
long f0f1_2 = f0_2 * (long) f1;
|
||||
long f0f2_2 = f0_2 * (long) f2;
|
||||
long f0f3_2 = f0_2 * (long) f3;
|
||||
long f0f4_2 = f0_2 * (long) f4;
|
||||
long f0f5_2 = f0_2 * (long) f5;
|
||||
long f0f6_2 = f0_2 * (long) f6;
|
||||
long f0f7_2 = f0_2 * (long) f7;
|
||||
long f0f8_2 = f0_2 * (long) f8;
|
||||
long f0f9_2 = f0_2 * (long) f9;
|
||||
long f1f1_2 = f1_2 * (long) f1;
|
||||
long f1f2_2 = f1_2 * (long) f2;
|
||||
long f1f3_4 = f1_2 * (long) f3_2;
|
||||
long f1f4_2 = f1_2 * (long) f4;
|
||||
long f1f5_4 = f1_2 * (long) f5_2;
|
||||
long f1f6_2 = f1_2 * (long) f6;
|
||||
long f1f7_4 = f1_2 * (long) f7_2;
|
||||
long f1f8_2 = f1_2 * (long) f8;
|
||||
long f1f9_76 = f1_2 * (long) f9_38;
|
||||
long f2f2 = f2 * (long) f2;
|
||||
long f2f3_2 = f2_2 * (long) f3;
|
||||
long f2f4_2 = f2_2 * (long) f4;
|
||||
long f2f5_2 = f2_2 * (long) f5;
|
||||
long f2f6_2 = f2_2 * (long) f6;
|
||||
long f2f7_2 = f2_2 * (long) f7;
|
||||
long f2f8_38 = f2_2 * (long) f8_19;
|
||||
long f2f9_38 = f2 * (long) f9_38;
|
||||
long f3f3_2 = f3_2 * (long) f3;
|
||||
long f3f4_2 = f3_2 * (long) f4;
|
||||
long f3f5_4 = f3_2 * (long) f5_2;
|
||||
long f3f6_2 = f3_2 * (long) f6;
|
||||
long f3f7_76 = f3_2 * (long) f7_38;
|
||||
long f3f8_38 = f3_2 * (long) f8_19;
|
||||
long f3f9_76 = f3_2 * (long) f9_38;
|
||||
long f4f4 = f4 * (long) f4;
|
||||
long f4f5_2 = f4_2 * (long) f5;
|
||||
long f4f6_38 = f4_2 * (long) f6_19;
|
||||
long f4f7_38 = f4 * (long) f7_38;
|
||||
long f4f8_38 = f4_2 * (long) f8_19;
|
||||
long f4f9_38 = f4 * (long) f9_38;
|
||||
long f5f5_38 = f5 * (long) f5_38;
|
||||
long f5f6_38 = f5_2 * (long) f6_19;
|
||||
long f5f7_76 = f5_2 * (long) f7_38;
|
||||
long f5f8_38 = f5_2 * (long) f8_19;
|
||||
long f5f9_76 = f5_2 * (long) f9_38;
|
||||
long f6f6_19 = f6 * (long) f6_19;
|
||||
long f6f7_38 = f6 * (long) f7_38;
|
||||
long f6f8_38 = f6_2 * (long) f8_19;
|
||||
long f6f9_38 = f6 * (long) f9_38;
|
||||
long f7f7_38 = f7 * (long) f7_38;
|
||||
long f7f8_38 = f7_2 * (long) f8_19;
|
||||
long f7f9_76 = f7_2 * (long) f9_38;
|
||||
long f8f8_19 = f8 * (long) f8_19;
|
||||
long f8f9_38 = f8 * (long) f9_38;
|
||||
long f9f9_38 = f9 * (long) f9_38;
|
||||
long h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
|
||||
long h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
|
||||
long h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
|
||||
long h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
|
||||
long h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38;
|
||||
long h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
|
||||
long h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
|
||||
long h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
|
||||
long h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38;
|
||||
long h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
|
||||
long carry0;
|
||||
long carry1;
|
||||
long carry2;
|
||||
long carry3;
|
||||
long carry4;
|
||||
long carry5;
|
||||
long carry6;
|
||||
long carry7;
|
||||
long carry8;
|
||||
long carry9;
|
||||
|
||||
h0 += h0;
|
||||
h1 += h1;
|
||||
h2 += h2;
|
||||
h3 += h3;
|
||||
h4 += h4;
|
||||
h5 += h5;
|
||||
h6 += h6;
|
||||
h7 += h7;
|
||||
h8 += h8;
|
||||
h9 += h9;
|
||||
|
||||
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||
|
||||
carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
|
||||
carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
|
||||
|
||||
carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
|
||||
carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
|
||||
|
||||
carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
|
||||
carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
|
||||
|
||||
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||
carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
|
||||
|
||||
carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
|
||||
|
||||
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||
|
||||
h[0] = (int)h0;
|
||||
h[1] = (int)h1;
|
||||
h[2] = (int)h2;
|
||||
h[3] = (int)h3;
|
||||
h[4] = (int)h4;
|
||||
h[5] = (int)h5;
|
||||
h[6] = (int)h6;
|
||||
h[7] = (int)h7;
|
||||
h[8] = (int)h8;
|
||||
h[9] = (int)h9;
|
||||
}
|
||||
|
||||
|
||||
}
|
65
src/main/java/org/whispersystems/curve25519/java/fe_sub.java
Normal file
65
src/main/java/org/whispersystems/curve25519/java/fe_sub.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_sub {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
h = f - g
|
||||
Can overlap h with f or g.
|
||||
|
||||
Preconditions:
|
||||
|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
|
||||
Postconditions:
|
||||
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_sub(int[] h,int[] f,int[] g)
|
||||
{
|
||||
int f0 = f[0];
|
||||
int f1 = f[1];
|
||||
int f2 = f[2];
|
||||
int f3 = f[3];
|
||||
int f4 = f[4];
|
||||
int f5 = f[5];
|
||||
int f6 = f[6];
|
||||
int f7 = f[7];
|
||||
int f8 = f[8];
|
||||
int f9 = f[9];
|
||||
int g0 = g[0];
|
||||
int g1 = g[1];
|
||||
int g2 = g[2];
|
||||
int g3 = g[3];
|
||||
int g4 = g[4];
|
||||
int g5 = g[5];
|
||||
int g6 = g[6];
|
||||
int g7 = g[7];
|
||||
int g8 = g[8];
|
||||
int g9 = g[9];
|
||||
int h0 = f0 - g0;
|
||||
int h1 = f1 - g1;
|
||||
int h2 = f2 - g2;
|
||||
int h3 = f3 - g3;
|
||||
int h4 = f4 - g4;
|
||||
int h5 = f5 - g5;
|
||||
int h6 = f6 - g6;
|
||||
int h7 = f7 - g7;
|
||||
int h8 = f8 - g8;
|
||||
int h9 = f9 - g9;
|
||||
h[0] = (int)h0;
|
||||
h[1] = (int)h1;
|
||||
h[2] = (int)h2;
|
||||
h[3] = (int)h3;
|
||||
h[4] = (int)h4;
|
||||
h[5] = (int)h5;
|
||||
h[6] = (int)h6;
|
||||
h[7] = (int)h7;
|
||||
h[8] = (int)h8;
|
||||
h[9] = (int)h9;
|
||||
}
|
||||
|
||||
|
||||
}
|
127
src/main/java/org/whispersystems/curve25519/java/fe_tobytes.java
Normal file
127
src/main/java/org/whispersystems/curve25519/java/fe_tobytes.java
Normal file
@@ -0,0 +1,127 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class fe_tobytes {
|
||||
|
||||
//CONVERT #include "fe.h"
|
||||
|
||||
/*
|
||||
Preconditions:
|
||||
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
|
||||
Write p=2^255-19; q=floor(h/p).
|
||||
Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
|
||||
|
||||
Proof:
|
||||
Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
|
||||
Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4.
|
||||
|
||||
Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
|
||||
Then 0<y<1.
|
||||
|
||||
Write r=h-pq.
|
||||
Have 0<=r<=p-1=2^255-20.
|
||||
Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
|
||||
|
||||
Write x=r+19(2^-255)r+y.
|
||||
Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
|
||||
|
||||
Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
|
||||
so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void fe_tobytes(byte[] s,int[] h)
|
||||
{
|
||||
int h0 = h[0];
|
||||
int h1 = h[1];
|
||||
int h2 = h[2];
|
||||
int h3 = h[3];
|
||||
int h4 = h[4];
|
||||
int h5 = h[5];
|
||||
int h6 = h[6];
|
||||
int h7 = h[7];
|
||||
int h8 = h[8];
|
||||
int h9 = h[9];
|
||||
int q;
|
||||
int carry0;
|
||||
int carry1;
|
||||
int carry2;
|
||||
int carry3;
|
||||
int carry4;
|
||||
int carry5;
|
||||
int carry6;
|
||||
int carry7;
|
||||
int carry8;
|
||||
int carry9;
|
||||
|
||||
q = (19 * h9 + (((int) 1) << 24)) >> 25;
|
||||
q = (h0 + q) >> 26;
|
||||
q = (h1 + q) >> 25;
|
||||
q = (h2 + q) >> 26;
|
||||
q = (h3 + q) >> 25;
|
||||
q = (h4 + q) >> 26;
|
||||
q = (h5 + q) >> 25;
|
||||
q = (h6 + q) >> 26;
|
||||
q = (h7 + q) >> 25;
|
||||
q = (h8 + q) >> 26;
|
||||
q = (h9 + q) >> 25;
|
||||
|
||||
/* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */
|
||||
h0 += 19 * q;
|
||||
/* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */
|
||||
|
||||
carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||
carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25;
|
||||
carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26;
|
||||
carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25;
|
||||
carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||
carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25;
|
||||
carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26;
|
||||
carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25;
|
||||
carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26;
|
||||
carry9 = h9 >> 25; h9 -= carry9 << 25;
|
||||
/* h10 = carry9 */
|
||||
|
||||
/*
|
||||
Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
|
||||
Have h0+...+2^230 h9 between 0 and 2^255-1;
|
||||
evidently 2^255 h10-2^255 q = 0.
|
||||
Goal: Output h0+...+2^230 h9.
|
||||
*/
|
||||
|
||||
s[0] = (byte)(h0 >> 0);
|
||||
s[1] = (byte)(h0 >> 8);
|
||||
s[2] = (byte)(h0 >> 16);
|
||||
s[3] = (byte)((h0 >> 24) | (h1 << 2));
|
||||
s[4] = (byte)(h1 >> 6);
|
||||
s[5] = (byte)(h1 >> 14);
|
||||
s[6] = (byte)((h1 >> 22) | (h2 << 3));
|
||||
s[7] = (byte)(h2 >> 5);
|
||||
s[8] = (byte)(h2 >> 13);
|
||||
s[9] = (byte)((h2 >> 21) | (h3 << 5));
|
||||
s[10] = (byte)(h3 >> 3);
|
||||
s[11] = (byte)(h3 >> 11);
|
||||
s[12] = (byte)((h3 >> 19) | (h4 << 6));
|
||||
s[13] = (byte)(h4 >> 2);
|
||||
s[14] = (byte)(h4 >> 10);
|
||||
s[15] = (byte)(h4 >> 18);
|
||||
s[16] = (byte)(h5 >> 0);
|
||||
s[17] = (byte)(h5 >> 8);
|
||||
s[18] = (byte)(h5 >> 16);
|
||||
s[19] = (byte)((h5 >> 24) | (h6 << 1));
|
||||
s[20] = (byte)(h6 >> 7);
|
||||
s[21] = (byte)(h6 >> 15);
|
||||
s[22] = (byte)((h6 >> 23) | (h7 << 3));
|
||||
s[23] = (byte)(h7 >> 5);
|
||||
s[24] = (byte)(h7 >> 13);
|
||||
s[25] = (byte)((h7 >> 21) | (h8 << 4));
|
||||
s[26] = (byte)(h8 >> 4);
|
||||
s[27] = (byte)(h8 >> 12);
|
||||
s[28] = (byte)((h8 >> 20) | (h9 << 6));
|
||||
s[29] = (byte)(h9 >> 2);
|
||||
s[30] = (byte)(h9 >> 10);
|
||||
s[31] = (byte)(h9 >> 18);
|
||||
}
|
||||
|
||||
|
||||
}
|
116
src/main/java/org/whispersystems/curve25519/java/ge_add.java
Normal file
116
src/main/java/org/whispersystems/curve25519/java/ge_add.java
Normal file
@@ -0,0 +1,116 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class ge_add {
|
||||
|
||||
//CONVERT #include "ge.h"
|
||||
|
||||
/*
|
||||
r = p + q
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void ge_add(ge_p1p1 r,ge_p3 p,ge_cached q)
|
||||
{
|
||||
int[] t0 = new int[10];
|
||||
//CONVERT #include "ge_add.h"
|
||||
|
||||
/* qhasm: enter ge_add */
|
||||
|
||||
/* qhasm: fe X1 */
|
||||
|
||||
/* qhasm: fe Y1 */
|
||||
|
||||
/* qhasm: fe Z1 */
|
||||
|
||||
/* qhasm: fe Z2 */
|
||||
|
||||
/* qhasm: fe T1 */
|
||||
|
||||
/* qhasm: fe ZZ */
|
||||
|
||||
/* qhasm: fe YpX2 */
|
||||
|
||||
/* qhasm: fe YmX2 */
|
||||
|
||||
/* qhasm: fe T2d2 */
|
||||
|
||||
/* qhasm: fe X3 */
|
||||
|
||||
/* qhasm: fe Y3 */
|
||||
|
||||
/* qhasm: fe Z3 */
|
||||
|
||||
/* qhasm: fe T3 */
|
||||
|
||||
/* qhasm: fe YpX1 */
|
||||
|
||||
/* qhasm: fe YmX1 */
|
||||
|
||||
/* qhasm: fe A */
|
||||
|
||||
/* qhasm: fe B */
|
||||
|
||||
/* qhasm: fe C */
|
||||
|
||||
/* qhasm: fe D */
|
||||
|
||||
/* qhasm: YpX1 = Y1+X1 */
|
||||
/* asm 1: fe_add.fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
|
||||
/* asm 2: fe_add.fe_add(>YpX1=r.X,<Y1=p.Y,<X1=p.X); */
|
||||
fe_add.fe_add(r.X,p.Y,p.X);
|
||||
|
||||
/* qhasm: YmX1 = Y1-X1 */
|
||||
/* asm 1: fe_sub.fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
|
||||
/* asm 2: fe_sub.fe_sub(>YmX1=r.Y,<Y1=p.Y,<X1=p.X); */
|
||||
fe_sub.fe_sub(r.Y,p.Y,p.X);
|
||||
|
||||
/* qhasm: A = YpX1*YpX2 */
|
||||
/* asm 1: fe_mul.fe_mul(>A=fe#3,<YpX1=fe#1,<YpX2=fe#15); */
|
||||
/* asm 2: fe_mul.fe_mul(>A=r.Z,<YpX1=r.X,<YpX2=q.YplusX); */
|
||||
fe_mul.fe_mul(r.Z,r.X,q.YplusX);
|
||||
|
||||
/* qhasm: B = YmX1*YmX2 */
|
||||
/* asm 1: fe_mul.fe_mul(>B=fe#2,<YmX1=fe#2,<YmX2=fe#16); */
|
||||
/* asm 2: fe_mul.fe_mul(>B=r.Y,<YmX1=r.Y,<YmX2=q.YminusX); */
|
||||
fe_mul.fe_mul(r.Y,r.Y,q.YminusX);
|
||||
|
||||
/* qhasm: C = T2d2*T1 */
|
||||
/* asm 1: fe_mul.fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */
|
||||
/* asm 2: fe_mul.fe_mul(>C=r.T,<T2d2=q.T2d,<T1=p.T); */
|
||||
fe_mul.fe_mul(r.T,q.T2d,p.T);
|
||||
|
||||
/* qhasm: ZZ = Z1*Z2 */
|
||||
/* asm 1: fe_mul.fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */
|
||||
/* asm 2: fe_mul.fe_mul(>ZZ=r.X,<Z1=p.Z,<Z2=q.Z); */
|
||||
fe_mul.fe_mul(r.X,p.Z,q.Z);
|
||||
|
||||
/* qhasm: D = 2*ZZ */
|
||||
/* asm 1: fe_add.fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */
|
||||
/* asm 2: fe_add.fe_add(>D=t0,<ZZ=r.X,<ZZ=r.X); */
|
||||
fe_add.fe_add(t0,r.X,r.X);
|
||||
|
||||
/* qhasm: X3 = A-B */
|
||||
/* asm 1: fe_sub.fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
|
||||
/* asm 2: fe_sub.fe_sub(>X3=r.X,<A=r.Z,<B=r.Y); */
|
||||
fe_sub.fe_sub(r.X,r.Z,r.Y);
|
||||
|
||||
/* qhasm: Y3 = A+B */
|
||||
/* asm 1: fe_add.fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
|
||||
/* asm 2: fe_add.fe_add(>Y3=r.Y,<A=r.Z,<B=r.Y); */
|
||||
fe_add.fe_add(r.Y,r.Z,r.Y);
|
||||
|
||||
/* qhasm: Z3 = D+C */
|
||||
/* asm 1: fe_add.fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */
|
||||
/* asm 2: fe_add.fe_add(>Z3=r.Z,<D=t0,<C=r.T); */
|
||||
fe_add.fe_add(r.Z,t0,r.T);
|
||||
|
||||
/* qhasm: T3 = D-C */
|
||||
/* asm 1: fe_sub.fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */
|
||||
/* asm 2: fe_sub.fe_sub(>T3=r.T,<D=t0,<C=r.T); */
|
||||
fe_sub.fe_sub(r.T,t0,r.T);
|
||||
|
||||
/* qhasm: return */
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class ge_cached {
|
||||
|
||||
public int[] YplusX;
|
||||
public int[] YminusX;
|
||||
public int[] Z;
|
||||
public int[] T2d;
|
||||
|
||||
public ge_cached() {
|
||||
YplusX = new int[10];
|
||||
YminusX = new int[10];
|
||||
Z = new int[10];
|
||||
T2d = new int[10];
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,149 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class ge_double_scalarmult {
|
||||
|
||||
//CONVERT #include "ge.h"
|
||||
|
||||
static public void slide(byte[] r,byte[] a)
|
||||
{
|
||||
int i;
|
||||
int b;
|
||||
int k;
|
||||
|
||||
for (i = 0;i < 256;++i) {
|
||||
//CONVERT r[i] = 1 & (a[i >> 3] >> (i & 7));
|
||||
r[i] = (byte)(1 & (a[i >> 3] >>> (i & 7)));
|
||||
}
|
||||
|
||||
for (i = 0;i < 256;++i)
|
||||
if (r[i] != 0) {
|
||||
for (b = 1;b <= 6 && i + b < 256;++b) {
|
||||
if (r[i + b] != 0) {
|
||||
if (r[i] + (r[i + b] << b) <= 15) {
|
||||
r[i] += r[i + b] << b; r[i + b] = 0;
|
||||
} else if (r[i] - (r[i + b] << b) >= -15) {
|
||||
r[i] -= r[i + b] << b;
|
||||
for (k = i + b;k < 256;++k) {
|
||||
if (r[k] == 0) {
|
||||
r[k] = 1;
|
||||
break;
|
||||
}
|
||||
r[k] = 0;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static ge_precomp Bi[];
|
||||
|
||||
static {
|
||||
Bi = new ge_precomp[8];
|
||||
Bi[0] = new ge_precomp(
|
||||
new int[]{ 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 },
|
||||
new int[]{ -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 },
|
||||
new int[]{ -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 }
|
||||
);
|
||||
Bi[1] = new ge_precomp(
|
||||
new int[]{ 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 },
|
||||
new int[]{ 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 },
|
||||
new int[]{ 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 }
|
||||
);
|
||||
Bi[2] = new ge_precomp(
|
||||
new int[]{ 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 },
|
||||
new int[]{ 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 },
|
||||
new int[]{ 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 }
|
||||
);
|
||||
Bi[3] = new ge_precomp(
|
||||
new int[]{ 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 },
|
||||
new int[]{ -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 },
|
||||
new int[]{ 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 }
|
||||
);
|
||||
Bi[4] = new ge_precomp(
|
||||
new int[]{ -22518993,-6692182,14201702,-8745502,-23510406,8844726,18474211,-1361450,-13062696,13821877 },
|
||||
new int[]{ -6455177,-7839871,3374702,-4740862,-27098617,-10571707,31655028,-7212327,18853322,-14220951 },
|
||||
new int[]{ 4566830,-12963868,-28974889,-12240689,-7602672,-2830569,-8514358,-10431137,2207753,-3209784 }
|
||||
);
|
||||
Bi[5] = new ge_precomp(
|
||||
new int[]{ -25154831,-4185821,29681144,7868801,-6854661,-9423865,-12437364,-663000,-31111463,-16132436 },
|
||||
new int[]{ 25576264,-2703214,7349804,-11814844,16472782,9300885,3844789,15725684,171356,6466918 },
|
||||
new int[]{ 23103977,13316479,9739013,-16149481,817875,-15038942,8965339,-14088058,-30714912,16193877 }
|
||||
);
|
||||
Bi[6] = new ge_precomp(
|
||||
new int[]{ -33521811,3180713,-2394130,14003687,-16903474,-16270840,17238398,4729455,-18074513,9256800 },
|
||||
new int[]{ -25182317,-4174131,32336398,5036987,-21236817,11360617,22616405,9761698,-19827198,630305 },
|
||||
new int[]{ -13720693,2639453,-24237460,-7406481,9494427,-5774029,-6554551,-15960994,-2449256,-14291300 }
|
||||
);
|
||||
Bi[7] = new ge_precomp(
|
||||
new int[]{ -3151181,-5046075,9282714,6866145,-31907062,-863023,-18940575,15033784,25105118,-7894876 },
|
||||
new int[]{ -24326370,15950226,-31801215,-14592823,-11662737,-5090925,1573892,-2625887,2198790,-15804619 },
|
||||
new int[]{ -3099351,10324967,-2241613,7453183,-5446979,-2735503,-13812022,-16236442,-32461234,-12290683 }
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
r = a * A + b * B
|
||||
where a = a[0]+256*a[1]+...+256^31 a[31].
|
||||
and b = b[0]+256*b[1]+...+256^31 b[31].
|
||||
B is the Ed25519 base point (x,4/5) with x positive.
|
||||
*/
|
||||
|
||||
public static void ge_double_scalarmult_vartime(ge_p2 r,byte[] a,ge_p3 A,byte[] b)
|
||||
{
|
||||
byte[] aslide = new byte[256];
|
||||
byte[] bslide = new byte[256];
|
||||
ge_cached Ai[] = new ge_cached[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
|
||||
for (int count=0; count < 8; count++)
|
||||
Ai[count] = new ge_cached();
|
||||
ge_p1p1 t = new ge_p1p1();
|
||||
ge_p3 u = new ge_p3();
|
||||
ge_p3 A2 = new ge_p3();
|
||||
int i;
|
||||
|
||||
slide(aslide,a);
|
||||
slide(bslide,b);
|
||||
|
||||
ge_p3_to_cached.ge_p3_to_cached(Ai[0],A);
|
||||
ge_p3_dbl.ge_p3_dbl(t,A); ge_p1p1_to_p3.ge_p1p1_to_p3(A2,t);
|
||||
ge_add.ge_add(t,A2,Ai[0]); ge_p1p1_to_p3.ge_p1p1_to_p3(u,t); ge_p3_to_cached.ge_p3_to_cached(Ai[1],u);
|
||||
ge_add.ge_add(t,A2,Ai[1]); ge_p1p1_to_p3.ge_p1p1_to_p3(u,t); ge_p3_to_cached.ge_p3_to_cached(Ai[2],u);
|
||||
ge_add.ge_add(t,A2,Ai[2]); ge_p1p1_to_p3.ge_p1p1_to_p3(u,t); ge_p3_to_cached.ge_p3_to_cached(Ai[3],u);
|
||||
ge_add.ge_add(t,A2,Ai[3]); ge_p1p1_to_p3.ge_p1p1_to_p3(u,t); ge_p3_to_cached.ge_p3_to_cached(Ai[4],u);
|
||||
ge_add.ge_add(t,A2,Ai[4]); ge_p1p1_to_p3.ge_p1p1_to_p3(u,t); ge_p3_to_cached.ge_p3_to_cached(Ai[5],u);
|
||||
ge_add.ge_add(t,A2,Ai[5]); ge_p1p1_to_p3.ge_p1p1_to_p3(u,t); ge_p3_to_cached.ge_p3_to_cached(Ai[6],u);
|
||||
ge_add.ge_add(t,A2,Ai[6]); ge_p1p1_to_p3.ge_p1p1_to_p3(u,t); ge_p3_to_cached.ge_p3_to_cached(Ai[7],u);
|
||||
|
||||
ge_p2_0.ge_p2_0(r);
|
||||
|
||||
for (i = 255;i >= 0;--i) {
|
||||
if (aslide[i] !=0 || bslide[i] != 0) break;
|
||||
}
|
||||
|
||||
for (;i >= 0;--i) {
|
||||
ge_p2_dbl.ge_p2_dbl(t,r);
|
||||
|
||||
if (aslide[i] > 0) {
|
||||
ge_p1p1_to_p3.ge_p1p1_to_p3(u,t);
|
||||
ge_add.ge_add(t,u,Ai[aslide[i]/2]);
|
||||
} else if (aslide[i] < 0) {
|
||||
ge_p1p1_to_p3.ge_p1p1_to_p3(u,t);
|
||||
ge_sub.ge_sub(t,u,Ai[(-aslide[i])/2]);
|
||||
}
|
||||
|
||||
if (bslide[i] > 0) {
|
||||
ge_p1p1_to_p3.ge_p1p1_to_p3(u,t);
|
||||
ge_madd.ge_madd(t,u,Bi[bslide[i]/2]);
|
||||
} else if (bslide[i] < 0) {
|
||||
ge_p1p1_to_p3.ge_p1p1_to_p3(u,t);
|
||||
ge_msub.ge_msub(t,u,Bi[(-bslide[i])/2]);
|
||||
}
|
||||
|
||||
ge_p1p1_to_p2.ge_p1p1_to_p2(r,t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class ge_frombytes {
|
||||
|
||||
//CONVERT #include "ge.h"
|
||||
|
||||
static int[] d = {
|
||||
//CONVERT #include "d.h"
|
||||
-10913610,13857413,-15372611,6949391,114729,-8787816,-6275908,-3247719,-18696448,-12055116
|
||||
} ;
|
||||
|
||||
static int[] sqrtm1 = {
|
||||
//CONVERT #include "sqrtm1.h"
|
||||
-32595792,-7943725,9377950,3500415,12389472,-272473,-25146209,-2005654,326686,11406482
|
||||
} ;
|
||||
|
||||
public static int ge_frombytes_negate_vartime(ge_p3 h,byte[] s)
|
||||
{
|
||||
int[] u = new int[10];
|
||||
int[] v = new int[10];
|
||||
int[] v3 = new int[10];
|
||||
int[] vxx = new int[10];
|
||||
int[] check = new int[10];
|
||||
|
||||
fe_frombytes.fe_frombytes(h.Y,s);
|
||||
fe_1.fe_1(h.Z);
|
||||
fe_sq.fe_sq(u,h.Y);
|
||||
fe_mul.fe_mul(v,u,d);
|
||||
fe_sub.fe_sub(u,u,h.Z); /* u = y^2-1 */
|
||||
fe_add.fe_add(v,v,h.Z); /* v = dy^2+1 */
|
||||
|
||||
fe_sq.fe_sq(v3,v);
|
||||
fe_mul.fe_mul(v3,v3,v); /* v3 = v^3 */
|
||||
fe_sq.fe_sq(h.X,v3);
|
||||
fe_mul.fe_mul(h.X,h.X,v);
|
||||
fe_mul.fe_mul(h.X,h.X,u); /* x = uv^7 */
|
||||
|
||||
fe_pow22523.fe_pow22523(h.X,h.X); /* x = (uv^7)^((q-5)/8) */
|
||||
fe_mul.fe_mul(h.X,h.X,v3);
|
||||
fe_mul.fe_mul(h.X,h.X,u); /* x = uv^3(uv^7)^((q-5)/8) */
|
||||
|
||||
fe_sq.fe_sq(vxx,h.X);
|
||||
fe_mul.fe_mul(vxx,vxx,v);
|
||||
fe_sub.fe_sub(check,vxx,u); /* vx^2-u */
|
||||
if (fe_isnonzero.fe_isnonzero(check) != 0) {
|
||||
fe_add.fe_add(check,vxx,u); /* vx^2+u */
|
||||
if (fe_isnonzero.fe_isnonzero(check) != 0) return -1;
|
||||
fe_mul.fe_mul(h.X,h.X,sqrtm1);
|
||||
}
|
||||
|
||||
if (fe_isnegative.fe_isnegative(h.X) == ((s[31] >>> 7) & 0x01)) {
|
||||
fe_neg.fe_neg(h.X,h.X);
|
||||
}
|
||||
|
||||
fe_mul.fe_mul(h.T,h.X,h.Y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
107
src/main/java/org/whispersystems/curve25519/java/ge_madd.java
Normal file
107
src/main/java/org/whispersystems/curve25519/java/ge_madd.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class ge_madd {
|
||||
|
||||
//CONVERT #include "ge.h"
|
||||
|
||||
/*
|
||||
r = p + q
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void ge_madd(ge_p1p1 r,ge_p3 p,ge_precomp q)
|
||||
{
|
||||
int[] t0 = new int[10];
|
||||
//CONVERT #include "ge_madd.h"
|
||||
|
||||
/* qhasm: enter ge_madd */
|
||||
|
||||
/* qhasm: fe X1 */
|
||||
|
||||
/* qhasm: fe Y1 */
|
||||
|
||||
/* qhasm: fe Z1 */
|
||||
|
||||
/* qhasm: fe T1 */
|
||||
|
||||
/* qhasm: fe ypx2 */
|
||||
|
||||
/* qhasm: fe ymx2 */
|
||||
|
||||
/* qhasm: fe xy2d2 */
|
||||
|
||||
/* qhasm: fe X3 */
|
||||
|
||||
/* qhasm: fe Y3 */
|
||||
|
||||
/* qhasm: fe Z3 */
|
||||
|
||||
/* qhasm: fe T3 */
|
||||
|
||||
/* qhasm: fe YpX1 */
|
||||
|
||||
/* qhasm: fe YmX1 */
|
||||
|
||||
/* qhasm: fe A */
|
||||
|
||||
/* qhasm: fe B */
|
||||
|
||||
/* qhasm: fe C */
|
||||
|
||||
/* qhasm: fe D */
|
||||
|
||||
/* qhasm: YpX1 = Y1+X1 */
|
||||
/* asm 1: fe_add.fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
|
||||
/* asm 2: fe_add.fe_add(>YpX1=r.X,<Y1=p.Y,<X1=p.X); */
|
||||
fe_add.fe_add(r.X,p.Y,p.X);
|
||||
|
||||
/* qhasm: YmX1 = Y1-X1 */
|
||||
/* asm 1: fe_sub.fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
|
||||
/* asm 2: fe_sub.fe_sub(>YmX1=r.Y,<Y1=p.Y,<X1=p.X); */
|
||||
fe_sub.fe_sub(r.Y,p.Y,p.X);
|
||||
|
||||
/* qhasm: A = YpX1*ypx2 */
|
||||
/* asm 1: fe_mul.fe_mul(>A=fe#3,<YpX1=fe#1,<ypx2=fe#15); */
|
||||
/* asm 2: fe_mul.fe_mul(>A=r.Z,<YpX1=r.X,<ypx2=q.yplusx); */
|
||||
fe_mul.fe_mul(r.Z,r.X,q.yplusx);
|
||||
|
||||
/* qhasm: B = YmX1*ymx2 */
|
||||
/* asm 1: fe_mul.fe_mul(>B=fe#2,<YmX1=fe#2,<ymx2=fe#16); */
|
||||
/* asm 2: fe_mul.fe_mul(>B=r.Y,<YmX1=r.Y,<ymx2=q.yminusx); */
|
||||
fe_mul.fe_mul(r.Y,r.Y,q.yminusx);
|
||||
|
||||
/* qhasm: C = xy2d2*T1 */
|
||||
/* asm 1: fe_mul.fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */
|
||||
/* asm 2: fe_mul.fe_mul(>C=r.T,<xy2d2=q.xy2d,<T1=p.T); */
|
||||
fe_mul.fe_mul(r.T,q.xy2d,p.T);
|
||||
|
||||
/* qhasm: D = 2*Z1 */
|
||||
/* asm 1: fe_add.fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */
|
||||
/* asm 2: fe_add.fe_add(>D=t0,<Z1=p.Z,<Z1=p.Z); */
|
||||
fe_add.fe_add(t0,p.Z,p.Z);
|
||||
|
||||
/* qhasm: X3 = A-B */
|
||||
/* asm 1: fe_sub.fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
|
||||
/* asm 2: fe_sub.fe_sub(>X3=r.X,<A=r.Z,<B=r.Y); */
|
||||
fe_sub.fe_sub(r.X,r.Z,r.Y);
|
||||
|
||||
/* qhasm: Y3 = A+B */
|
||||
/* asm 1: fe_add.fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
|
||||
/* asm 2: fe_add.fe_add(>Y3=r.Y,<A=r.Z,<B=r.Y); */
|
||||
fe_add.fe_add(r.Y,r.Z,r.Y);
|
||||
|
||||
/* qhasm: Z3 = D+C */
|
||||
/* asm 1: fe_add.fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */
|
||||
/* asm 2: fe_add.fe_add(>Z3=r.Z,<D=t0,<C=r.T); */
|
||||
fe_add.fe_add(r.Z,t0,r.T);
|
||||
|
||||
/* qhasm: T3 = D-C */
|
||||
/* asm 1: fe_sub.fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */
|
||||
/* asm 2: fe_sub.fe_sub(>T3=r.T,<D=t0,<C=r.T); */
|
||||
fe_sub.fe_sub(r.T,t0,r.T);
|
||||
|
||||
/* qhasm: return */
|
||||
}
|
||||
|
||||
|
||||
}
|
107
src/main/java/org/whispersystems/curve25519/java/ge_msub.java
Normal file
107
src/main/java/org/whispersystems/curve25519/java/ge_msub.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class ge_msub {
|
||||
|
||||
//CONVERT #include "ge.h"
|
||||
|
||||
/*
|
||||
r = p - q
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void ge_msub(ge_p1p1 r,ge_p3 p,ge_precomp q)
|
||||
{
|
||||
int[] t0 = new int[10];
|
||||
//CONVERT #include "ge_msub.h"
|
||||
|
||||
/* qhasm: enter ge_msub */
|
||||
|
||||
/* qhasm: fe X1 */
|
||||
|
||||
/* qhasm: fe Y1 */
|
||||
|
||||
/* qhasm: fe Z1 */
|
||||
|
||||
/* qhasm: fe T1 */
|
||||
|
||||
/* qhasm: fe ypx2 */
|
||||
|
||||
/* qhasm: fe ymx2 */
|
||||
|
||||
/* qhasm: fe xy2d2 */
|
||||
|
||||
/* qhasm: fe X3 */
|
||||
|
||||
/* qhasm: fe Y3 */
|
||||
|
||||
/* qhasm: fe Z3 */
|
||||
|
||||
/* qhasm: fe T3 */
|
||||
|
||||
/* qhasm: fe YpX1 */
|
||||
|
||||
/* qhasm: fe YmX1 */
|
||||
|
||||
/* qhasm: fe A */
|
||||
|
||||
/* qhasm: fe B */
|
||||
|
||||
/* qhasm: fe C */
|
||||
|
||||
/* qhasm: fe D */
|
||||
|
||||
/* qhasm: YpX1 = Y1+X1 */
|
||||
/* asm 1: fe_add.fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
|
||||
/* asm 2: fe_add.fe_add(>YpX1=r.X,<Y1=p.Y,<X1=p.X); */
|
||||
fe_add.fe_add(r.X,p.Y,p.X);
|
||||
|
||||
/* qhasm: YmX1 = Y1-X1 */
|
||||
/* asm 1: fe_sub.fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
|
||||
/* asm 2: fe_sub.fe_sub(>YmX1=r.Y,<Y1=p.Y,<X1=p.X); */
|
||||
fe_sub.fe_sub(r.Y,p.Y,p.X);
|
||||
|
||||
/* qhasm: A = YpX1*ymx2 */
|
||||
/* asm 1: fe_mul.fe_mul(>A=fe#3,<YpX1=fe#1,<ymx2=fe#16); */
|
||||
/* asm 2: fe_mul.fe_mul(>A=r.Z,<YpX1=r.X,<ymx2=q.yminusx); */
|
||||
fe_mul.fe_mul(r.Z,r.X,q.yminusx);
|
||||
|
||||
/* qhasm: B = YmX1*ypx2 */
|
||||
/* asm 1: fe_mul.fe_mul(>B=fe#2,<YmX1=fe#2,<ypx2=fe#15); */
|
||||
/* asm 2: fe_mul.fe_mul(>B=r.Y,<YmX1=r.Y,<ypx2=q.yplusx); */
|
||||
fe_mul.fe_mul(r.Y,r.Y,q.yplusx);
|
||||
|
||||
/* qhasm: C = xy2d2*T1 */
|
||||
/* asm 1: fe_mul.fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */
|
||||
/* asm 2: fe_mul.fe_mul(>C=r.T,<xy2d2=q.xy2d,<T1=p.T); */
|
||||
fe_mul.fe_mul(r.T,q.xy2d,p.T);
|
||||
|
||||
/* qhasm: D = 2*Z1 */
|
||||
/* asm 1: fe_add.fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */
|
||||
/* asm 2: fe_add.fe_add(>D=t0,<Z1=p.Z,<Z1=p.Z); */
|
||||
fe_add.fe_add(t0,p.Z,p.Z);
|
||||
|
||||
/* qhasm: X3 = A-B */
|
||||
/* asm 1: fe_sub.fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
|
||||
/* asm 2: fe_sub.fe_sub(>X3=r.X,<A=r.Z,<B=r.Y); */
|
||||
fe_sub.fe_sub(r.X,r.Z,r.Y);
|
||||
|
||||
/* qhasm: Y3 = A+B */
|
||||
/* asm 1: fe_add.fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
|
||||
/* asm 2: fe_add.fe_add(>Y3=r.Y,<A=r.Z,<B=r.Y); */
|
||||
fe_add.fe_add(r.Y,r.Z,r.Y);
|
||||
|
||||
/* qhasm: Z3 = D-C */
|
||||
/* asm 1: fe_sub.fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */
|
||||
/* asm 2: fe_sub.fe_sub(>Z3=r.Z,<D=t0,<C=r.T); */
|
||||
fe_sub.fe_sub(r.Z,t0,r.T);
|
||||
|
||||
/* qhasm: T3 = D+C */
|
||||
/* asm 1: fe_add.fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */
|
||||
/* asm 2: fe_add.fe_add(>T3=r.T,<D=t0,<C=r.T); */
|
||||
fe_add.fe_add(r.T,t0,r.T);
|
||||
|
||||
/* qhasm: return */
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class ge_p1p1 {
|
||||
|
||||
public int[] X;
|
||||
public int[] Y;
|
||||
public int[] Z;
|
||||
public int[] T;
|
||||
|
||||
public ge_p1p1() {
|
||||
X = new int[10];
|
||||
Y = new int[10];
|
||||
Z = new int[10];
|
||||
T = new int[10];
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,20 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class ge_p1p1_to_p2 {
|
||||
|
||||
//CONVERT #include "ge.h"
|
||||
|
||||
/*
|
||||
r = p
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void ge_p1p1_to_p2(ge_p2 r,ge_p1p1 p)
|
||||
{
|
||||
fe_mul.fe_mul(r.X,p.X,p.T);
|
||||
fe_mul.fe_mul(r.Y,p.Y,p.Z);
|
||||
fe_mul.fe_mul(r.Z,p.Z,p.T);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package org.whispersystems.curve25519.java;
|
||||
|
||||
public class ge_p1p1_to_p3 {
|
||||
|
||||
//CONVERT #include "ge.h"
|
||||
|
||||
/*
|
||||
r = p
|
||||
*/
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void ge_p1p1_to_p3(ge_p3 r,ge_p1p1 p)
|
||||
{
|
||||
fe_mul.fe_mul(r.X,p.X,p.T);
|
||||
fe_mul.fe_mul(r.Y,p.Y,p.Z);
|
||||
fe_mul.fe_mul(r.Z,p.Z,p.T);
|
||||
fe_mul.fe_mul(r.T,p.X,p.Y);
|
||||
}
|
||||
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user