From bbe837975e31bb2eb1e3907318a4601f906e751b Mon Sep 17 00:00:00 2001 From: davassi Date: Mon, 19 Dec 2016 19:03:11 +0100 Subject: [PATCH] Implemented prepareTransfer and getInputs --- src/main/java/jota/IotaAPIProxy.java | 371 ++++++++++++++++-- .../GetBalancesAndFormatResponse.java | 34 ++ .../dto/response/GetNewAddressResponse.java | 2 +- src/main/java/jota/model/Transfer.java | 63 ++- src/main/java/jota/pow/Curl.java | 8 + src/main/java/jota/utils/InputValidator.java | 47 +++ src/main/java/jota/utils/IotaAPIUtils.java | 6 +- src/main/java/jota/utils/Signing.java | 118 ++++-- src/test/java/jota/IotaAPIProxyTest.java | 2 +- 9 files changed, 576 insertions(+), 75 deletions(-) create mode 100644 src/main/java/jota/dto/response/GetBalancesAndFormatResponse.java diff --git a/src/main/java/jota/IotaAPIProxy.java b/src/main/java/jota/IotaAPIProxy.java index 8901c71..6bb6032 100644 --- a/src/main/java/jota/IotaAPIProxy.java +++ b/src/main/java/jota/IotaAPIProxy.java @@ -1,28 +1,60 @@ package jota; -import jota.dto.request.*; -import jota.dto.response.*; -import jota.model.Transaction; -import jota.utils.Converter; -import jota.utils.IotaAPIUtils; -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.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jota.dto.request.IotaAttachToTangleRequest; +import jota.dto.request.IotaBroadcastTransactionRequest; +import jota.dto.request.IotaCommandRequest; +import jota.dto.request.IotaFindTransactionsRequest; +import jota.dto.request.IotaGetBalancesRequest; +import jota.dto.request.IotaGetInclusionStateRequest; +import jota.dto.request.IotaGetTransactionsToApproveRequest; +import jota.dto.request.IotaGetTrytesRequest; +import jota.dto.request.IotaNeighborsRequest; +import jota.dto.request.IotaStoreTransactionsRequest; +import jota.dto.response.AddNeighborsResponse; +import jota.dto.response.BroadcastTransactionsResponse; +import jota.dto.response.FindTransactionResponse; +import jota.dto.response.GetAttachToTangleResponse; +import jota.dto.response.GetBalancesAndFormatResponse; +import jota.dto.response.GetBalancesResponse; +import jota.dto.response.GetBundleResponse; +import jota.dto.response.GetInclusionStateResponse; +import jota.dto.response.GetNeighborsResponse; +import jota.dto.response.GetNewAddressResponse; +import jota.dto.response.GetNodeInfoResponse; +import jota.dto.response.GetTipsResponse; +import jota.dto.response.GetTransactionsToApproveResponse; +import jota.dto.response.GetTrytesResponse; +import jota.dto.response.InterruptAttachingToTangleResponse; +import jota.dto.response.RemoveNeighborsResponse; +import jota.dto.response.StoreTransactionsResponse; +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 retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; /** * IotaAPIProxy Builder. Usage: @@ -174,6 +206,10 @@ public class IotaAPIProxy { final Call res = service.getBalances(IotaGetBalancesRequest.createIotaGetBalancesRequest(threshold, addresses)); return wrapCheckedException(res).body(); } + + public GetBalancesResponse getBalances(Integer threshold, List addresses) { + return getBalances(threshold, addresses.toArray(new String[] {})); + } public InterruptAttachingToTangleResponse interruptAttachingToTangle() { final Call res = service.interruptAttachingToTangle(IotaCommandRequest.createInterruptAttachToTangleRequest()); @@ -244,18 +280,20 @@ public class IotaAPIProxy { * newAddress * broadcastAndStore * sendTrytes - * - getTransactionsObjects - findTransactionObjects - getLatestInclusion - getInputs - prepareTransfers - sendTransfer - replayBundle - broadcastBundle - getBundle - getTransfers - getAccountData + * prepareTransfers + * getInputs + + getTransfers + sendTransfer + getBundle + + getTransactionsObjects + findTransactionObjects + getLatestInclusion + + replayBundle + broadcastBundle + getAccountData */ /** @@ -272,7 +310,6 @@ public class IotaAPIProxy { throw new IllegalStateException("BroadcastAndStore Illegal state Exception"); } return storeTransactions(trytes); - } /** @@ -296,16 +333,288 @@ public class IotaAPIProxy { throw new IllegalStateException("sendTrytes Illegal state Exception"); } - return Arrays.stream(res.getTrytes()) - .map(Converter::transactionObject) - .collect(Collectors.toList()); + //return Arrays.stream(res.getTrytes()).map(Converter::transactionObject).collect(Collectors.toList()); + final List trx = new ArrayList<>(); + + for (final String tx : Arrays.asList(res.getTrytes())) { + trx.add(Converter.transactionObject(tx)); + } + return trx; + } + + public List findAndGetTxs(final String addresses) { + + final FindTransactionResponse res = findTransactionsByAddresses(addresses); + return getTransactionsObjects(res.getHashes()); + } + + /** + * 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 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 trxs = new ArrayList<>(); + + for (final String tryte : trytesResponse.getTrytes()) { + trxs.add(Converter.transactionObject(tryte)); + } + return trxs; } + /** + * 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 prepareTransfers(final String seed, final List transfers, String remainder, List 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 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 = new String(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 inputsAddresses = new ArrayList<>(); + for (final Input i : inputs) { + inputsAddresses.add(i.getAddress()); + } + + GetBalancesResponse resbalances = getBalances(100, inputsAddresses); + String[] balances = resbalances.getBalances(); + + + List 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 trxb = bundle.getTransactions(); + List 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 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 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 addresses, + final List balances, long threshold, int start, int end) { + + GetBalancesResponse bres = getBalances(100, addresses); + + // 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 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 == false && totalBalance >= threshold) { + thresholdReached = true; + break; + } + } + } + + if (thresholdReached) { + return GetBalancesAndFormatResponse.create(inputs, totalBalance); + } + throw new IllegalStateException("Not enough balance"); + } + public GetBundleResponse getBundle(String transaction) { return null; //IotaAPIUtils.getBundle(transaction); } - public static class Builder { String protocol, host, port; diff --git a/src/main/java/jota/dto/response/GetBalancesAndFormatResponse.java b/src/main/java/jota/dto/response/GetBalancesAndFormatResponse.java new file mode 100644 index 0000000..493d063 --- /dev/null +++ b/src/main/java/jota/dto/response/GetBalancesAndFormatResponse.java @@ -0,0 +1,34 @@ +package jota.dto.response; + +import java.util.List; + +import jota.model.Input; + +public class GetBalancesAndFormatResponse extends AbstractResponse { + + private List input; + private long totalBalance; + + public List getInput() { + return input; + } + + public void setInput(List input) { + this.input = input; + } + + public long getTotalBalance() { + return totalBalance; + } + + public void setTotalBalance(long totalBalance) { + this.totalBalance = totalBalance; + } + + public static GetBalancesAndFormatResponse create(List inputs, long totalBalance2) { + GetBalancesAndFormatResponse res = new GetBalancesAndFormatResponse(); + res.setInput(inputs); + res.setTotalBalance(totalBalance2); + return res; + } +} diff --git a/src/main/java/jota/dto/response/GetNewAddressResponse.java b/src/main/java/jota/dto/response/GetNewAddressResponse.java index b54384d..484e17e 100644 --- a/src/main/java/jota/dto/response/GetNewAddressResponse.java +++ b/src/main/java/jota/dto/response/GetNewAddressResponse.java @@ -12,7 +12,7 @@ public class GetNewAddressResponse extends AbstractResponse { return res; } - public List getAddress() { + public List getAddresses() { return addresses; } } diff --git a/src/main/java/jota/model/Transfer.java b/src/main/java/jota/model/Transfer.java index fa149ee..a1b675e 100644 --- a/src/main/java/jota/model/Transfer.java +++ b/src/main/java/jota/model/Transfer.java @@ -1,7 +1,6 @@ package jota.model; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; +import com.google.gson.Gson; /** * Created by pinpong on 02.12.16. @@ -11,21 +10,33 @@ public class Transfer { private String timestamp; private String address; private String hash; - private Integer persistence; + private Boolean persistence; private long value; + private String message; + private String tag; - public Transfer(String timestamp, String address, String hash, Integer persistence, long value) { - + 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; this.persistence = persistence; this.value = value; + this.message = message; + this.tag = tag; + + } + + public Transfer(String address, long value, String message, String tag) { + this.address = address; + this.value = value; + this.message = message; + this.tag = tag; } @Override public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); + return new Gson().toJson(this); } public String getAddress() { @@ -36,7 +47,7 @@ public class Transfer { return hash; } - public Integer getPersistence() { + public Boolean getPersistence() { return persistence; } @@ -47,4 +58,40 @@ public class Transfer { 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 void setHash(String hash) { + this.hash = hash; + } + + public void setPersistence(Boolean persistence) { + this.persistence = persistence; + } + + public void setValue(long value) { + this.value = value; + } + + public void setMessage(String message) { + this.message = message; + } + + public void setTag(String tag) { + this.tag = tag; + } +} \ No newline at end of file diff --git a/src/main/java/jota/pow/Curl.java b/src/main/java/jota/pow/Curl.java index 764a54a..b804250 100644 --- a/src/main/java/jota/pow/Curl.java +++ b/src/main/java/jota/pow/Curl.java @@ -25,6 +25,10 @@ public class Curl { return this; } + + public Curl absorb(final int[] trits) { + return absorb(trits, 0, trits.length); + } public Curl transform() { @@ -56,6 +60,10 @@ public class Curl { return state; } + + public int[] squeeze(final int[] trits) { + return squeeze(trits, 0, trits.length); + } public int[] getState() { return state; diff --git a/src/main/java/jota/utils/InputValidator.java b/src/main/java/jota/utils/InputValidator.java index d658bd3..6e85443 100644 --- a/src/main/java/jota/utils/InputValidator.java +++ b/src/main/java/jota/utils/InputValidator.java @@ -1,5 +1,12 @@ 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. */ @@ -20,6 +27,10 @@ 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; @@ -38,4 +49,40 @@ 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 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; + } } diff --git a/src/main/java/jota/utils/IotaAPIUtils.java b/src/main/java/jota/utils/IotaAPIUtils.java index ee759cd..617f014 100644 --- a/src/main/java/jota/utils/IotaAPIUtils.java +++ b/src/main/java/jota/utils/IotaAPIUtils.java @@ -77,8 +77,10 @@ public class IotaAPIUtils { + trx.getNonce(); } - public static List signInputsAndReturn(String seed, List inputs, Bundle bundle, - List signatureFragments) { + public static List signInputsAndReturn(final String seed, + final List inputs, + final Bundle bundle, + final List signatureFragments) { bundle.finalize(); bundle.addTrytes(signatureFragments); diff --git a/src/main/java/jota/utils/Signing.java b/src/main/java/jota/utils/Signing.java index f04339f..e0f2e84 100644 --- a/src/main/java/jota/utils/Signing.java +++ b/src/main/java/jota/utils/Signing.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import jota.model.Bundle; import jota.pow.Curl; public class Signing { @@ -52,35 +53,6 @@ public class Signing { return a; } - public static int[] digests(int[] key) { - final Curl curl = new Curl(); - - int[] digests = new int[(int) Math.floor(key.length / 6561) * 243]; - int[] buffer = new int[243]; - - for (int i = 0; i < Math.floor(key.length / 6561); i++) { - int[] keyFragment = Arrays.copyOfRange(key, i * 6561, (i + 1) * 6561); - - for (int j = 0; j < 27; j++) { - - 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); - } - System.arraycopy(buffer, 0, keyFragment, j * 243, 243); - } - - curl.reset(); - curl.absorb(keyFragment, 0, keyFragment.length); - curl.squeeze(buffer, 0, buffer.length); - - System.arraycopy(buffer, 0, digests, i * 243, 243); - } - return digests; - } - public static int[] signatureFragment(int[] normalizedBundleFragment, int[] keyFragment) { int[] signatureFragment = keyFragment; @@ -109,9 +81,91 @@ public class Signing { 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); + curl.reset() + .absorb(digests) + .squeeze(address); return address; } + + public static int[] digests(int[] key) { + final Curl curl = new Curl(); + + int[] digests = new int[(int) Math.floor(key.length / 6561) * 243]; + int[] buffer = new int[243]; + + for (int i = 0; i < Math.floor(key.length / 6561); i++) { + int[] keyFragment = Arrays.copyOfRange(key, i * 6561, (i + 1) * 6561); + + for (int j = 0; j < 27; j++) { + + buffer = Arrays.copyOfRange(keyFragment, j * 243, (j + 1) * 243); + for (int k = 0; k < 26; k++) { + curl.reset() + .absorb(buffer) + .squeeze(buffer); + } + System.arraycopy(buffer, 0, keyFragment, j * 243, 243); + } + + curl.reset(); + curl.absorb(keyFragment, 0, keyFragment.length); + curl.squeeze(buffer, 0, buffer.length); + + System.arraycopy(buffer, 0, digests, i * 243, 243); + } + return digests; + } + + public static int[] digest(int[] normalizedBundleFragment, int[] signatureFragment) { + + 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); + } + curl.absorb(buffer); + } + curl.squeeze(buffer); + + return buffer; + } + + public static Boolean validateSignatures(String expectedAddress, String[] signatureFragments, String bundleHash) { + + Bundle bundle = new Bundle(); + + int[][] normalizedBundleFragments = new int[3][27]; + int[] normalizedBundleHash = bundle.normalizedBundle(bundleHash); + + // Split hash into 3 fragments + for (int i = 0; i < 3; i++) { + normalizedBundleFragments[i] = Arrays.copyOfRange(normalizedBundleHash, i * 27, (i + 1) * 27); + } + + // Get digests + int[] digests = new int[signatureFragments.length * 243 + 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]; + } + } + + String address = Converter.trytes(address(digests)); + + return (expectedAddress.equals(address)); + } } + diff --git a/src/test/java/jota/IotaAPIProxyTest.java b/src/test/java/jota/IotaAPIProxyTest.java index 9b16cda..7f62a73 100644 --- a/src/test/java/jota/IotaAPIProxyTest.java +++ b/src/test/java/jota/IotaAPIProxyTest.java @@ -129,6 +129,6 @@ public class IotaAPIProxyTest { @Test public void shouldCreateANewAddress() { final GetNewAddressResponse res = proxy.getNewAddress(TEST_SEED, 0, false, 1, false); - assertThat(res.getAddress(), Is.is(Collections.singletonList(TEST_ADDRESS_WITHOUT_CHECKSUM))); + assertThat(res.getAddresses(), Is.is(Collections.singletonList(TEST_ADDRESS_WITHOUT_CHECKSUM))); } } \ No newline at end of file