first commit

This commit is contained in:
davassi
2016-10-06 17:37:28 +02:00
commit baa9f32e9b
23 changed files with 1162 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
package jota;
/**
* IOTA's node command list
*/
public enum IotaAPICommands {
GET_NODE_INFO("getNodeInfo", 0),
GET_MILESTONE("getMilestone", 0),
GET_NEIGHBORS("getNeighbors", 0),
GET_TIPS("getTips",0),
GET_TRANSFER("getTransfers", 0), //
FIND_TRANSACTIONS("findTransactions", 0),
GET_INCLUSIONS_STATES("getInclusionStates", 0),
GET_BUNDLE("getBundle", 0),
GET_TRYTES("getTrytes", 0),
ANALYZE_TRANSACTIONS("analyzeTransactions", 0),
GET_NEW_ADDRESS("getNewAddress", 0),
PREPARE_TRANSFERS("prepareTransfers", 0),
GET_TRANSACTIONS_TO_APPROVE("getTransactionsToApprove", 0),
ATTACH_TO_TANGLE("attachToTangle", 0),
INTERRUPT_ATTACHING_TO_TANGLE("interruptAttachingToTangle", 0),
PUSH_TRANSACTIONS("pushTransactions", 0),
STORE_TRANSACTIONS("storeTransactions", 0),
TRANSFER("transfer", 0),
REPLAY_TRANSFER("replayTransfer",0),
PULL_TRANSACTIONS("pullTransactions", 0);
private IotaAPICommands(String command, int params) {
this.command = command;
this.params = params;
}
private String command;
private int params;
public String command() {
return command;
}
public int params() {
return params;
}
}
+226
View File
@@ -0,0 +1,226 @@
package jota;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Optional;
import java.util.Properties;
import jota.dto.request.*;
import jota.dto.response.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* IotaAPIProxy Builder. Usage:
*
* IotaApiProxy api = IotaApiProxy.Builder
* .protocol("http")
* .nodeAddress("localhost")
* .port(12345)
* .build();
*
* GetNodeInfoResponse response = api.getNodeInfo();
*
* @author davassi
*/
public class IotaAPIProxy {
private static final Logger log = LoggerFactory.getLogger(IotaAPIProxy.class);
private IotaAPIService service;
private IotaAPIProxy(final Builder builder) {
protocol = builder.protocol;
host = builder.host;
port = builder.port;
postConstruct();
}
private String protocol, host, port;
private void postConstruct() {
final String nodeUrl = protocol + "://" + host + ":" + port;
final Retrofit retrofit = new Retrofit.Builder()
.baseUrl(nodeUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
service = retrofit.create(IotaAPIService.class);
log.debug("Jota-API Java proxy pointing to node url: '{}'", nodeUrl);
}
public GetNodeInfoResponse getNodeInfo() {
final Call<GetNodeInfoResponse> res = service.getNodeInfo(IotaCommandRequest.createNodeInfoRequest());
return wrapCheckedException(res).body();
}
public GetMilestoneResponse getMilestone(Integer index) {
final Call<GetMilestoneResponse> res = service.getMilestone(IotaGetMilestoneRequest.createMilestoneRequest(index));
return wrapCheckedException(res).body();
}
public GetNeighborsResponse getNeighbors() {
final Call<GetNeighborsResponse> res = service.getNeighbors(IotaCommandRequest.createGetNeighborsRequest());
return wrapCheckedException(res).body();
}
public GetTipsResponse getTips() {
final Call<GetTipsResponse> res = service.getTips(IotaCommandRequest.createGetTipsRequest());
return wrapCheckedException(res).body();
}
public GetTransfersResponse getTransfers(String seed, Integer securityLevel) {
final Call<GetTransfersResponse> res = service.getTransfers(IotaGetTransferRequest.createGetTransferRequest(seed, securityLevel));
return wrapCheckedException(res).body();
}
public FindTransactionResponse findTransactions(String [] addresses, String [] digests, String [] approvees, String [] bundles ) {
final IotaFindTransactionsRequest findTransRequest = IotaFindTransactionsRequest
.createFindTransactionRequest()
.byAddresses(addresses)
.byDigests(digests)
.byApprovees(approvees)
.byBundles(bundles);
final Call<FindTransactionResponse> res = service.findTransactions(findTransRequest);
return wrapCheckedException(res).body();
}
public FindTransactionResponse findTransactionsByAddresses(String [] addresses) {
return findTransactions(addresses, null, null, null);
}
public FindTransactionResponse findTransactionsByBundles(String [] bundles) {
return findTransactions(null, null, null, bundles);
}
public FindTransactionResponse findTransactionsByApprovees(String [] approvees) {
return findTransactions(null, null, approvees, null);
}
public FindTransactionResponse findTransactionsByDigests(String [] digests) {
return findTransactions(null, digests, null, null);
}
public GetInclusionStateResponse getInclusionStates(String[] transactions, String[] tips) {
final Call<GetInclusionStateResponse> res = service.getInclusionStates(IotaGetInclusionStateRequest
.createGetInclusionStateRequest(transactions, tips));
return wrapCheckedException(res).body();
}
public GetInclusionStateResponse getInclusionStates(Collection<String> transactions, Collection<String> tips) {
final Call<GetInclusionStateResponse> res = service.getInclusionStates(IotaGetInclusionStateRequest
.createGetInclusionStateRequest(transactions, tips));
return wrapCheckedException(res).body();
}
protected static <T> Response<T> wrapCheckedException(final Call<T> call) {
try {
final Response<T> res = call.execute();
if (res.code() == 400) {
throw new IllegalAccessError(res.errorBody().toString());
}
return res;
} catch (IOException e) {
log.error("Execution of the API call raised exception. IOTA Node not reachable?", e);
throw new IllegalStateException(e.getMessage());
}
}
private static final String env(String env, String def) {
return Optional.ofNullable(System.getenv(env)).orElseGet(() -> {
log.warn("Enviroment variable '{}' is not defined, and actual value has not been specified. "
+ "Rolling back to default value: '{}'", env, def);
return def;
});
}
public static class Builder {
String protocol, host, port;
public IotaAPIProxy build() {
if (protocol == null || host == null || port == null) {
// check properties files.
if (!checkPropertiesFiles()) {
// last resort: best effort on enviroment variable,
// before assigning default values.
checkEnviromentVariables();
}
}
return new IotaAPIProxy(this);
}
private boolean checkPropertiesFiles() {
try (BufferedReader reader = Files.newBufferedReader(Paths.get("node_config.properties"))) {
final Properties nodeConfig = new Properties();
nodeConfig.load(reader);
Optional.ofNullable(nodeConfig.getProperty("iota.node.protocol"))
.filter(v -> protocol == null)
.ifPresent(v -> protocol = v);
Optional.ofNullable(nodeConfig.getProperty("iota.node.host"))
.filter(v -> host == null)
.ifPresent(v -> host = v);
Optional.ofNullable(nodeConfig.getProperty("iota.node.port"))
.filter(v -> port == null)
.ifPresent(v -> port = v);
} catch (IOException e1) {
log.debug("node_config.properties not found. Rolling back for another solution...");
}
return (port != null && protocol != null && host != null);
}
private void checkEnviromentVariables() {
Optional.of(env("IOTA_NODE_PROTOCOL", "http"))
.filter(v -> protocol == null)
.ifPresent(v -> protocol = v);
Optional.ofNullable(env("IOTA_NODE_HOST", "localhost"))
.filter(v -> host == null)
.ifPresent(v -> host = v);
Optional.ofNullable(env("IOTA_NODE_PORT", "14265"))
.filter(v -> port == null)
.ifPresent(v -> port = v);
}
public Builder host(String host) {
this.host = host;
return this;
}
public Builder port(String port) {
this.port = port;
return this;
}
public Builder protocol(String protocol) {
this.protocol = protocol;
return this;
}
}
}
+180
View File
@@ -0,0 +1,180 @@
package jota;
import jota.dto.request.*;
import jota.dto.response.*;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
/**
* IOTA API Proxy Service definition using Retrofit2
*
* @author davassi
*/
public interface IotaAPIService {
public static final String CONTENT_TYPE_HEADER = "Content-Type: application/json";
public static final String USER_AGENT_HEADER = "User-Agent: JOTA-API wrapper";
/**
* Returns information about your node.
*
* curl http://localhost:14265 \ -X POST \ -H 'Content-Type:
* application/json' \ -d '{"command": "getNodeInfo"}'
*
* @return a {@code NodeInfoResponse} object, if succesfull.
*/
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetNodeInfoResponse> getNodeInfo(@Body IotaCommandRequest request);
/**
* Returns a milestone from a given index.
*
* curl http://localhost:14265 \ -X POST \ -H 'Content-Type:
* application/json' \ -d '{"command": "getMilestone", "index": 8059}'
*
* @return
*/
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetMilestoneResponse> getMilestone(@Body IotaGetMilestoneRequest request);
/**
* Get the list of latest tips (unconfirmed transactions).
*
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
* -d '{"command": "getNeighbors"}'
*/
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetNeighborsResponse> getNeighbors(@Body IotaCommandRequest request);
/**
* Get the list of latest tips (unconfirmed transactions).
*
* curl http://localhost:14265 \ -X POST \ -H 'Content-Type:
* application/json' \ -d '{"command": "getTips"}'
*/
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetTipsResponse> getTips(@Body IotaCommandRequest request);
/**
* Get the list of latest tips (unconfirmed transactions).
*
* curl http://localhost:14265 \ -X POST \ -H 'Content-Type:
* application/json' \ -d '{"command": "getTips"}'
*/
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetTransfersResponse> getTransfers(@Body IotaGetTransferRequest request);
/**
* Find the transactions which match the specified input and return
*
* curl http://localhost:14265 \ -X POST \ -H 'Content-Type: application/json' \
* -d '{"command": "findTransactions", "addresses": ["RVORZ9SIIP9RCYMREUIXXVPQIPHVCNPQ9HZWYKFWYWZRE9JQKG9REPKIASHUUECPSQO9JT9XNMVKWYGVAZETAIRPTM"]}'
*/
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<FindTransactionResponse> findTransactions(@Body IotaFindTransactionsRequest request);
/**
* Get the inclusion states of a set of transactions. This is for determining if a transaction was accepted and confirmed by the network or not. You can search for multiple tips (and thus, milestones) to get past inclusion states of transactions.
*
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
* -d '{"command": "getInclusionStates", "transactions"Q9HZWYKFWYWZRE9JQKG9REPKIASHUUECPSQO9JT9XNMVKWYGVAZETAIRPTM"], "tips" : []}'
*
*
*/
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetInclusionStateResponse> getInclusionStates(@Body IotaGetInclusionStateRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetBundleResponse> getBundle(@Body IotaGetBundleRequest request);
/*
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetTrytesResponse> getTrytes(@Body IotaGetTrytesRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<AnalyzeTransactionResponse> analyzeTransactions(@Body IotaAnalyzeTransactionRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetNewAddressResponse> getNewAddress(@Body IotaGetNewAddressRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<PrepareTransfersResponse> prepareTransfers(@Body IotaPrepareTransfersRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetTransactionsToApproveResponse> getTransactionsToApprove(@Body IotaGetTransactionsToApproveRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<GetAttachToTangleResponse> attachToTangle(@Body IotaAttachToTangleRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<InterruptAttachingToTangleResponse> interruptAttachingToTangle(@Body IotaInterruptAttachingToTangleRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<PushTransactionsResponse> pushTransactions(@Body IotaPushTransactionsRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<StoreTransactionsResponse> storeTransactions(@Body IotaStoreTransactionsRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<TransferResponse> transfer(@Body IotaTransferRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<ReplayTransferResponse> replayTransfer(@Body IotaReplayTransferRequest request);
@Headers({ CONTENT_TYPE_HEADER, USER_AGENT_HEADER })
@POST("./")
Call<PullTransactionsResponse> pullTransactions(@Body IotaPullTransactionsRequest request);
* Get the list of transactions which were bundled with the specified tail transaction.
* This call returns the full value of all individual transactions, not just the hashes.
*/
}
@@ -0,0 +1,24 @@
package jota.dto.request;
import jota.IotaAPICommands;
public class IotaCommandRequest {
final String command;
protected IotaCommandRequest(IotaAPICommands command) {
this.command = command.command();
}
public static IotaCommandRequest createNodeInfoRequest() {
return new IotaCommandRequest(IotaAPICommands.GET_NODE_INFO);
}
public static IotaCommandRequest createGetTipsRequest() {
return new IotaCommandRequest(IotaAPICommands.GET_TIPS);
}
public static IotaCommandRequest createGetNeighborsRequest() {
return new IotaCommandRequest(IotaAPICommands.GET_NEIGHBORS);
}
}
@@ -0,0 +1,42 @@
package jota.dto.request;
import jota.IotaAPICommands;
public class IotaFindTransactionsRequest extends IotaCommandRequest {
private IotaFindTransactionsRequest() {
super(IotaAPICommands.FIND_TRANSACTIONS);
}
private String[] bundles; // List of bundle hashes. The hashes need to be extended to 81chars by padding the hash with 9's.
private String[] addresses;
private String[] digests;
private String[] approvees;
public static IotaFindTransactionsRequest createFindTransactionRequest() {
return new IotaFindTransactionsRequest();
}
public IotaFindTransactionsRequest byBundles(String [] bundles) {
this.bundles = bundles;
return this;
}
public IotaFindTransactionsRequest byAddresses(String [] addresses) {
this.addresses = addresses;
return this;
}
public IotaFindTransactionsRequest byDigests(String [] digests) {
this.digests = digests;
return this;
}
public IotaFindTransactionsRequest byApprovees(String [] approvees) {
this.approvees = approvees;
return this;
}
}
@@ -0,0 +1,18 @@
package jota.dto.request;
import jota.IotaAPICommands;
public class IotaGetBundleRequest extends IotaCommandRequest {
private String transaction;
private IotaGetInclusionStateRequest(final String transaction) {
super(IotaAPICommands.GET_BUNDLE);
this.transaction = transaction;
}
public static IotaGetBundleRequest createIotaGetBundleRequest(String transaction) {
return new IotaGetBundleRequest(transaction);
}
}
@@ -0,0 +1,29 @@
package jota.dto.request;
import jota.IotaAPICommands;
import java.util.Collection;
import java.util.stream.Collectors;
public class IotaGetInclusionStateRequest extends IotaCommandRequest {
private IotaGetInclusionStateRequest(final String[] transactions, final String[] tips) {
super(IotaAPICommands.GET_INCLUSIONS_STATES);
this.transactions = transactions;
this.tips = tips;
}
private String[] transactions;
private String[] tips;
public static IotaGetInclusionStateRequest createGetInclusionStateRequest(String[] transactions, String[] tips) {
return new IotaGetInclusionStateRequest(transactions, tips);
}
public static IotaGetInclusionStateRequest createGetInclusionStateRequest(Collection<String> transactions, Collection<String> tips) {
return createGetInclusionStateRequest(
transactions.toArray(new String[] {}),
tips.toArray(new String[] {}));
}
}
@@ -0,0 +1,17 @@
package jota.dto.request;
import jota.IotaAPICommands;
public class IotaGetMilestoneRequest extends IotaCommandRequest {
private String index;
private IotaGetMilestoneRequest(final String index) {
super(IotaAPICommands.GET_MILESTONE);
this.index = index;
}
public static IotaGetMilestoneRequest createMilestoneRequest(Integer index) {
return new IotaGetMilestoneRequest(String.valueOf(index));
}
}
@@ -0,0 +1,19 @@
package jota.dto.request;
import jota.IotaAPICommands;
public class IotaGetTransferRequest extends IotaCommandRequest {
private String seed;
private String securityLevel;
private IotaGetTransferRequest(final String seed, final String securityLevel) {
super(IotaAPICommands.GET_TRANSFER);
this.seed = seed;
this.securityLevel = securityLevel;
}
public static IotaGetTransferRequest createGetTransferRequest(String seed, Integer securityLevel) {
return new IotaGetTransferRequest(seed, String.valueOf(securityLevel));
}
}
@@ -0,0 +1,30 @@
package jota.dto.response;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public abstract class AbstractResponse {
private Integer duration;
public Integer getDuration() {
return duration;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this, false);
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj, false);
}
}
@@ -0,0 +1,10 @@
package jota.dto.response;
public class FindTransactionResponse extends AbstractResponse {
String [] addresses;
public String[] getAddresses() {
return addresses;
}
}
@@ -0,0 +1,93 @@
package jota.dto.response;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class GetBundleResponse extends AbstractResponse {
private Transactions[] transactions;
private String warning;
public String getWarning() {
return warning;
}
public Transactions[] getTransactions() {
return transactions;
}
static class Transactions {
private String signatureMessageChunk;
private String index;
private String approvalNonce;
private String hash;
private String digest;
private String type;
private String timestamp;
private String trunkTransaction;
private String branchTransaction;
private String signatureNonce;
private String address;
private String value;
private String bundle;
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
public String getAddress() {
return address;
}
public String getApprovalNonce() {
return approvalNonce;
}
public String getBranchTransaction() {
return branchTransaction;
}
public String getBundle() {
return bundle;
}
public String getDigest() {
return digest;
}
public String getHash() {
return hash;
}
public String getIndex() {
return index;
}
public String getSignatureMessageChunk() {
return signatureMessageChunk;
}
public String getSignatureNonce() {
return signatureNonce;
}
public String getTimestamp() {
return timestamp;
}
public String getType() {
return type;
}
public String getTrunkTransaction() {
return trunkTransaction;
}
public String getValue() {
return value;
}
}
}
@@ -0,0 +1,9 @@
package jota.dto.response;
public class GetInclusionStateResponse extends AbstractResponse {
boolean [] states;
public boolean[] getStates() {
return states;
}
}
@@ -0,0 +1,10 @@
package jota.dto.response;
public class GetMilestoneResponse extends AbstractResponse {
private String milestone;
public String getMilestone() {
return milestone;
}
}
@@ -0,0 +1,27 @@
package jota.dto.response;
public class GetNeighborsResponse extends AbstractResponse {
private Neighbors[] neighbors;
public Neighbors[] getNeighbors() {
return neighbors;
}
static class Neighbors {
private String numberOfAllTransactions;
private String address;
private String numberOfNewTransactions;
public String getAddress() {
return address;
}
public String getNumberOfAllTransactions() {
return numberOfAllTransactions;
}
public String getNumberOfNewTransactions() {
return numberOfNewTransactions;
}
}
}
@@ -0,0 +1,66 @@
package jota.dto.response;
public class GetNodeInfoResponse extends AbstractResponse {
private String incomingPacketsBacklog;
private String appName;
private String transactionsToRequest;
private String jreTotalMemory;
private String time;
private String neighbors;
private String milestoneIndex;
private String appVersion;
private String jreAvailableProcessors;
private String jreMaxMemory;
private String tips;
private String jreFreeMemory;
public String getIncomingPacketsBacklog() {
return incomingPacketsBacklog;
}
public String getAppName() {
return appName;
}
public String getTransactionsToRequest() {
return transactionsToRequest;
}
public String getJreTotalMemory() {
return jreTotalMemory;
}
public String getTime() {
return time;
}
public String getNeighbors() {
return neighbors;
}
public String getMilestoneIndex() {
return milestoneIndex;
}
public String getAppVersion() {
return appVersion;
}
public String getJreAvailableProcessors() {
return jreAvailableProcessors;
}
public String getJreMaxMemory() {
return jreMaxMemory;
}
public String getTips() {
return tips;
}
public String getJreFreeMemory() {
return jreFreeMemory;
}
}
@@ -0,0 +1,10 @@
package jota.dto.response;
public class GetTipsResponse extends AbstractResponse {
private String[] hashes;
public String[] getHashes() {
return hashes;
}
}
@@ -0,0 +1,34 @@
package jota.dto.response;
public class GetTransfersResponse extends AbstractResponse {
private Transfers[] transfers;
public static class Transfers {
private String timestamp;
private String address;
private String hash;
private String persistence;
private String value;
public String getAddress() {
return address;
}
public String getHash() {
return hash;
}
public String getPersistence() {
return persistence;
}
public String getTimestamp() {
return timestamp;
}
public String getValue() {
return value;
}
}
public Transfers[] getTransfers() {
return transfers;
}
}