mirror of
https://github.com/gosticks/iota.lib.java.git
synced 2026-10-03 21:16:59 +00:00
fixed merge #10
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
script: mvn package
|
||||
deploy:
|
||||
provider: releases
|
||||
file_glob: true
|
||||
file: "/home/travis/build/iotaledger/iota.lib.java/target/jota*.jar"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
repo: iotaledger/iota.lib.java
|
||||
all_branches: true
|
||||
@@ -4,11 +4,11 @@ The JOTA library is a simple Java wrapper around [[IOTA]](http://www.iotatoken.c
|
||||
|
||||
It allows to connect easily using java directly to a local or a remote [[IOTA node]](https://iota.readme.io/docs/syncing-to-the-network).
|
||||
|
||||
* **Latest release:** 1.0.0 Release
|
||||
* **Compatibility:** in development to be fully compatible with IOTA IRI v1.1.0
|
||||
* **Latest release:** 0.9.0 RC1
|
||||
* **Compatibility:** fully compatible with IOTA IRI v1.2.4
|
||||
* **API coverage:** 14 of 14 commands fully implemented
|
||||
* **License:** Apache License 2.0
|
||||
* **Readme updated:** 2016-11-12 21:05:02 (UTC)
|
||||
* **Readme updated:** 2016-01-19 21:05:02 (UTC)
|
||||
|
||||
A list of all *IOTA* JSON-REST API commands currently supported by jota wrapper can be found in the `Commands` enum (see [here](https://github.com/davassi/JOTA/blob/master/src/main/java/jota/IotaAPICommands.java) for more details).
|
||||
|
||||
@@ -32,27 +32,33 @@ Other dependencies:
|
||||
|
||||
Connect to your local node with the default settings is quite straightforward: it requires only 2 lines of code. For example, in order to fetch the Node Info:
|
||||
|
||||
IotaApiProxy api = new IotaApiProxy.Builder.build();
|
||||
IotaApi api = new IotaApi.Builder.build();
|
||||
GetNodeInfoResponse response = api.getNodeInfo();
|
||||
|
||||
of if you need to connect to a remote node on https:
|
||||
of if you need to connect to a remote node:
|
||||
|
||||
IotaApiProxy api = new IotaApiProxy.Builder
|
||||
.protocol("https")
|
||||
IotaApi api = new IotaApi.Builder
|
||||
.protocol("http")
|
||||
.nodeAddress("somewhere_over_the_rainbow")
|
||||
.port(54321)
|
||||
.port(14265)
|
||||
.build();
|
||||
|
||||
GetNodeInfoResponse response = api.getNodeInfo();
|
||||
|
||||
Jota is still *not* in the central maven repository. It will be available when it will cover 100% iota's rest interface.
|
||||
In order to communicate with *IOTA node*, JOTA needs to be aware of your node's exact configuration. If you dont want to use the builder the easiest way of providing this information is via a `node_config.properties` file, for example:
|
||||
|
||||
In order to communicate with *IOTA node*, JOTA needs to be aware of your node's exact configuration. The easiest way of providing this information is via a `node_config.properties` file, for example:
|
||||
|
||||
iota.node.protocol=http
|
||||
iota.node.protocol=http``****************``
|
||||
iota.node.host=127.0.0.1
|
||||
iota.node.port=14265
|
||||
|
||||
Jota is still *not* in the central maven repository. It will be available when it will cover 100% iota's rest interface.
|
||||
|
||||
##Warning
|
||||
- This is pre-release software!
|
||||
- There may be performance and stability issues.
|
||||
- You may loose all your money :)
|
||||
- Please report any issues using the <a href="">Issue Tracker</a>"
|
||||
|
||||
That's it!
|
||||
|
||||
##Examples
|
||||
@@ -63,7 +69,5 @@ There's an extensive list of test coverages on the src/test/java package of the
|
||||
|
||||
If JOTA has been useful to you and you feel like contributing, consider posting a bug report or a pull request. Alternatively, donations are very welcome too!
|
||||
|
||||
* Bitcoin: `3FGCHqhG1SUpgn2eS1Agq2KnxJemWnQFbB`
|
||||
|
||||
|
||||
|
||||
* Bitcoin (Gianni Davassi): `3FGCHqhG1SUpgn2eS1Agq2KnxJemWnQFbB`
|
||||
* Bitcoin (Adrian Ziser): `3FGCHqhG1SUpgn2eS1Agq2KnxJemWnQFbB`
|
||||
@@ -1,5 +1,7 @@
|
||||
iota.node.protocol=http
|
||||
#iota.node.host=138.68.126.141
|
||||
iota.node.host=node.iotawallet.info
|
||||
#iota.node.host=138.68.90.186
|
||||
#iota.node.host=192.168.11.2
|
||||
#iota.node.host=138.68.90.186
|
||||
iota.node.port=14265
|
||||
|
||||
|
||||
@@ -2,12 +2,20 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.iota</groupId>
|
||||
<groupId>org.iota</groupId>
|
||||
<artifactId>jota</artifactId>
|
||||
<version>1.0.0-RELEASE</version>
|
||||
<version>0.9.0-RC1</version>
|
||||
<name>JOTA</name>
|
||||
<description>JOTA library is a simple Java wrapper around IOTA Node's JSON-REST HTTP interface.</description>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<java-version>1.7</java-version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@@ -63,6 +71,7 @@
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -91,6 +100,13 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
@@ -0,0 +1,836 @@
|
||||
package jota;
|
||||
|
||||
import jota.dto.response.*;
|
||||
import jota.error.*;
|
||||
import jota.model.*;
|
||||
import jota.pow.ICurl;
|
||||
import jota.pow.JCurl;
|
||||
import jota.utils.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* IotaAPI Builder. Usage:
|
||||
* <p>
|
||||
* IotaApiProxy api = IotaApiProxy.Builder
|
||||
* .protocol("http")
|
||||
* .nodeAddress("localhost")
|
||||
* .port(12345)
|
||||
* .build();
|
||||
* <p>
|
||||
* GetNodeInfoResponse response = api.getNodeInfo();
|
||||
*
|
||||
* @author davassi
|
||||
*/
|
||||
public class IotaAPI extends IotaAPICore {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(IotaAPI.class);
|
||||
private ICurl customCurl;
|
||||
private StopWatch stopWatch;
|
||||
|
||||
protected IotaAPI(Builder builder) {
|
||||
super(builder);
|
||||
customCurl = builder.customCurl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new address from a seed and returns the remainderAddress.
|
||||
* This is either done deterministically, or by providing the index of the new remainderAddress
|
||||
*
|
||||
* @param seed Tryte-encoded seed. It should be noted that this seed is not transferred
|
||||
* @param index Optional (default null). Key index to start search from. If the index is provided, the generation of the address is not deterministic.
|
||||
* @param checksum Optional (default false). Adds 9-tryte address checksum
|
||||
* @param total Optional (default 1)Total number of addresses to generate
|
||||
* @param returnAll If true, it returns all addresses which were deterministically generated (until findTransactions returns null)
|
||||
* @return an array of strings with the specifed number of addresses
|
||||
*/
|
||||
public GetNewAddressResponse getNewAddress(final String seed, final int index, final boolean checksum, final int total, final boolean returnAll) {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
|
||||
List<String> allAddresses = new ArrayList<>();
|
||||
|
||||
// If total number of addresses to generate is supplied, simply generate
|
||||
// and return the list of all addresses
|
||||
if (total != 0) {
|
||||
for (int i = index; i < index + total; i++) {
|
||||
allAddresses.add(IotaAPIUtils.newAddress(seed, i, checksum, customCurl));
|
||||
}
|
||||
return GetNewAddressResponse.create(allAddresses, stopWatch.getElapsedTimeMili());
|
||||
}
|
||||
// No total provided: Continue calling findTransactions to see if address was
|
||||
// already created if null, return list of addresses
|
||||
for (int i = index; ; i++) {
|
||||
|
||||
final String newAddress = IotaAPIUtils.newAddress(seed, i, checksum, customCurl);
|
||||
final FindTransactionResponse response = findTransactionsByAddresses(newAddress);
|
||||
|
||||
allAddresses.add(newAddress);
|
||||
if (response.getHashes().length == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If !returnAll return only the last address that was generated
|
||||
if (!returnAll) {
|
||||
|
||||
//allAddresses = allAddresses.subList(allAddresses.size() - 2, allAddresses.size() - 1);
|
||||
allAddresses = allAddresses.subList(allAddresses.size() - 1, allAddresses.size());
|
||||
}
|
||||
return GetNewAddressResponse.create(allAddresses, stopWatch.getElapsedTimeMili());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} seed
|
||||
* @param {object} options
|
||||
* @param {function} callback
|
||||
* @method getTransfers
|
||||
* @property {int} start Starting key index
|
||||
* @property {int} end Ending key index
|
||||
* @property {bool} inclusionStates returns confirmation status of all transactions
|
||||
* @returns {object} success
|
||||
**/
|
||||
public GetTransferResponse getTransfers(String seed, Integer start, Integer end, Boolean inclusionStates) throws ArgumentException, InvalidBundleException, InvalidSignatureException, NoNodeInfoException, NoInclusionStatesExcpection {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
// validate & if needed pad seed
|
||||
if ((seed = InputValidator.validateSeed(seed)) == null) {
|
||||
throw new IllegalStateException("Invalid Seed");
|
||||
}
|
||||
|
||||
start = start != null ? 0 : start;
|
||||
|
||||
if (start > end || end > (start + 500)) {
|
||||
throw new ArgumentException();
|
||||
}
|
||||
StopWatch sw = new StopWatch();
|
||||
|
||||
System.out.println("GetTransfer started");
|
||||
GetNewAddressResponse gnr = getNewAddress(seed, start, false, end == null ? end - start : end, true);
|
||||
if (gnr != null && gnr.getAddresses() != null) {
|
||||
System.out.println("GetTransfers after getNewAddresses " + sw.getElapsedTimeMili() + " ms");
|
||||
Bundle[] bundles = bundlesFromAddresses(gnr.getAddresses().toArray(new String[gnr.getAddresses().size()]), inclusionStates);
|
||||
System.out.println("GetTransfers after bundlesFromAddresses " + sw.getElapsedTimeMili() + " ms");
|
||||
return GetTransferResponse.create(bundles, stopWatch.getElapsedTimeMili());
|
||||
}
|
||||
return GetTransferResponse.create(new Bundle[]{}, stopWatch.getElapsedTimeMili());
|
||||
}
|
||||
|
||||
public Bundle[] bundlesFromAddresses(String[] addresses, final Boolean inclusionStates) throws ArgumentException, InvalidBundleException, InvalidSignatureException, NoNodeInfoException, NoInclusionStatesExcpection {
|
||||
|
||||
List<Transaction> trxs = findTransactionObjects(addresses);
|
||||
// set of tail transactions
|
||||
List<String> tailTransactions = new ArrayList<>();
|
||||
List<String> nonTailBundleHashes = new ArrayList<>();
|
||||
|
||||
for (Transaction trx : trxs) {
|
||||
// Sort tail and nonTails
|
||||
if (Long.parseLong(trx.getCurrentIndex()) == 0) {
|
||||
tailTransactions.add(trx.getHash());
|
||||
} else {
|
||||
if (nonTailBundleHashes.indexOf(trx.getBundle()) == -1) {
|
||||
nonTailBundleHashes.add(trx.getBundle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Transaction> bundleObjects = findTransactionObjectsByBundle(nonTailBundleHashes.toArray(new String[nonTailBundleHashes.size()]));
|
||||
for (Transaction trx : bundleObjects) {
|
||||
// Sort tail and nonTails
|
||||
if (Long.parseLong(trx.getCurrentIndex()) == 0) {
|
||||
if (tailTransactions.indexOf(trx.getHash()) == -1) {
|
||||
tailTransactions.add(trx.getHash());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List<Bundle> finalBundles = new ArrayList<>();
|
||||
final String[] tailTxArray = tailTransactions.toArray(new String[tailTransactions.size()]);
|
||||
|
||||
// If inclusionStates, get the confirmation status
|
||||
// of the tail transactions, and thus the bundles
|
||||
GetInclusionStateResponse gisr = null;
|
||||
if (tailTxArray != null && tailTxArray.length != 0 && inclusionStates) {
|
||||
try {
|
||||
gisr = getLatestInclusion(tailTxArray);
|
||||
} catch (IllegalAccessError ignored) {
|
||||
throw new NoInclusionStatesExcpection();
|
||||
}
|
||||
if (gisr == null || gisr.getStates() == null || gisr.getStates().length == 0) {
|
||||
throw new NoInclusionStatesExcpection();
|
||||
}
|
||||
}
|
||||
final GetInclusionStateResponse finalInclusionStates = gisr;
|
||||
Parallel.For(Arrays.asList(tailTxArray),
|
||||
new Parallel.Operation<String>() {
|
||||
public void perform(String param) {
|
||||
|
||||
try {
|
||||
GetBundleResponse bundleResponse = getBundle(param);
|
||||
Bundle gbr = new Bundle(bundleResponse.getTransactions(), bundleResponse.getTransactions().size());
|
||||
if (gbr != null && gbr.getTransactions() != null) {
|
||||
if (inclusionStates) {
|
||||
boolean thisInclusion = finalInclusionStates.getStates()[Arrays.asList(tailTxArray).indexOf(param)];
|
||||
for (Transaction t : gbr.getTransactions()) {
|
||||
t.setPersistence(thisInclusion);
|
||||
}
|
||||
}
|
||||
finalBundles.add(gbr);
|
||||
}
|
||||
// If error returned from getBundle, simply ignore it because the bundle was most likely incorrect
|
||||
} catch (InvalidBundleException | ArgumentException | InvalidSignatureException e) {
|
||||
log.warn("GetBundleError: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Collections.sort(finalBundles);
|
||||
Bundle[] returnValue = new Bundle[finalBundles.size()];
|
||||
for (int i = 0; i < finalBundles.size(); i++) {
|
||||
returnValue[i] = new Bundle(finalBundles.get(i).getTransactions(), finalBundles.get(i).getTransactions().size());
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param trytes
|
||||
* @return a StoreTransactionsResponse
|
||||
*/
|
||||
public StoreTransactionsResponse broadcastAndStore(final String... trytes) throws BroadcastAndStoreException {
|
||||
|
||||
try {
|
||||
broadcastTransactions(trytes);
|
||||
} catch (Exception e) {
|
||||
log.error("Impossible to broadcastAndStore, aborting.", e);
|
||||
throw new BroadcastAndStoreException();
|
||||
}
|
||||
return storeTransactions(trytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Facade method: Gets transactions to approve, attaches to Tangle, broadcasts and stores
|
||||
*
|
||||
* @param {array} trytes
|
||||
* @param {int} depth
|
||||
* @param {int} minWeightMagnitude
|
||||
* @return
|
||||
*/
|
||||
public List<Transaction> sendTrytes(final String[] trytes, final int depth, final int minWeightMagnitude) {
|
||||
final GetTransactionsToApproveResponse txs = getTransactionsToApprove(depth);
|
||||
|
||||
// attach to tangle - do pow
|
||||
final GetAttachToTangleResponse res = attachToTangle(txs.getTrunkTransaction(), txs.getBranchTransaction(), minWeightMagnitude, trytes);
|
||||
|
||||
try {
|
||||
broadcastAndStore(res.getTrytes());
|
||||
} catch (BroadcastAndStoreException e) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
final List<Transaction> trx = new ArrayList<>();
|
||||
|
||||
for (final String tryte : Arrays.asList(res.getTrytes())) {
|
||||
trx.add(new Transaction(tryte, customCurl));
|
||||
}
|
||||
return trx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for getTrytes and transactionObjects
|
||||
* gets the trytes and transaction object from a list of transaction hashes
|
||||
*
|
||||
* @param {array} hashes
|
||||
* @return
|
||||
* @method getTransactionsObjects
|
||||
* @returns {function} callback
|
||||
* @returns {object} success
|
||||
**/
|
||||
public List<Transaction> getTransactionsObjects(String[] hashes) {
|
||||
|
||||
if (!InputValidator.isArrayOfHashes(hashes)) {
|
||||
throw new IllegalStateException("Not an Array of Hashes: " + Arrays.toString(hashes));
|
||||
}
|
||||
|
||||
final GetTrytesResponse trytesResponse = getTrytes(hashes);
|
||||
|
||||
final List<Transaction> trxs = new ArrayList<>();
|
||||
|
||||
for (final String tryte : trytesResponse.getTrytes()) {
|
||||
trxs.add(new Transaction(tryte, customCurl));
|
||||
}
|
||||
return trxs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for findTransactions, getTrytes and transactionObjects
|
||||
* Returns the transactionObject of a transaction hash. The input can be a valid
|
||||
* findTransactions input
|
||||
*
|
||||
* @param {object} input
|
||||
* @method getTransactionsObjects
|
||||
* @returns {function} callback
|
||||
* @returns {object} success
|
||||
**/
|
||||
public List<Transaction> findTransactionObjects(String[] input) {
|
||||
FindTransactionResponse ftr = findTransactions(input, null, null, null);
|
||||
if (ftr == null || ftr.getHashes() == null)
|
||||
return new ArrayList<>();
|
||||
// get the transaction objects of the transactions
|
||||
return getTransactionsObjects(ftr.getHashes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for findTransactions, getTrytes and transactionObjects
|
||||
* Returns the transactionObject of a transaction hash. The input can be a valid
|
||||
* findTransactions input
|
||||
*
|
||||
* @param {object} input
|
||||
* @method getTransactionsObjects
|
||||
* @returns {function} callback
|
||||
* @returns {object} success
|
||||
**/
|
||||
public List<Transaction> findTransactionObjectsByBundle(String[] input) {
|
||||
FindTransactionResponse ftr = findTransactions(null, null, null, input);
|
||||
if (ftr == null || ftr.getHashes() == null)
|
||||
return new ArrayList<>();
|
||||
|
||||
// get the transaction objects of the transactions
|
||||
return getTransactionsObjects(ftr.getHashes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares transfer by generating bundle, finding and signing inputs
|
||||
*
|
||||
* @param {string} seed
|
||||
* @param {object} transfers
|
||||
* @param {object} options
|
||||
* @param {function} callback
|
||||
* @return
|
||||
* @method prepareTransfers
|
||||
* @property {array} inputs Inputs used for signing. Needs to have correct keyIndex and address value
|
||||
* @property {string} address Remainder address
|
||||
* @returns {array} trytes Returns bundle trytes
|
||||
**/
|
||||
public List<String> prepareTransfers(String seed, final List<Transfer> transfers, String remainder, List<Input> inputs) throws NotEnoughBalanceException {
|
||||
|
||||
// Input validation of transfers object
|
||||
if (!InputValidator.isTransfersCollectionCorrect(transfers)) {
|
||||
throw new IllegalStateException("Invalid Transfer");
|
||||
}
|
||||
|
||||
// validate & if needed pad seed
|
||||
if ((seed = InputValidator.validateSeed(seed)) == null) {
|
||||
throw new IllegalStateException("Invalid Seed");
|
||||
}
|
||||
|
||||
|
||||
// Create a new bundle
|
||||
final Bundle bundle = new Bundle();
|
||||
final List<String> signatureFragments = new ArrayList<>();
|
||||
|
||||
long totalValue = 0;
|
||||
String tag = "";
|
||||
|
||||
// Iterate over all transfers, get totalValue
|
||||
// and prepare the signatureFragments, message and tag
|
||||
for (final Transfer transfer : transfers) {
|
||||
|
||||
int signatureMessageLength = 1;
|
||||
|
||||
// If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction)
|
||||
if (transfer.getMessage().length() > 2187) {
|
||||
|
||||
// Get total length, message / maxLength (2187 trytes)
|
||||
signatureMessageLength += Math.floor(transfer.getMessage().length() / 2187);
|
||||
|
||||
String msgCopy = transfer.getMessage();
|
||||
|
||||
// While there is still a message, copy it
|
||||
while (!msgCopy.isEmpty()) {
|
||||
|
||||
String fragment = StringUtils.substring(msgCopy, 0, 2187);
|
||||
msgCopy = StringUtils.substring(msgCopy, 2187, msgCopy.length());
|
||||
|
||||
// Pad remainder of fragment
|
||||
|
||||
fragment = StringUtils.rightPad(fragment, 2187, '9');
|
||||
|
||||
signatureFragments.add(fragment);
|
||||
}
|
||||
} else {
|
||||
// Else, get single fragment with 2187 of 9's trytes
|
||||
String fragment = StringUtils.substring(transfer.getMessage(), 0, 2187);
|
||||
|
||||
fragment = StringUtils.rightPad(fragment, 2187, '9');
|
||||
|
||||
signatureFragments.add(fragment);
|
||||
}
|
||||
|
||||
// get current timestamp in seconds
|
||||
long timestamp = (long) Math.floor(Calendar.getInstance().getTimeInMillis() / 1000);
|
||||
|
||||
// If no tag defined, get 27 tryte tag.
|
||||
tag = transfer.getTag().isEmpty() ? "999999999999999999999999999" : transfer.getTag();
|
||||
|
||||
// Pad for required 27 tryte length
|
||||
tag = StringUtils.rightPad(tag, 27, '9');
|
||||
|
||||
// Add first entry to the bundle
|
||||
bundle.addEntry(signatureMessageLength, transfer.getAddress(), transfer.getValue(), tag, timestamp);
|
||||
// Sum up total value
|
||||
totalValue += transfer.getValue();
|
||||
}
|
||||
|
||||
// Get inputs if we are sending tokens
|
||||
if (totalValue != 0) {
|
||||
|
||||
// Case 1: user provided inputs
|
||||
// Validate the inputs by calling getBalances
|
||||
if (inputs != null && !inputs.isEmpty()) {
|
||||
|
||||
// Get list if addresses of the provided inputs
|
||||
List<String> inputsAddresses = new ArrayList<>();
|
||||
for (final Input i : inputs) {
|
||||
inputsAddresses.add(i.getAddress());
|
||||
}
|
||||
|
||||
GetBalancesResponse balancesResponse = getBalances(100, inputsAddresses);
|
||||
String[] balances = balancesResponse.getBalances();
|
||||
|
||||
List<Input> confirmedInputs = new ArrayList<>();
|
||||
int totalBalance = 0;
|
||||
int i = 0;
|
||||
for (String balance : balances) {
|
||||
long thisBalance = Integer.parseInt(balance);
|
||||
totalBalance += thisBalance;
|
||||
|
||||
// If input has balance, add it to confirmedInputs
|
||||
if (thisBalance > 0) {
|
||||
Input inputEl = inputs.get(i++);
|
||||
inputEl.setBalance(thisBalance);
|
||||
confirmedInputs.add(inputEl);
|
||||
}
|
||||
}
|
||||
|
||||
// Return not enough balance error
|
||||
if (totalValue > totalBalance) {
|
||||
throw new IllegalStateException("Not enough balance");
|
||||
}
|
||||
|
||||
return addRemainder(seed, confirmedInputs, bundle, tag, totalValue, null, signatureFragments);
|
||||
}
|
||||
|
||||
// Case 2: Get inputs deterministically
|
||||
//
|
||||
// If no inputs provided, derive the addresses from the seed and
|
||||
// confirm that the inputs exceed the threshold
|
||||
else {
|
||||
|
||||
@SuppressWarnings("unchecked") GetBalancesAndFormatResponse newinputs = getInputs(seed, 0, 0, totalValue);
|
||||
// If inputs with enough balance
|
||||
return addRemainder(seed, newinputs.getInput(), bundle, tag, totalValue, null, signatureFragments);
|
||||
}
|
||||
} else {
|
||||
|
||||
// If no input required, don't sign and simply finalize the bundle
|
||||
bundle.finalize(customCurl);
|
||||
bundle.addTrytes(signatureFragments);
|
||||
|
||||
List<Transaction> trxb = bundle.getTransactions();
|
||||
List<String> bundleTrytes = new ArrayList<>();
|
||||
|
||||
for (Transaction trx : trxb) {
|
||||
bundleTrytes.add(trx.toTrytes());
|
||||
}
|
||||
Collections.reverse(bundleTrytes);
|
||||
return bundleTrytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the inputs of a seed
|
||||
*
|
||||
* @param {string} seed
|
||||
* @param {object} options
|
||||
* @param {function} callback
|
||||
* @method getInputs
|
||||
* @property {int} start Starting key index
|
||||
* @property {int} end Ending key index
|
||||
* @property {int} threshold Min balance required
|
||||
**/
|
||||
public GetBalancesAndFormatResponse getInputs(String seed, int start, int end, long threshold) {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
// validate the seed
|
||||
if (!InputValidator.isTrytes(seed, 0)) {
|
||||
throw new IllegalStateException("Invalid Seed");
|
||||
}
|
||||
|
||||
// validate & if needed pad seed
|
||||
if ((seed = InputValidator.validateSeed(seed)) == null) {
|
||||
throw new IllegalStateException("Invalid Seed");
|
||||
}
|
||||
|
||||
// If start value bigger than end, return error
|
||||
// or if difference between end and start is bigger than 500 keys
|
||||
if (start > end || end > (start + 500)) {
|
||||
throw new IllegalStateException("Invalid inputs provided");
|
||||
}
|
||||
|
||||
// Case 1: start and end
|
||||
//
|
||||
// If start and end is defined by the user, simply iterate through the keys
|
||||
// and call getBalances
|
||||
if (end != 0) {
|
||||
|
||||
List<String> allAddresses = new ArrayList<>();
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
|
||||
String address = IotaAPIUtils.newAddress(seed, i, false, customCurl);
|
||||
allAddresses.add(address);
|
||||
}
|
||||
|
||||
return getBalanceAndFormat(allAddresses, threshold, start, end, stopWatch);
|
||||
}
|
||||
// Case 2: iterate till threshold || end
|
||||
//
|
||||
// Either start from index: 0 or start (if defined) until threshold is reached.
|
||||
// Calls getNewAddress and deterministically generates and returns all addresses
|
||||
// We then do getBalance, format the output and return it
|
||||
else {
|
||||
final GetNewAddressResponse res = getNewAddress(seed, start, false, 0, true);
|
||||
return getBalanceAndFormat(res.getAddresses(), threshold, start, end, stopWatch);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls getBalances and formats the output
|
||||
// returns the final inputsObject then
|
||||
public GetBalancesAndFormatResponse getBalanceAndFormat(final List<String> addresses, long threshold, int start, int end, StopWatch stopWatch) {
|
||||
|
||||
GetBalancesResponse getBalancesResponse = getBalances(100, addresses);
|
||||
List<String> balances = Arrays.asList(getBalancesResponse.getBalances());
|
||||
|
||||
// If threshold defined, keep track of whether reached or not
|
||||
// else set default to true
|
||||
boolean thresholdReached = threshold == 0;
|
||||
int i = -1;
|
||||
|
||||
List<Input> inputs = new ArrayList<>();
|
||||
long totalBalance = 0;
|
||||
|
||||
for (String address : addresses) {
|
||||
|
||||
long balance = Long.parseLong(balances.get(++i));
|
||||
|
||||
if (balance > 0) {
|
||||
final Input newEntry = new Input(address, balance, start + i);
|
||||
|
||||
inputs.add(newEntry);
|
||||
// Increase totalBalance of all aggregated inputs
|
||||
totalBalance += balance;
|
||||
|
||||
if (!thresholdReached && totalBalance >= threshold) {
|
||||
thresholdReached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thresholdReached) {
|
||||
return GetBalancesAndFormatResponse.create(inputs, totalBalance, stopWatch.getElapsedTimeMili());
|
||||
}
|
||||
throw new IllegalStateException("Not enough balance");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated bundle transactions of a single transaction
|
||||
* Does validation of signatures, total sum as well as bundle order
|
||||
*
|
||||
* @param {string} transaction Hash of a tail transaction
|
||||
* @method getBundle
|
||||
* @returns {list} bundle Transaction objects
|
||||
**/
|
||||
public GetBundleResponse getBundle(String transaction) throws ArgumentException, InvalidBundleException, InvalidSignatureException {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
|
||||
Bundle bundle = traverseBundle(transaction, null, new Bundle());
|
||||
if (bundle == null) {
|
||||
throw new ArgumentException("Unknown Bundle");
|
||||
}
|
||||
|
||||
long totalSum = 0;
|
||||
String bundleHash = bundle.getTransactions().get(0).getBundle();
|
||||
|
||||
ICurl curl = new JCurl();
|
||||
curl.reset();
|
||||
|
||||
List<Signature> signaturesToValidate = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < bundle.getTransactions().size(); i++) {
|
||||
Transaction trx = bundle.getTransactions().get(i);
|
||||
Long bundleValue = Long.parseLong(trx.getValue());
|
||||
totalSum += bundleValue;
|
||||
|
||||
if (i != Integer.parseInt(bundle.getTransactions().get(i).getCurrentIndex())) {
|
||||
throw new ArgumentException("Invalid Bundle");
|
||||
}
|
||||
|
||||
String trxTrytes = trx.toTrytes().substring(2187, 2187 + 162);
|
||||
//System.out.println("Bundlesize "+bundle.getTransactions().size()+" "+trxTrytes);
|
||||
// Absorb bundle hash + value + timestamp + lastIndex + currentIndex trytes.
|
||||
curl.absorb(Converter.trits(trxTrytes));
|
||||
// Check if input transaction
|
||||
if (bundleValue < 0) {
|
||||
String address = trx.getAddress();
|
||||
Signature sig = new Signature();
|
||||
sig.setAddress(address);
|
||||
sig.getSignatureFragments().add(trx.getSignatureFragments());
|
||||
|
||||
// Find the subsequent txs with the remaining signature fragment
|
||||
for (int y = i; y < bundle.getTransactions().size() - 1; y++) {
|
||||
Transaction newBundleTx = bundle.getTransactions().get(i + 1);
|
||||
|
||||
// Check if new tx is part of the signature fragment
|
||||
if (newBundleTx.getAddress().equals(address) && Long.parseLong(newBundleTx.getValue()) == 0) {
|
||||
if (sig.getSignatureFragments().indexOf(newBundleTx.getSignatureFragments()) == -1)
|
||||
sig.getSignatureFragments().add(newBundleTx.getSignatureFragments());
|
||||
}
|
||||
}
|
||||
signaturesToValidate.add(sig);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for total sum, if not equal 0 return error
|
||||
if (totalSum != 0) throw new InvalidBundleException("Invalid Bundle Sum");
|
||||
int[] bundleFromTrxs = new int[243];
|
||||
curl.squeeze(bundleFromTrxs);
|
||||
String bundleFromTxString = Converter.trytes(bundleFromTrxs);
|
||||
|
||||
// Check if bundle hash is the same as returned by tx object
|
||||
if (!bundleFromTxString.equals(bundleHash)) throw new InvalidBundleException("Invalid Bundle Hash");
|
||||
// Last tx in the bundle should have currentIndex === lastIndex
|
||||
bundle.setLength(bundle.getTransactions().size());
|
||||
if (!bundle.getTransactions().get(bundle.getLength() - 1).getCurrentIndex().equals(bundle.getTransactions().get(bundle.getLength() - 1).getLastIndex()))
|
||||
throw new InvalidBundleException("Invalid Bundle");
|
||||
|
||||
// Validate the signatures
|
||||
for (Signature aSignaturesToValidate : signaturesToValidate) {
|
||||
String[] signatureFragments = aSignaturesToValidate.getSignatureFragments().toArray(new String[aSignaturesToValidate.getSignatureFragments().size()]);
|
||||
String address = aSignaturesToValidate.getAddress();
|
||||
boolean isValidSignature = new Signing().validateSignatures(address, signatureFragments, bundleHash);
|
||||
|
||||
if (!isValidSignature) throw new InvalidSignatureException();
|
||||
}
|
||||
|
||||
return GetBundleResponse.create(bundle.getTransactions(), stopWatch.getElapsedTimeMili());
|
||||
}
|
||||
|
||||
/**
|
||||
* Replays a transfer by doing Proof of Work again
|
||||
*
|
||||
* @param {string} tail
|
||||
* @param {int} depth
|
||||
* @param {int} minWeightMagnitude
|
||||
* @param {function} callback
|
||||
* @method replayBundle
|
||||
* @returns {object} analyzed Transaction objects
|
||||
**/
|
||||
public ReplayBundleResponse replayBundle(String transaction, int depth, int minWeightMagnitude) throws InvalidBundleException, ArgumentException, InvalidSignatureException {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
|
||||
List<String> bundleTrytes = new ArrayList<>();
|
||||
|
||||
GetBundleResponse bundleResponse = getBundle(transaction);
|
||||
Bundle bundle = new Bundle(bundleResponse.getTransactions(), bundleResponse.getTransactions().size());
|
||||
for (Transaction trx : bundle.getTransactions()) {
|
||||
|
||||
bundleTrytes.add(trx.toTrytes());
|
||||
}
|
||||
|
||||
List<Transaction> trxs = sendTrytes(bundleTrytes.toArray(new String[bundleTrytes.size()]), depth, minWeightMagnitude);
|
||||
|
||||
Boolean[] successful = new Boolean[trxs.size()];
|
||||
|
||||
for (int i = 0; i < trxs.size(); i++) {
|
||||
|
||||
final FindTransactionResponse response = findTransactionsByBundles(trxs.get(i).getBundle());
|
||||
|
||||
|
||||
successful[i] = response.getHashes().length != 0;
|
||||
}
|
||||
|
||||
return ReplayBundleResponse.create(successful, stopWatch.getElapsedTimeMili());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for getNodeInfo and getInclusionStates
|
||||
*
|
||||
* @param {array} hashes
|
||||
* @method getLatestInclusion
|
||||
* @returns {function} callback
|
||||
* @returns {array} state
|
||||
**/
|
||||
public GetInclusionStateResponse getLatestInclusion(String[] hashes) throws NoNodeInfoException {
|
||||
GetNodeInfoResponse getNodeInfoResponse = getNodeInfo();
|
||||
if (getNodeInfoResponse == null) throw new NoNodeInfoException();
|
||||
|
||||
String[] latestMilestone = {getNodeInfoResponse.getLatestSolidSubtangleMilestone()};
|
||||
|
||||
return getInclusionStates(hashes, latestMilestone);
|
||||
}
|
||||
|
||||
public SendTransferResponse sendTransfer(String seed, int depth, int minWeightMagnitude, final List<Transfer> transfers, Input[] inputs, String address) throws NotEnoughBalanceException {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
|
||||
List<String> trytes = prepareTransfers(seed, transfers, address, inputs == null ? null : Arrays.asList(inputs));
|
||||
List<Transaction> trxs = sendTrytes(trytes.toArray(new String[trytes.size()]), depth, minWeightMagnitude);
|
||||
|
||||
Boolean[] successful = new Boolean[trxs.size()];
|
||||
|
||||
for (int i = 0; i < trxs.size(); i++) {
|
||||
|
||||
final FindTransactionResponse response = findTransactionsByBundles(trxs.get(i).getBundle());
|
||||
|
||||
successful[i] = response.getHashes().length != 0;
|
||||
}
|
||||
|
||||
return SendTransferResponse.create(successful, stopWatch.getElapsedTimeMili());
|
||||
}
|
||||
|
||||
/**
|
||||
* Basically traverse the Bundle by going down the trunkTransactions until
|
||||
* the bundle hash of the transaction is no longer the same. In case the input
|
||||
* transaction hash is not a tail, we return an error.
|
||||
*
|
||||
* @param {string} trunkTx Hash of a trunk or a tail transaction of a bundle
|
||||
* @param {string} bundleHash
|
||||
* @param {array} bundle List of bundles to be populated
|
||||
* @method traverseBundle
|
||||
* @returns {array} bundle Transaction objects
|
||||
**/
|
||||
public Bundle traverseBundle(String trunkTx, String bundleHash, Bundle bundle) throws ArgumentException {
|
||||
GetTrytesResponse gtr = getTrytes(trunkTx);
|
||||
|
||||
if (gtr != null) {
|
||||
|
||||
if (gtr.getTrytes().length == 0) {
|
||||
throw new ArgumentException("Bundle transactions not visible");
|
||||
}
|
||||
|
||||
Transaction trx = new Transaction(gtr.getTrytes()[0], customCurl);
|
||||
if (trx == null || trx.getBundle() == null) {
|
||||
throw new ArgumentException("Invalid trytes, could not create object");
|
||||
}
|
||||
// If first transaction to search is not a tail, return error
|
||||
if (bundleHash == null && Integer.parseInt(trx.getCurrentIndex()) != 0) {
|
||||
throw new ArgumentException("Invalid tail transaction supplied.");
|
||||
}
|
||||
// If no bundle hash, define it
|
||||
if (bundleHash == null) {
|
||||
bundleHash = trx.getBundle();
|
||||
}
|
||||
// If different bundle hash, return with bundle
|
||||
if (!bundleHash.equals(trx.getBundle())) {
|
||||
return bundle;
|
||||
}
|
||||
// If only one bundle element, return
|
||||
if (Integer.parseInt(trx.getLastIndex()) == 0 && Integer.parseInt(trx.getCurrentIndex()) == 0) {
|
||||
return new Bundle(Collections.singletonList(trx), 1);
|
||||
}
|
||||
// Define new trunkTransaction for search
|
||||
trunkTx = trx.getTrunkTransaction();
|
||||
// Add transaction object to bundle
|
||||
bundle.getTransactions().add(trx);
|
||||
|
||||
// Continue traversing with new trunkTx
|
||||
return traverseBundle(trunkTx, bundleHash, bundle);
|
||||
} else {
|
||||
throw new ArgumentException("Get Trytes Response was null");
|
||||
}
|
||||
}
|
||||
|
||||
public String findTailTransactionHash(String hash) throws ArgumentException {
|
||||
GetTrytesResponse gtr = getTrytes(hash);
|
||||
|
||||
if (gtr == null) throw new ArgumentException("Invalid hash");
|
||||
|
||||
if (gtr.getTrytes().length == 0) {
|
||||
throw new ArgumentException("Bundle transactions not visible");
|
||||
}
|
||||
|
||||
Transaction trx = new Transaction(gtr.getTrytes()[0], customCurl);
|
||||
if (trx == null || trx.getBundle() == null) {
|
||||
throw new ArgumentException("Invalid trytes, could not create object");
|
||||
}
|
||||
if (Integer.parseInt(trx.getCurrentIndex()) == 0) return trx.getHash();
|
||||
else return findTailTransactionHash(trx.getBundle());
|
||||
}
|
||||
|
||||
public List<String> addRemainder(final String seed,
|
||||
final List<Input> inputs,
|
||||
final Bundle bundle,
|
||||
final String tag,
|
||||
final long totalValue,
|
||||
final String remainderAddress,
|
||||
final List<String> signatureFragments) throws NotEnoughBalanceException {
|
||||
|
||||
long totalTransferValue = totalValue;
|
||||
for (int i = 0; i < inputs.size(); i++) {
|
||||
long thisBalance = inputs.get(i).getBalance();
|
||||
long toSubtract = 0 - thisBalance;
|
||||
long timestamp = (long) Math.floor(Calendar.getInstance().getTimeInMillis() / 1000);
|
||||
|
||||
// Add input as bundle entry
|
||||
bundle.addEntry(2, inputs.get(i).getAddress(), toSubtract, tag, timestamp);
|
||||
// If there is a remainder value
|
||||
// Add extra output to send remaining funds to
|
||||
|
||||
if (thisBalance >= totalTransferValue) {
|
||||
long remainder = thisBalance - totalTransferValue;
|
||||
|
||||
// If user has provided remainder address
|
||||
// Use it to send remaining funds to
|
||||
if (remainder > 0 && remainderAddress != null) {
|
||||
// Remainder bundle entry
|
||||
bundle.addEntry(1, remainderAddress, remainder, tag, timestamp);
|
||||
// Final function for signing inputs
|
||||
return IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments, customCurl);
|
||||
} else if (remainder > 0) {
|
||||
// Generate a new Address by calling getNewAddress
|
||||
|
||||
GetNewAddressResponse res = getNewAddress(seed, 0, false, 0, false);
|
||||
// Remainder bundle entry
|
||||
bundle.addEntry(1, res.getAddresses().get(0), remainder, tag, timestamp);
|
||||
|
||||
// Final function for signing inputs
|
||||
return IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments, customCurl);
|
||||
} else {
|
||||
// If there is no remainder, do not add transaction to bundle
|
||||
// simply sign and return
|
||||
return IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments, customCurl);
|
||||
}
|
||||
|
||||
// If multiple inputs provided, subtract the totalTransferValue by
|
||||
// the inputs balance
|
||||
} else {
|
||||
totalTransferValue -= thisBalance;
|
||||
}
|
||||
}
|
||||
throw new NotEnoughBalanceException();
|
||||
}
|
||||
|
||||
public static class Builder extends IotaAPICore.Builder<Builder> {
|
||||
private ICurl customCurl;
|
||||
|
||||
public Builder withCustomCurl(ICurl curl) {
|
||||
customCurl = curl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IotaAPI build() {
|
||||
super.build();
|
||||
return new IotaAPI(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package jota;
|
||||
|
||||
/**
|
||||
* IOTA's node command list
|
||||
*
|
||||
* <p>
|
||||
* 'params' is not currently used.
|
||||
*/
|
||||
public enum IotaAPICommands {
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
package jota;
|
||||
|
||||
import jota.dto.request.*;
|
||||
import jota.dto.response.*;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 15.01.2017.
|
||||
*/
|
||||
public class IotaAPICore {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(IotaAPICore.class);
|
||||
|
||||
private IotaAPIService service;
|
||||
private String protocol, host, port;
|
||||
|
||||
protected IotaAPICore(final Builder builder) {
|
||||
protocol = builder.protocol;
|
||||
host = builder.host;
|
||||
port = builder.port;
|
||||
postConstruct();
|
||||
}
|
||||
|
||||
protected static <T> Response<T> wrapCheckedException(final Call<T> call) {
|
||||
try {
|
||||
final Response<T> res = call.execute();
|
||||
if (res.code() == 400) {
|
||||
throw new IllegalAccessError("400 " + res.errorBody().string());
|
||||
} else if (res.code() == 401) {
|
||||
throw new IllegalAccessError("401 " + res.errorBody().string());
|
||||
} else if (res.code() == 500) {
|
||||
throw new IllegalAccessError("500 " + res.errorBody().string());
|
||||
}
|
||||
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 String env(String env, String def) {
|
||||
final String value = System.getenv(env);
|
||||
if (value == null) {
|
||||
log.warn("Environment variable '{}' is not defined, and actual value has not been specified. "
|
||||
+ "Rolling back to default value: '{}'", env, def);
|
||||
return def;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private void postConstruct() {
|
||||
|
||||
final String nodeUrl = protocol + "://" + host + ":" + port;
|
||||
|
||||
final OkHttpClient client = new OkHttpClient.Builder()
|
||||
.readTimeout(5000, TimeUnit.SECONDS)
|
||||
.connectTimeout(5000, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
final Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl(nodeUrl)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(client)
|
||||
.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 GetNeighborsResponse getNeighbors() {
|
||||
final Call<GetNeighborsResponse> res = service.getNeighbors(IotaCommandRequest.createGetNeighborsRequest());
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public AddNeighborsResponse addNeighbors(String... uris) {
|
||||
final Call<AddNeighborsResponse> res = service.addNeighbors(IotaNeighborsRequest.createAddNeighborsRequest(uris));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public RemoveNeighborsResponse removeNeighbors(String... uris) {
|
||||
final Call<RemoveNeighborsResponse> res = service.removeNeighbors(IotaNeighborsRequest.createRemoveNeighborsRequest(uris));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetTipsResponse getTips() {
|
||||
final Call<GetTipsResponse> res = service.getTips(IotaCommandRequest.createGetTipsRequest());
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactions(String[] addresses, String[] tags, String[] approvees, String[] bundles) {
|
||||
|
||||
final IotaFindTransactionsRequest findTransRequest = IotaFindTransactionsRequest
|
||||
.createFindTransactionRequest()
|
||||
.byAddresses(addresses)
|
||||
.byTags(tags)
|
||||
.byApprovees(approvees)
|
||||
.byBundles(bundles);
|
||||
|
||||
final Call<FindTransactionResponse> res = service.findTransactions(findTransRequest);
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactionsByAddresses(final String... addresses) {
|
||||
return findTransactions(addresses, null, null, null);
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactionsByBundles(final String... bundles) {
|
||||
return findTransactions(null, null, null, bundles);
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactionsByApprovees(final String... approvees) {
|
||||
return findTransactions(null, null, approvees, null);
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactionsByDigests(final 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 GetTrytesResponse getTrytes(String... hashes) {
|
||||
final Call<GetTrytesResponse> res = service.getTrytes(IotaGetTrytesRequest.createGetTrytesRequest(hashes));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetTransactionsToApproveResponse getTransactionsToApprove(Integer depth) {
|
||||
final Call<GetTransactionsToApproveResponse> res = service.getTransactionsToApprove(IotaGetTransactionsToApproveRequest.createIotaGetTransactionsToApproveRequest(depth));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetBalancesResponse getBalances(Integer threshold, String[] addresses) {
|
||||
final Call<GetBalancesResponse> res = service.getBalances(IotaGetBalancesRequest.createIotaGetBalancesRequest(threshold, addresses));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetBalancesResponse getBalances(Integer threshold, List<String> addresses) {
|
||||
return getBalances(threshold, addresses.toArray(new String[]{}));
|
||||
}
|
||||
|
||||
public InterruptAttachingToTangleResponse interruptAttachingToTangle() {
|
||||
final Call<InterruptAttachingToTangleResponse> res = service.interruptAttachingToTangle(IotaCommandRequest.createInterruptAttachToTangleRequest());
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetAttachToTangleResponse attachToTangle(String trunkTransaction, String branchTransaction, Integer minWeightMagnitude, String... trytes) {
|
||||
final Call<GetAttachToTangleResponse> res = service.attachToTangle(IotaAttachToTangleRequest.createAttachToTangleRequest(trunkTransaction, branchTransaction, minWeightMagnitude, trytes));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public StoreTransactionsResponse storeTransactions(String... trytes) {
|
||||
final Call<StoreTransactionsResponse> res = service.storeTransactions(IotaStoreTransactionsRequest.createStoreTransactionsRequest(trytes));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public BroadcastTransactionsResponse broadcastTransactions(String... trytes) {
|
||||
final Call<BroadcastTransactionsResponse> res = service.broadcastTransactions(IotaBroadcastTransactionRequest.createBroadcastTransactionsRequest(trytes));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static class Builder<T extends Builder<T>> {
|
||||
|
||||
String protocol, host, port;
|
||||
|
||||
public IotaAPICore 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 IotaAPICore(this);
|
||||
}
|
||||
|
||||
private boolean checkPropertiesFiles() {
|
||||
|
||||
try {
|
||||
|
||||
FileReader fileReader = new FileReader("node_config.properties");
|
||||
BufferedReader bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
final Properties nodeConfig = new Properties();
|
||||
nodeConfig.load(bufferedReader);
|
||||
|
||||
if (nodeConfig.getProperty("iota.node.protocol") != null) {
|
||||
protocol = nodeConfig.getProperty("iota.node.protocol");
|
||||
}
|
||||
|
||||
if (nodeConfig.getProperty("iota.node.host") != null) {
|
||||
host = nodeConfig.getProperty("iota.node.host");
|
||||
}
|
||||
|
||||
if (nodeConfig.getProperty("iota.node.port") != null) {
|
||||
port = nodeConfig.getProperty("iota.node.port");
|
||||
}
|
||||
|
||||
} 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() {
|
||||
protocol = env("IOTA_NODE_PROTOCOL", "http");
|
||||
host = env("IOTA_NODE_HOST", "localhost");
|
||||
port = env("IOTA_NODE_PORT", "14265");
|
||||
}
|
||||
|
||||
public T host(String host) {
|
||||
this.host = host;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T port(String port) {
|
||||
this.port = port;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T protocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,721 +0,0 @@
|
||||
package jota;
|
||||
|
||||
import jota.dto.request.*;
|
||||
import jota.dto.response.*;
|
||||
import jota.model.Bundle;
|
||||
import jota.model.Input;
|
||||
import jota.model.Transaction;
|
||||
import jota.model.Transfer;
|
||||
import jota.utils.Converter;
|
||||
import jota.utils.InputValidator;
|
||||
import jota.utils.IotaAPIUtils;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 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 String protocol, host, port;
|
||||
|
||||
private IotaAPIProxy(final Builder builder) {
|
||||
protocol = builder.protocol;
|
||||
host = builder.host;
|
||||
port = builder.port;
|
||||
postConstruct();
|
||||
}
|
||||
|
||||
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) {
|
||||
final String value = System.getenv(env);
|
||||
if (value == null) {
|
||||
log.warn("Environment variable '{}' is not defined, and actual value has not been specified. "
|
||||
+ "Rolling back to default value: '{}'", env, def);
|
||||
return def;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private void postConstruct() {
|
||||
|
||||
final String nodeUrl = protocol + "://" + host + ":" + port;
|
||||
|
||||
final OkHttpClient client = new OkHttpClient.Builder()
|
||||
.readTimeout(120, TimeUnit.SECONDS)
|
||||
.connectTimeout(120, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
final Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl(nodeUrl)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(client)
|
||||
.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 GetNeighborsResponse getNeighbors() {
|
||||
final Call<GetNeighborsResponse> res = service.getNeighbors(IotaCommandRequest.createGetNeighborsRequest());
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public AddNeighborsResponse addNeighbors(String... uris) {
|
||||
final Call<AddNeighborsResponse> res = service.addNeighbors(IotaNeighborsRequest.createAddNeighborsRequest(uris));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public RemoveNeighborsResponse removeNeighbors(String... uris) {
|
||||
final Call<RemoveNeighborsResponse> res = service.removeNeighbors(IotaNeighborsRequest.createRemoveNeighborsRequest(uris));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetTipsResponse getTips() {
|
||||
final Call<GetTipsResponse> res = service.getTips(IotaCommandRequest.createGetTipsRequest());
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactions(String[] addresses, String[] tags, String[] approvees, String[] bundles) {
|
||||
|
||||
final IotaFindTransactionsRequest findTransRequest = IotaFindTransactionsRequest
|
||||
.createFindTransactionRequest()
|
||||
.byAddresses(addresses)
|
||||
.byTags(tags)
|
||||
.byApprovees(approvees)
|
||||
.byBundles(bundles);
|
||||
|
||||
final Call<FindTransactionResponse> res = service.findTransactions(findTransRequest);
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactionsByAddresses(final String... addresses) {
|
||||
return findTransactions(addresses, null, null, null);
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactionsByBundles(final String... bundles) {
|
||||
return findTransactions(null, null, null, bundles);
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactionsByApprovees(final String... approvees) {
|
||||
return findTransactions(null, null, approvees, null);
|
||||
}
|
||||
|
||||
public FindTransactionResponse findTransactionsByDigests(final 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();
|
||||
}
|
||||
|
||||
public GetTrytesResponse getTrytes(String... hashes) {
|
||||
final Call<GetTrytesResponse> res = service.getTrytes(IotaGetTrytesRequest.createGetTrytesRequest(hashes));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetTransactionsToApproveResponse getTransactionsToApprove(Integer depth) {
|
||||
final Call<GetTransactionsToApproveResponse> res = service.getTransactionsToApprove(IotaGetTransactionsToApproveRequest.createIotaGetTransactionsToApproveRequest(depth));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetBalancesResponse getBalances(Integer threshold, String[] addresses) {
|
||||
final Call<GetBalancesResponse> res = service.getBalances(IotaGetBalancesRequest.createIotaGetBalancesRequest(threshold, addresses));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetBalancesResponse getBalances(Integer threshold, List<String> addresses) {
|
||||
return getBalances(threshold, addresses.toArray(new String[] {}));
|
||||
}
|
||||
|
||||
public InterruptAttachingToTangleResponse interruptAttachingToTangle() {
|
||||
final Call<InterruptAttachingToTangleResponse> res = service.interruptAttachingToTangle(IotaCommandRequest.createInterruptAttachToTangleRequest());
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetAttachToTangleResponse attachToTangle(String trunkTransaction, String branchTransaction, Integer minWeightMagnitude, String... trytes) {
|
||||
final Call<GetAttachToTangleResponse> res = service.attachToTangle(IotaAttachToTangleRequest.createAttachToTangleRequest(trunkTransaction, branchTransaction, minWeightMagnitude, trytes));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public StoreTransactionsResponse storeTransactions(String... trytes) {
|
||||
final Call<StoreTransactionsResponse> res = service.storeTransactions(IotaStoreTransactionsRequest.createStoreTransactionsRequest(trytes));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public BroadcastTransactionsResponse broadcastTransactions(String... trytes) {
|
||||
final Call<BroadcastTransactionsResponse> res = service.broadcastTransactions(IotaBroadcastTransactionRequest.createBroadcastTransactionsRequest(trytes));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
// end of proxied calls.
|
||||
|
||||
/**
|
||||
* Generates a new address from a seed and returns the remainderAddress.
|
||||
* This is either done deterministically, or by providing the index of the new remainderAddress
|
||||
*
|
||||
* @param seed Tryte-encoded seed. It should be noted that this seed is not transferred
|
||||
* @param index Optional (default null). Key index to start search from. If the index is provided, the generation of the address is not deterministic.
|
||||
* @param checksum Optional (default false). Adds 9-tryte address checksum
|
||||
* @param total Optional (default 1)Total number of addresses to generate
|
||||
* @param returnAll If true, it returns all addresses which were deterministically generated (until findTransactions returns null)
|
||||
* @return an array of strings with the specifed number of addresses
|
||||
*/
|
||||
public GetNewAddressResponse getNewAddress(final String seed, final int index, final boolean checksum, final int total, final boolean returnAll) {
|
||||
|
||||
final List<String> allAddresses = new ArrayList<>();
|
||||
|
||||
// If total number of addresses to generate is supplied, simply generate
|
||||
// and return the list of all addresses
|
||||
if (total != 0) {
|
||||
for (int i = index; i < index + total; i++) {
|
||||
allAddresses.add(IotaAPIUtils.newAddress(seed, i, checksum));
|
||||
}
|
||||
return GetNewAddressResponse.create(allAddresses);
|
||||
}
|
||||
// No total provided: Continue calling findTransactions to see if address was
|
||||
// already created if null, return list of addresses
|
||||
for (int i = index; ; i++) {
|
||||
|
||||
final String newAddress = IotaAPIUtils.newAddress(seed, i, checksum);
|
||||
final FindTransactionResponse response = findTransactionsByAddresses(new String[]{newAddress});
|
||||
|
||||
allAddresses.add(newAddress);
|
||||
if (response.getHashes().length == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If !returnAll return only the last address that was generated
|
||||
if (!returnAll) {
|
||||
allAddresses.subList(0, allAddresses.size()-1).clear();
|
||||
}
|
||||
return GetNewAddressResponse.create(allAddresses);
|
||||
}
|
||||
|
||||
/*
|
||||
* newAddress
|
||||
* broadcastAndStore
|
||||
* sendTrytes
|
||||
* prepareTransfers
|
||||
* getInputs
|
||||
* getLatestInclusion
|
||||
|
||||
getTransfers
|
||||
sendTransfer
|
||||
getBundle
|
||||
|
||||
getTransactionsObjects
|
||||
findTransactionObjects
|
||||
|
||||
replayBundle
|
||||
broadcastBundle
|
||||
getAccountData
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param trytes
|
||||
* @return a StoreTransactionsResponse
|
||||
*/
|
||||
public StoreTransactionsResponse broadcastAndStore(final String ... trytes) {
|
||||
|
||||
try {
|
||||
broadcastTransactions(trytes);
|
||||
} catch (Exception e) {
|
||||
log.error("Impossible to broadcastAndStore, aborting.", e);
|
||||
throw new IllegalStateException("BroadcastAndStore Illegal state Exception");
|
||||
}
|
||||
return storeTransactions(trytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Facade method: Gets transactions to approve, attaches to Tangle, broadcasts and stores
|
||||
* @param {array} trytes
|
||||
* @param {int} depth
|
||||
* @param {int} minWeightMagnitude
|
||||
* @return
|
||||
*/
|
||||
public List<Transaction> sendTrytes(final String trytes, final int minWeightMagnitude) {
|
||||
|
||||
final GetTransactionsToApproveResponse txs = getTransactionsToApprove(minWeightMagnitude);
|
||||
|
||||
// attach to tangle - do pow
|
||||
final GetAttachToTangleResponse res = attachToTangle(txs.getTrunkTransaction(), txs.getBranchTransaction(), minWeightMagnitude, trytes);
|
||||
|
||||
try {
|
||||
broadcastAndStore(res.getTrytes());
|
||||
} catch (Exception e) {
|
||||
log.error("Impossible to sendTrytes, aborting.", e);
|
||||
throw new IllegalStateException("sendTrytes Illegal state Exception");
|
||||
}
|
||||
|
||||
//return Arrays.stream(res.getTrytes()).map(Converter::transactionObject).collect(Collectors.toList());
|
||||
final List<Transaction> trx = new ArrayList<>();
|
||||
|
||||
for (final String tx : Arrays.asList(res.getTrytes())) {
|
||||
trx.add(Converter.transactionObject(tx));
|
||||
}
|
||||
return trx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for getTrytes and transactionObjects
|
||||
* gets the trytes and transaction object from a list of transaction hashes
|
||||
*
|
||||
* @method getTransactionsObjects
|
||||
* @param {array} hashes
|
||||
* @return
|
||||
* @returns {function} callback
|
||||
* @returns {object} success
|
||||
**/
|
||||
public List<Transaction> getTransactionsObjects(String[] hashes) {
|
||||
|
||||
if (!InputValidator.isArrayOfHashes(hashes)) {
|
||||
throw new IllegalStateException("Not an Array of Hashes: " + Arrays.toString(hashes));
|
||||
}
|
||||
|
||||
final GetTrytesResponse trytesResponse = getTrytes(hashes);
|
||||
|
||||
final List<Transaction> trxs = new ArrayList<>();
|
||||
|
||||
for (final String tryte : trytesResponse.getTrytes()) {
|
||||
trxs.add(Converter.transactionObject(tryte));
|
||||
}
|
||||
return trxs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for findTransactions, getTrytes and transactionObjects
|
||||
* Returns the transactionObject of a transaction hash. The input can be a valid
|
||||
* findTransactions input
|
||||
*
|
||||
* @param {object} input
|
||||
* @method getTransactionsObjects
|
||||
* @returns {function} callback
|
||||
* @returns {object} success
|
||||
**/
|
||||
public List<Transaction> findTransactionObjects(String[] input) {
|
||||
FindTransactionResponse ftr = findTransactions(input, null, null, null);
|
||||
if (ftr == null || ftr.getHashes() == null)
|
||||
|
||||
return null;
|
||||
|
||||
// get the transaction objects of the transactions
|
||||
return getTransactionsObjects(ftr.getHashes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares transfer by generating bundle, finding and signing inputs
|
||||
*
|
||||
* @method prepareTransfers
|
||||
* @param {string} seed
|
||||
* @param {object} transfers
|
||||
* @param {object} options
|
||||
* @property {array} inputs Inputs used for signing. Needs to have correct keyIndex and address value
|
||||
* @property {string} address Remainder address
|
||||
* @param {function} callback
|
||||
* @return
|
||||
* @returns {array} trytes Returns bundle trytes
|
||||
**/
|
||||
public List<String> prepareTransfers(final String seed, final List<Transfer> transfers, String remainder, List<Input> inputs) {
|
||||
|
||||
// Input validation of transfers object
|
||||
if (!InputValidator.isTransfersCollectionCorrect(transfers)) {
|
||||
throw new IllegalStateException("Invalid Transfer");
|
||||
}
|
||||
|
||||
// Create a new bundle
|
||||
final Bundle bundle = new Bundle();
|
||||
final List<String> signatureFragments = new ArrayList<>();
|
||||
|
||||
int totalValue = 0;
|
||||
String tag;
|
||||
|
||||
// Iterate over all transfers, get totalValue
|
||||
// and prepare the signatureFragments, message and tag
|
||||
for (final Transfer transfer : transfers) {
|
||||
|
||||
int signatureMessageLength = 1;
|
||||
|
||||
// If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction)
|
||||
if (transfer.getMessage().length() > 2187) {
|
||||
|
||||
// Get total length, message / maxLength (2187 trytes)
|
||||
signatureMessageLength += Math.floor(transfer.getMessage().length() / 2187);
|
||||
|
||||
String msgCopy = transfer.getMessage();
|
||||
|
||||
// While there is still a message, copy it
|
||||
while (!msgCopy.isEmpty()) {
|
||||
|
||||
String fragment = StringUtils.substring(msgCopy, 0, 2187);
|
||||
msgCopy = StringUtils.substring(msgCopy, 2187, msgCopy.length());
|
||||
|
||||
// Pad remainder of fragment
|
||||
for (int j = 0; fragment.length() < 2187; j++) {
|
||||
fragment += "9";
|
||||
}
|
||||
|
||||
signatureFragments.add(fragment);
|
||||
}
|
||||
} else {
|
||||
// Else, get single fragment with 2187 of 9's trytes
|
||||
String fragment = StringUtils.substring(transfer.getMessage(), 0, 2187);
|
||||
|
||||
for (int j = 0; fragment.length() < 2187; j++) {
|
||||
fragment += '9';
|
||||
}
|
||||
|
||||
signatureFragments.add(fragment);
|
||||
}
|
||||
|
||||
// get current timestamp in seconds
|
||||
long timestamp = (long) Math.floor(Calendar.getInstance().getTimeInMillis() / 1000);
|
||||
|
||||
// If no tag defined, get 27 tryte tag.
|
||||
tag = transfer.getTag().isEmpty() ? "999999999999999999999999999" : transfer.getTag();
|
||||
|
||||
// Pad for required 27 tryte length
|
||||
for (int j = 0; tag.length() < 27; j++) {
|
||||
tag += '9';
|
||||
}
|
||||
|
||||
// Add first entry to the bundle
|
||||
bundle.addEntry(signatureMessageLength, transfer.getAddress(), transfer.getValue(), tag, timestamp);
|
||||
// Sum up total value
|
||||
totalValue += transfer.getValue();
|
||||
}
|
||||
|
||||
// Get inputs if we are sending tokens
|
||||
if (totalValue != 0) {
|
||||
|
||||
// Case 1: user provided inputs
|
||||
// Validate the inputs by calling getBalances
|
||||
if (!inputs.isEmpty()) {
|
||||
|
||||
// Get list if addresses of the provided inputs
|
||||
List<String> inputsAddresses = new ArrayList<>();
|
||||
for (final Input i : inputs) {
|
||||
inputsAddresses.add(i.getAddress());
|
||||
}
|
||||
|
||||
GetBalancesResponse resbalances = getBalances(100, inputsAddresses);
|
||||
String[] balances = resbalances.getBalances();
|
||||
|
||||
|
||||
List<Input> confirmedInputs = new ArrayList<>();
|
||||
int totalBalance = 0; int i = 0;
|
||||
for (String balance : balances) {
|
||||
long thisBalance = Integer.parseInt(balance);
|
||||
totalBalance += thisBalance;
|
||||
|
||||
// If input has balance, add it to confirmedInputs
|
||||
if (thisBalance > 0) {
|
||||
Input inputEl = inputs.get(i++);
|
||||
inputEl.setBalance(thisBalance);
|
||||
confirmedInputs.add(inputEl);
|
||||
}
|
||||
}
|
||||
|
||||
// Return not enough balance error
|
||||
if (totalValue > totalBalance) {
|
||||
throw new IllegalStateException("Not enough balance");
|
||||
}
|
||||
|
||||
return IotaAPIUtils.signInputsAndReturn(seed, confirmedInputs, bundle, signatureFragments);
|
||||
}
|
||||
|
||||
// Case 2: Get inputs deterministically
|
||||
//
|
||||
// If no inputs provided, derive the addresses from the seed and
|
||||
// confirm that the inputs exceed the threshold
|
||||
else {
|
||||
|
||||
GetBalancesAndFormatResponse newinputs = getInputs(seed, Collections.EMPTY_LIST, 0, 0, totalValue);
|
||||
// If inputs with enough balance
|
||||
return IotaAPIUtils.signInputsAndReturn(seed, newinputs.getInput(), bundle, signatureFragments);
|
||||
}
|
||||
} else {
|
||||
|
||||
// If no input required, don't sign and simply finalize the bundle
|
||||
bundle.finalize();
|
||||
bundle.addTrytes(signatureFragments);
|
||||
|
||||
List<Transaction> trxb = bundle.getTransactions();
|
||||
List<String> bundleTrytes = new ArrayList<>();
|
||||
for (Transaction tx : trxb) {
|
||||
jota.utils.IotaAPIUtils.transactionTrytes(tx);
|
||||
}
|
||||
Collections.reverse(bundleTrytes);
|
||||
return bundleTrytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the inputs of a seed
|
||||
*
|
||||
* @method getInputs
|
||||
* @param {string} seed
|
||||
* @param {object} options
|
||||
* @property {int} start Starting key index
|
||||
* @property {int} end Ending key index
|
||||
* @property {int} threshold Min balance required
|
||||
* @param {function} callback
|
||||
**/
|
||||
public GetBalancesAndFormatResponse getInputs(final String seed, final List<String> balances, int start, int end, int threshold) {
|
||||
|
||||
// validate the seed
|
||||
if (!InputValidator.isTrytes(seed, 0)) {
|
||||
throw new IllegalStateException("Invalid Seed");
|
||||
}
|
||||
|
||||
// If start value bigger than end, return error
|
||||
// or if difference between end and start is bigger than 500 keys
|
||||
if (start > end || end > (start + 500)) {
|
||||
throw new IllegalStateException("Invalid inputs provided");
|
||||
}
|
||||
|
||||
// Case 1: start and end
|
||||
//
|
||||
// If start and end is defined by the user, simply iterate through the keys
|
||||
// and call getBalances
|
||||
if (end != 0) {
|
||||
|
||||
List<String> allAddresses = new ArrayList<>();
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
|
||||
String address = IotaAPIUtils.newAddress(seed, i, false);
|
||||
allAddresses.add(address);
|
||||
}
|
||||
|
||||
return getBalanceAndFormat(allAddresses, balances, threshold, start, end);
|
||||
}
|
||||
// Case 2: iterate till threshold || end
|
||||
//
|
||||
// Either start from index: 0 or start (if defined) until threshold is reached.
|
||||
// Calls getNewAddress and deterministically generates and returns all addresses
|
||||
// We then do getBalance, format the output and return it
|
||||
else {
|
||||
|
||||
final GetNewAddressResponse res = getNewAddress(seed, start, false, 0, true);
|
||||
return getBalanceAndFormat(res.getAddresses(), balances, threshold, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls getBalances and formats the output
|
||||
// returns the final inputsObject then
|
||||
public GetBalancesAndFormatResponse getBalanceAndFormat(final List<String> addresses, List<String> balances, long threshold, int start, int end) {
|
||||
|
||||
if (balances == null || balances.isEmpty()) {
|
||||
GetBalancesResponse getBalancesResponse = getBalances(100, addresses);
|
||||
balances = Arrays.asList(getBalancesResponse.getBalances());
|
||||
}
|
||||
|
||||
// If threshold defined, keep track of whether reached or not
|
||||
// else set default to true
|
||||
boolean thresholdReached = threshold != 0 ? false : true; int i = -1;
|
||||
|
||||
List<Input> inputs = new ArrayList<>();
|
||||
long totalBalance = 0;
|
||||
|
||||
for (String address : addresses) {
|
||||
|
||||
long balance = Long.parseLong(balances.get(++i));
|
||||
|
||||
if (balance > 0) {
|
||||
final Input newEntry = new Input(address, balance, start+i);
|
||||
|
||||
inputs.add(newEntry);
|
||||
// Increase totalBalance of all aggregated inputs
|
||||
totalBalance += balance;
|
||||
|
||||
if (!thresholdReached && totalBalance >= threshold) {
|
||||
thresholdReached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thresholdReached) {
|
||||
return GetBalancesAndFormatResponse.create(inputs, totalBalance);
|
||||
}
|
||||
throw new IllegalStateException("Not enough balance");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated bundle transactions of a single transaction
|
||||
* Does validation of signatures, total sum as well as bundle order
|
||||
*
|
||||
* @method getBundle
|
||||
* @param {string} transaction Hash of a tail transaction
|
||||
* @returns {list} bundle Transaction objects
|
||||
**/
|
||||
public GetBundleResponse getBundle(String transaction) {
|
||||
return null; //IotaAPIUtils.getBundle(transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replays a transfer by doing Proof of Work again
|
||||
*
|
||||
* @method replayBundle
|
||||
* @param {string} tail
|
||||
* @param {int} depth
|
||||
* @param {int} minWeightMagnitude
|
||||
* @param {function} callback
|
||||
* @returns {object} analyzed Transaction objects
|
||||
**/
|
||||
public List<Transaction> replayTransfer(String transaction, int depth, int minWeightMagnitude) {
|
||||
|
||||
List<String> bundleTrytes = new ArrayList<>();
|
||||
|
||||
GetBundleResponse bundle = getBundle(transaction);
|
||||
|
||||
for (Transaction element : bundle.getTransactions()) {
|
||||
|
||||
bundleTrytes.add(IotaAPIUtils.transactionTrytes(element));
|
||||
}
|
||||
|
||||
return sendTrytes(bundleTrytes, minWeightMagnitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for getNodeInfo and getInclusionStates
|
||||
*
|
||||
* @method getLatestInclusion
|
||||
* @param {array} hashes
|
||||
* @returns {function} callback
|
||||
* @returns {array} state
|
||||
**/
|
||||
public GetInclusionStateResponse getLatestInclusion(String[] hashes) {
|
||||
GetNodeInfoResponse getNodeInfoResponse = getNodeInfo();
|
||||
if (getNodeInfoResponse == null) return null;
|
||||
|
||||
String[] latestMilestone = {getNodeInfoResponse.getLatestSolidSubtangleMilestone()};
|
||||
|
||||
return getInclusionStates(hashes, latestMilestone);
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
FileReader fileReader = new FileReader("node_config.properties");
|
||||
BufferedReader bufferedReader = new BufferedReader(fileReader);
|
||||
|
||||
final Properties nodeConfig = new Properties();
|
||||
nodeConfig.load(bufferedReader);
|
||||
|
||||
if (nodeConfig.getProperty("iota.node.protocol") != null) {
|
||||
protocol = nodeConfig.getProperty("iota.node.protocol");
|
||||
}
|
||||
|
||||
if (nodeConfig.getProperty("iota.node.host") != null) {
|
||||
host = nodeConfig.getProperty("iota.node.host");
|
||||
}
|
||||
|
||||
if (nodeConfig.getProperty("iota.node.port") != null) {
|
||||
port = nodeConfig.getProperty("iota.node.port");
|
||||
}
|
||||
|
||||
} 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() {
|
||||
protocol = env("IOTA_NODE_PROTOCOL", "http");
|
||||
host = env("IOTA_NODE_HOST", "localhost");
|
||||
port = env("IOTA_NODE_PORT", "14265");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Returns information about your node.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "getNodeInfo"}'
|
||||
*
|
||||
@@ -31,7 +31,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Get the list of latest tips (unconfirmed transactions).
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "getNeighbors"}'
|
||||
*/
|
||||
@@ -41,7 +41,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Add a list of neighbors to your node.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "addNeighbors", "uris": ["udp://8.8.8.8:14265", "udp://8.8.8.5:14265"]}'
|
||||
*/
|
||||
@@ -51,7 +51,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Removes a list of neighbors to your node.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "removeNeighbors", "uris": ["udp://8.8.8.8:14265", "udp://8.8.8.5:14265"]}'
|
||||
*/
|
||||
@@ -61,7 +61,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Get the list of latest tips (unconfirmed transactions).
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "getTips"}'
|
||||
*/
|
||||
@@ -71,7 +71,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Find the transactions which match the specified input and return
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 \ -X POST \ -H 'Content-Type: application/json' \
|
||||
* -d '{"command": "findTransactions", "addresses": ["RVORZ9SIIP9RCYMREUIXXVPQIPHVCNPQ9HZWYKFWYWZRE9JQKG9REPKIASHUUECPSQO9JT9XNMVKWYGVAZETAIRPTM"]}'
|
||||
*/
|
||||
@@ -82,7 +82,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "getInclusionStates", "transactions"Q9HZWYKFWYWZRE9JQKG9REPKIASHUUECPSQO9JT9XNMVKWYGVAZETAIRPTM"], "tips" : []}'
|
||||
*/
|
||||
@@ -92,7 +92,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Returns the raw trytes data of a transaction.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "getTrytes", "hashes": ["OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999"]}'
|
||||
*/
|
||||
@@ -102,7 +102,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Tip selection which returns trunkTransaction and branchTransaction. The input value is the latest coordinator milestone, as provided through the getNodeInfo API call.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "getTransactionsToApprove", "depth": 27}'
|
||||
*/
|
||||
@@ -112,7 +112,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* It returns the confirmed balance which a list of addresses have at the latest confirmed milestone.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "getBalances", "addresses": ["HBBYKAKTILIPVUKFOTSLHGENPTXYBNKXZFQFR9VQFWNBMTQNRVOUKPVPRNBSZVVILMAFBKOTBLGLWLOHQ"], "threshold": 100}'
|
||||
*/
|
||||
@@ -122,7 +122,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Attaches the specified transactions (trytes) to the Tangle by doing Proof of Work.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "attachToTangle", "trunkTransaction": "JVMTDGDPDFYHMZPMWEKKANBQSLSDTIIHAYQUMZOKHXXXGJHJDQPOMDOMNRDKYCZRUFZROZDADTHZC9999", "branchTransaction": "P9KFSJVGSPLXAEBJSHWFZLGP9GGJTIO9YITDEHATDTGAFLPLBZ9FOFWWTKMAZXZHFGQHUOXLXUALY9999", "minWeightMagnitude": 18, "trytes": ["TRYTVALUEHERE"]}'
|
||||
*/
|
||||
@@ -132,7 +132,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Interrupts and completely aborts the attachToTangle process.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "interruptAttachingToTangle" }
|
||||
*/
|
||||
@@ -142,7 +142,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Broadcast a list of transactions to all neighbors. The input trytes for this call are provided by attachToTangle.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "broadcastTransactions", "trytes": ["BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999"]}
|
||||
*/
|
||||
@@ -152,7 +152,7 @@ public interface IotaAPIService {
|
||||
|
||||
/**
|
||||
* Store transactions into the local storage. The trytes to be used for this call are returned by attachToTangle.
|
||||
*
|
||||
* <p>
|
||||
* curl http://localhost:14265 -X POST -H 'Content-Type: application/json'
|
||||
* -d '{"command": "storeTransactions", "trytes": ["BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999"]}'
|
||||
*/
|
||||
|
||||
@@ -20,4 +20,36 @@ public class IotaAttachToTangleRequest extends IotaCommandRequest {
|
||||
public static IotaAttachToTangleRequest createAttachToTangleRequest(final String trunkTransaction, final String branchTransaction, final Integer minWeightMagnitude, final String... trytes) {
|
||||
return new IotaAttachToTangleRequest(trunkTransaction, branchTransaction, minWeightMagnitude, trytes);
|
||||
}
|
||||
|
||||
public String getTrunkTransaction() {
|
||||
return trunkTransaction;
|
||||
}
|
||||
|
||||
public void setTrunkTransaction(String trunkTransaction) {
|
||||
this.trunkTransaction = trunkTransaction;
|
||||
}
|
||||
|
||||
public String getBranchTransaction() {
|
||||
return branchTransaction;
|
||||
}
|
||||
|
||||
public void setBranchTransaction(String branchTransaction) {
|
||||
this.branchTransaction = branchTransaction;
|
||||
}
|
||||
|
||||
public Integer getMinWeightMagnitude() {
|
||||
return minWeightMagnitude;
|
||||
}
|
||||
|
||||
public void setMinWeightMagnitude(Integer minWeightMagnitude) {
|
||||
this.minWeightMagnitude = minWeightMagnitude;
|
||||
}
|
||||
|
||||
public String[] getTrytes() {
|
||||
return trytes;
|
||||
}
|
||||
|
||||
public void setTrytes(String[] trytes) {
|
||||
this.trytes = trytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,12 @@ public class IotaBroadcastTransactionRequest extends IotaCommandRequest {
|
||||
public static IotaBroadcastTransactionRequest createBroadcastTransactionsRequest(final String... trytes) {
|
||||
return new IotaBroadcastTransactionRequest(trytes);
|
||||
}
|
||||
|
||||
public String[] getTrytes() {
|
||||
return trytes;
|
||||
}
|
||||
|
||||
public void setTrytes(String[] trytes) {
|
||||
this.trytes = trytes;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,38 @@ public class IotaFindTransactionsRequest extends IotaCommandRequest {
|
||||
return new IotaFindTransactionsRequest();
|
||||
}
|
||||
|
||||
public String[] getBundles() {
|
||||
return bundles;
|
||||
}
|
||||
|
||||
public void setBundles(String[] bundles) {
|
||||
this.bundles = bundles;
|
||||
}
|
||||
|
||||
public String[] getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
public void setAddresses(String[] addresses) {
|
||||
this.addresses = addresses;
|
||||
}
|
||||
|
||||
public String[] getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(String[] tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public String[] getApprovees() {
|
||||
return approvees;
|
||||
}
|
||||
|
||||
public void setApprovees(String[] approvees) {
|
||||
this.approvees = approvees;
|
||||
}
|
||||
|
||||
public IotaFindTransactionsRequest byBundles(String... bundles) {
|
||||
this.bundles = bundles;
|
||||
return this;
|
||||
|
||||
@@ -17,5 +17,20 @@ public class IotaGetBalancesRequest extends IotaCommandRequest {
|
||||
return new IotaGetBalancesRequest(threshold, addresses);
|
||||
}
|
||||
|
||||
public String[] getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
public void setAddresses(String[] addresses) {
|
||||
this.addresses = addresses;
|
||||
}
|
||||
|
||||
public Integer getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
public void setThreshold(Integer threshold) {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,4 +25,19 @@ public class IotaGetInclusionStateRequest extends IotaCommandRequest {
|
||||
tips.toArray(new String[]{}));
|
||||
}
|
||||
|
||||
public String[] getTransactions() {
|
||||
return transactions;
|
||||
}
|
||||
|
||||
public void setTransactions(String[] transactions) {
|
||||
this.transactions = transactions;
|
||||
}
|
||||
|
||||
public String[] getTips() {
|
||||
return tips;
|
||||
}
|
||||
|
||||
public void setTips(String[] tips) {
|
||||
this.tips = tips;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,12 @@ public class IotaGetTransactionsToApproveRequest extends IotaCommandRequest {
|
||||
public static IotaGetTransactionsToApproveRequest createIotaGetTransactionsToApproveRequest(Integer depth) {
|
||||
return new IotaGetTransactionsToApproveRequest(depth);
|
||||
}
|
||||
|
||||
public Integer getDepth() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
public void setDepth(Integer depth) {
|
||||
this.depth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,12 @@ public class IotaGetTrytesRequest extends IotaCommandRequest {
|
||||
public static IotaGetTrytesRequest createGetTrytesRequest(String... hashes) {
|
||||
return new IotaGetTrytesRequest(hashes);
|
||||
}
|
||||
|
||||
public String[] getHashes() {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
public void setHashes(String[] hashes) {
|
||||
this.hashes = hashes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,5 +18,13 @@ public class IotaNeighborsRequest extends IotaCommandRequest {
|
||||
public static IotaNeighborsRequest createRemoveNeighborsRequest(String... uris) {
|
||||
return new IotaNeighborsRequest(IotaAPICommands.REMOVE_NEIGHBORS, uris);
|
||||
}
|
||||
|
||||
public String[] getUris() {
|
||||
return uris;
|
||||
}
|
||||
|
||||
public void setUris(String[] uris) {
|
||||
this.uris = uris;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,4 +14,12 @@ public class IotaStoreTransactionsRequest extends IotaCommandRequest {
|
||||
public static IotaStoreTransactionsRequest createStoreTransactionsRequest(final String... trytes) {
|
||||
return new IotaStoreTransactionsRequest(trytes);
|
||||
}
|
||||
|
||||
public String[] getTrytes() {
|
||||
return trytes;
|
||||
}
|
||||
|
||||
public void setTrytes(String[] trytes) {
|
||||
this.trytes = trytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,16 @@ import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
public abstract class AbstractResponse {
|
||||
|
||||
private Integer duration;
|
||||
private Long duration;
|
||||
|
||||
public Integer getDuration() {
|
||||
public Long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(Long duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.List;
|
||||
|
||||
public class AnalyzeTransactionResponse extends AbstractResponse {
|
||||
|
||||
private List<Transaction> transactions = new ArrayList<Transaction>();
|
||||
private List<Transaction> transactions = new ArrayList<>();
|
||||
|
||||
public List<Transaction> getTransactions() {
|
||||
return transactions;
|
||||
|
||||
@@ -4,6 +4,10 @@ public class GetAttachToTangleResponse extends AbstractResponse {
|
||||
|
||||
private String[] trytes;
|
||||
|
||||
public GetAttachToTangleResponse(long duration) {
|
||||
setDuration(duration);
|
||||
}
|
||||
|
||||
public String[] getTrytes() {
|
||||
return trytes;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
package jota.dto.response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jota.model.Input;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GetBalancesAndFormatResponse extends AbstractResponse {
|
||||
|
||||
private List<Input> input;
|
||||
private long totalBalance;
|
||||
|
||||
|
||||
public static GetBalancesAndFormatResponse create(List<Input> inputs, long totalBalance2, long duration) {
|
||||
GetBalancesAndFormatResponse res = new GetBalancesAndFormatResponse();
|
||||
res.setInput(inputs);
|
||||
res.setTotalBalance(totalBalance2);
|
||||
res.setDuration(duration);
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<Input> getInput() {
|
||||
return input;
|
||||
}
|
||||
@@ -16,19 +24,12 @@ public class GetBalancesAndFormatResponse extends AbstractResponse {
|
||||
public void setInput(List<Input> input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
||||
public long getTotalBalance() {
|
||||
return totalBalance;
|
||||
}
|
||||
|
||||
|
||||
public void setTotalBalance(long totalBalance) {
|
||||
this.totalBalance = totalBalance;
|
||||
}
|
||||
|
||||
public static GetBalancesAndFormatResponse create(List<Input> inputs, long totalBalance2) {
|
||||
GetBalancesAndFormatResponse res = new GetBalancesAndFormatResponse();
|
||||
res.setInput(inputs);
|
||||
res.setTotalBalance(totalBalance2);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,15 @@ public class GetBundleResponse extends AbstractResponse {
|
||||
|
||||
private List<Transaction> transactions = new ArrayList<>();
|
||||
|
||||
public static GetBundleResponse create(List<Transaction> transactions, long duration) {
|
||||
GetBundleResponse res = new GetBundleResponse();
|
||||
res.transactions = transactions;
|
||||
res.setDuration(duration);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
public List<Transaction> getTransactions() {
|
||||
return transactions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,10 @@ public class GetNewAddressResponse extends AbstractResponse {
|
||||
|
||||
private List<String> addresses;
|
||||
|
||||
public static GetNewAddressResponse create(List<String> addresses) {
|
||||
public static GetNewAddressResponse create(List<String> addresses, long duration) {
|
||||
GetNewAddressResponse res = new GetNewAddressResponse();
|
||||
res.addresses = addresses;
|
||||
res.setDuration(duration);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package jota.dto.response;
|
||||
|
||||
import jota.model.Bundle;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 28.12.16.
|
||||
*/
|
||||
public class GetTransferResponse extends AbstractResponse {
|
||||
|
||||
private Bundle[] transferBundle;
|
||||
|
||||
public static GetTransferResponse create(Bundle[] transferBundle, long duration) {
|
||||
GetTransferResponse res = new GetTransferResponse();
|
||||
res.transferBundle = transferBundle;
|
||||
return res;
|
||||
}
|
||||
|
||||
public Bundle[] getTransfers() {
|
||||
return transferBundle;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package jota.dto.response;
|
||||
|
||||
import jota.model.Transfer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GetTransfersResponse extends AbstractResponse {
|
||||
|
||||
private List<Transfer> transfers = new ArrayList<>();
|
||||
|
||||
public List<Transfer> getTransfers() {
|
||||
return transfers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package jota.dto.response;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 01.01.17.
|
||||
*/
|
||||
public class ReplayBundleResponse extends AbstractResponse {
|
||||
|
||||
private Boolean[] successfully;
|
||||
|
||||
public static ReplayBundleResponse create(Boolean[] successfully, long duration) {
|
||||
ReplayBundleResponse res = new ReplayBundleResponse();
|
||||
res.successfully = successfully;
|
||||
res.setDuration(duration);
|
||||
return res;
|
||||
}
|
||||
|
||||
public Boolean[] getSuccessfully() {
|
||||
return successfully;
|
||||
}
|
||||
|
||||
public void setSuccessfully(Boolean[] successfully) {
|
||||
this.successfully = successfully;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package jota.dto.response;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 28.12.16.
|
||||
*/
|
||||
public class SendTransferResponse extends AbstractResponse {
|
||||
|
||||
private Boolean[] successfully;
|
||||
|
||||
public static SendTransferResponse create(Boolean[] successfully, long duration) {
|
||||
SendTransferResponse res = new SendTransferResponse();
|
||||
res.successfully = successfully;
|
||||
res.setDuration(duration);
|
||||
return res;
|
||||
}
|
||||
|
||||
public Boolean[] getSuccessfully() {
|
||||
return successfully;
|
||||
}
|
||||
|
||||
public void setSuccessfully(Boolean[] successfully) {
|
||||
this.successfully = successfully;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package jota.dto.response;
|
||||
|
||||
public class StoreTransactionsResponse extends AbstractResponse {
|
||||
// empty response
|
||||
public StoreTransactionsResponse(long duration) {
|
||||
setDuration(duration);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,4 +10,8 @@ public class ArgumentException extends BaseException {
|
||||
public ArgumentException() {
|
||||
super("Wrong arguments passed to function");
|
||||
}
|
||||
|
||||
public ArgumentException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jota.error;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -14,6 +15,10 @@ public class BaseException extends Exception {
|
||||
|
||||
public BaseException(String msg) {
|
||||
super(msg);
|
||||
if (messages == null) {
|
||||
messages = new ArrayList<>();
|
||||
}
|
||||
messages.add(msg);
|
||||
}
|
||||
|
||||
public BaseException(String msg, Exception cause) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package jota.error;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 22.01.2017.
|
||||
*/
|
||||
public class BroadcastAndStoreException extends BaseException {
|
||||
|
||||
public BroadcastAndStoreException() {
|
||||
super("Impossible to broadcastAndStore, aborting.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package jota.error;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 27.12.2016.
|
||||
*/
|
||||
public class InvalidBundleException extends BaseException {
|
||||
|
||||
public InvalidBundleException() {
|
||||
super("Invalid Bundle");
|
||||
}
|
||||
|
||||
public InvalidBundleException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package jota.error;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 27.12.2016.
|
||||
*/
|
||||
public class InvalidSignatureException extends BaseException {
|
||||
public InvalidSignatureException() {
|
||||
super("Invalid Signatures!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package jota.error;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 22.01.2017.
|
||||
*/
|
||||
public class NoInclusionStatesExcpection extends BaseException {
|
||||
|
||||
public NoInclusionStatesExcpection() {
|
||||
super("No inclusion states for the provided seed");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package jota.error;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 22.01.2017.
|
||||
*/
|
||||
public class NoNodeInfoException extends BaseException {
|
||||
|
||||
public NoNodeInfoException() {
|
||||
super("Node info could not be retrieved");
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package jota.error;
|
||||
* Created by Adrian on 09.12.2016.
|
||||
*/
|
||||
public class NotEnoughBalanceException extends BaseException {
|
||||
|
||||
|
||||
private static final long serialVersionUID = -3807270816402226476L;
|
||||
|
||||
public NotEnoughBalanceException() {
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
package jota.model;
|
||||
|
||||
import jota.pow.Curl;
|
||||
import jota.pow.ICurl;
|
||||
import jota.pow.JCurl;
|
||||
import jota.utils.Converter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Created by pinpong on 09.12.16.
|
||||
*/
|
||||
public class Bundle {
|
||||
|
||||
private List<Transaction> transactions;
|
||||
private int length;
|
||||
public class Bundle implements Comparable<Bundle> {
|
||||
|
||||
public static String EMPTY_HASH = "999999999999999999999999999999999999999999999999999999999999999999999999999999999";
|
||||
private List<Transaction> transactions;
|
||||
private int length;
|
||||
|
||||
|
||||
public Bundle() {
|
||||
@@ -30,10 +32,6 @@ public class Bundle {
|
||||
return transactions;
|
||||
}
|
||||
|
||||
public void setTransactions(List<Transaction> transactions) {
|
||||
this.transactions = transactions;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
@@ -43,48 +41,37 @@ public class Bundle {
|
||||
}
|
||||
|
||||
public void addEntry(int signatureMessageLength, String address, long value, String tag, long timestamp) {
|
||||
if (getTransactions() == null) {
|
||||
this.transactions = new ArrayList<>(getTransactions());
|
||||
}
|
||||
|
||||
for (int i = 0; i < signatureMessageLength; i++) {
|
||||
|
||||
List<Transaction> transactions = new ArrayList<>(getTransactions());
|
||||
transactions.add(new Transaction(address, String.valueOf(i == 0 ? value : 0), tag, String.valueOf(timestamp)));
|
||||
|
||||
setTransactions(transactions);
|
||||
|
||||
Transaction trx = new Transaction(address, String.valueOf(i == 0 ? value : 0), tag, String.valueOf(timestamp));
|
||||
getTransactions().add(trx);
|
||||
}
|
||||
}
|
||||
|
||||
public void finalize() {
|
||||
public void finalize(ICurl customCurl) {
|
||||
|
||||
Curl curl = new Curl();
|
||||
ICurl curl = customCurl == null ? new JCurl() : customCurl;
|
||||
curl.reset();
|
||||
|
||||
for (int i = 0; i < this.getTransactions().size(); i++) {
|
||||
|
||||
int[] valueTrits = Converter.trits(this.getTransactions().get(i).getValue());
|
||||
while (valueTrits.length < 81) {
|
||||
valueTrits[valueTrits.length] = 0;
|
||||
}
|
||||
int[] valueTrits = Converter.trits(this.getTransactions().get(i).getValue(), 81);
|
||||
|
||||
int[] timestampTrits = Converter.trits(this.getTransactions().get(i).getTimestamp());
|
||||
while (timestampTrits.length < 27) {
|
||||
timestampTrits[timestampTrits.length] = 0;
|
||||
}
|
||||
int[] timestampTrits = Converter.trits(this.getTransactions().get(i).getTimestamp(), 27);
|
||||
|
||||
int[] currentIndexTrits = Converter.trits(this.getTransactions().get(i).setCurrentIndex("" + i));
|
||||
while (currentIndexTrits.length < 27) {
|
||||
currentIndexTrits[currentIndexTrits.length] = 0;
|
||||
}
|
||||
int[] currentIndexTrits = Converter.trits(this.getTransactions().get(i).setCurrentIndex("" + i), 27);
|
||||
|
||||
int[] lastIndexTrits = Converter.trits(this.getTransactions().get(i).setLastIndex("" + (this.getTransactions().size() - 1)), 27);
|
||||
|
||||
int[] lastIndexTrits = Converter.trits(this.getTransactions().get(i).setLastIndex("" + (this.getTransactions().size() - 1)));
|
||||
while (lastIndexTrits.length < 27) {
|
||||
lastIndexTrits[lastIndexTrits.length] = 0;
|
||||
}
|
||||
|
||||
int[] t = Converter.trits(this.getTransactions().get(i).getAddress() + Converter.trytes(valueTrits) + this.getTransactions().get(i).getTag() + Converter.trytes(timestampTrits) + Converter.trytes(currentIndexTrits) + Converter.trytes(lastIndexTrits));
|
||||
curl.absorb(t, 0, t.length);
|
||||
}
|
||||
|
||||
int[] hash = new int[90];
|
||||
int[] hash = new int[243];
|
||||
curl.squeeze(hash, 0, hash.length);
|
||||
String hashInTrytes = Converter.trytes(hash);
|
||||
|
||||
@@ -98,14 +85,12 @@ public class Bundle {
|
||||
String emptySignatureFragment = "";
|
||||
String emptyHash = EMPTY_HASH;
|
||||
|
||||
for (int j = 0; emptySignatureFragment.length() < 2187; j++) {
|
||||
emptySignatureFragment += '9';
|
||||
}
|
||||
emptySignatureFragment = StringUtils.rightPad(emptySignatureFragment, 2187, '9');
|
||||
|
||||
for (int i = 0; i < this.getTransactions().size(); i++) {
|
||||
|
||||
// Fill empty signatureMessageFragment
|
||||
this.getTransactions().get(i).setSignatureFragments(signatureFragments.get(i) == null ? signatureFragments.get(i) : emptySignatureFragment);
|
||||
this.getTransactions().get(i).setSignatureFragments((signatureFragments.size() <= i || signatureFragments.get(i).isEmpty()) ? emptySignatureFragment : signatureFragments.get(i));
|
||||
// Fill empty trunkTransaction
|
||||
this.getTransactions().get(i).setTrunkTransaction(emptyHash);
|
||||
|
||||
@@ -118,14 +103,14 @@ public class Bundle {
|
||||
}
|
||||
|
||||
public int[] normalizedBundle(String bundleHash) {
|
||||
int[] normalizedBundle = new int[33 * 27 + 27];
|
||||
int[] normalizedBundle = new int[81];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
||||
long sum = 0;
|
||||
for (int j = 0; j < 27; j++) {
|
||||
|
||||
sum += (normalizedBundle[i * 27 + j] = Converter.value(Converter.trits("" + bundleHash.charAt(i * 27 + j))));
|
||||
sum += (normalizedBundle[i * 27 + j] = Converter.value(Converter.tritsString("" + bundleHash.charAt(i * 27 + j))));
|
||||
}
|
||||
|
||||
if (sum >= 0) {
|
||||
@@ -155,4 +140,8 @@ public class Bundle {
|
||||
return normalizedBundle;
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public int compareTo(Bundle o) {
|
||||
return Long.compare(Long.parseLong(this.getTransactions().get(0).getTimestamp()), Long.parseLong(o.getTransactions().get(0).getTimestamp()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package jota.model;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 09.12.2016.
|
||||
*/
|
||||
public class Inputs {
|
||||
private List<Input> inputsList;
|
||||
private long totalBalance;
|
||||
|
||||
public Inputs(List<Input> inputsList, long totalBalance) {
|
||||
this.inputsList = inputsList;
|
||||
this.totalBalance = totalBalance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new Gson().toJson(this);
|
||||
}
|
||||
|
||||
public List<Input> getInputsList() {
|
||||
return inputsList;
|
||||
}
|
||||
|
||||
public void setInputsList(List<Input> inputsList) {
|
||||
inputsList = inputsList;
|
||||
}
|
||||
|
||||
public long getTotalBalance() {
|
||||
return totalBalance;
|
||||
}
|
||||
|
||||
public void setTotalBalance(long totalBalance) {
|
||||
this.totalBalance = totalBalance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package jota.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 27.12.2016.
|
||||
*/
|
||||
public class Signature {
|
||||
|
||||
String address;
|
||||
List<String> signatureFragments;
|
||||
|
||||
public Signature() {
|
||||
this.signatureFragments = new ArrayList<>();
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public List<String> getSignatureFragments() {
|
||||
return signatureFragments;
|
||||
}
|
||||
|
||||
public void setSignatureFragments(List<String> signatureFragments) {
|
||||
this.signatureFragments = signatureFragments;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,23 @@
|
||||
package jota.model;
|
||||
|
||||
import jota.pow.ICurl;
|
||||
import jota.pow.JCurl;
|
||||
import jota.utils.Converter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 02.12.16.
|
||||
*/
|
||||
public class Transaction {
|
||||
private static final Logger log = LoggerFactory.getLogger(Transaction.class);
|
||||
private ICurl customCurl;
|
||||
|
||||
private String hash;
|
||||
private String signatureFragments;
|
||||
private String address;
|
||||
@@ -21,8 +32,21 @@ public class Transaction {
|
||||
private String nonce;
|
||||
private Boolean persistence;
|
||||
|
||||
public Transaction() {
|
||||
public Transaction(ICurl curl) {
|
||||
customCurl = curl;
|
||||
}
|
||||
|
||||
public Transaction() {
|
||||
customCurl = null;
|
||||
}
|
||||
|
||||
public Transaction(String trytes) {
|
||||
transactionObject(trytes);
|
||||
}
|
||||
|
||||
public Transaction(String trytes, ICurl customCurl) {
|
||||
transactionObject(trytes);
|
||||
this.customCurl = customCurl;
|
||||
}
|
||||
|
||||
public Transaction(String signatureFragments, String currentIndex, String lastIndex, String nonce, String hash, String tag, String timestamp, String trunkTransaction, String branchTransaction, String address, String value, String bundle) {
|
||||
@@ -157,4 +181,72 @@ public class Transaction {
|
||||
public void setPersistence(Boolean persistence) {
|
||||
this.persistence = persistence;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return obj != null && ((Transaction) obj).getHash().equals(this.getHash());
|
||||
}
|
||||
|
||||
public String toTrytes() {
|
||||
int[] valueTrits = Converter.trits(this.getValue(), 81);
|
||||
|
||||
int[] timestampTrits = Converter.trits(this.getTimestamp(), 27);
|
||||
|
||||
|
||||
int[] currentIndexTrits = Converter.trits(this.getCurrentIndex(), 27);
|
||||
|
||||
|
||||
int[] lastIndexTrits = Converter.trits(this.getLastIndex(), 27);
|
||||
|
||||
|
||||
return this.getSignatureFragments()
|
||||
+ this.getAddress()
|
||||
+ Converter.trytes(valueTrits)
|
||||
+ this.getTag()
|
||||
+ Converter.trytes(timestampTrits)
|
||||
+ Converter.trytes(currentIndexTrits)
|
||||
+ Converter.trytes(lastIndexTrits)
|
||||
+ this.getBundle()
|
||||
+ this.getTrunkTransaction()
|
||||
+ this.getBranchTransaction()
|
||||
+ this.getNonce();
|
||||
}
|
||||
|
||||
public void transactionObject(final String trytes) {
|
||||
|
||||
if (StringUtils.isEmpty(trytes)) {
|
||||
log.warn("Warning: empty trytes in input for transactionObject");
|
||||
return;
|
||||
}
|
||||
|
||||
// validity check
|
||||
for (int i = 2279; i < 2295; i++) {
|
||||
if (trytes.charAt(i) != '9') {
|
||||
log.warn("Trytes {} does not seem a valid tryte", trytes);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int[] transactionTrits = Converter.trits(trytes);
|
||||
int[] hash = new int[243];
|
||||
|
||||
final ICurl curl = customCurl == null ? new JCurl() : customCurl; // we need a fluent JCurl.
|
||||
|
||||
// generate the correct transaction hash
|
||||
curl.reset();
|
||||
curl.absorb(transactionTrits, 0, transactionTrits.length);
|
||||
curl.squeeze(hash, 0, hash.length);
|
||||
|
||||
this.setHash(Converter.trytes(hash));
|
||||
this.setSignatureFragments(trytes.substring(0, 2187));
|
||||
this.setAddress(trytes.substring(2187, 2268));
|
||||
this.setValue("" + Converter.longValue(Arrays.copyOfRange(transactionTrits, 6804, 6837)));
|
||||
this.setTag(trytes.substring(2295, 2322));
|
||||
this.setTimestamp("" + Converter.longValue(Arrays.copyOfRange(transactionTrits, 6966, 6993)));
|
||||
this.setCurrentIndex("" + Converter.longValue(Arrays.copyOfRange(transactionTrits, 6993, 7020)));
|
||||
this.setLastIndex("" + Converter.longValue(Arrays.copyOfRange(transactionTrits, 7020, 7047)));
|
||||
this.setBundle(trytes.substring(2349, 2430));
|
||||
this.setTrunkTransaction(trytes.substring(2430, 2511));
|
||||
this.setBranchTransaction(trytes.substring(2511, 2592));
|
||||
this.setNonce(trytes.substring(2592, 2673));
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public class Transfer {
|
||||
private String tag;
|
||||
|
||||
public Transfer(String timestamp, String address, String hash, Boolean persistence, long value, String message,
|
||||
String tag) {
|
||||
String tag) {
|
||||
this.timestamp = timestamp;
|
||||
this.address = address;
|
||||
this.hash = hash;
|
||||
@@ -43,54 +43,54 @@ public class Transfer {
|
||||
return address;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public Boolean getPersistence() {
|
||||
return persistence;
|
||||
}
|
||||
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setTimestamp(String timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void setHash(String hash) {
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
public Boolean getPersistence() {
|
||||
return persistence;
|
||||
}
|
||||
|
||||
public void setPersistence(Boolean persistence) {
|
||||
this.persistence = persistence;
|
||||
}
|
||||
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(String timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setTag(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package jota.pow;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 07.01.2017.
|
||||
*/
|
||||
public interface ICurl {
|
||||
JCurl absorb(final int[] trits, int offset, int length);
|
||||
|
||||
JCurl absorb(final int[] trits);
|
||||
|
||||
int[] squeeze(final int[] trits, int offset, int length);
|
||||
|
||||
int[] squeeze(final int[] trits);
|
||||
|
||||
JCurl transform();
|
||||
|
||||
JCurl reset();
|
||||
|
||||
int[] getState();
|
||||
|
||||
void setState(int[] state);
|
||||
}
|
||||
@@ -3,9 +3,9 @@ package jota.pow;
|
||||
/**
|
||||
* (c) 2016 Come-from-Beyond
|
||||
* <p>
|
||||
* Curl belongs to the sponge function family.
|
||||
* JCurl belongs to the sponge function family.
|
||||
*/
|
||||
public class Curl {
|
||||
public class JCurl implements ICurl {
|
||||
|
||||
public static final int HASH_LENGTH = 243;
|
||||
private static final int STATE_LENGTH = 3 * HASH_LENGTH;
|
||||
@@ -15,22 +15,23 @@ public class Curl {
|
||||
|
||||
private int[] state = new int[STATE_LENGTH];
|
||||
|
||||
public Curl absorb(final int[] trits, int offset, int length) {
|
||||
public JCurl absorb(final int[] trits, int offset, int length) {
|
||||
|
||||
do {
|
||||
System.arraycopy(trits, offset, state, 0, length < HASH_LENGTH ? length : HASH_LENGTH);
|
||||
transform();
|
||||
offset += HASH_LENGTH;
|
||||
} while ((length -= HASH_LENGTH) > 0);
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Curl absorb(final int[] trits) {
|
||||
|
||||
|
||||
public JCurl absorb(final int[] trits) {
|
||||
return absorb(trits, 0, trits.length);
|
||||
}
|
||||
|
||||
public Curl transform() {
|
||||
public JCurl transform() {
|
||||
|
||||
final int[] scratchpad = new int[STATE_LENGTH];
|
||||
int scratchpadIndex = 0;
|
||||
@@ -43,13 +44,13 @@ public class Curl {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Curl reset() {
|
||||
public JCurl reset() {
|
||||
for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) {
|
||||
state[stateIndex] = 0;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public int[] squeeze(final int[] trits, int offset, int length) {
|
||||
|
||||
do {
|
||||
@@ -60,7 +61,7 @@ public class Curl {
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
public int[] squeeze(final int[] trits) {
|
||||
return squeeze(trits, 0, trits.length);
|
||||
}
|
||||
@@ -68,7 +69,8 @@ public class Curl {
|
||||
public int[] getState() {
|
||||
return state;
|
||||
}
|
||||
public void setState(int[] state) {
|
||||
this.state = state;
|
||||
|
||||
public void setState(int[] state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
package jota.utils;
|
||||
|
||||
import jota.pow.JCurl;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import jota.pow.Curl;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 02.12.16.
|
||||
*/
|
||||
@@ -33,12 +32,16 @@ public class Checksum {
|
||||
return addressWithRecalculateChecksum.equals(addressWithChecksum);
|
||||
}
|
||||
|
||||
private static boolean isAddressWithChecksum(String addressWithChecksum) {
|
||||
return InputValidator.checkAddress(addressWithChecksum) && addressWithChecksum.length() == Constants.ADDRESS_LENGTH_WITH_CHECKSUM;
|
||||
public static boolean isAddressWithChecksum(String address) {
|
||||
return InputValidator.checkAddress(address) && address.length() == Constants.ADDRESS_LENGTH_WITH_CHECKSUM;
|
||||
}
|
||||
|
||||
public static boolean isAddressWithoutChecksum(String address) {
|
||||
return InputValidator.checkAddress(address) && address.length() == Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM;
|
||||
}
|
||||
|
||||
public static String calculateChecksum(String address) {
|
||||
Curl curl = new Curl();
|
||||
JCurl curl = new JCurl();
|
||||
curl.reset();
|
||||
curl.setState(Converter.copyTrits(address, curl.getState()));
|
||||
curl.transform();
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
package jota.utils;
|
||||
|
||||
import jota.model.Transaction;
|
||||
import jota.pow.Curl;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class Converter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Converter.class);
|
||||
|
||||
private static final int RADIX = 3;
|
||||
private static final int MAX_TRIT_VALUE = (RADIX - 1) / 2, MIN_TRIT_VALUE = -MAX_TRIT_VALUE;
|
||||
@@ -66,15 +60,46 @@ public class Converter {
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] trits(final String trytes) {
|
||||
final int[] trits = new int[trytes.length() * NUMBER_OF_TRITS_IN_A_TRYTE];
|
||||
public static int[] convertToIntArray(List<Integer> integers) {
|
||||
int[] ret = new int[integers.size()];
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = integers.get(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int[] trits(final String trytes, int length) {
|
||||
int[] trits = trits(trytes);
|
||||
|
||||
List<Integer> tritsList = new LinkedList<>();
|
||||
|
||||
for (int i : trits)
|
||||
tritsList.add(i);
|
||||
|
||||
while (tritsList.size() < length)
|
||||
tritsList.add(0);
|
||||
|
||||
return convertToIntArray(tritsList);
|
||||
}
|
||||
|
||||
public static int[] tritsString(final String trytes) {
|
||||
int[] d = new int[3 * trytes.length()];
|
||||
for (int i = 0; i < trytes.length(); i++) {
|
||||
System.arraycopy(TRYTE_TO_TRITS_MAPPINGS[Constants.TRYTE_ALPHABET.indexOf(trytes.charAt(i))], 0, d, i * NUMBER_OF_TRITS_IN_A_TRYTE, NUMBER_OF_TRITS_IN_A_TRYTE);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public static int[] trits(final String trytes) {
|
||||
final List<Integer> trits = new LinkedList<>();
|
||||
if (InputValidator.isValue(trytes)) {
|
||||
|
||||
int value = Integer.parseInt(trytes);
|
||||
long value = Long.parseLong(trytes);
|
||||
|
||||
long absoluteValue = value < 0 ? -value : value;
|
||||
|
||||
int position = 0;
|
||||
|
||||
while (absoluteValue > 0) {
|
||||
|
||||
int remainder = (int) (absoluteValue % RADIX);
|
||||
@@ -85,22 +110,21 @@ public class Converter {
|
||||
absoluteValue++;
|
||||
}
|
||||
|
||||
trits[trits.length] = remainder;
|
||||
trits.add(position++, remainder);
|
||||
}
|
||||
if (value < 0) {
|
||||
|
||||
for (int i = 0; i < trits.length; i++) {
|
||||
|
||||
trits[i] = -trits[i];
|
||||
for (int i = 0; i < trits.size(); i++) {
|
||||
trits.set(i, -trits.get(i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
int[] d = new int[3 * trytes.length()];
|
||||
for (int i = 0; i < trytes.length(); i++) {
|
||||
System.arraycopy(TRYTE_TO_TRITS_MAPPINGS[Constants.TRYTE_ALPHABET.indexOf(trytes.charAt(i))], 0, trits, i * NUMBER_OF_TRITS_IN_A_TRYTE, NUMBER_OF_TRITS_IN_A_TRYTE);
|
||||
System.arraycopy(TRYTE_TO_TRITS_MAPPINGS[Constants.TRYTE_ALPHABET.indexOf(trytes.charAt(i))], 0, d, i * NUMBER_OF_TRITS_IN_A_TRYTE, NUMBER_OF_TRITS_IN_A_TRYTE);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
return trits;
|
||||
return convertToIntArray(trits);
|
||||
}
|
||||
|
||||
public static void copyTrits(final long value, final int[] destination, final int offset, final int size) {
|
||||
@@ -168,6 +192,15 @@ public class Converter {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static long longValue(final int[] trits) {
|
||||
long value = 0;
|
||||
|
||||
for (int i = trits.length; i-- > 0; ) {
|
||||
value = value * 3 + trits[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void increment(final int[] trits, final int size) {
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
@@ -178,47 +211,5 @@ public class Converter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Transaction transactionObject(final String trytes) {
|
||||
|
||||
if (StringUtils.isEmpty(trytes)) {
|
||||
log.warn("Warning: empty trytes in input for transactionObject");
|
||||
return null;
|
||||
}
|
||||
|
||||
// validity check
|
||||
for (int i = 2279; i < 2295; i++) {
|
||||
if (trytes.charAt(i) != '9') {
|
||||
log.warn("Trytes {} does not seem a valid tryte", trytes);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int[] transactionTrits = Converter.trits(trytes);
|
||||
int[] hash = new int[90];
|
||||
|
||||
final Curl curl = new Curl(); // we need a fluent Curl.
|
||||
|
||||
// generate the correct transaction hash
|
||||
curl.reset();
|
||||
curl.absorb(transactionTrits, 0, transactionTrits.length);
|
||||
curl.squeeze(hash, 0, hash.length);
|
||||
|
||||
Transaction trx = new Transaction();
|
||||
|
||||
trx.setHash(Converter.trytes(hash));
|
||||
trx.setSignatureFragments(trytes.substring(0, 2187));
|
||||
trx.setAddress(trytes.substring(2187, 2268));
|
||||
trx.setValue("" + Converter.value(Arrays.copyOfRange(transactionTrits, 6804, 6837)));
|
||||
trx.setTag(trytes.substring(2295, 2322));
|
||||
trx.setTimestamp("" + Converter.value(Arrays.copyOfRange(transactionTrits, 6966, 6993)));
|
||||
trx.setCurrentIndex("" + Converter.value(Arrays.copyOfRange(transactionTrits, 6993, 7020)));
|
||||
trx.setLastIndex("" + Converter.value(Arrays.copyOfRange(transactionTrits, 7020, 7047)));
|
||||
trx.setBundle(trytes.substring(2349, 2430));
|
||||
trx.setTrunkTransaction(trytes.substring(2430, 2511));
|
||||
trx.setBranchTransaction(trytes.substring(2511, 2592));
|
||||
trx.setNonce(trytes.substring(2592, 2673));
|
||||
|
||||
return trx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
package jota.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import jota.model.Transaction;
|
||||
import jota.model.Transfer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 02.12.16.
|
||||
@@ -27,13 +26,14 @@ public class InputValidator {
|
||||
public static boolean isTrytes(final String trytes, final int length) {
|
||||
return trytes.matches("^[A-Z9]{" + (length == 0 ? "0," : length) + "}$");
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValue(final String value) {
|
||||
return StringUtils.isNumeric(value);
|
||||
return NumberUtils.isNumber(value);
|
||||
}
|
||||
|
||||
public static boolean isArrayOfHashes(String[] hashes) {
|
||||
if (hashes == null) return false;
|
||||
if (hashes == null)
|
||||
return false;
|
||||
|
||||
for (String hash : hashes) {
|
||||
// Check if address with checksum
|
||||
@@ -49,16 +49,16 @@ public class InputValidator {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* checks if input is correct hash collections
|
||||
*
|
||||
* @method isTransfersArray
|
||||
* @param {array} hash
|
||||
* @returns {boolean}
|
||||
**/
|
||||
* checks if input is correct hash collections
|
||||
*
|
||||
* @param {array} hash
|
||||
* @method isTransfersArray
|
||||
* @returns {boolean}
|
||||
**/
|
||||
public static boolean isTransfersCollectionCorrect(final List<Transfer> transfers) {
|
||||
|
||||
|
||||
for (final Transfer transfer : transfers) {
|
||||
if (!isTransfersArray(transfer)) {
|
||||
return false;
|
||||
@@ -66,9 +66,9 @@ public class InputValidator {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isTransfersArray(final Transfer transfer) {
|
||||
|
||||
|
||||
if (!isAddress(transfer.getAddress())) {
|
||||
return false;
|
||||
}
|
||||
@@ -79,10 +79,15 @@ public class InputValidator {
|
||||
}
|
||||
|
||||
// Check if tag is correct trytes of {0,27} trytes
|
||||
if (!isTrytes(transfer.getTag(), 27)) {
|
||||
return false;
|
||||
}
|
||||
return isTrytes(transfer.getTag(), 27);
|
||||
}
|
||||
|
||||
return true;
|
||||
public static String validateSeed(String seed) {
|
||||
if (seed.length() > 81)
|
||||
return null;
|
||||
|
||||
seed = StringUtils.rightPad(seed, 81, '9');
|
||||
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package jota.utils;
|
||||
|
||||
import jota.model.Bundle;
|
||||
import jota.model.Input;
|
||||
import jota.model.Transaction;
|
||||
import jota.pow.ICurl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jota.model.Bundle;
|
||||
import jota.model.Input;
|
||||
import jota.model.Transaction;
|
||||
|
||||
/**
|
||||
* Client Side computation service
|
||||
*
|
||||
@@ -29,11 +29,11 @@ public class IotaAPIUtils {
|
||||
* @param checksum
|
||||
* @return an String with address
|
||||
*/
|
||||
public static String newAddress(String seed, int index, boolean checksum) {
|
||||
|
||||
final int[] key = Signing.key(Converter.trits(seed), index, 2);
|
||||
final int[] digests = Signing.digests(key);
|
||||
final int[] addressTrits = Signing.address(digests);
|
||||
public static String newAddress(String seed, int index, boolean checksum, ICurl curl) {
|
||||
Signing signing = new Signing(curl);
|
||||
final int[] key = signing.key(Converter.trits(seed), index, 2);
|
||||
final int[] digests = signing.digests(key);
|
||||
final int[] addressTrits = signing.address(digests);
|
||||
|
||||
String address = Converter.trytes(addressTrits);
|
||||
|
||||
@@ -43,45 +43,11 @@ public class IotaAPIUtils {
|
||||
return address;
|
||||
}
|
||||
|
||||
public static String transactionTrytes(Transaction trx) {
|
||||
int[] valueTrits = Converter.trits(trx.getValue());
|
||||
while (valueTrits.length < 81) {
|
||||
valueTrits[valueTrits.length] = 0;
|
||||
}
|
||||
|
||||
int[] timestampTrits = Converter.trits(trx.getTimestamp());
|
||||
while (timestampTrits.length < 27) {
|
||||
timestampTrits[timestampTrits.length] = 0;
|
||||
}
|
||||
|
||||
int[] currentIndexTrits = Converter.trits(trx.getTimestamp());
|
||||
while (currentIndexTrits.length < 27) {
|
||||
currentIndexTrits[currentIndexTrits.length] = 0;
|
||||
}
|
||||
|
||||
int[] lastIndexTrits = Converter.trits(trx.getCurrentIndex());
|
||||
while (lastIndexTrits.length < 27) {
|
||||
lastIndexTrits[lastIndexTrits.length] = 0;
|
||||
}
|
||||
|
||||
return trx.getSignatureFragments()
|
||||
+ trx.getAddress()
|
||||
+ Converter.trytes(valueTrits)
|
||||
+ trx.getTag()
|
||||
+ Converter.trytes(timestampTrits)
|
||||
+ Converter.trytes(currentIndexTrits)
|
||||
+ Converter.trytes(lastIndexTrits)
|
||||
+ trx.getBundle()
|
||||
+ trx.getTrunkTransaction()
|
||||
+ trx.getBranchTransaction()
|
||||
+ trx.getNonce();
|
||||
}
|
||||
|
||||
public static List<String> signInputsAndReturn(final String seed,
|
||||
final List<Input> inputs,
|
||||
public static List<String> signInputsAndReturn(final String seed,
|
||||
final List<Input> inputs,
|
||||
final Bundle bundle,
|
||||
final List<String> signatureFragments) {
|
||||
bundle.finalize();
|
||||
final List<String> signatureFragments, ICurl curl) {
|
||||
bundle.finalize(curl);
|
||||
bundle.addTrytes(signatureFragments);
|
||||
|
||||
// SIGNING OF INPUTS
|
||||
@@ -95,9 +61,9 @@ public class IotaAPIUtils {
|
||||
|
||||
// Get the corresponding keyIndex of the address
|
||||
int keyIndex = 0;
|
||||
for (int k = 0; k < inputs.size(); k++) {
|
||||
if (inputs.get(k).getAddress().equals(thisAddress)) {
|
||||
keyIndex = inputs.get(k).getKeyIndex();
|
||||
for (Input input : inputs) {
|
||||
if (input.getAddress().equals(thisAddress)) {
|
||||
keyIndex = input.getKeyIndex();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -105,7 +71,7 @@ public class IotaAPIUtils {
|
||||
String bundleHash = bundle.getTransactions().get(i).getBundle();
|
||||
|
||||
// Get corresponding private key of address
|
||||
int[] key = Signing.key(Converter.trits(seed), keyIndex, 2);
|
||||
int[] key = new Signing(curl).key(Converter.trits(seed), keyIndex, 2);
|
||||
|
||||
// First 6561 trits for the firstFragment
|
||||
int[] firstFragment = Arrays.copyOfRange(key, 0, 6561);
|
||||
@@ -117,7 +83,7 @@ public class IotaAPIUtils {
|
||||
int[] firstBundleFragment = Arrays.copyOfRange(normalizedBundleHash, 0, 27);
|
||||
|
||||
// Calculate the new signatureFragment with the first bundle fragment
|
||||
int[] firstSignedFragment = Signing.signatureFragment(firstBundleFragment, firstFragment);
|
||||
int[] firstSignedFragment = new Signing(curl).signatureFragment(firstBundleFragment, firstFragment);
|
||||
|
||||
// Convert signature to trytes and assign the new signatureFragment
|
||||
bundle.getTransactions().get(i).setSignatureFragments(Converter.trytes(firstSignedFragment));
|
||||
@@ -126,7 +92,7 @@ public class IotaAPIUtils {
|
||||
// find the second transaction to add the remainder of the signature
|
||||
for (int j = 0; j < bundle.getTransactions().size(); j++) {
|
||||
// Same address as well as value = 0 (as we already spent the input)
|
||||
if (bundle.getTransactions().get(j).getAddress() == thisAddress && Long.parseLong(bundle.getTransactions().get(j).getValue()) == 0) {
|
||||
if (bundle.getTransactions().get(j).getAddress().equals(thisAddress) && Long.parseLong(bundle.getTransactions().get(j).getValue()) == 0) {
|
||||
// Use the second 6562 trits
|
||||
int[] secondFragment = Arrays.copyOfRange(key, 6561, 6561 * 2);
|
||||
|
||||
@@ -134,7 +100,7 @@ public class IotaAPIUtils {
|
||||
int[] secondBundleFragment = Arrays.copyOfRange(normalizedBundleHash, 27, 27 * 2);
|
||||
|
||||
// Calculate the new signature
|
||||
int[] secondSignedFragment = Signing.signatureFragment(secondBundleFragment, secondFragment);
|
||||
int[] secondSignedFragment = new Signing(curl).signatureFragment(secondBundleFragment, secondFragment);
|
||||
|
||||
// Convert signature to trytes and assign it again to this bundle entry
|
||||
bundle.getTransactions().get(j).setSignatureFragments(Converter.trytes(secondSignedFragment));
|
||||
@@ -147,7 +113,7 @@ public class IotaAPIUtils {
|
||||
|
||||
// Convert all bundle entries into trytes
|
||||
for (Transaction tx : bundle.getTransactions()) {
|
||||
bundleTrytes.add(IotaAPIUtils.transactionTrytes(tx));
|
||||
bundleTrytes.add(tx.toTrytes());
|
||||
}
|
||||
Collections.reverse(bundleTrytes);
|
||||
return bundleTrytes;
|
||||
|
||||
@@ -16,24 +16,28 @@ public class IotaUnitConverter {
|
||||
return (long) (amount / Math.pow(10, toUnit.getValue()));
|
||||
}
|
||||
|
||||
public static String convertRawIotaAmountToDisplayText(long amount) {
|
||||
public static String convertRawIotaAmountToDisplayText(long amount, boolean extended) {
|
||||
IotaUnits unit = findOptimalIotaUnitToDisplay(amount);
|
||||
double amountInDisplayUnit = convertAmountTo(amount, unit);
|
||||
return createAmountWithUnitDisplayText(amountInDisplayUnit, unit);
|
||||
return createAmountWithUnitDisplayText(amountInDisplayUnit, unit, extended);
|
||||
}
|
||||
|
||||
public static double convertAmountTo(long amount, IotaUnits target) {
|
||||
return amount / Math.pow(10, target.getValue());
|
||||
}
|
||||
|
||||
private static String createAmountWithUnitDisplayText(double amountInUnit, IotaUnits unit) {
|
||||
String result = createAmountDisplayText(amountInUnit, unit);
|
||||
private static String createAmountWithUnitDisplayText(double amountInUnit, IotaUnits unit, boolean extended) {
|
||||
String result = createAmountDisplayText(amountInUnit, unit, extended);
|
||||
result += " " + unit.getUnit();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String createAmountDisplayText(double amountInUnit, IotaUnits unit) {
|
||||
DecimalFormat df = new DecimalFormat("##0.##################");
|
||||
public static String createAmountDisplayText(double amountInUnit, IotaUnits unit, boolean extended) {
|
||||
DecimalFormat df;
|
||||
if (extended) df = new DecimalFormat("##0.##################");
|
||||
else
|
||||
df = new DecimalFormat("##0.##");
|
||||
|
||||
String result = "";
|
||||
// display unit as integer if value is between 1-999 or in decimal format
|
||||
result += unit == IotaUnits.IOTA ? (long) amountInUnit : df.format(amountInUnit);
|
||||
@@ -43,7 +47,7 @@ public class IotaUnitConverter {
|
||||
public static IotaUnits findOptimalIotaUnitToDisplay(long amount) {
|
||||
int length = String.valueOf(amount).length();
|
||||
|
||||
if (amount < 0) {// do not count "-" sign
|
||||
if (amount < 0) {// do not count "-" sign
|
||||
length -= 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package jota.utils;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 30.11.16.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Table of IOTA units based off of the standard system of Units
|
||||
**/
|
||||
public enum IotaUnits {
|
||||
|
||||
|
||||
IOTA("i", 0),
|
||||
KILO_IOTA("Ki", 3),
|
||||
MEGA_IOTA("Mi", 6),
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package jota.utils;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 15.01.2017.
|
||||
*/
|
||||
public class NamedThreadFactory implements ThreadFactory {
|
||||
private final String baseName;
|
||||
private final AtomicInteger threadNum = new AtomicInteger(0);
|
||||
|
||||
public NamedThreadFactory(String baseName) {
|
||||
this.baseName = baseName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Thread newThread(Runnable r) {
|
||||
Thread t = Executors.defaultThreadFactory().newThread(r);
|
||||
|
||||
t.setName(baseName + "-" + threadNum.getAndIncrement());
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package jota.utils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 15.01.2017.
|
||||
*/
|
||||
public class Parallel {
|
||||
private static final int NUM_CORES = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
private static final ExecutorService forPool = Executors.newFixedThreadPool(NUM_CORES * 2, new NamedThreadFactory("Parallel.For"));
|
||||
|
||||
public static <T> void For(final Iterable<T> elements, final Operation<T> operation) {
|
||||
try {
|
||||
// invokeAll blocks for us until all submitted tasks in the call complete
|
||||
forPool.invokeAll(createCallables(elements, operation));
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Collection<Callable<Void>> createCallables(final Iterable<T> elements, final Operation<T> operation) {
|
||||
List<Callable<Void>> callables = new LinkedList<>();
|
||||
for (final T elem : elements) {
|
||||
callables.add(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() {
|
||||
operation.perform(elem);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return callables;
|
||||
}
|
||||
|
||||
public interface Operation<T> {
|
||||
void perform(T pParameter);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,25 @@
|
||||
package jota.utils;
|
||||
|
||||
import jota.model.Bundle;
|
||||
import jota.pow.ICurl;
|
||||
import jota.pow.JCurl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import jota.model.Bundle;
|
||||
import jota.pow.Curl;
|
||||
|
||||
public class Signing {
|
||||
private ICurl curl;
|
||||
|
||||
static int[] key(int[] seed, int index, int length) {
|
||||
public Signing() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public Signing(ICurl curl) {
|
||||
this.curl = curl == null ? new JCurl() : curl;
|
||||
}
|
||||
|
||||
public int[] key(int[] seed, int index, int length) {
|
||||
|
||||
for (int i = 0; i < index; i++) {
|
||||
for (int j = 0; j < 243; j++) {
|
||||
@@ -21,7 +31,6 @@ public class Signing {
|
||||
}
|
||||
}
|
||||
|
||||
final Curl curl = new Curl();
|
||||
curl.reset();
|
||||
curl.absorb(seed, 0, seed.length);
|
||||
curl.squeeze(seed, 0, seed.length);
|
||||
@@ -44,7 +53,7 @@ public class Signing {
|
||||
return to(key);
|
||||
}
|
||||
|
||||
private static int[] to(List<Integer> key) {
|
||||
private int[] to(List<Integer> key) {
|
||||
int a[] = new int[key.size()];
|
||||
int i = 0;
|
||||
for (Integer v : key) {
|
||||
@@ -53,42 +62,37 @@ public class Signing {
|
||||
return a;
|
||||
}
|
||||
|
||||
public static int[] signatureFragment(int[] normalizedBundleFragment, int[] keyFragment) {
|
||||
public int[] signatureFragment(int[] normalizedBundleFragment, int[] keyFragment) {
|
||||
|
||||
int[] signatureFragment = keyFragment;
|
||||
int[] hash;
|
||||
|
||||
Curl curl = new Curl();
|
||||
|
||||
for (int i = 0; i < 27; i++) {
|
||||
|
||||
hash = Arrays.copyOfRange(signatureFragment, i * 243, (i + 1) * 243);
|
||||
hash = Arrays.copyOfRange(keyFragment, i * 243, (i + 1) * 243);
|
||||
|
||||
for (int j = 0; j < 13 - normalizedBundleFragment[i]; j++) {
|
||||
curl.reset()
|
||||
.absorb(hash, 0, hash.length)
|
||||
.squeeze(hash, 0, hash.length);
|
||||
.absorb(hash, 0, hash.length)
|
||||
.squeeze(hash, 0, hash.length);
|
||||
}
|
||||
|
||||
for (int j = 0; j < 243; j++) {
|
||||
signatureFragment[i * 243 + j] = hash[j];
|
||||
System.arraycopy(hash, j, keyFragment, i * 243 + j, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return signatureFragment;
|
||||
return keyFragment;
|
||||
}
|
||||
|
||||
public static int[] address(int[] digests) {
|
||||
final Curl curl = new Curl();
|
||||
public int[] address(int[] digests) {
|
||||
int[] address = new int[243];
|
||||
curl.reset()
|
||||
.absorb(digests)
|
||||
.squeeze(address);
|
||||
.absorb(digests)
|
||||
.squeeze(address);
|
||||
return address;
|
||||
}
|
||||
|
||||
public static int[] digests(int[] key) {
|
||||
final Curl curl = new Curl();
|
||||
|
||||
public int[] digests(int[] key) {
|
||||
|
||||
int[] digests = new int[(int) Math.floor(key.length / 6561) * 243];
|
||||
int[] buffer = new int[243];
|
||||
@@ -101,8 +105,8 @@ public class Signing {
|
||||
buffer = Arrays.copyOfRange(keyFragment, j * 243, (j + 1) * 243);
|
||||
for (int k = 0; k < 26; k++) {
|
||||
curl.reset()
|
||||
.absorb(buffer)
|
||||
.squeeze(buffer);
|
||||
.absorb(buffer)
|
||||
.squeeze(buffer);
|
||||
}
|
||||
System.arraycopy(buffer, 0, keyFragment, j * 243, 243);
|
||||
}
|
||||
@@ -116,20 +120,19 @@ public class Signing {
|
||||
return digests;
|
||||
}
|
||||
|
||||
public static int[] digest(int[] normalizedBundleFragment, int[] signatureFragment) {
|
||||
|
||||
public int[] digest(int[] normalizedBundleFragment, int[] signatureFragment) {
|
||||
curl.reset();
|
||||
int[] buffer = new int[243];
|
||||
|
||||
Curl curl = new Curl().reset();
|
||||
|
||||
for (int i = 0; i < 27; i++) {
|
||||
buffer = Arrays.copyOfRange(signatureFragment, i * 243, (i + 1) * 243);
|
||||
|
||||
for (int j = normalizedBundleFragment[i] + 13; j-- > 0; ) {
|
||||
|
||||
new Curl().reset()
|
||||
.absorb(buffer)
|
||||
.squeeze(buffer);
|
||||
ICurl jCurl = new JCurl();
|
||||
jCurl.reset();
|
||||
jCurl.absorb(buffer);
|
||||
jCurl.squeeze(buffer);
|
||||
}
|
||||
curl.absorb(buffer);
|
||||
}
|
||||
@@ -138,7 +141,7 @@ public class Signing {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static Boolean validateSignatures(String expectedAddress, String[] signatureFragments, String bundleHash) {
|
||||
public Boolean validateSignatures(String expectedAddress, String[] signatureFragments, String bundleHash) {
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
@@ -151,18 +154,16 @@ public class Signing {
|
||||
}
|
||||
|
||||
// Get digests
|
||||
int[] digests = new int[signatureFragments.length * 243 + 243];
|
||||
int[] digests = new int[signatureFragments.length * 243];
|
||||
|
||||
for (int i = 0; i < signatureFragments.length; i++) {
|
||||
|
||||
int[] digestBuffer = digest(normalizedBundleFragments[i % 3], Converter.trits(signatureFragments[i]));
|
||||
|
||||
for (int j = 0; j < 243; j++) {
|
||||
|
||||
digests[i * 243 + j] = digestBuffer[j];
|
||||
System.arraycopy(digestBuffer, j, digests, i * 243 + j, 1);
|
||||
}
|
||||
}
|
||||
|
||||
String address = Converter.trytes(address(digests));
|
||||
|
||||
return (expectedAddress.equals(address));
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package jota.utils;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 15.01.2017.
|
||||
*/
|
||||
public class StopWatch {
|
||||
private long startTime = 0;
|
||||
private boolean running = false;
|
||||
private long currentTime = 0;
|
||||
|
||||
public StopWatch() {
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
public void reStart() {
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
public StopWatch stop() {
|
||||
this.running = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
this.running = false;
|
||||
currentTime = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
this.running = true;
|
||||
this.startTime = System.currentTimeMillis() - currentTime;
|
||||
}
|
||||
|
||||
//elaspsed time in milliseconds
|
||||
public long getElapsedTimeMili() {
|
||||
long elapsed = 0;
|
||||
if (running) {
|
||||
elapsed = (System.currentTimeMillis() - startTime);
|
||||
}
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
//elaspsed time in seconds
|
||||
public long getElapsedTimeSecs() {
|
||||
long elapsed = 0;
|
||||
if (running) {
|
||||
elapsed = (System.currentTimeMillis() - startTime) / 1000;
|
||||
}
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
//elaspsed time in minutes
|
||||
public long getElapsedTimeMin() {
|
||||
long elapsed = 0;
|
||||
if (running) {
|
||||
elapsed = (System.currentTimeMillis() - startTime) / 1000 / 60;
|
||||
}
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
//elaspsed time in hours
|
||||
public long getElapsedTimeHour() {
|
||||
long elapsed = 0;
|
||||
if (running) {
|
||||
elapsed = ((System.currentTimeMillis() - startTime) / 1000 / 3600);
|
||||
}
|
||||
return elapsed;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package jota.utils;
|
||||
* Created by pinpong on 01.12.16.
|
||||
*/
|
||||
public class TrytesConverter {
|
||||
|
||||
|
||||
/**
|
||||
* Conversion of ascii encoded bytes to trytes.
|
||||
* Input is a string (can be stringified JSON object), return value is Trytes
|
||||
@@ -19,9 +19,9 @@ public class TrytesConverter {
|
||||
* b. The second value is the remainder (decimal value - first value), divided by 27
|
||||
* 3. The two values returned from Step 2. are then input as indices into the available values list ('9ABCDEFGHIJKLMNOPQRSTUVWXYZ') to get the correct tryte value
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* EXAMPLE
|
||||
*
|
||||
* <p>
|
||||
* Lets say we want to convert the ASCII character "Z".
|
||||
* 1. 'Z' has a decimal value of 90.
|
||||
* 2. 90 can be represented as 9 + 3 * 27. To make it simpler:
|
||||
@@ -32,10 +32,9 @@ public class TrytesConverter {
|
||||
* b. The second tryte value is '9ABCDEFGHIJKLMNOPQRSTUVWXYZ'[3] === "C"
|
||||
* Our tryte pair is "IC"
|
||||
* <p>
|
||||
*
|
||||
* @param inputString
|
||||
* @return
|
||||
* The ASCII char "Z" is represented as "IC" in trytes.
|
||||
*
|
||||
* @param inputString
|
||||
* @return The ASCII char "Z" is represented as "IC" in trytes.
|
||||
*/
|
||||
public static String toTrytes(String inputString) {
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ public class InputValidatorTest {
|
||||
private static final String TEST_HASH = "OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999";
|
||||
private static final String TEST_MESSAGE = "JOTA";
|
||||
private static final String TEST_TAG = "JOTASPAM9999999999999999999";
|
||||
|
||||
@Test
|
||||
public void shouldIsAddress() {
|
||||
assertEquals(InputValidator.isAddress(TEST_ADDRESS_WITHOUT_CHECKSUM), true);
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
package jota;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import jota.dto.response.*;
|
||||
import jota.model.Transfer;
|
||||
import org.hamcrest.core.Is;
|
||||
import org.hamcrest.core.IsNull;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Let's do some integration test coverage against a default local real node.
|
||||
*
|
||||
* @author davassi
|
||||
*/
|
||||
public class IotaAPIProxyTest {
|
||||
|
||||
private static Gson gson = new GsonBuilder().create();
|
||||
|
||||
private static final String TEST_SEED = "AAA999999999999999999999999999999999999999999999999999999999999999999999999999999";
|
||||
private static final String TEST_ADDRESS_WITHOUT_CHECKSUM = "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTH";
|
||||
private static final String TEST_ADDRESS_WITH_CHECKSUM = "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA";
|
||||
private static final String TEST_HASH = "OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999";
|
||||
private static final String TEST_TRYTES = "BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999";
|
||||
private static final String TEST_MILESTONE = "SMYMAKKPSUKCKDRUEYCGZJTYCZ9HHDMDUWBAPXARGURPQRHTAJDASRWMIDTPTBNDKDEFBUTBGGAFX9999";
|
||||
private static final Integer TEST_MILESTONE_INDEX = 8059;
|
||||
private static final String TEST_MESSAGE = "JOTA";
|
||||
private static final String TEST_TAG = "JOTASPAM9999999999999999999";
|
||||
|
||||
|
||||
private IotaAPIProxy proxy;
|
||||
|
||||
@Before
|
||||
public void createProxyInstance() {
|
||||
proxy = new IotaAPIProxy.Builder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetNodeInfo() {
|
||||
GetNodeInfoResponse nodeInfo = proxy.getNodeInfo();
|
||||
assertThat(nodeInfo.getAppVersion(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getAppName(), IsNull.notNullValue());
|
||||
//assertThat(nodeInfo.getJreVersion(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getJreAvailableProcessors(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getJreFreeMemory(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getJreMaxMemory(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getJreTotalMemory(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getLatestMilestone(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getLatestMilestoneIndex(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getLatestSolidSubtangleMilestone(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getLatestSolidSubtangleMilestoneIndex(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getNeighbors(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getPacketsQueueSize(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getTime(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getTips(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getTransactionsToRequest(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetNeighbors() {
|
||||
GetNeighborsResponse neighbors = proxy.getNeighbors();
|
||||
assertThat(neighbors.getNeighbors(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddNeighbors() {
|
||||
AddNeighborsResponse res = proxy.addNeighbors("udp://8.8.8.8:14265");
|
||||
assertThat(res, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRemoveNeighbors() {
|
||||
RemoveNeighborsResponse res = proxy.removeNeighbors("udp://8.8.8.8:14265");
|
||||
assertThat(res, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetTips() {
|
||||
GetTipsResponse tips = proxy.getTips();
|
||||
assertThat(tips, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByAddresses() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByAddresses(TEST_ADDRESS_WITH_CHECKSUM);
|
||||
System.err.println(gson.toJson(trans));
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByApprovees() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByApprovees(new String[]{TEST_HASH});
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByBundles() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByBundles(TEST_HASH);
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByDigests() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByDigests(TEST_HASH);
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
|
||||
// ###
|
||||
|
||||
@Test
|
||||
public void shouldGetTrytes() {
|
||||
GetTrytesResponse res = proxy.getTrytes(TEST_HASH);
|
||||
assertThat(res.getTrytes(), IsNull.notNullValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetInclusionStates() {
|
||||
GetInclusionStateResponse res = proxy.getInclusionStates(new String[]{TEST_ADDRESS_WITH_CHECKSUM}, new String[]{"DNSBRJWNOVUCQPILOQIFDKBFJMVOTGHLIMLLRXOHFTJZGRHJUEDAOWXQRYGDI9KHYFGYDWQJZKX999999"});
|
||||
assertThat(res.getStates(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test // very long execution
|
||||
public void shouldGetTransactionsToApprove() {
|
||||
GetTransactionsToApproveResponse res = proxy.getTransactionsToApprove(27);
|
||||
assertThat(res.getTrunkTransaction(), IsNull.notNullValue());
|
||||
assertThat(res.getBranchTransaction(), IsNull.notNullValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetBalances() {
|
||||
GetBalancesResponse res = proxy.getBalances(100, new String[]{TEST_ADDRESS_WITH_CHECKSUM});
|
||||
System.err.println(res);
|
||||
assertThat(res.getBalances(), IsNull.notNullValue());
|
||||
assertThat(res.getMilestone(), IsNull.notNullValue());
|
||||
assertThat(res.getMilestoneIndex(), IsNull.notNullValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIotaApiProxyInstanceWithDefaultValues() {
|
||||
IotaAPIProxy proxy = new IotaAPIProxy.Builder().build();
|
||||
assertThat(proxy, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateANewAddress() {
|
||||
final GetNewAddressResponse res = proxy.getNewAddress(TEST_SEED, 0, false, 1, false);
|
||||
assertThat(res.getAddresses(), Is.is(Collections.singletonList(TEST_ADDRESS_WITHOUT_CHECKSUM)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrepareTransfer() {
|
||||
List<Transfer> transfers = new ArrayList<>();
|
||||
transfers.add(new jota.model.Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0, TEST_MESSAGE, TEST_TAG));
|
||||
proxy.prepareTransfers(TEST_SEED, transfers, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSendTrytes() {
|
||||
proxy.sendTrytes(TEST_TRYTES, 18);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetLastInclusionState() {
|
||||
GetInclusionStateResponse res = proxy.getLatestInclusion(new String[]{TEST_HASH});
|
||||
assertThat(res.getStates(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionObjects() {
|
||||
assertThat(proxy.findTransactionObjects(new String[]{TEST_ADDRESS_WITH_CHECKSUM}), IsNull.notNullValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package jota;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import jota.dto.response.*;
|
||||
import jota.error.*;
|
||||
import jota.model.Bundle;
|
||||
import jota.model.Transaction;
|
||||
import jota.model.Transfer;
|
||||
import org.hamcrest.core.Is;
|
||||
import org.hamcrest.core.IsNull;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Let's do some integration test coverage against a default local real node.
|
||||
*
|
||||
* @author davassi
|
||||
*/
|
||||
public class IotaAPITest {
|
||||
|
||||
private static final String TEST_SEED1 = "AAA999999999999999999999999999999999999999999999999999999999999999999999999999999";
|
||||
private static final String TEST_SEED2 = "IHDEENZYITYVYSPKAURUZAQKGVJEREFDJMYTANNXXGPZ9GJWTEOJJ9IPMXOGZNQLSNMFDSQOTZAEETUEA";
|
||||
private static final String TEST_ADDRESS_WITHOUT_CHECKSUM = "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTH";
|
||||
private static final String TEST_ADDRESS_WITH_CHECKSUM = "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA";
|
||||
private static final String TEST_HASH = "CKZ9TYPLUWH9FUSYJMPIZBVHWFZXTZMVOJLC9KOICSTBBQWXYTOTMCVPSPMYNDONTXHRULRFAWD999999";
|
||||
private static final String TEST_TRYTES = "BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999";
|
||||
private static final String TEST_MILESTONE = "SMYMAKKPSUKCKDRUEYCGZJTYCZ9HHDMDUWBAPXARGURPQRHTAJDASRWMIDTPTBNDKDEFBUTBGGAFX9999";
|
||||
private static final Integer TEST_MILESTONE_INDEX = 8059;
|
||||
private static final String TEST_MESSAGE = "JOTA";
|
||||
private static final String TEST_TAG = "JOTASPAM9999999999999999999";
|
||||
private static final String[] TEST_ADDRESSES = new String[]{"KHJXD9XKXPIVQRGREUIPVJTMEY9L9MXZAAKBBRYNINTIOXWBRMNLLW9MLGAXMGQWDBZLCOGFCBNKTDLDC"
|
||||
, "MQSAAEDPMKIAJPPGNLIHPQIVFGLHNEGG9JNMSHOQSVQHBQBMLNHY9WVRTCOYUOWOIJHBQXIQVFPDF9YRW"
|
||||
, "RGYOHMECRNVPYYPIAKEWHSOLBYOQPRFRPOJGHUMEGLICCUIPTZEXLWDLLPBNRXONUTQGLSAJSLHRXFVQD"
|
||||
, "FOJHXRVJRFMJTFDUWJYYZXCZIJXKQALLXMLKHZFDMHWTIBBXUKSNSUYJLKYRQBNXKRSUXZHDTPWXYD9YF"
|
||||
, "B9YNPQO9EXID9RDEEGLCBJBYKBLWHTOQOZKTLJDFPJZOPKJJTNUYUVVTDJPBCBYIWGPSCMNRZFGFHFSXH"
|
||||
, "NQEFOAFIYKZOUXDFQ9X9PHCNSDETRTJZINZ9EYGKU99QJLDSTSC9VTBAA9FHLNLNYQXWLTNPRJDWCGIPP"
|
||||
, "CEGLBSXDJVXGKGOUHRGMAQDRVYXCQLXBKUDWKFFSIABCUYRATFPTEEDIFYGAASKFZYREHLBIXBTKP9KLC"
|
||||
, "QLOXU9GIQXPPE9UUT9DSIDSIESRIXMTGZJMKLSJTNBCRELAVLWVJLUOLKGFCWAEPEQWZWPBV9YZJJEHUS"
|
||||
, "XIRMYJSGQXMM9YPHJVVLAVGBBLEEMOOKHHBFWKEAXJFONZLNSLBCGPQEVDMMOGHFVRDSYTETIFOIVNCR9"
|
||||
, "PDVVBYBXMHZKADPAYOKQNDPHRSWTHAWQ9GRVIBOIMZQTYCWEPCDWDVRSOUNASVBDLBOAMVLYEVVCMAM9N"
|
||||
, "U9GAIAPUUQWJGISAZWPLHUELTZ9WSHWXS9JLPKOWHRRIVUKGWCTJMBULVMKTETTUNHZ9HWHBALUCJIROU"
|
||||
, "VFPMKZLLMDUOEKNBEKQZPTNZJZF9UHRWSTHXLWQQ9OAXTZQHTZPAWNJNXKAZFSDFWKFQEKZIGJTLWQFLO"
|
||||
, "IGHK9XIWOAYBZUEZHQLEXBPTXSWVANIOUZZCPNKUIJIJOJNAQCJWUJHYKCZOIKVAAHDGAWJZKLTPVQL9G"
|
||||
, "LXQPWMNXSUZTEYNC9ZBBFHY9YWCCOVKBNIIOUSVXZJZMJKJFDUWGUVXYCHGKUHEEIDHSGEWFAHVJPRIJT"
|
||||
, "AKFDX9PGGQLZUWRMZ9YBDF9CG9TWXCNALCSXSAWHFIMGXCSYCJLSWIQDGGVDRMNEKKECQEYAITGNLNJFQ"
|
||||
, "YX9QSPYMSFVOW9UVZRDVOCPYYMUTDHCCPKHMXQSJQJYIXVCHILKW9GBYJTYGLIKBTRQMDCYBMLLNGSSIK"
|
||||
, "DSYCJKNG9TAGJHSKZQ9XLKAKNSKJFZIPVEDGJFXRTFGENHZFQGXHWDBNXLLDABDMOYELPG9DIXSNJFWAR"
|
||||
, "9ANNACZYLDDPZILLQBQG9YMG9XJUMTAENDFQ9HMSSEFWYOAXPJTUXBFTSAXDJPAO9FKTWBBSCSFMOUR9I"
|
||||
, "WDTFFXHBHMFQQVXQLBFJFVVHVIIAVYM9PFAZCHMKET9ESMHIRHSMVDJBZTXPTAFVIASMSXRDCIYVWVQNO"
|
||||
, "XCCPS9GMTSUB9DXPVKLTBDHOFX9PJMBYZQYQEXMRQDPGQPLWRGZGXODYJKGVFOHHYUJRCSXAIDGYSAWRB"
|
||||
, "KVEBCGMEOPDPRCQBPIEMZTTXYBURGZVNH9PLHKPMM9D9FUKWIGLKZROGNSYIFHULLWQWXCNAW9HKKVIDC"};
|
||||
private static Gson gson = new GsonBuilder().create();
|
||||
private IotaAPI iotaClient;
|
||||
|
||||
@Before
|
||||
public void createApiClientInstance() {
|
||||
iotaClient = new IotaAPI.Builder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIotaApiProxyInstanceWithDefaultValues() {
|
||||
IotaAPI proxy = new IotaAPI.Builder().build();
|
||||
assertThat(proxy, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldGetInputs() {
|
||||
GetBalancesAndFormatResponse res = iotaClient.getInputs(TEST_SEED1, 0, 0, 0);
|
||||
System.out.println(res);
|
||||
assertThat(res, IsNull.notNullValue());
|
||||
assertThat(res.getTotalBalance(), IsNull.notNullValue());
|
||||
assertThat(res.getInput(), IsNull.notNullValue());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldCreateANewAddress() {
|
||||
final GetNewAddressResponse res = iotaClient.getNewAddress(TEST_SEED1, 0, false, 100, false);
|
||||
assertThat(res.getAddresses().get(0), Is.is(TEST_ADDRESS_WITHOUT_CHECKSUM));
|
||||
System.out.println(new Gson().toJson(res));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrepareTransfer() {
|
||||
List<Transfer> transfers = new ArrayList<>();
|
||||
transfers.add(new jota.model.Transfer(TEST_ADDRESS_WITH_CHECKSUM, 0, TEST_MESSAGE, TEST_TAG));
|
||||
transfers.add(new jota.model.Transfer(TEST_ADDRESS_WITH_CHECKSUM, 1, TEST_MESSAGE, TEST_TAG));
|
||||
List<String> trytes = null;
|
||||
try {
|
||||
trytes = iotaClient.prepareTransfers(TEST_SEED1, transfers, null, null);
|
||||
} catch (NotEnoughBalanceException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Assert.assertNotNull(trytes);
|
||||
assertThat(trytes.isEmpty(), Is.is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetLastInclusionState() throws NoNodeInfoException {
|
||||
GetInclusionStateResponse res = iotaClient.getLatestInclusion(new String[]{TEST_HASH});
|
||||
assertThat(res.getStates(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionObjects() {
|
||||
List<Transaction> ftr = iotaClient.findTransactionObjects(TEST_ADDRESSES);
|
||||
assertThat(ftr, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetBundle() throws InvalidBundleException, ArgumentException, InvalidSignatureException {
|
||||
GetBundleResponse gbr = iotaClient.getBundle(TEST_HASH);
|
||||
assertThat(gbr, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetTransfers() throws InvalidBundleException, ArgumentException, InvalidSignatureException, NoInclusionStatesExcpection, NoNodeInfoException {
|
||||
GetTransferResponse gtr = iotaClient.getTransfers(TEST_SEED1, 0, 0, false);
|
||||
assertThat(gtr.getTransfers(), IsNull.notNullValue());
|
||||
|
||||
for (Bundle test : gtr.getTransfers()) {
|
||||
for (Transaction trx : test.getTransactions()) {
|
||||
System.out.println(new Gson().toJson(trx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void shouldSendTrytes() {
|
||||
iotaClient.sendTrytes(new String[]{TEST_TRYTES}, 9, 18);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void shouldNotSendTransfer() throws ArgumentException, InvalidSignatureException, InvalidBundleException, NotEnoughBalanceException {
|
||||
List<Transfer> transfers = new ArrayList<>();
|
||||
transfers.add(new jota.model.Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM, 10000990, "JUSTANOTHERTEST", TEST_TAG));
|
||||
SendTransferResponse str = iotaClient.sendTransfer(TEST_SEED2, 9, 18, transfers, null, null);
|
||||
assertThat(str.getSuccessfully(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void shouldSendTransfer() throws ArgumentException, InvalidSignatureException, InvalidBundleException, NotEnoughBalanceException {
|
||||
List<Transfer> transfers = new ArrayList<>();
|
||||
transfers.add(new jota.model.Transfer(TEST_ADDRESS_WITHOUT_CHECKSUM, 0, "JUSTANOTHERTEST", TEST_TAG));
|
||||
SendTransferResponse str = iotaClient.sendTransfer(TEST_SEED2, 9, 18, transfers, null, null);
|
||||
assertThat(str.getSuccessfully(), IsNull.notNullValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package jota;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import jota.dto.response.*;
|
||||
import org.hamcrest.core.IsNull;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 15.01.2017.
|
||||
*/
|
||||
public class IotaCoreApiTest {
|
||||
|
||||
private static final String TEST_BUNDLE = "XZKJUUMQOYUQFKMWQZNTFMSS9FKJLOEV9DXXXWPMQRTNCOUSUQNTBIJTVORLOQPLYZOTMLFRHYKMTGZZU";
|
||||
private static final String TEST_ADDRESS_WITH_CHECKSUM = "PNGMCSNRCTRHCHPXYTPKEJYPCOWKOMRXZFHH9N9VDIKMNVAZCMIYRHVJIAZARZTUETJVFDMBEBIQE9QTHBFWDAOEFA";
|
||||
private static final String TEST_HASH = "OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999";
|
||||
private static IotaAPICore proxy;
|
||||
|
||||
@Before
|
||||
public void createProxyInstance() {
|
||||
proxy = new IotaAPICore.Builder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetNodeInfo() {
|
||||
GetNodeInfoResponse nodeInfo = proxy.getNodeInfo();
|
||||
System.out.println(new Gson().toJson(nodeInfo));
|
||||
assertThat(nodeInfo.getAppVersion(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getAppName(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getJreVersion(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getJreAvailableProcessors(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getJreFreeMemory(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getJreMaxMemory(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getJreTotalMemory(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getLatestMilestone(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getLatestMilestoneIndex(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getLatestSolidSubtangleMilestone(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getLatestSolidSubtangleMilestoneIndex(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getNeighbors(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getPacketsQueueSize(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getTime(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getTips(), IsNull.notNullValue());
|
||||
assertThat(nodeInfo.getTransactionsToRequest(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetNeighbors() {
|
||||
GetNeighborsResponse neighbors = proxy.getNeighbors();
|
||||
assertThat(neighbors.getNeighbors(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddNeighbors() {
|
||||
AddNeighborsResponse res = proxy.addNeighbors("udp://8.8.8.8:14265");
|
||||
assertThat(res, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRemoveNeighbors() {
|
||||
RemoveNeighborsResponse res = proxy.removeNeighbors("udp://8.8.8.8:14265");
|
||||
assertThat(res, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetTips() {
|
||||
GetTipsResponse tips = proxy.getTips();
|
||||
assertThat(tips, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByAddresses() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByAddresses(TEST_ADDRESS_WITH_CHECKSUM);
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByApprovees() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByApprovees(new String[]{TEST_HASH});
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByBundles() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByBundles(TEST_HASH);
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByDigests() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByDigests(TEST_HASH);
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetTrytes() {
|
||||
GetTrytesResponse res = proxy.getTrytes(TEST_HASH);
|
||||
assertThat(res.getTrytes(), IsNull.notNullValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetInclusionStates() {
|
||||
GetInclusionStateResponse res = proxy.getInclusionStates(new String[]{"DBPECSH9YLSSTQDGERUHJBBJTKVUDBMTJLG9WPHBINGHIFOSJMDJLARTVOXXWEFQJLLBINOHCZGYFSMUEXWPPMTOFW"}, new String[]{"EJDQOQHMLJGBMFWB9WJSPRCYIGNPO9WRHDCEQXIMPVPIJ9JV9RJGVHNX9EPGXFOOKBABCVMMAAX999999"});
|
||||
assertThat(res.getStates(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalAccessError.class)
|
||||
public void shouldNotGetInclusionStates() {
|
||||
GetInclusionStateResponse res = proxy.getInclusionStates(new String[]{TEST_ADDRESS_WITH_CHECKSUM}, new String[]{"DNSBRJWNOVUCQPILOQIFDKBFJMVOTGHLIMLLRXOHFTJZGRHJUEDAOWXQRYGDI9KHYFGYDWQJZKX999999"});
|
||||
assertThat(res.getStates(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test // very long execution
|
||||
public void shouldGetTransactionsToApprove() {
|
||||
GetTransactionsToApproveResponse res = proxy.getTransactionsToApprove(27);
|
||||
assertThat(res.getTrunkTransaction(), IsNull.notNullValue());
|
||||
assertThat(res.getBranchTransaction(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactions() {
|
||||
String test = TEST_BUNDLE;
|
||||
FindTransactionResponse resp = proxy.findTransactions(new String[]{test}, new String[]{test}, new String[]{test}, new String[]{test});
|
||||
System.out.println(new Gson().toJson(resp));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetBalances() {
|
||||
GetBalancesResponse res = proxy.getBalances(100, new String[]{TEST_ADDRESS_WITH_CHECKSUM});
|
||||
System.err.println(res);
|
||||
assertThat(res.getBalances(), IsNull.notNullValue());
|
||||
assertThat(res.getMilestone(), IsNull.notNullValue());
|
||||
assertThat(res.getMilestoneIndex(), IsNull.notNullValue());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -48,11 +48,11 @@ public class IotaUnitConverterTest {
|
||||
|
||||
@Test
|
||||
public void shouldConvertRawIotaAmountToDisplayText() {
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1), "1 i");
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000), "1 Ki");
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000), "1 Mi" );
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000), "1 Gi" );
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000L), "1 Ti");
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000000L), "1 Pi");
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1, false), "1 i");
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000, false), "1 Ki");
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000, false), "1 Mi");
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000, false), "1 Gi");
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000L, false), "1 Ti");
|
||||
assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000000L, false), "1 Pi");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ package jota;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SendMessageTest {
|
||||
|
||||
@Test
|
||||
public void sendMessage() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendMessage() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package jota;
|
||||
|
||||
import jota.utils.TrytesConverter;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 01.12.16.
|
||||
*/
|
||||
@@ -15,19 +15,21 @@ public class TrytesConverterTest {
|
||||
@Test
|
||||
public void shouldConvertStringToTrytes() {
|
||||
assertEquals(TrytesConverter.toTrytes("Z"), "IC");
|
||||
assertEquals(TrytesConverter.toTrytes("JOTA JOTA"), "TBYBCCKBEATBYBCCKB");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldConvertTrytesToString() {
|
||||
assertEquals(TrytesConverter.toString("IC"), "Z");
|
||||
assertEquals(TrytesConverter.toString("TBYBCCKBEATBYBCCKB"), "JOTA JOTA");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldConvertBackAndForth() {
|
||||
String str = RandomStringUtils.randomAlphabetic(1000).toUpperCase();
|
||||
System.err.println(str);
|
||||
String back = TrytesConverter.toString(TrytesConverter.toTrytes(str));
|
||||
|
||||
|
||||
assertTrue(str.equals(back));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user