diff --git a/README.md b/README.md
index b42ccab6..96d311eb 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
![IOTA Logo][iota-logo]
# IOTA Java Library
-
+[![Codacy Badge][codacy-badge]][codacy]
[![JitPack][jitpack-badge]][jitpack]
[![Build Status][travis-badge]][travis]
[![License][license-badge]][license]
@@ -36,7 +36,7 @@ All the boilerplate code for connecting to the node REST interface has been hidd
1. [Disclaimer](#disclaimer)
1. [Supporting the project](#supporting-the-project)
1. [Join the Discussion](#join-the-discussion)
-1. [License](#license)
+1. [License](codacity#license)
## Installation
@@ -56,7 +56,7 @@ allprojects {
Add this in your module `build.gradle` file:
```gradle
dependencies {
- compile 'com.github.iotaledger:iota-java:1.0.0-beta1'
+ compile 'com.github.iotaledger:iota-java:1.0.0-beta2'
}
```
@@ -82,7 +82,7 @@ Add this in your module `pom.xml` file:
```
For the [VERSION_INFORMATION], you can choose to set it to a release number, or the first 10 characters of a commit hash.
-`17e0de8ed5` or `1.0.0-beta1`
+`f3200c8380` or `1.0.0-beta2`
Or check it out at [Jitpack](https://jitpack.io/#iotaledger/iota-java) for more specific information
@@ -96,7 +96,7 @@ Reference your new project to the "jota" project.
##### Linking jar
In order to build the jar; the following command should be run on the "jota" project:
-'mvn clean install -Prelease -DprofileIdEnabled=true'
+'mvn clean install'
This will generate a jar with the following format: `jota-[VERSION]-jar-with-dependencies.jar`
@@ -279,17 +279,19 @@ See the License for the specific language governing permissions and
limitations under the License.
```
-[iota-logo]: https://raw.githubusercontent.com/iotaledger/documentation/master/source/images/iota-logo.png
+[iota-logo]: https://avatars0.githubusercontent.com/u/20126597?s=200&v=4
+[codacy]: https://www.codacy.com/app/kwek20/iota-java?utm_source=github.com&utm_medium=referral&utm_content=iotaledger/iota-java&utm_campaign=Badge_Grade
+[codacy-badge]: https://api.codacy.com/project/badge/Grade/92feea51a15c4e589386c269475b8761
[jitpack]: https://jitpack.io/#iotaledger/iota-java
[jitpack-badge]: https://jitpack.io/v/iotaledger/iota-java.svg
[travis]: https://travis-ci.org/iotaledger/iota-java
[travis-badge]: https://travis-ci.org/iotaledger/iota-java.svg?branch=master
[license]: https://github.com/iotaledger/iota-java/blob/master/LICENSE
[license-badge]: https://img.shields.io/github/license/iotaledger/iota-java.svg
-[iota-iri]: https://github.com/iotaledger/iri/tree/v1.5.0
-[iota-iri-badge]: https://img.shields.io/badge/IOTA%20IRI%20compatibility-v1.5.0-blue.svg
+[iota-iri]: https://github.com/iotaledger/iri/tree/v1.5.5
+[iota-iri-badge]: https://img.shields.io/badge/IOTA%20IRI%20compatibility-v1.5.5-blue.svg
[iota-api]: https://iota.readme.io/reference
-[iota-api-badge]: https://img.shields.io/badge/IOTA%20API%20coverage-14/15%20commands-red.svg
+[iota-api-badge]: https://img.shields.io/badge/IOTA%20API%20coverage-15/15%20commands-green.svg
[javadoc]: https://iotaledger.github.io/iota-java/javadoc/
[issues]: https://github.com/iotaledger/iota-java/issues
[new-issue]: https://github.com/iotaledger/iota-java/issues/new
diff --git a/jota/pom.xml b/jota/pom.xml
index 80ee0e1d..94943293 100644
--- a/jota/pom.xml
+++ b/jota/pom.xml
@@ -5,7 +5,7 @@
org.iotajota-parent
- 1.0.0-beta1
+ 1.0.0-beta2JOTA : Library
diff --git a/jota/src/main/java/jota/IotaAPI.java b/jota/src/main/java/jota/IotaAPI.java
index d7f4d1d7..263b787e 100644
--- a/jota/src/main/java/jota/IotaAPI.java
+++ b/jota/src/main/java/jota/IotaAPI.java
@@ -15,8 +15,6 @@
import java.util.*;
import java.util.stream.Collectors;
-import static jota.utils.Constants.*;
-
/**
* IotaAPI Builder. Usage:
@@ -29,7 +27,6 @@
*
* {@code GetNodeInfoResponse response = api.getNodeInfo();}
*
- * @author davassi
*/
public class IotaAPI extends IotaAPICore {
@@ -44,50 +41,148 @@ protected IotaAPI(Builder builder) {
/**
* 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.
- *
+ *
+ * Deprecated -> Use the new functions {@link #getNextAvailableAddress}, {@link #getAddressesUnchecked} and {@link #generateNewAddresses}
* @param seed Tryte-encoded seed. It should be noted that this seed is not transferred.
* @param security Security level to be used for the private key / address. Can be 1, 2 or 3.
* @param index Key index to start search from. If the index is provided, the generation of the address is not deterministic.
* @param checksum Adds 9-tryte address checksum.
* @param total Total number of addresses to generate.
* @param returnAll If true, it returns all addresses which were deterministically generated (until findTransactions returns null).
- * @return An array of strings with the specifed number of addresses.
+ * @return GetNewAddressResponse containing an array of strings with the specified number of addresses.
* @throws ArgumentException is thrown when the specified input is not valid.
*/
+ @Deprecated
public GetNewAddressResponse getNewAddress(final String seed, int security, final int index, final boolean checksum, final int total, final boolean returnAll) throws ArgumentException {
- StopWatch stopWatch = new StopWatch();
-
- List allAddresses = new ArrayList<>();
-
// If total number of addresses to generate is supplied, simply generate
// and return the list of all addresses
if (total != 0) {
- for (int i = index; i < index + total; i++) {
- allAddresses.add(IotaAPIUtils.newAddress(seed, security, i, checksum, customCurl.clone()));
- }
- return GetNewAddressResponse.create(allAddresses, stopWatch.getElapsedTimeMili());
+ return getAddressesUnchecked(seed, security, checksum, index, total);
}
+ // If !returnAll return only the last address that was generated
+ if (!returnAll) {
+ return generateNewAddresses(seed, security, checksum, 0, 1, true);
+ } else {
+ return generateNewAddresses(seed, security, checksum, 0, 1, false);
+ }
+ }
+
+ /**
+ * Checks all addresses until the first unspent address is found. Starts at index 0.
+ * @param seed Tryte-encoded seed. It should be noted that this seed is not transferred.
+ * @param security Security level to be used for the private key / address. Can be 1, 2 or 3.
+ * @param checksum Adds 9-tryte address checksum.
+ * @return GetNewAddressResponse containing an array of strings with the specified number of addresses.
+ * @throws ArgumentException is thrown when the specified input is not valid.
+ */
+ public GetNewAddressResponse getNextAvailableAddress(String seed, int security, boolean checksum) throws ArgumentException {
+ return generateNewAddresses(seed, security, checksum, 0, 1, false);
+ }
+
+ /**
+ * Checks all addresses until the first unspent address is found.
+ * @param seed Tryte-encoded seed. It should be noted that this seed is not transferred.
+ * @param security Security level to be used for the private key / address. Can be 1, 2 or 3.
+ * @param checksum Adds 9-tryte address checksum.
+ * @param index Key index to start search from.
+ * @return GetNewAddressResponse containing an array of strings with the specified number of addresses.
+ * @throws ArgumentException is thrown when the specified input is not valid.
+ */
+ public GetNewAddressResponse getNextAvailableAddress(String seed, int security, boolean checksum, int index) throws ArgumentException {
+ return generateNewAddresses(seed, security, checksum, index, 1, false);
+ }
+
+ /**
+ * Generates new addresses, meaning addresses which were not spend from, according to the connected node.
+ * Starts at index 0, untill amount of unspent addresses are found.
+ * @param seed Tryte-encoded seed. It should be noted that this seed is not transferred.
+ * @param security Security level to be used for the private key / address. Can be 1, 2 or 3.
+ * @param checksum Adds 9-tryte address checksum.
+ * @param amount Total number of addresses to generate.
+ * @return GetNewAddressResponse containing an array of strings with the specified number of addresses.
+ * @throws ArgumentException is thrown when the specified input is not valid.
+ */
+ public GetNewAddressResponse generateNewAddresses(String seed, int security, boolean checksum, int amount) throws ArgumentException {
+ return generateNewAddresses(seed, security, checksum, 0, amount, false);
+ }
+
+ /**
+ * Generates new addresses, meaning addresses which were not spend from, according to the connected node.
+ * Stops when amount of unspent addresses are found,starting from index
+ * @param seed Tryte-encoded seed. It should be noted that this seed is not transferred.
+ * @param security Security level to be used for the private key / address. Can be 1, 2 or 3.
+ * @param checksum Adds 9-tryte address checksum.
+ * @param index Key index to start search from.
+ * @param amount Total number of addresses to generate.
+ * @return GetNewAddressResponse containing an array of strings with the specified number of addresses.
+ * @throws ArgumentException is thrown when the specified input is not valid.
+ */
+ public GetNewAddressResponse generateNewAddresses(String seed, int security, boolean checksum, int index, int amount) throws ArgumentException {
+ return generateNewAddresses(seed, security, checksum, 0, amount, false);
+ }
+
+ /**
+ * Generates new addresses, meaning addresses which were not spend from, according to the connected node.
+ * Stops when amount of unspent addresses are found,starting from index
+ * @param seed Tryte-encoded seed. It should be noted that this seed is not transferred.
+ * @param security Security level to be used for the private key / address. Can be 1, 2 or 3.
+ * @param checksum Adds 9-tryte address checksum.
+ * @param index Key index to start search from.
+ * @param amount Total number of addresses to generate.
+ * @param addSpendAddresses If true, it returns all addresses, even those who were determined to be spent from
+ * @return GetNewAddressResponse containing an array of strings with the specified number of addresses.
+ * @throws ArgumentException is thrown when the specified input is not valid.
+ */
+ public GetNewAddressResponse generateNewAddresses(String seed, int security, boolean checksum, int index, int amount, boolean addSpendAddresses) throws ArgumentException {
+ if ((!InputValidator.isValidSeed(seed))) {
+ throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR);
+ }
+
+ StopWatch stopWatch = new StopWatch();
+ List allAddresses = new ArrayList<>();
- // No total provided: Continue calling findTransactions to see if address was
- // already created if null, return list of addresses
- for (int i = index; ; i++) {
+ for (int i = index, numUnspentFound=0; numUnspentFound < amount; i++) {
final String newAddress = IotaAPIUtils.newAddress(seed, security, i, checksum, customCurl.clone());
final FindTransactionResponse response = findTransactionsByAddresses(newAddress);
- allAddresses.add(newAddress);
+
if (response.getHashes().length == 0) {
- break;
+ //Unspent address
+ allAddresses.add(newAddress);
+ numUnspentFound++;
+ } else if (addSpendAddresses) {
+ //Spend address, were interested anyways
+ allAddresses.add(newAddress);
}
}
+
+ return GetNewAddressResponse.create(allAddresses, stopWatch.getElapsedTimeMili());
+ }
+
+ /**
+ * Generates amount of addresses, starting from index
+ * This does not mean that these addresses are safe to use (unspent)
+ * @param seed Tryte-encoded seed. It should be noted that this seed is not transferred.
+ * @param security Security level to be used for the private key / address. Can be 1, 2 or 3.
+ * @param checksum Adds 9-tryte address checksum.
+ * @param index Key index to start search from. The generation of the address is not deterministic.
+ * @param amount Total number of addresses to generate.
+ * @return GetNewAddressResponse containing an array of strings with the specified number of addresses.
+ * @throws ArgumentException is thrown when the specified input is not valid.
+ */
+ public GetNewAddressResponse getAddressesUnchecked(String seed, int security, boolean checksum, int index, int amount) throws ArgumentException {
+ if ((!InputValidator.isValidSeed(seed))) {
+ throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR);
+ }
+
+ StopWatch stopWatch = new StopWatch();
- // If !returnAll return only the last address that was generated
- if (!returnAll) {
-
- //allAddresses = allAddresses.subList(allAddresses.size() - 2, allAddresses.size() - 1);
- allAddresses = allAddresses.subList(allAddresses.size() - 1, allAddresses.size());
+ List allAddresses = new ArrayList<>();
+ for (int i = index; i < index + amount; i++) {
+ allAddresses.add(IotaAPIUtils.newAddress(seed, security, i, checksum, customCurl.clone()));
}
return GetNewAddressResponse.create(allAddresses, stopWatch.getElapsedTimeMili());
}
@@ -105,11 +200,11 @@ public GetTransferResponse getTransfers(String seed, int security, Integer start
// validate seed
if ((!InputValidator.isValidSeed(seed))) {
- throw new IllegalStateException(INVALID_SEED_INPUT_ERROR);
+ throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR);
}
if (start > end || end > (start + 500)) {
- throw new ArgumentException(INVALID_INPUT_ERROR);
+ throw new ArgumentException(Constants.INVALID_INPUT_ERROR);
}
StopWatch stopWatch = new StopWatch();
@@ -167,7 +262,7 @@ public Bundle[] bundlesFromAddresses(String[] addresses, final Boolean inclusion
if (tailTxArray.length != 0 && inclusionStates) {
gisr = getLatestInclusion(tailTxArray);
if (gisr == null || gisr.getStates() == null || gisr.getStates().length == 0) {
- throw new IllegalStateException(GET_INCLUSION_STATE_RESPONSE_ERROR);
+ throw new IllegalStateException(Constants.GET_INCLUSION_STATE_RESPONSE_ERROR);
}
}
final GetInclusionStateResponse finalInclusionStates = gisr;
@@ -192,7 +287,7 @@ public void perform(String param) {
}
// If error returned from getBundle, simply ignore it because the bundle was most likely incorrect
} catch (ArgumentException e) {
- log.warn(GET_BUNDLE_RESPONSE_ERROR);
+ log.warn(Constants.GET_BUNDLE_RESPONSE_ERROR);
}
}
});
@@ -206,24 +301,24 @@ public void perform(String param) {
}
/**
- * Wrapper function that broadcasts and stores the specified trytes.
+ * Wrapper function that stores and broadcasts the specified trytes.
*
* @param trytes The trytes.
- * @return A StoreTransactionsResponse.
+ * @return A BroadcastTransactionsResponse.
* @throws ArgumentException is thrown when the specified input is not valid.
*/
- public StoreTransactionsResponse broadcastAndStore(final String... trytes) throws ArgumentException {
+ public BroadcastTransactionsResponse storeAndBroadcast(final String... trytes) throws ArgumentException {
if (!InputValidator.isArrayOfAttachedTrytes(trytes)) {
- throw new ArgumentException(INVALID_TRYTES_INPUT_ERROR);
+ throw new ArgumentException(Constants.INVALID_TRYTES_INPUT_ERROR);
}
try {
- broadcastTransactions(trytes);
+ storeTransactions(trytes);
} catch (Exception e) {
throw new ArgumentException(e.toString());
}
- return storeTransactions(trytes);
+ return broadcastTransactions(trytes);
}
/**
@@ -243,7 +338,7 @@ public List sendTrytes(final String[] trytes, final int depth, fina
final GetAttachToTangleResponse res = attachToTangle(txs.getTrunkTransaction(), txs.getBranchTransaction(), minWeightMagnitude, trytes);
try {
- broadcastAndStore(res.getTrytes());
+ storeAndBroadcast(res.getTrytes());
} catch (ArgumentException e) {
return new ArrayList<>();
}
@@ -266,7 +361,7 @@ public List sendTrytes(final String[] trytes, final int depth, fina
public List findTransactionsObjectsByHashes(String[] hashes) throws ArgumentException {
if (!InputValidator.isArrayOfHashes(hashes)) {
- throw new IllegalStateException(INVALID_HASHES_INPUT_ERROR);
+ throw new IllegalStateException(Constants.INVALID_HASHES_INPUT_ERROR);
}
final GetTrytesResponse trytesResponse = getTrytes(hashes);
@@ -365,16 +460,16 @@ public List prepareTransfers(String seed, int security, final List prepareTransfers(String seed, int security, final List totalBalance) {
- throw new IllegalStateException(NOT_ENOUGH_BALANCE_ERROR);
+ throw new IllegalStateException(Constants.NOT_ENOUGH_BALANCE_ERROR);
}
return addRemainder(seed, security, confirmedInputs, bundle, tag, totalValue, remainder, signatureFragments);
@@ -541,17 +636,17 @@ public GetBalancesAndFormatResponse getInputs(String seed, int security, int sta
// validate the seed
if ((!InputValidator.isValidSeed(seed))) {
- throw new IllegalStateException(INVALID_SEED_INPUT_ERROR);
+ throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR);
}
- if (security < 1) {
- throw new ArgumentException(INVALID_SECURITY_LEVEL_INPUT_ERROR);
+ if (!InputValidator.isValidSecurityLevel(security)) {
+ throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR);
}
// 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_INPUT_ERROR);
+ if ((start > end && end > 0) || end > (start + 500)) {
+ throw new IllegalStateException(Constants.INVALID_INPUT_ERROR);
}
StopWatch stopWatch = new StopWatch();
@@ -597,8 +692,8 @@ public GetBalancesAndFormatResponse getInputs(String seed, int security, int sta
**/
public GetBalancesAndFormatResponse getBalanceAndFormat(final List addresses, final List tips, long threshold, int start, StopWatch stopWatch, int security) throws ArgumentException, IllegalStateException {
- if (security < 1) {
- throw new ArgumentException(INVALID_SECURITY_LEVEL_INPUT_ERROR);
+ if (!InputValidator.isValidSecurityLevel(security)) {
+ throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR);
}
GetBalancesResponse getBalancesResponse = getBalances(100, addresses, tips);
@@ -633,7 +728,7 @@ public GetBalancesAndFormatResponse getBalanceAndFormat(final List addre
if (thresholdReached) {
return GetBalancesAndFormatResponse.create(inputs, totalBalance, stopWatch.getElapsedTimeMili());
}
- throw new IllegalStateException(NOT_ENOUGH_BALANCE_ERROR);
+ throw new IllegalStateException(Constants.NOT_ENOUGH_BALANCE_ERROR);
}
/**
@@ -647,84 +742,20 @@ public GetBalancesAndFormatResponse getBalanceAndFormat(final List addre
public GetBundleResponse getBundle(String transaction) throws ArgumentException {
if (!InputValidator.isHash(transaction)) {
- throw new ArgumentException(INVALID_HASHES_INPUT_ERROR);
- }
-
- Bundle bundle = traverseBundle(transaction, null, new Bundle());
- if (bundle == null) {
- throw new ArgumentException(INVALID_BUNDLE_ERROR);
+ throw new ArgumentException(Constants.INVALID_HASHES_INPUT_ERROR);
}
-
+
StopWatch stopWatch = new StopWatch();
-
- long totalSum = 0;
- String bundleHash = bundle.getTransactions().get(0).getBundle();
- ICurl curl = SpongeFactory.create(SpongeFactory.Mode.KERL);
- curl.reset();
-
- List signaturesToValidate = new ArrayList<>();
-
- for (int i = 0; i < bundle.getTransactions().size(); i++) {
- Transaction trx = bundle.getTransactions().get(i);
- Long bundleValue = trx.getValue();
- totalSum += bundleValue;
-
- if (i != bundle.getTransactions().get(i).getCurrentIndex()) {
- throw new ArgumentException(INVALID_BUNDLE_ERROR);
- }
-
- String trxTrytes = trx.toTrytes().substring(2187, 2187 + 162);
- // Absorb bundle hash + value + timestamp + lastIndex + currentIndex trytes.
- curl.absorb(Converter.trits(trxTrytes));
- // Check if input transaction
- if (bundleValue < 0) {
- String address = trx.getAddress();
- Signature sig = new Signature();
- sig.setAddress(address);
- sig.getSignatureFragments().add(trx.getSignatureFragments());
-
- // Find the subsequent txs with the remaining signature fragment
- for (int y = i + 1; y < bundle.getTransactions().size(); y++) {
- Transaction newBundleTx = bundle.getTransactions().get(y);
-
- // Check if new tx is part of the signature fragment
- if (newBundleTx.getAddress().equals(address) && newBundleTx.getValue() == 0) {
- if (sig.getSignatureFragments().indexOf(newBundleTx.getSignatureFragments()) == -1)
- sig.getSignatureFragments().add(newBundleTx.getSignatureFragments());
- }
- }
- signaturesToValidate.add(sig);
- }
+ Bundle bundle = traverseBundle(transaction, null, new Bundle());
+ if (bundle == null) {
+ throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR);
}
- // Check for total sum, if not equal 0 return error
- if (totalSum != 0)
- throw new ArgumentException(INVALID_BUNDLE_SUM_ERROR);
- int[] bundleFromTrxs = new int[243];
- curl.squeeze(bundleFromTrxs);
- String bundleFromTxString = Converter.trytes(bundleFromTrxs);
-
- // Check if bundle hash is the same as returned by tx object
- if (!bundleFromTxString.equals(bundleHash)) {
- throw new ArgumentException(INVALID_BUNDLE_HASH_ERROR);
- }
+ if (!BundleValidator.isBundle(bundle)){
+ throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR);
+ }
- // Last tx in the bundle should have currentIndex === lastIndex
- bundle.setLength(bundle.getTransactions().size());
- if (!(bundle.getTransactions().get(bundle.getLength() - 1).getCurrentIndex() == (bundle.getTransactions().get(bundle.getLength() - 1).getLastIndex())))
- throw new ArgumentException(INVALID_BUNDLE_ERROR);
-
- // Validate the signatures
- for (Signature aSignaturesToValidate : signaturesToValidate) {
- String[] signatureFragments = aSignaturesToValidate.getSignatureFragments().toArray(new String[aSignaturesToValidate.getSignatureFragments().size()]);
- String address = aSignaturesToValidate.getAddress();
- boolean isValidSignature = new Signing(customCurl.clone()).validateSignatures(address, signatureFragments, bundleHash);
-
- if (!isValidSignature)
- throw new ArgumentException(INVALID_SIGNATURES_ERROR);
- }
-
return GetBundleResponse.create(bundle.getTransactions(), stopWatch.getElapsedTimeMili());
}
@@ -744,9 +775,12 @@ public GetBundleResponse getBundle(String transaction) throws ArgumentException
* @throws ArgumentException is thrown when the specified input is not valid.
*/
public GetAccountDataResponse getAccountData(String seed, int security, int index, boolean checksum, int total, boolean returnAll, int start, int end, boolean inclusionStates, long threshold) throws ArgumentException {
-
+ if (!InputValidator.isValidSecurityLevel(security)) {
+ throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR);
+ }
+
if (start > end || end > (start + 1000)) {
- throw new ArgumentException(INVALID_INPUT_ERROR);
+ throw new ArgumentException(Constants.INVALID_INPUT_ERROR);
}
StopWatch stopWatch = new StopWatch();
@@ -813,7 +847,7 @@ public Boolean checkWereAddressSpentFrom(String address) throws ArgumentExceptio
public ReplayBundleResponse replayBundle(String tailTransactionHash, int depth, int minWeightMagnitude, String reference) throws ArgumentException {
if (!InputValidator.isHash(tailTransactionHash)) {
- throw new ArgumentException(INVALID_TAIL_HASH_INPUT_ERROR);
+ throw new ArgumentException(Constants.INVALID_TAIL_HASH_INPUT_ERROR);
}
StopWatch stopWatch = new StopWatch();
@@ -904,8 +938,8 @@ public SendTransferResponse sendTransfer(String seed, int security, int depth, i
* transaction hash is not a tail, we return an error.
*
* @param trunkTx Hash of a trunk or a tail transaction of a bundle.
- * @param bundleHash The bundle hashes.
- * @param bundle List of bundles to be populated.
+ * @param bundleHash The bundle hash.
+ * @param bundle bundle to be populated.
* @return Transaction objects.
* @throws ArgumentException is thrown when an invalid input is provided.
*/
@@ -915,16 +949,16 @@ public Bundle traverseBundle(String trunkTx, String bundleHash, Bundle bundle) t
if (gtr != null) {
if (gtr.getTrytes().length == 0) {
- throw new ArgumentException(INVALID_BUNDLE_ERROR);
+ throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR);
}
Transaction trx = new Transaction(gtr.getTrytes()[0], customCurl.clone());
if (trx.getBundle() == null) {
- throw new ArgumentException(INVALID_TRYTES_INPUT_ERROR);
+ throw new ArgumentException(Constants.INVALID_TRYTES_INPUT_ERROR);
}
// If first transaction to search is not a tail, return error
if (bundleHash == null && trx.getCurrentIndex() != 0) {
- throw new ArgumentException(INVALID_TAIL_HASH_INPUT_ERROR);
+ throw new ArgumentException(Constants.INVALID_TAIL_HASH_INPUT_ERROR);
}
// If no bundle hash, define it
if (bundleHash == null) {
@@ -932,6 +966,7 @@ public Bundle traverseBundle(String trunkTx, String bundleHash, Bundle bundle) t
}
// If different bundle hash, return with bundle
if (!bundleHash.equals(trx.getBundle())) {
+ bundle.setLength(bundle.getTransactions().size());
return bundle;
}
// If only one bundle element, return
@@ -946,7 +981,7 @@ public Bundle traverseBundle(String trunkTx, String bundleHash, Bundle bundle) t
// Continue traversing with new trunkTx
return traverseBundle(trunkTx, bundleHash, bundle);
} else {
- throw new ArgumentException(GET_TRYTES_RESPONSE_ERROR);
+ throw new ArgumentException(Constants.GET_TRYTES_RESPONSE_ERROR);
}
}
@@ -1020,19 +1055,22 @@ public List initiateTransfer(int securitySum, String inputAddress,
public List initiateTransfer(int securitySum, String inputAddress, String remainderAddress,
List transfers, List tips,
boolean testMode) throws ArgumentException {
-
+ if (securitySum < Constants.MIN_SECURITY_LEVEL) {
+ throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR);
+ }
+
// validate input address
if (!InputValidator.isAddress(inputAddress))
- throw new ArgumentException(INVALID_ADDRESSES_INPUT_ERROR);
+ throw new ArgumentException(Constants.INVALID_ADDRESSES_INPUT_ERROR);
// validate remainder address
if (remainderAddress != null && !InputValidator.isAddress(remainderAddress)) {
- throw new ArgumentException(INVALID_ADDRESSES_INPUT_ERROR);
+ throw new ArgumentException(Constants.INVALID_ADDRESSES_INPUT_ERROR);
}
// Input validation of transfers object
if (!InputValidator.isTransfersCollectionValid(transfers)) {
- throw new ArgumentException(INVALID_TRANSFERS_INPUT_ERROR);
+ throw new ArgumentException(Constants.INVALID_TRANSFERS_INPUT_ERROR);
}
// Create a new bundle
@@ -1145,7 +1183,7 @@ public List initiateTransfer(int securitySum, String inputAddress,
}
// Return not enough balance error
if (totalValue > totalBalance) {
- throw new IllegalStateException(NOT_ENOUGH_BALANCE_ERROR);
+ throw new IllegalStateException(Constants.NOT_ENOUGH_BALANCE_ERROR);
}
// If there is a remainder value
@@ -1156,7 +1194,7 @@ public List initiateTransfer(int securitySum, String inputAddress,
// Remainder bundle entry if necessary
if (remainderAddress == null) {
- throw new IllegalStateException(NO_REMAINDER_ADDRESS_ERROR);
+ throw new IllegalStateException(Constants.NO_REMAINDER_ADDRESS_ERROR);
}
bundle.addEntry(1, remainderAddress, remainder, tag, timestamp);
@@ -1167,7 +1205,7 @@ public List initiateTransfer(int securitySum, String inputAddress,
return bundle.getTransactions();
} else {
- throw new RuntimeException(INVALID_VALUE_TRANSFER_ERROR);
+ throw new RuntimeException(Constants.INVALID_VALUE_TRANSFER_ERROR);
}
}
@@ -1282,10 +1320,47 @@ public List addRemainder(final String seed,
totalTransferValue -= thisBalance;
}
}
- throw new IllegalStateException(NOT_ENOUGH_BALANCE_ERROR);
+ throw new IllegalStateException(Constants.NOT_ENOUGH_BALANCE_ERROR);
}
-
+ /**
+ * Checks if a transaction hash is promotable
+ * @param tail the transaction we want to promote
+ * @return true if it is, otherwise false
+ * @throws ArgumentException when we can't get the consistency of this transaction
+ */
+ public boolean isPromotable(Transaction tail) throws ArgumentException {
+ long lowerBound = tail.getAttachmentTimestamp();
+ CheckConsistencyResponse consistencyResponse = checkConsistency(tail.getHash());
+
+ return consistencyResponse.getState() && isAboveMaxDepth(lowerBound);
+ }
+
+ /**
+ * Checks if a transaction hash is promotable
+ * @param tail the transaction hash we want to check
+ * @return true if it is, otherwise false
+ * @throws ArgumentException when we can't get the consistency of this transaction
+ * or when the transaction is not found
+ */
+ public boolean isPromotable(String tail) throws ArgumentException {
+ GetTrytesResponse transaction = getTrytes(tail);
+ if (0 == transaction.getTrytes().length) {
+ throw new ArgumentException(Constants.TRANSACTION_NOT_FOUND);
+ }
+
+ return isPromotable(new Transaction(transaction.getTrytes()[0]));
+ }
+
+ private boolean isAboveMaxDepth (long attachmentTimestamp) {
+ // Check against future timestamps
+ return attachmentTimestamp < System.currentTimeMillis() &&
+ // Check if transaction wasn't issued before last 6 milestones
+ // Milestones are being issued every ~2mins
+ System.currentTimeMillis() - attachmentTimestamp < 11 * 60 * 1000;
+ }
+
+
/**
* Attempts to promote a transaction using a provided bundle and, if successful, returns the promoting Transactions.
*
@@ -1322,7 +1397,7 @@ public List promoteTransaction(final String tail, final int depth,
bundle.getTransactions().stream().map(tx -> tx.toTrytes()).toArray(String[]::new));
try {
- broadcastAndStore(res.getTrytes());
+ storeAndBroadcast(res.getTrytes());
} catch (ArgumentException e) {
return Collections.emptyList();
}
diff --git a/jota/src/main/java/jota/dto/response/GetNewAddressResponse.java b/jota/src/main/java/jota/dto/response/GetNewAddressResponse.java
index d9fc5adc..e2ce8d79 100644
--- a/jota/src/main/java/jota/dto/response/GetNewAddressResponse.java
+++ b/jota/src/main/java/jota/dto/response/GetNewAddressResponse.java
@@ -27,4 +27,12 @@ public static GetNewAddressResponse create(List addresses, long duration
public List getAddresses() {
return addresses;
}
+
+ /**
+ * Gets the first address, for quick access
+ * @return The address
+ */
+ public String first() {
+ return addresses.get(0);
+ }
}
diff --git a/jota/src/main/java/jota/dto/response/GetNodeInfoResponse.java b/jota/src/main/java/jota/dto/response/GetNodeInfoResponse.java
index 132a4559..e23a1298 100644
--- a/jota/src/main/java/jota/dto/response/GetNodeInfoResponse.java
+++ b/jota/src/main/java/jota/dto/response/GetNodeInfoResponse.java
@@ -21,6 +21,8 @@ public class GetNodeInfoResponse extends AbstractResponse {
private long time;
private int tips;
private int transactionsToRequest;
+
+ private String[] features;
/**
* The name of the IOTA software the node currently running (IRI stands for Initial Reference Implementation).
@@ -167,4 +169,12 @@ public int getTips() {
public int getTransactionsToRequest() {
return transactionsToRequest;
}
+
+ /**
+ *
+ * @return
+ */
+ public String[] getFeatures() {
+ return features;
+ }
}
\ No newline at end of file
diff --git a/jota/src/main/java/jota/error/BaseException.java b/jota/src/main/java/jota/error/BaseException.java
index b1f91cc0..6a4ce901 100644
--- a/jota/src/main/java/jota/error/BaseException.java
+++ b/jota/src/main/java/jota/error/BaseException.java
@@ -4,9 +4,6 @@
import java.util.Arrays;
import java.util.Collection;
-/**
- * @author Adrian
- */
public class BaseException extends Exception {
/**
diff --git a/jota/src/main/java/jota/model/Bundle.java b/jota/src/main/java/jota/model/Bundle.java
index 3ed38406..a5bde369 100644
--- a/jota/src/main/java/jota/model/Bundle.java
+++ b/jota/src/main/java/jota/model/Bundle.java
@@ -3,6 +3,8 @@
import jota.pow.ICurl;
import jota.pow.SpongeFactory;
import jota.utils.Converter;
+import jota.utils.Signing;
+
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
@@ -12,7 +14,6 @@
/**
* This class represents a Bundle, a set of transactions.
*
- * @author pinpong
**/
public class Bundle implements Comparable {
@@ -25,7 +26,7 @@ public class Bundle implements Comparable {
* Initializes a new instance of the Bundle class without transactions.
*/
public Bundle() {
- this(new ArrayList(), 0);
+ this(new ArrayList<>(), 0);
}
/**
@@ -181,41 +182,7 @@ public void addTrytes(List signatureFragments) {
* @return normalizedBundle A normalized bundle hash.
*/
public int[] normalizedBundle(String bundleHash) {
- int[] normalizedBundle = new int[81];
-
- for (int i = 0; i < 3; i++) {
-
- long sum = 0;
- for (int j = 0; j < 27; j++) {
-
- sum += (normalizedBundle[i * 27 + j] = Converter.value(Converter.tritsString("" + bundleHash.charAt(i * 27 + j))));
- }
-
- if (sum >= 0) {
- while (sum-- > 0) {
- for (int j = 0; j < 27; j++) {
- if (normalizedBundle[i * 27 + j] > -13) {
- normalizedBundle[i * 27 + j]--;
- break;
- }
- }
- }
- } else {
-
- while (sum++ < 0) {
-
- for (int j = 0; j < 27; j++) {
-
- if (normalizedBundle[i * 27 + j] < 13) {
- normalizedBundle[i * 27 + j]++;
- break;
- }
- }
- }
- }
- }
-
- return normalizedBundle;
+ return new Signing().normalizedBundle(bundleHash);
}
diff --git a/jota/src/main/java/jota/model/Input.java b/jota/src/main/java/jota/model/Input.java
index 0e33c38c..285e3f19 100644
--- a/jota/src/main/java/jota/model/Input.java
+++ b/jota/src/main/java/jota/model/Input.java
@@ -6,7 +6,6 @@
/**
* This class represents an Input.
*
- * @author Adrian
**/
public class Input {
diff --git a/jota/src/main/java/jota/model/Inputs.java b/jota/src/main/java/jota/model/Inputs.java
index 8d142e10..11a05525 100644
--- a/jota/src/main/java/jota/model/Inputs.java
+++ b/jota/src/main/java/jota/model/Inputs.java
@@ -47,7 +47,7 @@ public List getInputsList() {
* @param inputsList The input list.
*/
public void setInputsList(List inputsList) {
- inputsList = inputsList;
+ this.inputsList = inputsList;
}
/**
diff --git a/jota/src/main/java/jota/model/Neighbor.java b/jota/src/main/java/jota/model/Neighbor.java
index 48daa618..2bbe89eb 100644
--- a/jota/src/main/java/jota/model/Neighbor.java
+++ b/jota/src/main/java/jota/model/Neighbor.java
@@ -3,7 +3,6 @@
/**
* This class represents an Neighbor.
*
- * @author pinpong
**/
public class Neighbor {
diff --git a/jota/src/main/java/jota/model/Signature.java b/jota/src/main/java/jota/model/Signature.java
index 02f9b014..7e985a8d 100644
--- a/jota/src/main/java/jota/model/Signature.java
+++ b/jota/src/main/java/jota/model/Signature.java
@@ -6,7 +6,6 @@
/**
* This class represents an Signature.
*
- * @author Adrian
**/
public class Signature {
diff --git a/jota/src/main/java/jota/model/Transaction.java b/jota/src/main/java/jota/model/Transaction.java
index c9782dfe..b74933a9 100644
--- a/jota/src/main/java/jota/model/Transaction.java
+++ b/jota/src/main/java/jota/model/Transaction.java
@@ -2,7 +2,10 @@
import jota.pow.ICurl;
import jota.pow.SpongeFactory;
+import jota.utils.Constants;
import jota.utils.Converter;
+import jota.utils.InputValidator;
+
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@@ -14,7 +17,6 @@
/**
* This class represents an iota transaction.
*
- * @author pinpong
*/
public class Transaction {
@@ -39,6 +41,28 @@ public class Transaction {
private String tag;
private long attachmentTimestampLowerBound;
private long attachmentTimestampUpperBound;
+
+ /**
+ * Converts an array of transaction trytes into an array of transaction objects.
+ * @param trytes the array of transactions trytes
+ * @return the transaction objects
+ */
+ public static Transaction[] asTransactionObjects(String... trytes) {
+ Transaction[] transactions = new Transaction[trytes.length];
+ for (int i = 0; i < trytes.length; i++) {
+ transactions[i] = asTransactionObject(trytes[i]);
+ }
+ return transactions;
+ }
+
+ /**
+ * Converts transaction trytes into a transaction object.
+ * @param trytes the transaction trytes
+ * @return the transaction object
+ */
+ public static Transaction asTransactionObject(String trytes) {
+ return new Transaction(trytes);
+ }
/**
* Initializes a new instance of the Signature class.
@@ -73,15 +97,7 @@ public Transaction(String address, long value, String tag, long timestamp) {
this.obsoleteTag = tag;
this.timestamp = timestamp;
}
-
- public long getAttachmentTimestampLowerBound() {
- return attachmentTimestampLowerBound;
- }
-
- public void setAttachmentTimestampLowerBound(long attachmentTimestampLowerBound) {
- this.attachmentTimestampLowerBound = attachmentTimestampLowerBound;
- }
-
+
/**
* Initializes a new instance of the Signature class.
*/
@@ -110,6 +126,14 @@ public Transaction(String trytes, ICurl customCurl) {
transactionObject(trytes);
this.customCurl = customCurl;
}
+
+ public long getAttachmentTimestampLowerBound() {
+ return attachmentTimestampLowerBound;
+ }
+
+ public void setAttachmentTimestampLowerBound(long attachmentTimestampLowerBound) {
+ this.attachmentTimestampLowerBound = attachmentTimestampLowerBound;
+ }
public long getAttachmentTimestampUpperBound() {
return attachmentTimestampUpperBound;
@@ -452,15 +476,13 @@ public void transactionObject(final String trytes) {
}
// 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;
- }
+ if (!InputValidator.isNinesTrytes(trytes.substring(2279, 2295), 16)) {
+ log.warn("Trytes {} does not seem a valid tryte", trytes);
+ return;
}
int[] transactionTrits = Converter.trits(trytes);
- int[] hash = new int[243];
+ int[] hash = new int[Constants.HASH_LENGTH_TRITS];
ICurl curl = SpongeFactory.create(SpongeFactory.Mode.CURLP81);
// generate the correct transaction hash
@@ -469,20 +491,28 @@ public void transactionObject(final String trytes) {
curl.squeeze(hash, 0, hash.length);
this.setHash(Converter.trytes(hash));
- this.setSignatureFragments(trytes.substring(0, 2187));
- this.setAddress(trytes.substring(2187, 2268));
+ this.setSignatureFragments(trytes.substring(0, Constants.MESSAGE_LENGTH));
+ this.setAddress(trytes.substring(Constants.MESSAGE_LENGTH, Constants.MESSAGE_LENGTH + Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM));
this.setValue(Converter.longValue(Arrays.copyOfRange(transactionTrits, 6804, 6837)));
- this.setObsoleteTag(trytes.substring(2295, 2322));
+ this.setObsoleteTag(trytes.substring(2295, 2295 + Constants.TAG_LENGTH));
this.setTimestamp(Converter.longValue(Arrays.copyOfRange(transactionTrits, 6966, 6993)));
this.setCurrentIndex(Converter.longValue(Arrays.copyOfRange(transactionTrits, 6993, 7020)));
this.setLastIndex(Converter.longValue(Arrays.copyOfRange(transactionTrits, 7020, 7047)));
- this.setBundle(trytes.substring(2349, 2430));
- this.setTrunkTransaction(trytes.substring(2430, 2511));
- this.setBranchTransaction(trytes.substring(2511, 2592));
- this.setTag(trytes.substring(2592, 2619));
+ this.setBundle(trytes.substring(2349, 2349 + Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM));
+ this.setTrunkTransaction(trytes.substring(2430, 2430 + Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM));
+ this.setBranchTransaction(trytes.substring(2511, 2511 + Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM));
+ this.setTag(trytes.substring(2592, 2592 + Constants.TAG_LENGTH));
this.setAttachmentTimestamp(Converter.longValue(Arrays.copyOfRange(transactionTrits, 7857, 7884)));
this.setAttachmentTimestampLowerBound(Converter.longValue(Arrays.copyOfRange(transactionTrits, 7884, 7911)));
this.setAttachmentTimestampUpperBound(Converter.longValue(Arrays.copyOfRange(transactionTrits, 7911, 7938)));
this.setNonce(trytes.substring(2646, 2673));
}
+
+ /**
+ * Checks if the current index is 0
+ * @return if this is a tail transaction
+ */
+ public boolean isTailTransaction() {
+ return getCurrentIndex() == 0;
+ }
}
diff --git a/jota/src/main/java/jota/model/Transfer.java b/jota/src/main/java/jota/model/Transfer.java
index 0b33b1bb..ffffd9b0 100644
--- a/jota/src/main/java/jota/model/Transfer.java
+++ b/jota/src/main/java/jota/model/Transfer.java
@@ -5,7 +5,6 @@
/**
* This class represents a Transfer.
*
- * @author pinpong
*/
public class Transfer {
diff --git a/jota/src/main/java/cfb/pearldiver/PearlDiver.java b/jota/src/main/java/jota/pow/pearldiver/PearlDiver.java
similarity index 98%
rename from jota/src/main/java/cfb/pearldiver/PearlDiver.java
rename to jota/src/main/java/jota/pow/pearldiver/PearlDiver.java
index 5ec91f66..42358fbc 100644
--- a/jota/src/main/java/cfb/pearldiver/PearlDiver.java
+++ b/jota/src/main/java/jota/pow/pearldiver/PearlDiver.java
@@ -1,9 +1,9 @@
-package cfb.pearldiver;
+package jota.pow.pearldiver;
-import static cfb.pearldiver.PearlDiver.State.CANCELLED;
-import static cfb.pearldiver.PearlDiver.State.COMPLETED;
-import static cfb.pearldiver.PearlDiver.State.RUNNING;
import static jota.pow.JCurl.NUMBER_OF_ROUNDSP81;
+import static jota.pow.pearldiver.PearlDiver.State.CANCELLED;
+import static jota.pow.pearldiver.PearlDiver.State.COMPLETED;
+import static jota.pow.pearldiver.PearlDiver.State.RUNNING;
/**
diff --git a/jota/src/main/java/cfb/pearldiver/PearlDiverLocalPoW.java b/jota/src/main/java/jota/pow/pearldiver/PearlDiverLocalPoW.java
similarity index 95%
rename from jota/src/main/java/cfb/pearldiver/PearlDiverLocalPoW.java
rename to jota/src/main/java/jota/pow/pearldiver/PearlDiverLocalPoW.java
index a470be68..7d2d8112 100644
--- a/jota/src/main/java/cfb/pearldiver/PearlDiverLocalPoW.java
+++ b/jota/src/main/java/jota/pow/pearldiver/PearlDiverLocalPoW.java
@@ -1,4 +1,4 @@
-package cfb.pearldiver;
+package jota.pow.pearldiver;
import jota.IotaLocalPoW;
import jota.utils.Converter;
diff --git a/jota/src/main/java/jota/utils/BundleValidator.java b/jota/src/main/java/jota/utils/BundleValidator.java
new file mode 100644
index 00000000..2eb2f04f
--- /dev/null
+++ b/jota/src/main/java/jota/utils/BundleValidator.java
@@ -0,0 +1,145 @@
+package jota.utils;
+
+import static jota.utils.Constants.INVALID_BUNDLE_ERROR;
+import static jota.utils.Constants.INVALID_BUNDLE_HASH_ERROR;
+import static jota.utils.Constants.INVALID_BUNDLE_SUM_ERROR;
+import static jota.utils.Constants.INVALID_SIGNATURES_ERROR;
+
+import static jota.pow.JCurl.HASH_LENGTH;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import jota.error.ArgumentException;
+import jota.model.Bundle;
+import jota.model.Transaction;
+import jota.pow.ICurl;
+import jota.pow.SpongeFactory;
+
+public class BundleValidator {
+
+ /**
+ * Validates all signatures of a bundle
+ * @param bundle the bundle
+ * @param bundleHash
+ * @param customCurl
+ * @return true if all signatures are valid. Otherwise false
+ */
+ public static boolean validateSignatures(Bundle bundle, ICurl customCurl) {
+ for (int i = 0; i < bundle.getLength(); i++) {
+ Transaction tx = bundle.getTransactions().get(i);
+
+ // check whether input transaction
+ if (tx.getValue() >= 0) {
+ continue;
+ }
+
+ List fragments = new ArrayList<>();
+ fragments.add(tx.getSignatureFragments());
+
+ // find the subsequent txs containing the remaining signature
+ // message fragments for this input transaction
+ for (int j = i; j < bundle.getLength()-1; j++ ){
+ Transaction otherTx = bundle.getTransactions().get(j+1);
+ if (otherTx.getValue() != 0 || !otherTx.getAddress().equals(tx.getAddress())) {
+ continue;
+ }
+
+ fragments.add(otherTx.getSignatureFragments());
+ }
+
+ boolean valid = new Signing(customCurl).validateSignatures(tx.getAddress(), fragments.toArray(new String[fragments.size()]), tx.getBundle());
+ if (!valid) {
+ return false;
+ }
+
+ }
+ return true;
+ }
+
+ /**
+ * Checks if a bundle is syntactically valid.
+ * Validates signatures and overall structure
+ * @param bundle the bundle to verify
+ * @return true if the bundle is valid.
+ * @throws ArgumentException if there is an error with the bundle
+ */
+ public static boolean isBundle(Bundle bundle) throws ArgumentException {
+ return isBundle(bundle, null);
+ }
+
+ /**
+ * Checks if a bundle is syntactically valid.
+ * Validates signatures and overall structure
+ * @param bundle the bundle to verify
+ * @param customCurlMode
+ * @return true if the bundle is valid.
+ * @throws ArgumentException if there is an error with the bundle
+ */
+ public static boolean isBundle(Bundle bundle, Optional customCurlMode) throws ArgumentException {
+ ICurl curl = null;
+ if (null == customCurlMode || !customCurlMode.isPresent()) {
+ curl = SpongeFactory.create(SpongeFactory.Mode.KERL);
+ } else {
+ curl = SpongeFactory.create(customCurlMode.get());
+ }
+
+ int totalSum = 0;
+ int lastIndex = bundle.getLength() - 1;
+
+ for (int i = 0; i < bundle.getLength(); i++) {
+ Transaction tx = bundle.getTransactions().get(i);
+ totalSum += tx.getValue();
+
+ if (tx.getCurrentIndex() != i ){
+ throw new ArgumentException(INVALID_BUNDLE_ERROR);
+ }
+ if (tx.getLastIndex() != lastIndex ){
+ throw new ArgumentException(INVALID_BUNDLE_ERROR);
+ }
+
+ int[] txTrits = Converter.trits(tx.toTrytes().substring(2187, 2187 + 162));
+ curl.absorb(txTrits);
+
+ // continue if output or signature tx
+ if (tx.getValue() >= 0 ){
+ continue;
+ }
+
+ // here we have an input transaction (negative value)
+ List fragments = new ArrayList<>();
+ fragments.add(tx.getSignatureFragments());
+
+ // find the subsequent txs containing the remaining signature
+ // message fragments for this input transaction
+ for (int j = i; j < bundle.getLength()-1; j++) {
+ Transaction tx2 = bundle.getTransactions().get(j+1);
+
+ // check if the tx is part of the input transaction
+ if (tx.getAddress().equals(tx2.getAddress()) && tx2.getValue() == 0) {
+ // append the signature message fragment
+ fragments.add(tx2.getSignatureFragments());
+ }
+ }
+ boolean valid = new Signing(curl.clone()).validateSignatures(tx.getAddress(), fragments.toArray(new String[fragments.size()]), tx.getBundle());
+ if (!valid) {
+ throw new ArgumentException(INVALID_SIGNATURES_ERROR);
+ }
+ }
+
+ // sum of all transaction must be 0
+ if (totalSum != 0) {
+ throw new ArgumentException(INVALID_BUNDLE_SUM_ERROR);
+ }
+
+ int[] bundleHashTrits = new int[HASH_LENGTH];
+ curl.squeeze(bundleHashTrits, 0, HASH_LENGTH);
+ String bundleHash = Converter.trytes(bundleHashTrits);
+
+ if (!bundleHash.equals(bundle.getTransactions().get(0).getBundle())) {
+ throw new ArgumentException(INVALID_BUNDLE_HASH_ERROR);
+ }
+ return true;
+ }
+}
diff --git a/jota/src/main/java/jota/utils/Constants.java b/jota/src/main/java/jota/utils/Constants.java
index 3b40a237..f184e64f 100644
--- a/jota/src/main/java/jota/utils/Constants.java
+++ b/jota/src/main/java/jota/utils/Constants.java
@@ -3,9 +3,13 @@
/**
* This class defines the global constants.
*
- * @author pinpong
*/
public class Constants {
+
+ /**
+ *
+ */
+ public final static int KEY_LENGTH = 6561;
/**
* This String contains all possible characters of the tryte alphabet
@@ -16,43 +20,67 @@ public class Constants {
* The maximum seed length
*/
public static final int SEED_LENGTH_MAX = 81;
+
+ /**
+ * The length of a hash in trits
+ */
+ public static int HASH_LENGTH_TRITS = 243;
/**
- * The length of an address without checksum
+ * The length of an address without checksum in trytes
*/
public static int ADDRESS_LENGTH_WITHOUT_CHECKSUM = 81;
/**
- * The length of an address with checksum
+ * The length of an address with checksum in trytes
*/
public static int ADDRESS_LENGTH_WITH_CHECKSUM = 90;
/**
- * The length of an message
+ * The length of a message
*/
public static int MESSAGE_LENGTH = 2187;
+
+ /**
+ * Size of a full transaction, whether it has been attached or not.
+ */
+ public static int TRANSACTION_SIZE = 2673;
/**
* The length of an tag
*/
public static int TAG_LENGTH = 27;
+
+ /**
+ * Maximum security level of an address
+ */
+ public static int MIN_SECURITY_LEVEL = 1;
+
+ /**
+ * Minimum security level of an address
+ */
+ public static int MAX_SECURITY_LEVEL = 3;
public static final String INVALID_TRYTES_INPUT_ERROR = "Invalid trytes provided.";
public static final String INVALID_HASHES_INPUT_ERROR = "Invalid hashes provided.";
public static final String INVALID_TAIL_HASH_INPUT_ERROR = "Invalid tail hash provided.";
public static final String INVALID_SEED_INPUT_ERROR = "Invalid seed provided.";
public static final String INVALID_SECURITY_LEVEL_INPUT_ERROR = "Invalid security level provided.";
+ public static final String INVALID_INDEX_INPUT_ERROR = "Invalid index provided.";
public static final String INVALID_ATTACHED_TRYTES_INPUT_ERROR = "Invalid attached trytes provided.";
public static final String INVALID_TRANSFERS_INPUT_ERROR = "Invalid transfers provided.";
public static final String INVALID_ADDRESSES_INPUT_ERROR = "Invalid addresses provided.";
public static final String INVALID_INPUT_ERROR = "Invalid input provided.";
-
+ public static final String INVALID_TAG_ERROR = "Invalid tag provided.";
+
public static final String INVALID_BUNDLE_ERROR = "Invalid bundle.";
public static final String INVALID_BUNDLE_SUM_ERROR = "Invalid bundle sum.";
public static final String INVALID_BUNDLE_HASH_ERROR = "Invalid bundle hash.";
public static final String INVALID_SIGNATURES_ERROR = "Invalid signatures.";
public static final String INVALID_VALUE_TRANSFER_ERROR = "Invalid value transfer: the transfer does not require a signature.";
+ public static final String TRANSACTION_NOT_FOUND = "Transaction was not found on the node";
+
public static final String NOT_ENOUGH_BALANCE_ERROR = "Not enough balance.";
public static final String NO_REMAINDER_ADDRESS_ERROR = "No remainder address defined.";
diff --git a/jota/src/main/java/jota/utils/Converter.java b/jota/src/main/java/jota/utils/Converter.java
index b86fecc9..cfd87e54 100644
--- a/jota/src/main/java/jota/utils/Converter.java
+++ b/jota/src/main/java/jota/utils/Converter.java
@@ -197,9 +197,9 @@ public static int[] copyTrits(final String input, final int[] destination) {
}
/**
- * Converts trites to trytes.
+ * Converts trits to trytes.
*
- * @param trits Teh trits to be converted.
+ * @param trits The trits to be converted.
* @param offset The offset to start from.
* @param size The size.
* @return The trytes.
@@ -248,6 +248,45 @@ public static int value(final int[] trits) {
}
return value;
}
+
+ /**
+ * Converts the specified integer to its corresponding trits value.
+ *
+ * @param value the integer we want to convert
+ * @return The array of trits
+ */
+ public static int[] fromValue(int value) {
+ if (0 == value) {
+ return new int[] {0};
+ }
+
+ int[] destination = new int[
+ (int) (1 + Math.floor(Math.log(2 * Math.max(1, Math.abs(value))) / Math.log(3)))
+ ];
+
+ int i = 0;
+ int absoluteValue = value < 0 ? -value : value;
+ while (absoluteValue > 0) {
+ int remainder = absoluteValue % RADIX;
+ absoluteValue = (int) Math.floor(absoluteValue / RADIX);
+
+ if (remainder > MAX_TRIT_VALUE) {
+ remainder = MIN_TRIT_VALUE;
+ absoluteValue++;
+ }
+
+ destination[i] = remainder;
+ i++;
+ }
+
+ if (value < 0) {
+ for (int j = 0; j < destination.length; j++) {
+ destination[j] = -destination[j];
+ }
+ }
+
+ return destination;
+ }
/**
* Converts the specified trits to its corresponding integer value.
diff --git a/jota/src/main/java/jota/utils/ExtractJson.java b/jota/src/main/java/jota/utils/ExtractJson.java
new file mode 100644
index 00000000..ea1ece6e
--- /dev/null
+++ b/jota/src/main/java/jota/utils/ExtractJson.java
@@ -0,0 +1,15 @@
+package jota.utils;
+
+import com.google.gson.JsonObject;
+
+import jota.model.Bundle;
+
+public class ExtractJson {
+ public static JsonObject fromBundle(Bundle bundle) {
+ JsonObject json = new JsonObject();
+
+
+
+ return json;
+ }
+}
diff --git a/jota/src/main/java/jota/utils/InputValidator.java b/jota/src/main/java/jota/utils/InputValidator.java
index 9f636ad9..dac05302 100644
--- a/jota/src/main/java/jota/utils/InputValidator.java
+++ b/jota/src/main/java/jota/utils/InputValidator.java
@@ -1,18 +1,21 @@
package jota.utils;
import jota.error.ArgumentException;
+import jota.model.Input;
import jota.model.Transfer;
import org.apache.commons.lang3.math.NumberUtils;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.List;
import static jota.utils.Constants.INVALID_ADDRESSES_INPUT_ERROR;
+import static jota.utils.Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR;
import static jota.utils.Constants.INVALID_TRANSFERS_INPUT_ERROR;
/**
* This class provides methods to validate the parameters of different iota API methods.
*
- * @author pinpong
*/
public class InputValidator {
@@ -24,7 +27,7 @@ public class InputValidator {
**/
public static boolean isAddress(String address) {
return (address.length() == Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM ||
- address.length() == Constants.ADDRESS_LENGTH_WITH_CHECKSUM) && isTrytes(address, address.length());
+ address.length() == Constants.ADDRESS_LENGTH_WITH_CHECKSUM) && isTrytes(address);
}
/**
@@ -62,7 +65,7 @@ public static boolean isAddressesArrayValid(String[] addresses) throws ArgumentE
*
* @param address The address to validate.
* @return true if the specified string is an address; otherwise, false.
- * @throws ArgumentException is thrown when the specified input is not valid.
+ * @throws ArgumentException when the specified input is not valid.
**/
public static boolean checkAddress(String address) throws ArgumentException {
if (!isAddress(address)) {
@@ -72,15 +75,49 @@ public static boolean checkAddress(String address) throws ArgumentException {
}
/**
- * Determines whether the specified string contains only characters from the trytes alphabet (see ).
+ * Determines whether the specified string contains only characters from the trytes alphabet
+ * @param trytes The trytes to validate.
+ * @return true if the specified trytes are trytes, otherwise false.
+ */
+ public static boolean isTrytes(String trytes) {
+ return isTrytesOfExactLength(trytes, 0);
+ }
+
+ /**
+ * Determines whether the specified string contains only characters from the trytes alphabet.
*
* @param trytes The trytes to validate.
* @param length The length.
- * @return true if the specified trytes are trytes otherwise, false.
+ * @return true if the specified trytes are trytes and have the correct size, otherwise false.
**/
- public static boolean isTrytes(final String trytes, final int length) {
+ public static boolean isTrytesOfExactLength(String trytes, int length) {
+ if (length < 0 ) return false;
+
return trytes.matches("^[A-Z9]{" + (length == 0 ? "0," : length) + "}$");
}
+
+ /**
+ * Determines whether the specified string contains only characters from the trytes alphabet
+ * and has a maximum (including) of the provided length
+ * @param trytes The trytes to validate.
+ * @param maxLength The length.
+ * @return true if the specified trytes are trytes and have the correct size, otherwise false.
+ */
+ public static boolean isTrytesOfMaxLength(String trytes, int maxLength) {
+ if (trytes.length() > maxLength) return false;
+ return isTrytesOfExactLength(trytes, 0);
+ }
+
+ /**
+ * Determines whether the specified string consist only of '9'.
+ *
+ * @param trytes The trytes to validate.
+ * @param length The length.
+ * @return true if the specified string consist only of '9'; otherwise, false.
+ **/
+ public static boolean isEmptyTrytes(final String trytes) {
+ return isNinesTrytes(trytes, 0);
+ }
/**
* Determines whether the specified string consist only of '9'.
@@ -104,15 +141,16 @@ public static boolean isValue(final String value) {
}
/**
- * Determines whether the specified string array contains only trytes.
- *
+ * Deprecated due to ambigue function name, please switch to {@link #areTransactionTrytes}
+ * Determines whether the specified string array contains only trytes of a transaction length
* @param trytes The trytes array to validate.
* @return true if the specified array contains only valid trytes otherwise, false.
**/
+ @Deprecated
public static boolean isArrayOfTrytes(String[] trytes) {
for (String tryte : trytes) {
// Check if correct 2673 trytes
- if (!isTrytes(tryte, 2673)) {
+ if (!isTrytesOfExactLength(tryte, Constants.TRANSACTION_SIZE)) {
return false;
}
}
@@ -130,15 +168,8 @@ public static boolean isArrayOfHashes(String[] hashes) {
return false;
for (String hash : hashes) {
- // Check if address with checksum
- if (hash.length() == 90) {
- if (!isTrytes(hash, 90)) {
- return false;
- }
- } else {
- if (!isTrytes(hash, 81)) {
- return false;
- }
+ if (!isHash(hash)) {
+ return false;
}
}
return true;
@@ -149,6 +180,7 @@ public static boolean isArrayOfHashes(String[] hashes) {
*
* @param transfers The transfers list to validate.
* @return true if the specified transfers are valid; otherwise, false.
+ * @throws ArgumentException when the specified input is not valid.
**/
public static boolean isTransfersCollectionValid(final List transfers) throws ArgumentException {
@@ -183,26 +215,96 @@ public static boolean isValidTransfer(final Transfer transfer) {
}
// Check if message is correct trytes encoded of any length
- if (transfer.getMessage() == null || !isTrytes(transfer.getMessage(), transfer.getMessage().length())) {
+ if (transfer.getMessage() == null || !isTrytesOfExactLength(transfer.getMessage(), transfer.getMessage().length())) {
return false;
}
// Check if tag is correct trytes encoded and not longer than 27 trytes
- if (transfer.getTag() == null || !isTrytes(transfer.getTag(), transfer.getTag().length()) || transfer.getTag().length() > Constants.TAG_LENGTH) {
+ return isValidTag(transfer.getTag());
+ }
+
+ /**
+ * Checks if the tags are valid.
+ *
+ * @param tags The tags to validate.
+ * @return true if the specified tags are valid; otherwise, false.
+ **/
+ public static boolean areValidTags(String... tags) {
+ // Input validation of tags
+ if (tags == null || tags.length == 0) {
return false;
}
+ for (final String tag : tags) {
+ if (!isValidTag(tag)) {
+ return false;
+ }
+ }
return true;
}
+
+ /**
+ * Checks if the tag is valid.
+ *
+ * @param tag The tag to validate.
+ * @return true if the specified tag is valid; otherwise, false.
+ **/
+ public static boolean isValidTag(String tag) {
+ return tag != null && tag.length() <= Constants.TAG_LENGTH && isTrytes(tag);
+ }
+
+ /**
+ * Checks if the inputs are valid.
+ *
+ * @param inputs The inputs to validate.
+ * @return true if the specified inputs are valid; otherwise, false.
+ **/
+ public static boolean areValidInputs(Input... inputs){
+ // Input validation of input objects
+ if (inputs == null || inputs.length == 0) {
+ return false;
+ }
+ for (final Input input : inputs) {
+ if (!isValidInput(input)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
- * Checks if the seed is valid. If not, an exception is thrown.
+ * Checks if the input is valid.
*
- * @param seed The seed to validate.
- * @return true if the specified seed is valid; otherwise, false.
+ * @param input The input to validate.
+ * @return true if the specified input is valid; otherwise, false.
+ **/
+ public static boolean isValidInput(Input input){
+ if (input == null) {
+ return false;
+ }
+
+ if (!isAddress(input.getAddress())) {
+ return false;
+ }
+
+ if (input.getKeyIndex() < 0) {
+ return false;
+ }
+
+ return isValidSecurityLevel(input.getSecurity());
+ }
+
+ /**
+ * Checks if the seed is valid.
+ *
+ * @param seed The input to validate.
+ * @return true if the specified input is valid; otherwise, false.
**/
public static boolean isValidSeed(String seed) {
- return isTrytes(seed, seed.length());
+ if (seed.length() > Constants.SEED_LENGTH_MAX) return false;
+
+ return isTrytes(seed);
}
/**
@@ -212,14 +314,17 @@ public static boolean isValidSeed(String seed) {
* @return true if the specified hashes are valid; otherwise, false.
**/
public static boolean isHashes(List hashes) {
+ if (hashes == null || hashes.size() == 0) {
+ return false;
+ }
for (String hash : hashes) {
- if (!isTrytes(hash, 81)) {
+ if (!isHash(hash)) {
return false;
}
}
return true;
}
-
+
/**
* Checks if input is correct hash.
*
@@ -227,9 +332,70 @@ public static boolean isHashes(List hashes) {
* @return true if the specified hash are valid; otherwise, false.
**/
public static boolean isHash(String hash) {
- if (!isTrytes(hash, 81)) {
+ // Check if address with checksum
+ if (hash.length() == Constants.ADDRESS_LENGTH_WITH_CHECKSUM) {
+ return isTrytesOfExactLength(hash, 0); //We already checked length
+ } else {
+ return isTrytesOfExactLength(hash, Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM);
+ }
+ }
+
+ /**
+ * Checks if the uris are valid.
+ *
+ * @param uris The uris to validate.
+ * @return true if the specified uris are valid; otherwise, false.
+ **/
+ public static boolean areValidUris(String... uris) {
+ if (uris == null || uris.length == 0) {
return false;
}
+ for (String uri : uris) {
+ if (!isValidUri(uri)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks if the uri is valid.
+ *
+ * @param uri The uri to validate.
+ * @return true if the specified uri is valid; otherwise, false.
+ **/
+ public static boolean isValidUri(String uri) {
+ if (uri.length() < 7) {
+ return false;
+ }
+
+ String protocol = uri.substring(0, 6);
+ if (!"tcp://".equals(protocol) && !"udp://".equals(protocol)) {
+ return false;
+ }
+
+ try {
+ new URI(uri);
+ return true;
+ } catch (URISyntaxException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Determines whether the specified string array contains only trytes of a transaction length
+ * @param trytes The trytes array to validate.
+ * @return true if the specified array contains only valid trytes otherwise, false.
+ **/
+ public static boolean areTransactionTrytes(String... trytes) {
+ for (String tryteValue : trytes) {
+
+ // Check if correct 2673 trytes
+ if (!isTrytesOfExactLength(tryteValue, Constants.TRANSACTION_SIZE)) {
+ return false;
+ }
+ }
+
return true;
}
@@ -244,11 +410,11 @@ public static boolean isArrayOfAttachedTrytes(String[] trytes) {
for (String tryteValue : trytes) {
// Check if correct 2673 trytes
- if (!isTrytes(tryteValue, 2673)) {
+ if (!isTrytesOfExactLength(tryteValue, Constants.TRANSACTION_SIZE)) {
return false;
}
- String lastTrytes = tryteValue.substring(2673 - (3 * 81));
+ String lastTrytes = tryteValue.substring(Constants.TRANSACTION_SIZE - (3 * 81));
if (isNinesTrytes(lastTrytes, lastTrytes.length())) {
return false;
@@ -257,4 +423,13 @@ public static boolean isArrayOfAttachedTrytes(String[] trytes) {
return true;
}
+
+ /**
+ * Checks if the security level is valid
+ * @param level the level
+ * @return true if the level is between 1 and 3(inclusive); otherwise, false.
+ */
+ public static boolean isValidSecurityLevel(int level) {
+ return level >= Constants.MIN_SECURITY_LEVEL && level <= Constants.MAX_SECURITY_LEVEL;
+ }
}
diff --git a/jota/src/main/java/jota/utils/IotaAPIUtils.java b/jota/src/main/java/jota/utils/IotaAPIUtils.java
index ac9b3a84..786d14de 100644
--- a/jota/src/main/java/jota/utils/IotaAPIUtils.java
+++ b/jota/src/main/java/jota/utils/IotaAPIUtils.java
@@ -16,7 +16,6 @@
/**
* Client Side computation service.
*
- * @author davassi
*/
public class IotaAPIUtils {
@@ -33,7 +32,7 @@ public class IotaAPIUtils {
*/
public static String newAddress(String seed, int security, int index, boolean checksum, ICurl curl) throws ArgumentException {
- if (security < 1) {
+ if (!InputValidator.isValidSecurityLevel(security)) {
throw new ArgumentException(INVALID_SECURITY_LEVEL_INPUT_ERROR);
}
diff --git a/jota/src/main/java/jota/utils/Signing.java b/jota/src/main/java/jota/utils/Signing.java
index edd433ac..7c996227 100644
--- a/jota/src/main/java/jota/utils/Signing.java
+++ b/jota/src/main/java/jota/utils/Signing.java
@@ -9,43 +9,56 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import static jota.pow.JCurl.HASH_LENGTH;
-import static jota.utils.Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR;
+
+import static jota.utils.Constants.*;
public class Signing {
- public final static int KEY_LENGTH = 6561;
+
private ICurl curl;
+
+ public Signing() {
+ this(Optional.empty());
+ }
+
+ public Signing(ICurl curl) {
+ this(curl != null ? Optional.of(curl) : Optional.empty());
+ }
+
/**
- * public Signing() {
- * this(null);
- * }
- *
- * /**
*
* @param curl
*/
- public Signing(ICurl curl) {
- this.curl = curl == null ? SpongeFactory.create(SpongeFactory.Mode.KERL) : curl;
+ public Signing(Optional curl) {
+ this.curl = curl == null || !curl.isPresent() ? SpongeFactory.create(SpongeFactory.Mode.KERL) : curl.get();
}
-
+
/**
- * @param inSeed
- * @param index
- * @param security
- * @return
- * @throws ArgumentException is thrown when the specified security level is not valid.
+ * Returns the sub-seed trits given a seed and an index
+ * @param inSeed the seed
+ * @param index the index
+ * @return the sub-seed
*/
- public int[] key(final int[] inSeed, final int index, int security) throws ArgumentException {
- if (security < 1) {
- throw new ArgumentException(INVALID_SECURITY_LEVEL_INPUT_ERROR);
+ public int[] subseed(int[] inSeed, int index) throws ArgumentException {
+ if (index < 0) {
+ throw new ArgumentException(INVALID_INDEX_INPUT_ERROR);
}
-
+
int[] seed = inSeed.clone();
+ /*
+ *
+ * index 0 = [0,0,0,1,0,-1,0,1,1,0,-1,1,-1,0]
+ * index 1 = [1,0,0,1,0,-1,0,1,1,0,-1,1,-1,0]
+ * index 2 = [-1,1,0,1,0,-1,0,1,1,0,-1,1,-1,0]
+ * index 3 = [0,1,0,1,0,-1,0,1,1,0,-1,1,-1,0]
+ */
+
// Derive subseed.
for (int i = 0; i < index; i++) {
for (int j = 0; j < seed.length; j++) {
@@ -56,7 +69,43 @@ public int[] key(final int[] inSeed, final int index, int security) throws Argum
}
}
}
+ return seed;
+ }
+
+ /**
+ * Calculates security based on inSeed length / key length
+ * @param inSeed
+ * @param index
+ * @return
+ * @throws ArgumentException is thrown when the specified security level is not valid
+ * ArgumentException is thrown when inSeed length is not dividable by 3
+ * ArgumentException is thrown when index is below 1
+ */
+ public int[] key(final int[] inSeed, int index) throws ArgumentException {
+ return key(inSeed, index, (int) Math.floor(inSeed.length / KEY_LENGTH));
+ }
+
+ /**
+ *
+ * @param inSeed
+ * @param index
+ * @param security
+ * @return
+ * @throws ArgumentException is thrown when the specified security level is not valid
+ * ArgumentException is thrown when inSeed length is not dividable by 3
+ * ArgumentException is thrown when index is below 1
+ */
+ public int[] key(final int[] inSeed, final int index, int security) throws ArgumentException {
+ if (!InputValidator.isValidSecurityLevel(security)) {
+ throw new ArgumentException(INVALID_SECURITY_LEVEL_INPUT_ERROR);
+ }
+
+ if (inSeed.length % 3 != 0) {
+ throw new ArgumentException(INVALID_SEED_INPUT_ERROR);
+ }
+ int[] seed = subseed(inSeed, index);
+
ICurl curl = this.getICurlObject(SpongeFactory.Mode.KERL);
curl.reset();
curl.absorb(seed, 0, seed.length);
@@ -81,21 +130,11 @@ public int[] key(final int[] inSeed, final int index, int security) throws Argum
return key;
}
- public int[] signatureFragment(int[] normalizedBundleFragment, int[] keyFragment) {
- int[] signatureFragment = keyFragment.clone();
-
- for (int i = 0; i < 27; i++) {
-
- for (int j = 0; j < 13 - normalizedBundleFragment[i]; j++) {
- curl.reset()
- .absorb(signatureFragment, i * HASH_LENGTH, HASH_LENGTH)
- .squeeze(signatureFragment, i * HASH_LENGTH, HASH_LENGTH);
- }
- }
-
- return signatureFragment;
- }
-
+ /**
+ * Address generates the address trits from the given digests.
+ * @param digests the digests
+ * @return the address trits
+ */
public int[] address(int[] digests) {
int[] address = new int[HASH_LENGTH];
ICurl curl = this.getICurlObject(SpongeFactory.Mode.KERL);
@@ -105,9 +144,18 @@ public int[] address(int[] digests) {
return address;
}
- public int[] digests(int[] key) {
+ /**
+ * Digests hashes each segment of each key fragment 26 times and returns them.
+ * @param key the key trits
+ * @return the digests
+ * @throws ArgumentException if the security level is invalid
+ */
+ public int[] digests(int[] key) throws ArgumentException {
int security = (int) Math.floor(key.length / KEY_LENGTH);
-
+ if (!InputValidator.isValidSecurityLevel(security)) {
+ throw new ArgumentException(INVALID_SECURITY_LEVEL_INPUT_ERROR);
+ }
+
int[] digests = new int[security * HASH_LENGTH];
int[] keyFragment = new int[KEY_LENGTH];
@@ -130,6 +178,12 @@ public int[] digests(int[] key) {
return digests;
}
+ /**
+ *
+ * @param normalizedBundleFragment
+ * @param signatureFragment
+ * @return
+ */
public int[] digest(int[] normalizedBundleFragment, int[] signatureFragment) {
curl.reset();
ICurl jCurl = this.getICurlObject(SpongeFactory.Mode.KERL);
@@ -149,6 +203,21 @@ public int[] digest(int[] normalizedBundleFragment, int[] signatureFragment) {
return buffer;
}
+
+ public int[] signatureFragment(int[] normalizedBundleFragment, int[] keyFragment) {
+ int[] signatureFragment = keyFragment.clone();
+
+ for (int i = 0; i < 27; i++) {
+
+ for (int j = 0; j < 13 - normalizedBundleFragment[i]; j++) {
+ curl.reset()
+ .absorb(signatureFragment, i * HASH_LENGTH, HASH_LENGTH)
+ .squeeze(signatureFragment, i * HASH_LENGTH, HASH_LENGTH);
+ }
+ }
+
+ return signatureFragment;
+ }
public Boolean validateSignatures(Bundle signedBundle, String inputAddress) {
String bundleHash = "";
@@ -175,7 +244,6 @@ public Boolean validateSignatures(Bundle signedBundle, String inputAddress) {
public Boolean validateSignatures(String expectedAddress, String[] signatureFragments, String bundleHash) {
-
Bundle bundle = new Bundle();
int[][] normalizedBundleFragments = new int[3][27];
@@ -203,5 +271,49 @@ public Boolean validateSignatures(String expectedAddress, String[] signatureFrag
private ICurl getICurlObject(SpongeFactory.Mode mode) {
return SpongeFactory.create(mode);
}
+
+ /**
+ * Normalizes the given bundle hash, with resulting digits summing to zero.
+ * It returns a slice with the tryte decimal representation without any 13/M values.
+ * @param bundleHash the bundle hash
+ * @return the normalized bundle hash in trits
+ */
+ public int[] normalizedBundle(String bundleHash) {
+ int[] normalizedBundle = new int[81];
+
+ for (int i = 0; i < 3; i++) {
+
+ long sum = 0;
+ for (int j = 0; j < 27; j++) {
+
+ sum += (normalizedBundle[i * 27 + j] = Converter.value(Converter.tritsString("" + bundleHash.charAt(i * 27 + j))));
+ }
+
+ if (sum >= 0) {
+ while (sum-- > 0) {
+ for (int j = 0; j < 27; j++) {
+ if (normalizedBundle[i * 27 + j] > -13) {
+ normalizedBundle[i * 27 + j]--;
+ break;
+ }
+ }
+ }
+ } else {
+
+ while (sum++ < 0) {
+
+ for (int j = 0; j < 27; j++) {
+
+ if (normalizedBundle[i * 27 + j] < 13) {
+ normalizedBundle[i * 27 + j]++;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return normalizedBundle;
+ }
}
diff --git a/jota/src/main/java/jota/utils/TrytesConverter.java b/jota/src/main/java/jota/utils/TrytesConverter.java
index 8b90b75c..a0d370d4 100644
--- a/jota/src/main/java/jota/utils/TrytesConverter.java
+++ b/jota/src/main/java/jota/utils/TrytesConverter.java
@@ -8,7 +8,7 @@
public class TrytesConverter {
/**
- * Conversion of ascii encoded bytes to trytes.
+ * Conversion of ASCII encoded bytes to trytes.
* Input is a string (can be stringified JSON object), return value is Trytes
*
* How the conversion works:
@@ -37,7 +37,7 @@ public class TrytesConverter {
* @param inputString The input String.
* @return The ASCII char "Z" is represented as "IC" in trytes.
*/
- public static String toTrytes(String inputString) {
+ public static String asciiToTrytes(String inputString) {
StringBuilder trytes = new StringBuilder();
@@ -62,15 +62,13 @@ public static String toTrytes(String inputString) {
}
/**
- * Trytes to bytes
- * Reverse operation from the byteToTrytes function in send.js
+ * Converts Trytes of even length to an ASCII string.
+ * Reverse operation from the asciiToTrytes
* 2 Trytes == 1 Byte
- * We assume that the trytes are a JSON encoded object thus for our encoding:
- * First character = {
- * Last character = }
- * Everything after that is 9's padding
+ * @param inputTrytes the trytes we want to convert
+ * @return an ASCII string or null when the inputTrytes are uneven
*/
- public static String toString(String inputTrytes) {
+ public static String trytesToAscii(String inputTrytes) {
// If input length is odd, return null
if (inputTrytes.length() % 2 != 0)
diff --git a/jota/src/test/java/jota/InputValidatorTest.java b/jota/src/test/java/jota/InputValidatorTest.java
index ce47dce8..ba561acb 100644
--- a/jota/src/test/java/jota/InputValidatorTest.java
+++ b/jota/src/test/java/jota/InputValidatorTest.java
@@ -34,7 +34,7 @@ public void shouldCheckAddress() throws ArgumentException {
@Test
public void shouldIsTrytes() {
- assertEquals(InputValidator.isTrytes(TEST_TRYTES, TEST_TRYTES.length()), true);
+ assertEquals(InputValidator.isTrytesOfExactLength(TEST_TRYTES, TEST_TRYTES.length()), true);
}
@Test
diff --git a/jota/src/test/java/jota/IotaAPITest.java b/jota/src/test/java/jota/IotaAPITest.java
index 9c4a5c9b..582c3aa0 100644
--- a/jota/src/test/java/jota/IotaAPITest.java
+++ b/jota/src/test/java/jota/IotaAPITest.java
@@ -28,7 +28,6 @@
/**
* Let's do some integration test coverage against a default local real node.
*
- * @author davassi
*/
public class IotaAPITest {
@@ -42,8 +41,8 @@ public class IotaAPITest {
private static final String TEST_HASH = "AKK9DUFNOAAWCERFSUQTV9EOGFTBTSYYRQHEBQWRMDDBPYOS9UIEMQZEEVEQJZIWHKLRKCUJPOW9AC999";
private static final String TEST_INVALID_TRYTES = "BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999";
private static final String TEST_TRYTES = "CCWCXCGDEAXCGDEAPCEAHDTCGDHDRAADTCGDGDPCVCTC9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999WITPXKWONPNUPBESJTXQTZTFXFSTDUWVGYHW9VRDULWGBKDVAAJLOSAEPUSCMSOIYLKMIZLPEKAOYAXMWFLDKQJI99999999999999999999UUC9999999999999999FUCK9YOUIVBUGXD99999999999B99999999IDPWGXASJFLLGCDGPQVXYGSNUESCZQCEKVREGLZX9FCYQVUWESEKWSMHZTGMJLGBOLKU9GILFITSJLZBWEHH9RSRNBPSKKUZBBJDSYHYWYHLJUOAFCKMXGTRFTZHKKWSVKGRHHJECSILLLZXKYJAYAQYEOINSZ9999OCYREQNINOB9XMLJOXMDFJDTXYO9PANNXQSW9HPFAAHEPTSPHTNEBOFFNRPKSUQNTKSACROOJPXF99999UUC9999999999999999FUCK9YOUPNMHCEJIE999999999L99999999IGMVBPROUATGVGQTHYIYFVQETRW";
- private static final String TEST_MESSAGE = "JUSTANOTHERJOTATEST";
- private static final String TEST_TAG = "JOTASPAM9999999999999999999";
+ private static final String TEST_MESSAGE = "JUSTANOTHERIOTATEST";
+ private static final String TEST_TAG = "IOTAJAVASPAM999999999999999";
private static final int MIN_WEIGHT_MAGNITUDE = 14;
private static final int DEPTH = 9;
@@ -128,31 +127,31 @@ public void shouldGetInputs() throws ArgumentException {
@Test
public void shouldCreateANewAddressWithChecksum() throws ArgumentException {
- final GetNewAddressResponse res1 = iotaAPI.getNewAddress(TEST_SEED1, 1, 0, true, 5, false);
+ final GetNewAddressResponse res1 = iotaAPI.getAddressesUnchecked(TEST_SEED1, 1, true, 0, 5);
assertThat(res1.getAddresses().get(0), Is.is(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_1));
- final GetNewAddressResponse res2 = iotaAPI.getNewAddress(TEST_SEED1, 2, 0, true, 5, false);
+ final GetNewAddressResponse res2 = iotaAPI.getAddressesUnchecked(TEST_SEED1, 2, true, 0, 5);
assertThat(res2.getAddresses().get(0), Is.is(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2));
- final GetNewAddressResponse res3 = iotaAPI.getNewAddress(TEST_SEED1, 3, 0, true, 5, false);
+ final GetNewAddressResponse res3 = iotaAPI.getAddressesUnchecked(TEST_SEED1, 3, true, 0, 5);
assertThat(res3.getAddresses().get(0), Is.is(TEST_ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_3));
}
@Test
public void shouldCreateANewAddressWithoutChecksum() throws ArgumentException {
- final GetNewAddressResponse res1 = iotaAPI.getNewAddress(TEST_SEED1, 1, 0, false, 5, false);
+ final GetNewAddressResponse res1 = iotaAPI.getAddressesUnchecked(TEST_SEED1, 1, false, 0, 5);
assertThat(res1.getAddresses().get(0), Is.is(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_1));
- final GetNewAddressResponse res2 = iotaAPI.getNewAddress(TEST_SEED1, 2, 0, false, 5, false);
+ final GetNewAddressResponse res2 = iotaAPI.getAddressesUnchecked(TEST_SEED1, 2, false, 0, 5);
assertThat(res2.getAddresses().get(0), Is.is(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_2));
- final GetNewAddressResponse res3 = iotaAPI.getNewAddress(TEST_SEED1, 3, 0, false, 5, false);
+ final GetNewAddressResponse res3 = iotaAPI.getAddressesUnchecked(TEST_SEED1, 3, false, 0, 5);
assertThat(res3.getAddresses().get(0), Is.is(TEST_ADDRESS_WITHOUT_CHECKSUM_SECURITY_LEVEL_3));
}
@Test
public void shouldCreate100Addresses() throws ArgumentException {
- GetNewAddressResponse res = iotaAPI.getNewAddress(TEST_SEED1, 2, 0, false, 100, false);
+ GetNewAddressResponse res = iotaAPI.getAddressesUnchecked(TEST_SEED1, 2, false, 0, 100);
assertEquals(res.getAddresses().size(), 100);
}
@@ -270,9 +269,9 @@ public void shouldBroadcastAndStore() throws ArgumentException {
@Category(IntegrationTest.class)
public void shouldFailBeforeSnapshotTimeStamp() throws ArgumentException {
try {
- iotaAPI.broadcastAndStore(TEST_TRYTES);
+ iotaAPI.storeAndBroadcast(TEST_TRYTES);
fail("Transaction did not fail on old timestamp value");
- } catch (IllegalAccessError e) {
+ } catch (ArgumentException e) {
//TODO Check for specific error
}
}
diff --git a/jota/src/test/java/jota/IotaLocalPoWTest.java b/jota/src/test/java/jota/IotaLocalPoWTest.java
index 34890ba5..51dfdcd2 100644
--- a/jota/src/test/java/jota/IotaLocalPoWTest.java
+++ b/jota/src/test/java/jota/IotaLocalPoWTest.java
@@ -1,9 +1,10 @@
package jota;
-import cfb.pearldiver.PearlDiverLocalPoW;
import jota.dto.response.SendTransferResponse;
import jota.error.ArgumentException;
import jota.model.Transfer;
+import jota.pow.pearldiver.PearlDiverLocalPoW;
+
import org.hamcrest.core.IsNull;
import org.junit.Before;
import org.junit.Ignore;
diff --git a/jota/src/test/java/jota/SigningTest.java b/jota/src/test/java/jota/SigningTest.java
index 21eca600..18db5d1f 100644
--- a/jota/src/test/java/jota/SigningTest.java
+++ b/jota/src/test/java/jota/SigningTest.java
@@ -52,13 +52,13 @@ public void testLongSeedKeyGeneration() throws ArgumentException {
Signing signing = new Signing(curl);
String seed = "EV9QRJFJZVFNLYUFXWKXMCRRPNAZYQVEYB9VEPUHQNXJCWKZFVUCTQJFCUAMXAHMMIUQUJDG9UGGQBPIY";
- for(int i = 1; i < 5; i++) {
+ for(int i = Constants.MIN_SECURITY_LEVEL; i < Constants.MAX_SECURITY_LEVEL; i++) {
int[] key1 = signing.key(Converter.trits(seed), 0, i);
- assertEquals(Signing.KEY_LENGTH * i, key1.length);
+ assertEquals(Constants.KEY_LENGTH * i, key1.length);
int[] key2 = signing.key(Converter.trits(seed + seed), 0, i);
- assertEquals(Signing.KEY_LENGTH * i, key2.length );
+ assertEquals(Constants.KEY_LENGTH * i, key2.length );
int[] key3 = signing.key(Converter.trits(seed + seed + seed), 0, i);
- assertEquals(Signing.KEY_LENGTH * i, key3.length );
+ assertEquals(Constants.KEY_LENGTH * i, key3.length );
}
}
@@ -68,7 +68,7 @@ public void testSigning() throws ArgumentException {
// address of our test seed
// (but remove the checksum) with the key of our fifth address
String hashToSign = removeChecksum(FIRST_ADDR);
- Signing signing = new Signing(null);
+ Signing signing = new Signing();
final int[] key = signing.key(Converter.trits(TEST_SEED), 5, 2);
int[] normalizedHash = new Bundle().normalizedBundle(hashToSign);
int[] signature = signing.signatureFragment(Arrays.copyOfRange(normalizedHash, 0, 27), Arrays.copyOfRange(key, 0, 6561));
@@ -79,18 +79,18 @@ public void testSigning() throws ArgumentException {
@Test
public void testKeyLength() throws ArgumentException {
- Signing signing = new Signing(null);
+ Signing signing = new Signing();
int[] key = signing.key(Converter.trits(TEST_SEED), 5, 1);
- assertEquals(Signing.KEY_LENGTH, key.length);
+ assertEquals(Constants.KEY_LENGTH, key.length);
key = signing.key(Converter.trits(TEST_SEED), 5, 2);
- assertEquals(2 * Signing.KEY_LENGTH, key.length);
+ assertEquals(2 * Constants.KEY_LENGTH, key.length);
key = signing.key(Converter.trits(TEST_SEED), 5, 3);
- assertEquals(3 * Signing.KEY_LENGTH, key.length);
+ assertEquals(3 * Constants.KEY_LENGTH, key.length);
}
@Test
public void testVerifying() throws ArgumentException {
- assertTrue(new Signing(null).validateSignatures(removeChecksum(SIXTH_ADDR), new String[]{SIGNATURE1, SIGNATURE2}, removeChecksum(FIRST_ADDR)));
+ assertTrue(new Signing().validateSignatures(removeChecksum(SIXTH_ADDR), new String[]{SIGNATURE1, SIGNATURE2}, removeChecksum(FIRST_ADDR)));
}
private String removeChecksum(String address) throws ArgumentException {
diff --git a/jota/src/test/java/jota/TrytesConverterTest.java b/jota/src/test/java/jota/TrytesConverterTest.java
index 1b129732..c0740397 100644
--- a/jota/src/test/java/jota/TrytesConverterTest.java
+++ b/jota/src/test/java/jota/TrytesConverterTest.java
@@ -14,20 +14,20 @@ public class TrytesConverterTest {
@Test
public void shouldConvertStringToTrytes() {
- assertEquals(TrytesConverter.toTrytes("Z"), "IC");
- assertEquals(TrytesConverter.toTrytes("JOTA JOTA"), "TBYBCCKBEATBYBCCKB");
+ assertEquals(TrytesConverter.asciiToTrytes("Z"), "IC");
+ assertEquals(TrytesConverter.asciiToTrytes("JOTA JOTA"), "TBYBCCKBEATBYBCCKB");
}
@Test
public void shouldConvertTrytesToString() {
- assertEquals(TrytesConverter.toString("IC"), "Z");
- assertEquals(TrytesConverter.toString("TBYBCCKBEATBYBCCKB"), "JOTA JOTA");
+ assertEquals(TrytesConverter.trytesToAscii("IC"), "Z");
+ assertEquals(TrytesConverter.trytesToAscii("TBYBCCKBEATBYBCCKB"), "JOTA JOTA");
}
@Test
public void shouldConvertBackAndForth() {
String str = RandomStringUtils.randomAlphabetic(1000).toUpperCase();
- String back = TrytesConverter.toString(TrytesConverter.toTrytes(str));
+ String back = TrytesConverter.trytesToAscii(TrytesConverter.asciiToTrytes(str));
assertTrue(str.equals(back));
}
}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 10fa9ee5..60c7e682 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
org.iotajota-parent
- 1.0.0-beta1
+ 1.0.0-beta2pomJOTAJOTA library is a simple Java wrapper around IOTA Node's JSON-REST HTTP interface.
diff --git a/ruleset.xml b/ruleset.xml
new file mode 100644
index 00000000..eb0f082b
--- /dev/null
+++ b/ruleset.xml
@@ -0,0 +1,96 @@
+
+
+
+
+ The Iota ruleset contains a collection of good practices which should be followed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+