mirror of
https://github.com/gosticks/iota.lib.java.git
synced 2026-09-16 13:10:21 +00:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # src/main/java/jota/IotaAPIProxy.java # src/main/java/jota/error/ArgumentException.java # src/main/java/jota/error/BaseException.java # src/main/java/jota/error/NotEnoughBalanceException.java # src/main/java/jota/model/Bundle.java # src/main/java/jota/model/Transaction.java # src/main/java/jota/model/Transfer.java # src/main/java/jota/utils/Converter.java # src/main/java/jota/utils/InputValidator.java # src/main/java/jota/utils/IotaAPIUtils.java # src/main/java/jota/utils/Signing.java # src/test/java/jota/IotaAPIProxyTest.java
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
package jota;
|
||||
|
||||
import com.sun.org.apache.xpath.internal.Arg;
|
||||
import jota.dto.request.*;
|
||||
import jota.dto.response.*;
|
||||
import jota.error.ArgumentException;
|
||||
import jota.error.NotEnoughBalanceException;
|
||||
import jota.model.*;
|
||||
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.NotImplementedException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import retrofit2.Call;
|
||||
@@ -26,13 +26,13 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* IotaAPIProxy Builder. Usage:
|
||||
* <p>
|
||||
*
|
||||
* IotaApiProxy api = IotaApiProxy.Builder
|
||||
* .protocol("http")
|
||||
* .nodeAddress("localhost")
|
||||
* .port(12345)
|
||||
* .build();
|
||||
* <p>
|
||||
*
|
||||
* GetNodeInfoResponse response = api.getNodeInfo();
|
||||
*
|
||||
* @author davassi
|
||||
@@ -174,6 +174,10 @@ public class IotaAPIProxy {
|
||||
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());
|
||||
@@ -197,10 +201,6 @@ public class IotaAPIProxy {
|
||||
|
||||
// end of proxied calls.
|
||||
|
||||
public GetBundleResponse getBundle(String transaction) {
|
||||
return IotaAPIUtils.getBundle(transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -215,7 +215,7 @@ public class IotaAPIProxy {
|
||||
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) {
|
||||
@@ -224,13 +224,13 @@ public class IotaAPIProxy {
|
||||
}
|
||||
return GetNewAddressResponse.create(allAddresses);
|
||||
}
|
||||
// No total provided: Continue calling findTransactions to see if address was
|
||||
// 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 String newAddress = IotaAPIUtils.newAddress(seed, i, checksum);
|
||||
final FindTransactionResponse response = findTransactionsByAddresses(new String[]{newAddress});
|
||||
|
||||
|
||||
allAddresses.add(newAddress);
|
||||
if (response.getHashes().length == 0) {
|
||||
break;
|
||||
@@ -239,279 +239,180 @@ public class IotaAPIProxy {
|
||||
|
||||
// If !returnAll return only the last address that was generated
|
||||
if (!returnAll) {
|
||||
allAddresses.subList(0, allAddresses.size() - 1).clear();
|
||||
allAddresses.subList(0, allAddresses.size()-1).clear();
|
||||
}
|
||||
return GetNewAddressResponse.create(allAddresses);
|
||||
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;
|
||||
}
|
||||
|
||||
public Transaction[] sendTrytes(String[] trytes, int depth, int minWeightMagnitude) {
|
||||
GetTransactionsToApproveResponse transactionsToApproveResponse = getTransactionsToApprove(depth);
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
GetAttachToTangleResponse attachToTangleResponse =
|
||||
attachToTangle(transactionsToApproveResponse.getTrunkTransaction(),
|
||||
transactionsToApproveResponse.getBranchTransactionToApprove(), minWeightMagnitude, trytes);
|
||||
if (!InputValidator.isArrayOfHashes(hashes)) {
|
||||
throw new IllegalStateException("Not an Array of Hashes: " + Arrays.toString(hashes));
|
||||
}
|
||||
|
||||
broadcastTransactions(attachToTangleResponse.getTrytes());
|
||||
|
||||
return analyzeTransactions(attachToTangleResponse.getTrytes());
|
||||
final GetTrytesResponse trytesResponse = getTrytes(hashes);
|
||||
|
||||
final List<Transaction> trxs = new ArrayList<>();
|
||||
|
||||
for (final String tryte : trytesResponse.getTrytes()) {
|
||||
trxs.add(Converter.transactionObject(tryte));
|
||||
}
|
||||
return trxs;
|
||||
}
|
||||
|
||||
private Transaction[] analyzeTransactions(String[] trytes) {
|
||||
throw new NotImplementedException("MISSING");
|
||||
}
|
||||
|
||||
|
||||
public Inputs getInputs(String seed, Integer start, Integer end, int threshold) throws ArgumentException, NotEnoughBalanceException {
|
||||
if (start == null || start < 0)
|
||||
start = 0;
|
||||
|
||||
if (end == null || end < 0)
|
||||
end = 0;
|
||||
|
||||
// If start value bigger than end, return error
|
||||
if (start > end)
|
||||
throw new ArgumentException();
|
||||
|
||||
// or if difference between end and start is bigger than 500 keys
|
||||
if (end - start > 500)
|
||||
throw new ArgumentException();
|
||||
|
||||
// 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) {
|
||||
String[] addresses = new String[end - start];
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
String address = IotaAPIUtils.newAddress(seed, i, false);
|
||||
addresses[i] = address;
|
||||
}
|
||||
|
||||
return getBalancesAndFormat(addresses, start, end, threshold);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
List<String> addressList = getNewAddress(seed, start, true, 0, true).getAddresses();
|
||||
String[] addresses = addressList.toArray(new String[addressList.size()]);
|
||||
return getBalancesAndFormat(addresses, start, end, threshold);
|
||||
}
|
||||
}
|
||||
|
||||
private Inputs getBalancesAndFormat(String[] addresses) throws NotEnoughBalanceException {
|
||||
return getBalancesAndFormat(addresses, null, null, null);
|
||||
}
|
||||
|
||||
private Inputs getBalancesAndFormat(String[] addresses, Integer start, Integer end, Integer threshold) throws NotEnoughBalanceException {
|
||||
GetBalancesResponse getBalancesResponse = getBalances(threshold, addresses);
|
||||
|
||||
String[] balances = getBalancesResponse.getBalances();
|
||||
|
||||
Inputs inputs = new Inputs(new ArrayList<Input>(), 0);
|
||||
|
||||
boolean threshholdReached = false;
|
||||
|
||||
for (int i = 0; i < addresses.length; i++) {
|
||||
if (Long.parseLong(balances[i]) > 0) {
|
||||
inputs.getInputsList().add(new Input(addresses[i], Long.parseLong(balances[i]), start + i));
|
||||
inputs.setTotalBalance(inputs.getTotalBalance() + inputs.getInputsList().get(i).getBalance());
|
||||
|
||||
if (inputs.getTotalBalance() >= threshold) {
|
||||
threshholdReached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (threshholdReached)
|
||||
return inputs;
|
||||
else {
|
||||
throw new NotEnoughBalanceException();
|
||||
}
|
||||
}
|
||||
|
||||
public Bundle[] getTransfers(String seed, Integer start, Integer end, Boolean inclusionStates) throws ArgumentException {
|
||||
start = start != null ? 0 : start;
|
||||
end = end == null ? null : end;
|
||||
inclusionStates = inclusionStates != null ? inclusionStates : null;
|
||||
|
||||
if (start > end || end > (start + 500)) {
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
GetNewAddressResponse gnr = getNewAddress(seed, start, false, end == null ? end - start : end, true);
|
||||
if (gnr != null && gnr.getAddresses() != null) {
|
||||
return bundlesFromAddresses(gnr.getAddresses().toArray(new String[gnr.getAddresses().size()]), inclusionStates);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Bundle[] bundlesFromAddresses(String[] addresses, Boolean inclusionStates) throws ArgumentException {
|
||||
|
||||
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 {
|
||||
nonTailBundleHashes.add(trx.getBundle());
|
||||
}
|
||||
}
|
||||
if (nonTailBundleHashes.isEmpty()) return null;
|
||||
|
||||
Transaction[] bundleObjects = findTransactionObjects(addresses);
|
||||
for (Transaction trx : bundleObjects) {
|
||||
// Sort tail and nonTails
|
||||
if (Long.parseLong(trx.getCurrentIndex()) == 0) {
|
||||
tailTransactions.add(trx.getHash());
|
||||
}
|
||||
}
|
||||
|
||||
List<GetBundleResponse> finalBundles = new ArrayList<>();
|
||||
String[] tailTxArray = tailTransactions.toArray(new String[tailTransactions.size()]);
|
||||
|
||||
// If inclusionStates, get the confirmation status
|
||||
// of the tail transactions, and thus the bundles
|
||||
if (inclusionStates) {
|
||||
GetInclusionStateResponse gisr = getLatestInclusion(tailTxArray);
|
||||
if (gisr == null || gisr.getStates() == null || gisr.getStates().length == 0) return null;
|
||||
for (String trx : tailTxArray) {
|
||||
GetBundleResponse gbr = getBundle(trx);
|
||||
if (gbr != null && gbr.getTransactions() != null) {
|
||||
if (inclusionStates) {
|
||||
boolean thisInclusion = gisr.getStates()[Arrays.asList(tailTxArray).indexOf(trx)];
|
||||
for (Transaction t : gbr.getTransactions()) {
|
||||
t.setPersistence(thisInclusion);
|
||||
}
|
||||
}
|
||||
finalBundles.add(gbr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(finalBundles, new Comparator<GetBundleResponse>() {
|
||||
public int compare(GetBundleResponse c1, GetBundleResponse c2) {
|
||||
if (Long.parseLong(c1.getTransactions().get(0).getTimestamp()) > Long.parseLong(c2.getTransactions().get(0).getTimestamp()))
|
||||
return -1;
|
||||
if (Long.parseLong(c1.getTransactions().get(0).getTimestamp()) < Long.parseLong(c2.getTransactions().get(0).getTimestamp()))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
public GetInclusionStateResponse getLatestInclusion(String[] hashes) {
|
||||
GetNodeInfoResponse getNodeInfoResponse = getNodeInfo();
|
||||
if (getNodeInfoResponse == null) return null;
|
||||
|
||||
String[] latestMilestone = {getNodeInfoResponse.getLatestSolidSubtangleMilestone()};
|
||||
|
||||
return getInclusionStates(hashes, latestMilestone);
|
||||
}
|
||||
|
||||
public Transaction[] findTransactionObjects(String[] input) throws ArgumentException {
|
||||
/**
|
||||
* 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;
|
||||
if (ftr == null || ftr.getHashes() == null)
|
||||
|
||||
return null;
|
||||
|
||||
// get the transaction objects of the transactions
|
||||
return getTransactionsObjects(ftr.getHashes());
|
||||
}
|
||||
|
||||
public Transaction[] getTransactionsObjects(String[] hashes) throws ArgumentException {
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
// If not array of hashes, return error
|
||||
if (!InputValidator.isArrayOfHashes(hashes)) {
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
// get the trytes of the transaction hashes
|
||||
GetTrytesResponse gtr = getTrytes(hashes);
|
||||
if (gtr == null || gtr.getTrytes() == null) return null;
|
||||
List<Transaction> transactionObjects = new ArrayList<>();
|
||||
|
||||
// call transactionObjects for each trytes
|
||||
for (String transactionInTrytes : gtr.getTrytes()) {
|
||||
|
||||
// If no trytes returned, simply push null as placeholder
|
||||
if (transactionInTrytes == null) {
|
||||
transactionObjects.add(null);
|
||||
} else {
|
||||
transactionObjects.add(Converter.transactionObject(transactionInTrytes));
|
||||
}
|
||||
}
|
||||
return transactionObjects.toArray(new Transaction[transactionObjects.size()]);
|
||||
}
|
||||
|
||||
public Transaction[] sendTransfer(String seed, int depth, int minWeightMagnitude, Transfer[] transfers, Input[]
|
||||
inputs, String address) throws NotEnoughBalanceException, ArgumentException {
|
||||
String[] trytes = prepareTransfers(seed, transfers, inputs, address);
|
||||
return sendTrytes(trytes, depth, minWeightMagnitude);
|
||||
}
|
||||
|
||||
public String[] prepareTransfers(String seed, Transfer[] transfers, Input[] inputs, String remainderAddress) throws
|
||||
NotEnoughBalanceException, ArgumentException {
|
||||
//InputValidator.checkTransferArray(transfers);
|
||||
// If message or tag is not supplied, provide it
|
||||
|
||||
for (Transfer transfer : transfers) {
|
||||
|
||||
if (transfer.getAddress() == null)
|
||||
transfer.getMessage().isEmpty();
|
||||
if (transfer.getTag() == null)
|
||||
transfer.getMessage().isEmpty();
|
||||
// Input validation of transfers object
|
||||
if (!InputValidator.isTransfersCollectionCorrect(transfers)) {
|
||||
throw new IllegalStateException("Invalid Transfer");
|
||||
}
|
||||
|
||||
// Create a new bundle
|
||||
Bundle bundle = new Bundle();
|
||||
long totalValue = 0;
|
||||
List signatureFragments = new ArrayList();
|
||||
String tag = "";
|
||||
//
|
||||
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 (Transfer transfer : transfers) {
|
||||
int signatureMessageLength = 1;
|
||||
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 += (int) Math.floor(((double) transfer.getMessage().length() / 2187));
|
||||
signatureMessageLength += Math.floor(transfer.getMessage().length() / 2187);
|
||||
|
||||
String msgCopy = transfer.getMessage();
|
||||
|
||||
// While there is still a message, copy it
|
||||
while (msgCopy != null) {
|
||||
String fragment = msgCopy.substring(0, 2187);
|
||||
msgCopy = msgCopy.substring(2187, msgCopy.length());
|
||||
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';
|
||||
fragment += "9";
|
||||
}
|
||||
|
||||
signatureFragments.add(fragment);
|
||||
}
|
||||
} else {
|
||||
// Else, get single fragment with 2187 of 9's trytes
|
||||
String fragment = "";
|
||||
|
||||
if (transfer.getMessage() != null) {
|
||||
fragment = transfer.getMessage().substring(0, 2187);
|
||||
}
|
||||
String fragment = StringUtils.substring(transfer.getMessage(), 0, 2187);
|
||||
|
||||
for (int j = 0; fragment.length() < 2187; j++) {
|
||||
fragment += '9';
|
||||
@@ -521,49 +422,48 @@ public class IotaAPIProxy {
|
||||
}
|
||||
|
||||
// get current timestamp in seconds
|
||||
// var timestamp = Math.floor(Date.now() / 1000);
|
||||
long millis = System.currentTimeMillis() / 1000;
|
||||
long timestamp = (long) Math.floor(Calendar.getInstance().getTimeInMillis() / 1000);
|
||||
|
||||
// If no tag defined, get 27 tryte tag.
|
||||
tag = transfer.getTag() != null ? transfer.getTag() : "999999999999999999999999999";
|
||||
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 entries to the bundle
|
||||
// Slice the address in case the user provided a checksummed one
|
||||
bundle.addEntry(signatureMessageLength, transfer.getAddress().substring(0, 81), transfer.getValue(), tag, millis);
|
||||
// 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) {
|
||||
// Get list if addresses of the provided inputs
|
||||
|
||||
List<String> inputAddresses = new ArrayList();
|
||||
for (Input input : inputs) {
|
||||
inputAddresses.add(input.getAddress());
|
||||
// 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 balances = getBalances(100, inputAddresses.toArray(new String[inputAddresses.size()]));
|
||||
GetBalancesResponse resbalances = getBalances(100, inputsAddresses);
|
||||
String[] balances = resbalances.getBalances();
|
||||
|
||||
|
||||
List<Input> confirmedInputs = new ArrayList<Input>();
|
||||
|
||||
long totalBalance = 0;
|
||||
for (int i = 0; i < balances.getBalances().length; i++) {
|
||||
long thisBalance = Long.parseLong(balances.getBalances()[i]);
|
||||
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[i];
|
||||
Input inputEl = inputs.get(i++);
|
||||
inputEl.setBalance(thisBalance);
|
||||
confirmedInputs.add(inputEl);
|
||||
}
|
||||
@@ -571,11 +471,10 @@ public class IotaAPIProxy {
|
||||
|
||||
// Return not enough balance error
|
||||
if (totalValue > totalBalance) {
|
||||
//throw new NotEnoughBalanceException(totalBalance, totalValue);
|
||||
throw new NotEnoughBalanceException();
|
||||
throw new IllegalStateException("Not enough balance");
|
||||
}
|
||||
|
||||
addRemainder(seed, confirmedInputs, totalValue, bundle, tag, remainderAddress, signatureFragments);
|
||||
return IotaAPIUtils.signInputsAndReturn(seed, confirmedInputs, bundle, signatureFragments);
|
||||
}
|
||||
|
||||
// Case 2: Get inputs deterministically
|
||||
@@ -583,75 +482,145 @@ public class IotaAPIProxy {
|
||||
// If no inputs provided, derive the addresses from the seed and
|
||||
// confirm that the inputs exceed the threshold
|
||||
else {
|
||||
Inputs input = getInputs(seed, null, null, (int) totalValue);
|
||||
if (input != null && input.getInputsList() != null) {
|
||||
addRemainder(seed, input.getInputsList(), totalValue, bundle, tag, remainderAddress, signatureFragments);
|
||||
} else {
|
||||
throw new NotEnoughBalanceException();
|
||||
}
|
||||
|
||||
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 trx : bundle.getTransactions()) {
|
||||
bundleTrytes.add(IotaAPIUtils.transactionTrytes(trx));
|
||||
for (Transaction tx : trxb) {
|
||||
jota.utils.IotaAPIUtils.transactionTrytes(tx);
|
||||
}
|
||||
return bundleTrytes.toArray(new String[bundleTrytes.size()]);
|
||||
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);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calls getBalances and formats the output
|
||||
// returns the final inputsObject then
|
||||
public GetBalancesAndFormatResponse getBalanceAndFormat(final List<String> addresses,
|
||||
final List<String> balances, long threshold, int start, int end) {
|
||||
|
||||
private void addRemainder(String seed, List<Input> inputs, long totalValue, Bundle bundle, String tag,
|
||||
String remainderAddress, List<String> signatureFragments) {
|
||||
for (Input input : inputs) {
|
||||
long thisBalance = input.getBalance();
|
||||
long totalTransferValue = totalValue;
|
||||
long toSubtract = 0 - thisBalance;
|
||||
long timestamp = (new Date()).getTime();
|
||||
GetBalancesResponse bres = getBalances(100, addresses);
|
||||
|
||||
// Add input as bundle entry
|
||||
bundle.addEntry(2, input.getAddress(), toSubtract, tag, timestamp);
|
||||
// If there is a remainder value
|
||||
// Add extra output to send remaining funds to
|
||||
// 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 (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
|
||||
IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments);
|
||||
} else if (remainder > 0) {
|
||||
// Generate a new Address by calling getNewAddress
|
||||
String address = getNewAddress(seed, 0, false, 0, false).getAddresses().get(0);
|
||||
// Remainder bundle entry
|
||||
bundle.addEntry(1, address, remainder, tag, timestamp);
|
||||
|
||||
// Final function for signing inputs
|
||||
IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments);
|
||||
} else {
|
||||
// If there is no remainder, do not add transaction to bundle
|
||||
// simply sign and return
|
||||
IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments);
|
||||
if (!thresholdReached && totalBalance >= threshold) {
|
||||
thresholdReached = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If multiple inputs provided, subtract the totalTransferValue by
|
||||
// the inputs balance
|
||||
} else {
|
||||
totalTransferValue -= thisBalance;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
@@ -723,4 +692,4 @@ public class IotaAPIProxy {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package jota.dto.response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jota.model.Input;
|
||||
|
||||
public class GetBalancesAndFormatResponse extends AbstractResponse {
|
||||
|
||||
private List<Input> input;
|
||||
private long totalBalance;
|
||||
|
||||
public List<Input> getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ public class GetNodeInfoResponse extends AbstractResponse {
|
||||
|
||||
private String appName;
|
||||
private String appVersion;
|
||||
private String jreVersion;
|
||||
private int jreAvailableProcessors;
|
||||
private long jreFreeMemory;
|
||||
private long jreMaxMemory;
|
||||
@@ -26,6 +27,10 @@ public class GetNodeInfoResponse extends AbstractResponse {
|
||||
return appVersion;
|
||||
}
|
||||
|
||||
public String getJreVersion() {
|
||||
return jreVersion;
|
||||
}
|
||||
|
||||
public Integer getJreAvailableProcessors() {
|
||||
return jreAvailableProcessors;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ package jota.dto.response;
|
||||
public class GetTransactionsToApproveResponse extends AbstractResponse {
|
||||
|
||||
private String trunkTransaction;
|
||||
private String branchTransactionToApprove;
|
||||
|
||||
public String getBranchTransactionToApprove() {
|
||||
return branchTransactionToApprove;
|
||||
}
|
||||
private String branchTransaction;
|
||||
|
||||
public String getTrunkTransaction() {
|
||||
return trunkTransaction;
|
||||
}
|
||||
}
|
||||
|
||||
public String getBranchTransaction() {
|
||||
return branchTransaction;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
package jota.error;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 09.12.2016.
|
||||
*/
|
||||
public class ArgumentException extends BaseException {
|
||||
|
||||
private static final long serialVersionUID = -7850044681919575720L;
|
||||
|
||||
public ArgumentException() {
|
||||
super("wrong arguments passed to function");
|
||||
super("Wrong arguments passed to function");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
package jota.error;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Created by Adrian on 09.12.2016.
|
||||
*/
|
||||
public class BaseException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 5617085097507773343L;
|
||||
|
||||
protected Collection<String> messages;
|
||||
|
||||
public BaseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
|
||||
public BaseException(String msg, Exception cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
|
||||
public BaseException(Collection<String> messages) {
|
||||
super();
|
||||
this.messages = messages;
|
||||
}
|
||||
|
||||
|
||||
public BaseException(Collection<String> messages, Exception cause) {
|
||||
super(cause);
|
||||
this.messages = messages;
|
||||
@@ -33,19 +31,6 @@ public class BaseException extends Exception {
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String msg;
|
||||
|
||||
if (this.messages != null && !this.messages.isEmpty()) {
|
||||
msg = "[";
|
||||
|
||||
for (String message : this.messages) {
|
||||
msg += message + ",";
|
||||
}
|
||||
|
||||
msg = StringUtils.removeEnd(msg, ",") + "]";
|
||||
|
||||
} else msg = super.getMessage();
|
||||
|
||||
return msg;
|
||||
return Arrays.toString(messages.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ package jota.error;
|
||||
* Created by Adrian on 09.12.2016.
|
||||
*/
|
||||
public class NotEnoughBalanceException extends BaseException {
|
||||
|
||||
private static final long serialVersionUID = -3807270816402226476L;
|
||||
|
||||
public NotEnoughBalanceException() {
|
||||
super("not enough balance dude");
|
||||
super("Not enough balance");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package jota.model;
|
||||
|
||||
import jota.pow.Curl;
|
||||
import jota.utils.Constants;
|
||||
import jota.utils.Converter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -43,18 +42,14 @@ public class Bundle {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public void addEntry(int signatureMessageLength, String slice, long value, String tag, long timestamp) {
|
||||
public void addEntry(int signatureMessageLength, String address, long value, String tag, long timestamp) {
|
||||
for (int i = 0; i < signatureMessageLength; i++) {
|
||||
//TODO
|
||||
|
||||
/* var transactionObject = new Object();
|
||||
transactionObject.address = address;
|
||||
transactionObject.value = i == 0 ? value : 0;
|
||||
transactionObject.tag = tag;
|
||||
transactionObject.timestamp = timestamp;
|
||||
List<Transaction> transactions = new ArrayList<>(getTransactions());
|
||||
transactions.add(new Transaction(address, String.valueOf(i == 0 ? value : 0), tag, String.valueOf(timestamp)));
|
||||
|
||||
setTransactions(transactions);
|
||||
|
||||
this.bundle[this.bundle.length] = transactionObject;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +79,9 @@ public class Bundle {
|
||||
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];
|
||||
@@ -149,7 +144,6 @@ public class Bundle {
|
||||
for (int j = 0; j < 27; j++) {
|
||||
|
||||
if (normalizedBundle[i * 27 + j] < 13) {
|
||||
|
||||
normalizedBundle[i * 27 + j]++;
|
||||
break;
|
||||
}
|
||||
@@ -162,4 +156,3 @@ public class Bundle {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,14 @@ public class Transaction {
|
||||
this.nonce = nonce;
|
||||
}
|
||||
|
||||
|
||||
public Transaction(String address, String value, String tag, String timestamp) {
|
||||
this.address = address;
|
||||
this.value = value;
|
||||
this.tag = tag;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
@@ -149,4 +157,4 @@ public class Transaction {
|
||||
public void setPersistence(Boolean persistence) {
|
||||
this.persistence = persistence;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package jota.model;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 02.12.16.
|
||||
@@ -17,8 +15,8 @@ public class Transfer {
|
||||
private String message;
|
||||
private String tag;
|
||||
|
||||
public Transfer(String timestamp, String address, String hash, Boolean persistence, long value, String message, String tag) {
|
||||
|
||||
public Transfer(String timestamp, String address, String hash, Boolean persistence, long value, String message,
|
||||
String tag) {
|
||||
this.timestamp = timestamp;
|
||||
this.address = address;
|
||||
this.hash = hash;
|
||||
@@ -30,12 +28,10 @@ public class Transfer {
|
||||
}
|
||||
|
||||
public Transfer(String address, long value, String message, String tag) {
|
||||
|
||||
this.address = address;
|
||||
this.value = value;
|
||||
this.message = message;
|
||||
this.tag = tag;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,4 +94,4 @@ public class Transfer {
|
||||
public void setTag(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,15 +15,41 @@ public class Curl {
|
||||
|
||||
private int[] state = new int[STATE_LENGTH];
|
||||
|
||||
public void absorb(final int[] trits, int offset, int length) {
|
||||
public Curl 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) {
|
||||
return absorb(trits, 0, trits.length);
|
||||
}
|
||||
|
||||
public Curl transform() {
|
||||
|
||||
final int[] scratchpad = new int[STATE_LENGTH];
|
||||
int scratchpadIndex = 0;
|
||||
for (int round = 0; round < NUMBER_OF_ROUNDS; round++) {
|
||||
System.arraycopy(state, 0, scratchpad, 0, STATE_LENGTH);
|
||||
for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) {
|
||||
state[stateIndex] = TRUTH_TABLE[scratchpad[scratchpadIndex] + scratchpad[scratchpadIndex += (scratchpadIndex < 365 ? 364 : -365)] * 3 + 4];
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Curl 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 {
|
||||
@@ -34,27 +60,15 @@ public class Curl {
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public void transform() {
|
||||
|
||||
final int[] scratchpad = new int[STATE_LENGTH];
|
||||
int scratchpadIndex = 0;
|
||||
for (int round = 0; round < NUMBER_OF_ROUNDS; round++) {
|
||||
System.arraycopy(state, 0, scratchpad, 0, STATE_LENGTH);
|
||||
for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) {
|
||||
state[stateIndex] = TRUTH_TABLE[scratchpad[scratchpadIndex] + scratchpad[scratchpadIndex += (scratchpadIndex < 365 ? 364 : -365)] * 3 + 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) {
|
||||
state[stateIndex] = 0;
|
||||
}
|
||||
|
||||
public int[] squeeze(final int[] trits) {
|
||||
return squeeze(trits, 0, trits.length);
|
||||
}
|
||||
|
||||
public int[] getState() {
|
||||
return state;
|
||||
}
|
||||
public void setState(int[] state) { this.state = state; }
|
||||
public void setState(int[] state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public class Checksum {
|
||||
}
|
||||
|
||||
private static String getAddress(String addressWithChecksum) {
|
||||
return addressWithChecksum.substring(0, Constants.addressLengthWithoutChecksum);
|
||||
return addressWithChecksum.substring(0, Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM);
|
||||
}
|
||||
|
||||
public static boolean isValidChecksum(String addressWithChecksum) {
|
||||
@@ -34,7 +34,7 @@ public class Checksum {
|
||||
}
|
||||
|
||||
private static boolean isAddressWithChecksum(String addressWithChecksum) {
|
||||
return InputValidator.checkAddress(addressWithChecksum) && addressWithChecksum.length() == Constants.addressLengthWithChecksum;
|
||||
return InputValidator.checkAddress(addressWithChecksum) && addressWithChecksum.length() == Constants.ADDRESS_LENGTH_WITH_CHECKSUM;
|
||||
}
|
||||
|
||||
public static String calculateChecksum(String address) {
|
||||
|
||||
@@ -7,7 +7,8 @@ public class Constants {
|
||||
|
||||
public static final String TRYTE_ALPHABET = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
public static int addressLengthWithoutChecksum = 81;
|
||||
public static int addressLengthWithChecksum = 90;
|
||||
public static final int SEED_LENGTH_MAX = 81;
|
||||
|
||||
public static int ADDRESS_LENGTH_WITHOUT_CHECKSUM = 81;
|
||||
public static int ADDRESS_LENGTH_WITH_CHECKSUM = 90;
|
||||
}
|
||||
|
||||
@@ -4,16 +4,23 @@ import jota.model.Transaction;
|
||||
import jota.pow.Curl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Converter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Converter.class);
|
||||
|
||||
public static final int RADIX = 3;
|
||||
public static final int MAX_TRIT_VALUE = (RADIX - 1) / 2, MIN_TRIT_VALUE = -MAX_TRIT_VALUE;
|
||||
private static final int RADIX = 3;
|
||||
private static final int MAX_TRIT_VALUE = (RADIX - 1) / 2, MIN_TRIT_VALUE = -MAX_TRIT_VALUE;
|
||||
|
||||
public static final int NUMBER_OF_TRITS_IN_A_BYTE = 5;
|
||||
public static final int NUMBER_OF_TRITS_IN_A_TRYTE = 3;
|
||||
static final int[][] BYTE_TO_TRITS_MAPPINGS = new int[243][];
|
||||
static final int[][] TRYTE_TO_TRITS_MAPPINGS = new int[27][];
|
||||
private static final int NUMBER_OF_TRITS_IN_A_BYTE = 5;
|
||||
private static final int NUMBER_OF_TRITS_IN_A_TRYTE = 3;
|
||||
private static final int[][] BYTE_TO_TRITS_MAPPINGS = new int[243][];
|
||||
private static final int[][] TRYTE_TO_TRITS_MAPPINGS = new int[27][];
|
||||
|
||||
static {
|
||||
|
||||
@@ -146,20 +153,26 @@ public class Converter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Transaction transactionObject(String trytes) {
|
||||
if (trytes == null) return null;
|
||||
|
||||
|
||||
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];
|
||||
|
||||
Curl curl = new Curl();
|
||||
final Curl curl = new Curl(); // we need a fluent Curl.
|
||||
|
||||
// generate the correct transaction hash
|
||||
curl.reset();
|
||||
@@ -183,4 +196,4 @@ public class Converter {
|
||||
|
||||
return trx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
package jota.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import jota.model.Transaction;
|
||||
import jota.model.Transfer;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 02.12.16.
|
||||
*/
|
||||
public class InputValidator {
|
||||
|
||||
public static boolean isAddress(String address) {
|
||||
return (address.length() == Constants.addressLengthWithoutChecksum ||
|
||||
address.length() == Constants.addressLengthWithChecksum) && isTrytes(address, address.length());
|
||||
return (address.length() == Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM ||
|
||||
address.length() == Constants.ADDRESS_LENGTH_WITH_CHECKSUM) && isTrytes(address, address.length());
|
||||
}
|
||||
|
||||
public static boolean checkAddress(String address) {
|
||||
@@ -20,13 +27,15 @@ 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);
|
||||
}
|
||||
|
||||
public static boolean isArrayOfHashes(String[] hashes) {
|
||||
if (hashes == null) return false;
|
||||
|
||||
for (int i = 0; i < hashes.length; i++) {
|
||||
String hash = hashes[i];
|
||||
|
||||
for (String hash : hashes) {
|
||||
// Check if address with checksum
|
||||
if (hash.length() == 90) {
|
||||
if (!isTrytes(hash, 90)) {
|
||||
@@ -39,6 +48,41 @@ public class InputValidator {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if input is correct hash collections
|
||||
*
|
||||
* @method isTransfersArray
|
||||
* @param {array} hash
|
||||
* @returns {boolean}
|
||||
**/
|
||||
public static boolean isTransfersCollectionCorrect(final List<Transfer> transfers) {
|
||||
|
||||
for (final Transfer transfer : transfers) {
|
||||
if (!isTransfersArray(transfer)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isTransfersArray(final Transfer transfer) {
|
||||
|
||||
if (!isAddress(transfer.getAddress())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if message is correct trytes of any length
|
||||
if (!isTrytes(transfer.getMessage(), 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if tag is correct trytes of {0,27} trytes
|
||||
if (!isTrytes(transfer.getTag(), 27)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package jota.utils;
|
||||
|
||||
import jota.dto.response.GetBundleResponse;
|
||||
import jota.model.Bundle;
|
||||
import jota.model.Input;
|
||||
import jota.model.Transaction;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
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
|
||||
*
|
||||
@@ -44,10 +43,6 @@ public class IotaAPIUtils {
|
||||
return address;
|
||||
}
|
||||
|
||||
public static GetBundleResponse getBundle(final String transaction) {
|
||||
throw new NotImplementedException("Not yet implemented");
|
||||
}
|
||||
|
||||
public static String transactionTrytes(Transaction trx) {
|
||||
int[] valueTrits = Converter.trits(trx.getValue());
|
||||
while (valueTrits.length < 81) {
|
||||
@@ -82,8 +77,10 @@ public class IotaAPIUtils {
|
||||
+ trx.getNonce();
|
||||
}
|
||||
|
||||
public static List<String> signInputsAndReturn(String seed, List<Input> inputs, Bundle bundle,
|
||||
List<String> signatureFragments) {
|
||||
public static List<String> signInputsAndReturn(final String seed,
|
||||
final List<Input> inputs,
|
||||
final Bundle bundle,
|
||||
final List<String> signatureFragments) {
|
||||
bundle.finalize();
|
||||
bundle.addTrytes(signatureFragments);
|
||||
|
||||
@@ -156,3 +153,4 @@ public class IotaAPIUtils {
|
||||
return bundleTrytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package jota.utils;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 30.11.16.
|
||||
* Created by Sascha on 30.11.16.
|
||||
*/
|
||||
public class IotaUnitConverter {
|
||||
|
||||
@@ -13,4 +15,54 @@ public class IotaUnitConverter {
|
||||
private static long convertUnits(long amount, IotaUnits toUnit) {
|
||||
return (long) (amount / Math.pow(10, toUnit.getValue()));
|
||||
}
|
||||
|
||||
public static String convertRawIotaAmountToDisplayText(long amount) {
|
||||
IotaUnits unit = findOptimalIotaUnitToDisplay(amount);
|
||||
double amountInDisplayUnit = convertAmountTo(amount, unit);
|
||||
return createAmountWithUnitDisplayText(amountInDisplayUnit, unit);
|
||||
}
|
||||
|
||||
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);
|
||||
result += " " + unit.getUnit();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String createAmountDisplayText(double amountInUnit, IotaUnits unit) {
|
||||
DecimalFormat 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);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IotaUnits findOptimalIotaUnitToDisplay(long amount) {
|
||||
int length = String.valueOf(amount).length();
|
||||
|
||||
if (amount < 0) {// do not count "-" sign
|
||||
length -= 1;
|
||||
}
|
||||
|
||||
IotaUnits units = IotaUnits.IOTA;
|
||||
|
||||
if (length >= 1 && length <= 3) {
|
||||
units = IotaUnits.IOTA;
|
||||
} else if (length > 3 && length <= 6) {
|
||||
units = IotaUnits.KILO_IOTA;
|
||||
} else if (length > 6 && length <= 9) {
|
||||
units = IotaUnits.MEGA_IOTA;
|
||||
} else if (length > 9 && length <= 12) {
|
||||
units = IotaUnits.GIGA_IOTA;
|
||||
} else if (length > 12 && length <= 15) {
|
||||
units = IotaUnits.TERA_IOTA;
|
||||
} else if (length > 15 && length <= 18) {
|
||||
units = IotaUnits.PETA_IOTA;
|
||||
}
|
||||
return units;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package jota.utils;
|
||||
* 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,20 @@
|
||||
package jota.utils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 13.12.16.
|
||||
*/
|
||||
public class SeedRandomGenerator {
|
||||
|
||||
public static String generateNewSeed() {
|
||||
char[] chars = Constants.TRYTE_ALPHABET.toCharArray();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
SecureRandom random = new SecureRandom();
|
||||
for (int i = 0; i < Constants.SEED_LENGTH_MAX; i++) {
|
||||
char c = chars[random.nextInt(chars.length)];
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.sun.org.apache.xpath.internal.operations.Bool;
|
||||
import jota.model.Bundle;
|
||||
import jota.pow.Curl;
|
||||
|
||||
@@ -36,7 +35,6 @@ public class Signing {
|
||||
while (length-- > 0) {
|
||||
|
||||
for (int i = 0; i < 27; i++) {
|
||||
|
||||
curl.squeeze(buffer, offset, buffer.length);
|
||||
for (int j = 0; j < 243; j++) {
|
||||
key.add(buffer[j]);
|
||||
@@ -55,6 +53,40 @@ public class Signing {
|
||||
return a;
|
||||
}
|
||||
|
||||
public static 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);
|
||||
|
||||
for (int j = 0; j < 13 - normalizedBundleFragment[i]; j++) {
|
||||
curl.reset()
|
||||
.absorb(hash, 0, hash.length)
|
||||
.squeeze(hash, 0, hash.length);
|
||||
}
|
||||
|
||||
for (int j = 0; j < 243; j++) {
|
||||
signatureFragment[i * 243 + j] = hash[j];
|
||||
}
|
||||
}
|
||||
|
||||
return signatureFragment;
|
||||
}
|
||||
|
||||
public static int[] address(int[] digests) {
|
||||
final Curl curl = new Curl();
|
||||
int[] address = new int[243];
|
||||
curl.reset()
|
||||
.absorb(digests)
|
||||
.squeeze(address);
|
||||
return address;
|
||||
}
|
||||
|
||||
public static int[] digests(int[] key) {
|
||||
final Curl curl = new Curl();
|
||||
|
||||
@@ -68,9 +100,9 @@ public class Signing {
|
||||
|
||||
buffer = Arrays.copyOfRange(keyFragment, j * 243, (j + 1) * 243);
|
||||
for (int k = 0; k < 26; k++) {
|
||||
curl.reset();
|
||||
curl.absorb(buffer, 0, buffer.length);
|
||||
curl.squeeze(buffer, 0, buffer.length);
|
||||
curl.reset()
|
||||
.absorb(buffer)
|
||||
.squeeze(buffer);
|
||||
}
|
||||
System.arraycopy(buffer, 0, keyFragment, j * 243, 243);
|
||||
}
|
||||
@@ -84,82 +116,28 @@ public class Signing {
|
||||
return digests;
|
||||
}
|
||||
|
||||
public static int[] address(int[] digests) {
|
||||
final Curl curl = new Curl();
|
||||
int[] address = new int[243];
|
||||
curl.reset();
|
||||
curl.absorb(digests, 0, digests.length);
|
||||
curl.squeeze(address, 0, address.length);
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
**/
|
||||
public static 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);
|
||||
|
||||
for (int j = 0; j < 13 - normalizedBundleFragment[i]; j++) {
|
||||
|
||||
curl.reset();
|
||||
curl.absorb(hash, 0, hash.length);
|
||||
curl.squeeze(hash, 0, hash.length);
|
||||
}
|
||||
|
||||
for (int j = 0; j < 243; j++) {
|
||||
|
||||
signatureFragment[i * 243 + j] = hash[j];
|
||||
}
|
||||
}
|
||||
|
||||
return signatureFragment;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
**/
|
||||
public static int[] digest(int[] normalizedBundleFragment, int[] signatureFragment) {
|
||||
|
||||
int[] buffer = new int[243];
|
||||
|
||||
Curl curl = new Curl();
|
||||
|
||||
curl.reset();
|
||||
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; ) {
|
||||
|
||||
Curl jCurl = new Curl();
|
||||
|
||||
jCurl.reset();
|
||||
jCurl.absorb(buffer, 0, buffer.length);
|
||||
jCurl.squeeze(buffer, 0, buffer.length);
|
||||
new Curl().reset()
|
||||
.absorb(buffer)
|
||||
.squeeze(buffer);
|
||||
}
|
||||
|
||||
curl.absorb(buffer, 0, buffer.length);
|
||||
curl.absorb(buffer);
|
||||
}
|
||||
|
||||
curl.squeeze(buffer, 0, buffer.length);
|
||||
curl.squeeze(buffer);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
**/
|
||||
public static Boolean validateSignatures(String expectedAddress, String[] signatureFragments, String bundleHash) {
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
@@ -190,3 +168,4 @@ public class Signing {
|
||||
return (expectedAddress.equals(address));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +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>
|
||||
* EXAMPLES
|
||||
*
|
||||
* EXAMPLE
|
||||
*
|
||||
* 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:
|
||||
@@ -30,10 +32,11 @@ public class TrytesConverter {
|
||||
* b. The second tryte value is '9ABCDEFGHIJKLMNOPQRSTUVWXYZ'[3] === "C"
|
||||
* Our tryte pair is "IC"
|
||||
* <p>
|
||||
* RESULT:
|
||||
* 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) {
|
||||
|
||||
StringBuilder trytes = new StringBuilder();
|
||||
@@ -67,10 +70,9 @@ public class TrytesConverter {
|
||||
* Last character = }
|
||||
* Everything after that is 9's padding
|
||||
*/
|
||||
|
||||
public static String toString(String inputTrytes) {
|
||||
|
||||
String string = "";
|
||||
StringBuilder string = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < inputTrytes.length(); i += 2) {
|
||||
// get a trytes pair
|
||||
@@ -81,10 +83,9 @@ public class TrytesConverter {
|
||||
int decimalValue = firstValue + secondValue * 27;
|
||||
|
||||
String character = Character.toString((char) decimalValue);
|
||||
|
||||
string += character;
|
||||
string.append(character);
|
||||
}
|
||||
|
||||
return string;
|
||||
return string.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,17 @@ package jota;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import jota.dto.response.*;
|
||||
import jota.error.ArgumentException;
|
||||
import jota.error.NotEnoughBalanceException;
|
||||
import jota.model.Inputs;
|
||||
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;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Let's do some integration test coverage against a default local real node.
|
||||
@@ -33,7 +31,9 @@ public class IotaAPIProxyTest {
|
||||
private static final String TEST_TRYTES = "BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999";
|
||||
private static final String TEST_MILESTONE = "SMYMAKKPSUKCKDRUEYCGZJTYCZ9HHDMDUWBAPXARGURPQRHTAJDASRWMIDTPTBNDKDEFBUTBGGAFX9999";
|
||||
private static final Integer TEST_MILESTONE_INDEX = 8059;
|
||||
private static Transfer transfer = new Transfer(TEST_ADDRESS_WITH_CHECKSUM,0, "JAVALIB","JAVA");
|
||||
private static final String TEST_MESSAGE = "JOTA";
|
||||
private static final String TEST_TAG = "JOTASPAM9999999999999999999";
|
||||
|
||||
|
||||
private IotaAPIProxy proxy;
|
||||
|
||||
@@ -46,6 +46,21 @@ public class IotaAPIProxyTest {
|
||||
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
|
||||
@@ -76,25 +91,25 @@ public class IotaAPIProxyTest {
|
||||
public void shouldFindTransactionsByAddresses() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByAddresses(TEST_ADDRESS_WITH_CHECKSUM);
|
||||
System.err.println(gson.toJson(trans));
|
||||
assertThat(trans, IsNull.notNullValue());
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByApprovees() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByApprovees(new String[]{TEST_HASH});
|
||||
assertThat(trans, IsNull.notNullValue());
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByBundles() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByBundles(TEST_HASH);
|
||||
assertThat(trans, IsNull.notNullValue());
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindTransactionsByDigests() {
|
||||
FindTransactionResponse trans = proxy.findTransactionsByDigests(TEST_HASH);
|
||||
assertThat(trans, IsNull.notNullValue());
|
||||
assertThat(trans.getHashes(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
|
||||
@@ -103,27 +118,32 @@ public class IotaAPIProxyTest {
|
||||
@Test
|
||||
public void shouldGetTrytes() {
|
||||
GetTrytesResponse res = proxy.getTrytes(TEST_HASH);
|
||||
assertThat(res, IsNull.notNullValue());
|
||||
assertThat(res.getTrytes(), IsNull.notNullValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetInclusionStates() {
|
||||
GetInclusionStateResponse res = proxy.getInclusionStates(new String[]{TEST_ADDRESS_WITH_CHECKSUM}, new String[]{"DNSBRJWNOVUCQPILOQIFDKBFJMVOTGHLIMLLRXOHFTJZGRHJUEDAOWXQRYGDI9KHYFGYDWQJZKX999999"});
|
||||
assertThat(res, IsNull.notNullValue());
|
||||
assertThat(res.getStates(), IsNull.notNullValue());
|
||||
}
|
||||
|
||||
@Test // very long execution
|
||||
public void shouldGetTransactionsToApprove() {
|
||||
GetTransactionsToApproveResponse res = proxy.getTransactionsToApprove(27);
|
||||
assertThat(res, IsNull.notNullValue());
|
||||
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, IsNull.notNullValue());
|
||||
assertThat(res.getBalances(), IsNull.notNullValue());
|
||||
assertThat(res.getMilestone(), IsNull.notNullValue());
|
||||
assertThat(res.getMilestoneIndex(), IsNull.notNullValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -139,13 +159,25 @@ public class IotaAPIProxyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetGetInputs() throws ArgumentException, NotEnoughBalanceException {
|
||||
final Inputs res = proxy.getInputs(TEST_SEED, 0, 1, 1);
|
||||
assertTrue(res.getTotalBalance() > 0);
|
||||
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 shouldSendTransfer() throws ArgumentException, NotEnoughBalanceException {
|
||||
proxy.sendTransfer(TEST_SEED,1,13, new Transfer[]{transfer}, null, null);
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -35,4 +35,24 @@ public class IotaUnitConverterTest {
|
||||
public void shouldConvertUnitTiToPi() {
|
||||
assertEquals(IotaUnitConverter.convertUnits(1000, IotaUnits.TERA_IOTA, IotaUnits.PETA_IOTA), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindOptimizeUnitToDisplay() {
|
||||
assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1), IotaUnits.IOTA);
|
||||
assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000), IotaUnits.KILO_IOTA);
|
||||
assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000000), IotaUnits.MEGA_IOTA);
|
||||
assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000000000), IotaUnits.GIGA_IOTA);
|
||||
assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000000000000L), IotaUnits.TERA_IOTA);
|
||||
assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000000000000000L), IotaUnits.PETA_IOTA);
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package jota;
|
||||
|
||||
import jota.utils.Constants;
|
||||
import jota.utils.InputValidator;
|
||||
import jota.utils.SeedRandomGenerator;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 13.12.16.
|
||||
*/
|
||||
public class SeedRandomGeneratorTest {
|
||||
|
||||
@Test
|
||||
public void shouldGenerateNewSeed() {
|
||||
|
||||
String generatedSeed = SeedRandomGenerator.generateNewSeed();
|
||||
assertEquals(InputValidator.isAddress(generatedSeed), true);
|
||||
assertEquals(generatedSeed.length(), Constants.SEED_LENGTH_MAX);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package jota;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class SendMessageTest {
|
||||
|
||||
@Test
|
||||
public void sendMessage() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,9 @@ package jota;
|
||||
import jota.utils.TrytesConverter;
|
||||
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.
|
||||
@@ -17,5 +20,14 @@ public class TrytesConverterTest {
|
||||
public void shouldConvertTrytesToString() {
|
||||
assertEquals(TrytesConverter.toString("IC"), "Z");
|
||||
}
|
||||
|
||||
@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