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:
catbref
2018-12-21 11:14:16 +00:00
parent aab6b69da1
commit c4ed4b378c
284 changed files with 835 additions and 2781 deletions

View 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;
}
}

View 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;
}
}

View 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 {};
}

View 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;
}
}

View 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);
}
}

View 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
}
}
}

View 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);
}
}

View 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();
}
}

View 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(); }
}
);
}

View 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);
}
}

View 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;
}
}

View 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);
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}

View 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;
}
}

View 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);
}
}
}

View 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";
}
}

View 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);
}
}

View 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 {
}

View 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);
}
}
}

View 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";
}
}
}

View 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);
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}

View 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();
}
}

View 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();
}
}
});
}
}

View 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());
}
}

View 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!");
}
}
}
}

View 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);
}
}
}

View 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;
}
}

View 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;
}
}

View 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();
}
}

View 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;
}
}

View 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());
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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;
}
}

View 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;

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View 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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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() {
}
}

View 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);
}
}

View 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);
}
}

View 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;
}
}

View File

@@ -0,0 +1,7 @@
package org.whispersystems.curve25519.java;
public interface Sha512 {
public void calculateDigest(byte[] out, byte[] in, long length);
}

View File

@@ -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;
}
}

View 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);
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View 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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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 */
}
}

View File

@@ -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];
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View 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 */
}
}

View 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 */
}
}

View File

@@ -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];
}
}

View File

@@ -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);
}
}

View File

@@ -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