diff --git a/src/main/java/jota/IotaAPIProxy.java b/src/main/java/jota/IotaAPIProxy.java index 5ce6806..8901c71 100644 --- a/src/main/java/jota/IotaAPIProxy.java +++ b/src/main/java/jota/IotaAPIProxy.java @@ -2,6 +2,8 @@ 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; @@ -15,10 +17,12 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Properties; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * IotaAPIProxy Builder. Usage: @@ -193,10 +197,6 @@ public class IotaAPIProxy { // end of proxied calls. - public GetBundleResponse getBundle(String transaction) { - return IotaAPIUtils.getBundle(transaction); - } - /** * Generates a new address from a seed and returns the remainderAddress. * This is either done deterministically, or by providing the index of the new remainderAddress @@ -240,8 +240,72 @@ public class IotaAPIProxy { return GetNewAddressResponse.create(allAddresses); } + /* + * newAddress + * broadcastAndStore + * sendTrytes + * + getTransactionsObjects + findTransactionObjects + getLatestInclusion + getInputs + prepareTransfers + sendTransfer + replayBundle + broadcastBundle + getBundle + getTransfers + getAccountData + */ + /** + * + * @param trytes + * @return a StoreTransactionsResponse + */ + public StoreTransactionsResponse broadcastAndStore(final String ... trytes) { + + try { + broadcastTransactions(trytes); + } catch (Exception e) { + log.error("Impossible to broadcastAndStore, aborting.", e); + throw new IllegalStateException("BroadcastAndStore Illegal state Exception"); + } + return storeTransactions(trytes); + + } + + /** + * Facade method: Gets transactions to approve, attaches to Tangle, broadcasts and stores + * @param {array} trytes + * @param {int} depth + * @param {int} minWeightMagnitude + * @return + */ + public List sendTrytes(final String trytes, final int minWeightMagnitude) { + + final GetTransactionsToApproveResponse txs = getTransactionsToApprove(minWeightMagnitude); + + // attach to tangle - do pow + final GetAttachToTangleResponse res = attachToTangle(txs.getTrunkTransaction(), txs.getBranchTransactionToApprove(), minWeightMagnitude, trytes); + + try { + broadcastAndStore(res.getTrytes()); + } catch (Exception e) { + log.error("Impossible to sendTrytes, aborting.", e); + throw new IllegalStateException("sendTrytes Illegal state Exception"); + } + + return Arrays.stream(res.getTrytes()) + .map(Converter::transactionObject) + .collect(Collectors.toList()); + } + 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/error/ArgumentException.java b/src/main/java/jota/error/ArgumentException.java index 759b694..c3da421 100644 --- a/src/main/java/jota/error/ArgumentException.java +++ b/src/main/java/jota/error/ArgumentException.java @@ -4,7 +4,10 @@ package jota.error; * Created by Adrian on 09.12.2016. */ public class ArgumentException extends BaseException { + + private static final long serialVersionUID = -7850044681919575720L; + public ArgumentException() { - super("wrong arguments passed to function"); + super("Wrong arguments passed to function"); } } diff --git a/src/main/java/jota/error/BaseException.java b/src/main/java/jota/error/BaseException.java index b1eb210..2931408 100644 --- a/src/main/java/jota/error/BaseException.java +++ b/src/main/java/jota/error/BaseException.java @@ -1,31 +1,29 @@ package jota.error; -import org.apache.commons.lang3.StringUtils; - +import java.util.Arrays; import java.util.Collection; /** * Created by Adrian on 09.12.2016. */ public class BaseException extends Exception { + + private static final long serialVersionUID = 5617085097507773343L; + protected Collection messages; public BaseException(String msg) { super(msg); } - public BaseException(String msg, Exception cause) { super(msg, cause); } - public BaseException(Collection messages) { - super(); this.messages = messages; } - public BaseException(Collection messages, Exception cause) { super(cause); this.messages = messages; @@ -33,19 +31,6 @@ public class BaseException extends Exception { @Override public String getMessage() { - String msg; - - if (this.messages != null && !this.messages.isEmpty()) { - msg = "["; - - for (String message : this.messages) { - msg += message + ","; - } - - msg = StringUtils.removeEnd(msg, ",") + "]"; - - } else msg = super.getMessage(); - - return msg; + return Arrays.toString(messages.toArray()); } } diff --git a/src/main/java/jota/error/NotEnoughBalanceException.java b/src/main/java/jota/error/NotEnoughBalanceException.java index c7099d6..64c516c 100644 --- a/src/main/java/jota/error/NotEnoughBalanceException.java +++ b/src/main/java/jota/error/NotEnoughBalanceException.java @@ -4,7 +4,10 @@ package jota.error; * Created by Adrian on 09.12.2016. */ public class NotEnoughBalanceException extends BaseException { + + private static final long serialVersionUID = -3807270816402226476L; + public NotEnoughBalanceException() { - super("not enough balance dude"); + super("Not enough balance"); } } diff --git a/src/main/java/jota/model/Bundle.java b/src/main/java/jota/model/Bundle.java new file mode 100644 index 0000000..675fbd7 --- /dev/null +++ b/src/main/java/jota/model/Bundle.java @@ -0,0 +1,162 @@ +package jota.model; + +import jota.pow.Curl; +import jota.utils.Constants; +import jota.utils.Converter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by pinpong on 09.12.16. + */ +public class Bundle { + + private List transactions; + private int length; + + public static String EMPTY_HASH = "999999999999999999999999999999999999999999999999999999999999999999999999999999999"; + + + public Bundle() { + this(new ArrayList(), 0); + } + + public Bundle(List transactions, int length) { + this.transactions = transactions; + this.length = length; + } + + public List getTransactions() { + return transactions; + } + + public void setTransactions(List transactions) { + this.transactions = transactions; + } + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } + + public void addEntry(int signatureMessageLength, String slice, long value, String tag, long timestamp) { + for (int i = 0; i < signatureMessageLength; i++) { + //TODO + +/* var transactionObject = new Object(); + transactionObject.address = address; + transactionObject.value = i == 0 ? value : 0; + transactionObject.tag = tag; + transactionObject.timestamp = timestamp; + + this.bundle[this.bundle.length] = transactionObject; +*/ + } + } + + public void finalize() { + + Curl curl = new Curl(); + curl.reset(); + + for (int i = 0; i < this.getTransactions().size(); i++) { + + int[] valueTrits = Converter.trits(this.getTransactions().get(i).getValue()); + while (valueTrits.length < 81) { + valueTrits[valueTrits.length] = 0; + } + + int[] timestampTrits = Converter.trits(this.getTransactions().get(i).getTimestamp()); + while (timestampTrits.length < 27) { + timestampTrits[timestampTrits.length] = 0; + } + + int[] currentIndexTrits = Converter.trits(this.getTransactions().get(i).setCurrentIndex("" + i)); + while (currentIndexTrits.length < 27) { + currentIndexTrits[currentIndexTrits.length] = 0; + } + + int[] lastIndexTrits = Converter.trits(this.getTransactions().get(i).setLastIndex("" + (this.getTransactions().size() - 1))); + while (lastIndexTrits.length < 27) { + lastIndexTrits[lastIndexTrits.length] = 0; + } + int[] t = Converter.trits(this.getTransactions().get(i).getAddress() + Converter.trytes(valueTrits) + this.getTransactions().get(i).getTag() + Converter.trytes(timestampTrits) + Converter.trytes(currentIndexTrits) + Converter.trytes(lastIndexTrits)); + curl.absorb(t, 0, t.length); + } + + int[] hash = new int[90]; + curl.squeeze(hash, 0, hash.length); + String hashInTrytes = Converter.trytes(hash); + + for (int i = 0; i < this.getTransactions().size(); i++) { + this.getTransactions().get(i).setBundle(hashInTrytes); + } + } + + + public void addTrytes(List signatureFragments) { + String emptySignatureFragment = ""; + String emptyHash = EMPTY_HASH; + + for (int j = 0; emptySignatureFragment.length() < 2187; j++) { + emptySignatureFragment += '9'; + } + + for (int i = 0; i < this.getTransactions().size(); i++) { + + // Fill empty signatureMessageFragment + this.getTransactions().get(i).setSignatureFragments(signatureFragments.get(i) == null ? signatureFragments.get(i) : emptySignatureFragment); + // Fill empty trunkTransaction + this.getTransactions().get(i).setTrunkTransaction(emptyHash); + + // Fill empty branchTransaction + this.getTransactions().get(i).setBranchTransaction(emptyHash); + + // Fill empty nonce + this.getTransactions().get(i).setNonce(emptyHash); + } + } + + public int[] normalizedBundle(String bundleHash) { + int[] normalizedBundle = new int[33 * 27 + 27]; + + for (int i = 0; i < 3; i++) { + + long sum = 0; + for (int j = 0; j < 27; j++) { + + sum += (normalizedBundle[i * 27 + j] = Converter.value(Converter.trits("" + bundleHash.charAt(i * 27 + j)))); + } + + 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/src/main/java/jota/model/Input.java b/src/main/java/jota/model/Input.java new file mode 100644 index 0000000..16ce445 --- /dev/null +++ b/src/main/java/jota/model/Input.java @@ -0,0 +1,48 @@ +package jota.model; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * Created by Adrian on 09.12.2016. + */ +public class Input { + private String address; + private long balance; + private int keyIndex; + + public Input(String address, long balance, int keyIndex) { + this.address = address; + this.balance = balance; + this.keyIndex = keyIndex; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public long getBalance() { + return balance; + } + + public void setBalance(long balance) { + this.balance = balance; + } + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } +} \ 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 d72ed45..764a54a 100644 --- a/src/main/java/jota/pow/Curl.java +++ b/src/main/java/jota/pow/Curl.java @@ -15,15 +15,37 @@ public class Curl { private int[] state = new int[STATE_LENGTH]; - public void absorb(final int[] trits, int offset, int length) { + public Curl absorb(final int[] trits, int offset, int length) { do { System.arraycopy(trits, offset, state, 0, length < HASH_LENGTH ? length : HASH_LENGTH); transform(); offset += HASH_LENGTH; } while ((length -= HASH_LENGTH) > 0); + + return this; } + public Curl transform() { + + final int[] scratchpad = new int[STATE_LENGTH]; + int scratchpadIndex = 0; + for (int round = 0; round < NUMBER_OF_ROUNDS; round++) { + System.arraycopy(state, 0, scratchpad, 0, STATE_LENGTH); + for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) { + state[stateIndex] = TRUTH_TABLE[scratchpad[scratchpadIndex] + scratchpad[scratchpadIndex += (scratchpadIndex < 365 ? 364 : -365)] * 3 + 4]; + } + } + return this; + } + + public Curl reset() { + for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) { + state[stateIndex] = 0; + } + return this; + } + public int[] squeeze(final int[] trits, int offset, int length) { do { @@ -35,26 +57,10 @@ public class Curl { return state; } - public void transform() { - - final int[] scratchpad = new int[STATE_LENGTH]; - int scratchpadIndex = 0; - for (int round = 0; round < NUMBER_OF_ROUNDS; round++) { - System.arraycopy(state, 0, scratchpad, 0, STATE_LENGTH); - for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) { - state[stateIndex] = TRUTH_TABLE[scratchpad[scratchpadIndex] + scratchpad[scratchpadIndex += (scratchpadIndex < 365 ? 364 : -365)] * 3 + 4]; - } - } - } - - public void reset() { - for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) { - state[stateIndex] = 0; - } - } - public int[] getState() { return state; } - public void setState(int[] state) { this.state = state; } + public void setState(int[] state) { + this.state = state; + } } diff --git a/src/main/java/jota/utils/Constants.java b/src/main/java/jota/utils/Constants.java index 2b9d4a5..5f8f9a2 100644 --- a/src/main/java/jota/utils/Constants.java +++ b/src/main/java/jota/utils/Constants.java @@ -11,5 +11,4 @@ public class Constants { public static int ADDRESS_LENGTH_WITHOUT_CHECKSUM = 81; public static int ADDRESS_LENGTH_WITH_CHECKSUM = 90; - } diff --git a/src/main/java/jota/utils/Converter.java b/src/main/java/jota/utils/Converter.java index 0a94107..90550e7 100644 --- a/src/main/java/jota/utils/Converter.java +++ b/src/main/java/jota/utils/Converter.java @@ -4,8 +4,15 @@ import jota.model.Transaction; import jota.pow.Curl; import java.util.Arrays; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Converter { + + private static final Logger log = LoggerFactory.getLogger(Converter.class); private static final int RADIX = 3; private static final int MAX_TRIT_VALUE = (RADIX - 1) / 2, MIN_TRIT_VALUE = -MAX_TRIT_VALUE; @@ -146,20 +153,26 @@ public class Converter { } } } - - public static Transaction transactionObject(String trytes) { - if (trytes == null) return null; - + + public static Transaction transactionObject(final String trytes) { + + if (StringUtils.isEmpty(trytes)) { + log.warn("Warning: empty trytes in input for transactionObject"); + return null; + } + // validity check for (int i = 2279; i < 2295; i++) { if (trytes.charAt(i) != '9') { + log.warn("Trytes {} does not seem a valid tryte", trytes); return null; } } + int[] transactionTrits = Converter.trits(trytes); int[] hash = new int[90]; - Curl curl = new Curl(); + final Curl curl = new Curl(); // we need a fluent Curl. // generate the correct transaction hash curl.reset(); @@ -183,4 +196,4 @@ public class Converter { return trx; } -} \ No newline at end of file +} diff --git a/src/main/java/jota/utils/IotaAPIUtils.java b/src/main/java/jota/utils/IotaAPIUtils.java index 3da5c2d..ee759cd 100644 --- a/src/main/java/jota/utils/IotaAPIUtils.java +++ b/src/main/java/jota/utils/IotaAPIUtils.java @@ -1,10 +1,17 @@ package jota.utils; -import jota.dto.response.GetBundleResponse; -import org.apache.commons.lang3.NotImplementedException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jota.model.Bundle; +import jota.model.Input; +import jota.model.Transaction; + /** * Client Side computation service * @@ -36,8 +43,112 @@ public class IotaAPIUtils { return address; } - public static GetBundleResponse getBundle(final String transaction) { - throw new NotImplementedException("Not yet implemented"); + public static String transactionTrytes(Transaction trx) { + int[] valueTrits = Converter.trits(trx.getValue()); + while (valueTrits.length < 81) { + valueTrits[valueTrits.length] = 0; + } + + int[] timestampTrits = Converter.trits(trx.getTimestamp()); + while (timestampTrits.length < 27) { + timestampTrits[timestampTrits.length] = 0; + } + + int[] currentIndexTrits = Converter.trits(trx.getTimestamp()); + while (currentIndexTrits.length < 27) { + currentIndexTrits[currentIndexTrits.length] = 0; + } + + int[] lastIndexTrits = Converter.trits(trx.getCurrentIndex()); + while (lastIndexTrits.length < 27) { + lastIndexTrits[lastIndexTrits.length] = 0; + } + + return trx.getSignatureFragments() + + trx.getAddress() + + Converter.trytes(valueTrits) + + trx.getTag() + + Converter.trytes(timestampTrits) + + Converter.trytes(currentIndexTrits) + + Converter.trytes(lastIndexTrits) + + trx.getBundle() + + trx.getTrunkTransaction() + + trx.getBranchTransaction() + + trx.getNonce(); + } + + public static List signInputsAndReturn(String seed, List inputs, Bundle bundle, + List signatureFragments) { + bundle.finalize(); + bundle.addTrytes(signatureFragments); + + // SIGNING OF INPUTS + // + // Here we do the actual signing of the inputs + // Iterate over all bundle transactions, find the inputs + // Get the corresponding private key and calculate the signatureFragment + for (int i = 0; i < bundle.getTransactions().size(); i++) { + if (Long.parseLong(bundle.getTransactions().get(i).getValue()) < 0) { + String thisAddress = bundle.getTransactions().get(i).getAddress(); + + // Get the corresponding keyIndex of the address + int keyIndex = 0; + for (int k = 0; k < inputs.size(); k++) { + if (inputs.get(k).getAddress().equals(thisAddress)) { + keyIndex = inputs.get(k).getKeyIndex(); + break; + } + } + + String bundleHash = bundle.getTransactions().get(i).getBundle(); + + // Get corresponding private key of address + int[] key = Signing.key(Converter.trits(seed), keyIndex, 2); + + // First 6561 trits for the firstFragment + int[] firstFragment = Arrays.copyOfRange(key, 0, 6561); + + // Get the normalized bundle hash + int[] normalizedBundleHash = bundle.normalizedBundle(bundleHash); + + // First bundle fragment uses 27 trytes + int[] firstBundleFragment = Arrays.copyOfRange(normalizedBundleHash, 0, 27); + + // Calculate the new signatureFragment with the first bundle fragment + int[] firstSignedFragment = Signing.signatureFragment(firstBundleFragment, firstFragment); + + // Convert signature to trytes and assign the new signatureFragment + bundle.getTransactions().get(i).setSignatureFragments(Converter.trytes(firstSignedFragment)); + + // Because the signature is > 2187 trytes, we need to + // find the second transaction to add the remainder of the signature + for (int j = 0; j < bundle.getTransactions().size(); j++) { + // Same address as well as value = 0 (as we already spent the input) + if (bundle.getTransactions().get(j).getAddress() == thisAddress && Long.parseLong(bundle.getTransactions().get(j).getValue()) == 0) { + // Use the second 6562 trits + int[] secondFragment = Arrays.copyOfRange(key, 6561, 6561 * 2); + + // The second 27 to 54 trytes of the bundle hash + int[] secondBundleFragment = Arrays.copyOfRange(normalizedBundleHash, 27, 27 * 2); + + // Calculate the new signature + int[] secondSignedFragment = Signing.signatureFragment(secondBundleFragment, secondFragment); + + // Convert signature to trytes and assign it again to this bundle entry + bundle.getTransactions().get(j).setSignatureFragments(Converter.trytes(secondSignedFragment)); + } + } + } + } + + List bundleTrytes = new ArrayList<>(); + + // Convert all bundle entries into trytes + for (Transaction tx : bundle.getTransactions()) { + bundleTrytes.add(IotaAPIUtils.transactionTrytes(tx)); + } + Collections.reverse(bundleTrytes); + return bundleTrytes; } } diff --git a/src/main/java/jota/utils/IotaUnitConverter.java b/src/main/java/jota/utils/IotaUnitConverter.java index cdd4529..d25a39e 100644 --- a/src/main/java/jota/utils/IotaUnitConverter.java +++ b/src/main/java/jota/utils/IotaUnitConverter.java @@ -43,8 +43,9 @@ public class IotaUnitConverter { public static IotaUnits findOptimalIotaUnitToDisplay(long amount) { int length = String.valueOf(amount).length(); - if (amount < 0) // do not count "-" sign + if (amount < 0) {// do not count "-" sign length -= 1; + } IotaUnits units = IotaUnits.IOTA; diff --git a/src/main/java/jota/utils/IotaUnits.java b/src/main/java/jota/utils/IotaUnits.java index 28e52f3..779dde0 100644 --- a/src/main/java/jota/utils/IotaUnits.java +++ b/src/main/java/jota/utils/IotaUnits.java @@ -8,6 +8,7 @@ package jota.utils; * Table of IOTA units based off of the standard system of Units **/ public enum IotaUnits { + IOTA("i", 0), KILO_IOTA("Ki", 3), MEGA_IOTA("Mi", 6), diff --git a/src/main/java/jota/utils/SeedRandomGenerator.java b/src/main/java/jota/utils/SeedRandomGenerator.java index 8e702cb..fa8b58c 100644 --- a/src/main/java/jota/utils/SeedRandomGenerator.java +++ b/src/main/java/jota/utils/SeedRandomGenerator.java @@ -1,6 +1,6 @@ package jota.utils; -import java.util.Random; +import java.security.SecureRandom; /** * Created by pinpong on 13.12.16. @@ -10,7 +10,7 @@ public class SeedRandomGenerator { public static String generateNewSeed() { char[] chars = Constants.TRYTE_ALPHABET.toCharArray(); StringBuilder builder = new StringBuilder(); - Random random = new Random(); + SecureRandom random = new SecureRandom(); for (int i = 0; i < Constants.SEED_LENGTH_MAX; i++) { char c = chars[random.nextInt(chars.length)]; builder.append(c); diff --git a/src/main/java/jota/utils/Signing.java b/src/main/java/jota/utils/Signing.java index a5e2553..f04339f 100644 --- a/src/main/java/jota/utils/Signing.java +++ b/src/main/java/jota/utils/Signing.java @@ -34,7 +34,6 @@ public class Signing { while (length-- > 0) { for (int i = 0; i < 27; i++) { - curl.squeeze(buffer, offset, buffer.length); for (int j = 0; j < 243; j++) { key.add(buffer[j]); @@ -81,6 +80,31 @@ public class Signing { } return digests; } + + public static int[] signatureFragment(int[] normalizedBundleFragment, int[] keyFragment) { + + int[] signatureFragment = keyFragment; + int[] hash; + + Curl curl = new Curl(); + + for (int i = 0; i < 27; i++) { + + hash = Arrays.copyOfRange(signatureFragment, i * 243, (i + 1) * 243); + + for (int j = 0; j < 13 - normalizedBundleFragment[i]; j++) { + curl.reset() + .absorb(hash, 0, hash.length) + .squeeze(hash, 0, hash.length); + } + + for (int j = 0; j < 243; j++) { + signatureFragment[i * 243 + j] = hash[j]; + } + } + + return signatureFragment; + } public static int[] address(int[] digests) { final Curl curl = new Curl(); diff --git a/src/main/java/jota/utils/TrytesConverter.java b/src/main/java/jota/utils/TrytesConverter.java index 5c5d843..ab9d02a 100644 --- a/src/main/java/jota/utils/TrytesConverter.java +++ b/src/main/java/jota/utils/TrytesConverter.java @@ -4,7 +4,7 @@ package jota.utils; * Created by pinpong on 01.12.16. */ public class TrytesConverter { - + /** * Conversion of ascii encoded bytes to trytes. * Input is a string (can be stringified JSON object), return value is Trytes @@ -19,7 +19,9 @@ public class TrytesConverter { * b. The second value is the remainder (decimal value - first value), divided by 27 * 3. The two values returned from Step 2. are then input as indices into the available values list ('9ABCDEFGHIJKLMNOPQRSTUVWXYZ') to get the correct tryte value *

- * EXAMPLES + * + * EXAMPLE + * * Lets say we want to convert the ASCII character "Z". * 1. 'Z' has a decimal value of 90. * 2. 90 can be represented as 9 + 3 * 27. To make it simpler: @@ -30,10 +32,11 @@ public class TrytesConverter { * b. The second tryte value is '9ABCDEFGHIJKLMNOPQRSTUVWXYZ'[3] === "C" * Our tryte pair is "IC" *

- * RESULT: - * The ASCII char "Z" is represented as "IC" in trytes. + * + * @param inputString + * @return + * The ASCII char "Z" is represented as "IC" in trytes. */ - public static String toTrytes(String inputString) { StringBuilder trytes = new StringBuilder(); @@ -67,7 +70,6 @@ public class TrytesConverter { * Last character = } * Everything after that is 9's padding */ - public static String toString(String inputTrytes) { StringBuilder string = new StringBuilder(); @@ -81,7 +83,6 @@ public class TrytesConverter { int decimalValue = firstValue + secondValue * 27; String character = Character.toString((char) decimalValue); - string.append(character); } diff --git a/src/test/java/jota/SendMessageTest.java b/src/test/java/jota/SendMessageTest.java new file mode 100644 index 0000000..9a62c04 --- /dev/null +++ b/src/test/java/jota/SendMessageTest.java @@ -0,0 +1,12 @@ +package jota; + +import org.junit.Test; + +public class SendMessageTest { + + @Test + public void sendMessage() { + + } + +} diff --git a/src/test/java/jota/TrytesConverterTest.java b/src/test/java/jota/TrytesConverterTest.java index 7741cff..28b54a6 100644 --- a/src/test/java/jota/TrytesConverterTest.java +++ b/src/test/java/jota/TrytesConverterTest.java @@ -3,6 +3,9 @@ package jota; import jota.utils.TrytesConverter; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.apache.commons.lang3.RandomStringUtils; /** * Created by pinpong on 01.12.16. @@ -17,5 +20,14 @@ public class TrytesConverterTest { public void shouldConvertTrytesToString() { assertEquals(TrytesConverter.toString("IC"), "Z"); } + + @Test + public void shouldConvertBackAndForth() { + String str = RandomStringUtils.randomAlphabetic(1000).toUpperCase(); + System.err.println(str); + String back = TrytesConverter.toString(TrytesConverter.toTrytes(str)); + + assertTrue(str.equals(back)); + } } diff --git a/teststore.txt b/teststore.txt new file mode 100644 index 0000000..833978f --- /dev/null +++ b/teststore.txt @@ -0,0 +1 @@ +curl http://localhost:14265 -X POST -H Content-Type: application/json -d {'command': 'storeTransactions', 'trytes': ['GYPRVHBEZOOFXSHQBLCYW9ICTCISLHDBNMMVYD9JJHQMPQCTIQAQTJNNNJ9IDXLRCCOYOXYPCLR9PBEY9ORZIEPPDNTI9CQWYZUOTAVBXPSBOFEQAPFLWXSWUIUSJMSJIIIZWIKIRH9GCOEVZFKNXEVCUCIIWZQCQEUVRZOCMEL9AMGXJNMLJCIA9UWGRPPHCEOPTSVPKPPPCMQXYBHMSODTWUOABPKWFFFQJHCBVYXLHEWPD9YUDFTGNCYAKQKVEZYRBQRBXIAUX9SVEDUKGMTWQIYXRGSWYRK9SRONVGTW9YGHSZRIXWGPCCUCDRMAXBPDFVHSRYWHGB9DQSQFQKSNICGPIPTRZINYRXQAFSWSEWIFRMSBMGTNYPRWFSOIIWWT9IDSELM9JUOOWFNCCSHUSMGNROBFJX9JQ9XT9PKEGQYQAWAFPRVRRVQPUQBHLSNTEFCDKBWRCDX9EYOBB9KPMTLNNQLADBDLZPRVBCKVCYQEOLARJYAGTBFR9QLPKZBOYWZQOVKCVYRGYI9ZEFIQRKYXLJBZJDBJDJVQZCGYQMROVHNDBLGNLQODPUXFNTADDVYNZJUVPGB9LVPJIYLAPBOEHPMRWUIAJXVQOEM9ROEYUOTNLXVVQEYRQWDTQGDLEYFIYNDPRAIXOZEBCS9P99AZTQQLKEILEVXMSHBIDHLXKUOMMNFKPYHONKEYDCHMUNTTNRYVMMEYHPGASPZXASKRUPWQSHDMU9VPS99ZZ9SJJYFUJFFMFORBYDILBXCAVJDPDFHTTTIYOVGLRDYRTKHXJORJVYRPTDH9ZCPZ9ZADXZFRSFPIQKWLBRNTWJHXTOAUOL9FVGTUMMPYGYICJDXMOESEVDJWLMCVTJLPIEKBE9JTHDQWV9MRMEWFLPWGJFLUXI9BXPSVWCMUWLZSEWHBDZKXOLYNOZAPOYLQVZAQMOHGTTQEUAOVKVRRGAHNGPUEKHFVPVCOYSJAWHZU9DRROHBETBAFTATVAUGOEGCAYUXACLSSHHVYDHMDGJP9AUCLWLNTFEVGQGHQXSKEMVOVSKQEEWHWZUDTYOBGCURRZSJZLFVQQAAYQO9TRLFFN9HTDQXBSPPJYXMNGLLBHOMNVXNOWEIDMJVCLLDFHBDONQJCJVLBLCSMDOUQCKKCQJMGTSTHBXPXAMLMSXRIPUBMBAWBFNLHLUJTRJLDERLZFUBUSMF999XNHLEEXEENQJNOFFPNPQ9PQICHSATPLZVMVIWLRTKYPIXNFGYWOJSQDAXGFHKZPFLPXQEHCYEAGTIWIJEZTAVLNUMAFWGGLXMBNUQTOFCNLJTCDMWVVZGVBSEBCPFSM99FLOIDTCLUGPSEDLOKZUAEVBLWNMODGZBWOVQT9DPFOTSKRABQAVOQ9RXWBMAKFYNDCZOJGTCIDMQSQQSODKDXTPFLNOKSIZEOY9HFUTLQRXQMEPGOXQGLLPNSXAUCYPGZMNWMQWSWCKAQYKXJTWINSGPPZG9HLDLEAWUWEVCTVRCBDFOXKUROXH9HXXAXVPEJFRSLOGRVGYZASTEBAQNXJJROCYRTDPYFUIQJVDHAKEG9YACV9HCPJUEUKOYFNWDXCCJBIFQKYOXGRDHVTHEQUMHO999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999RKWEEVD99A99999999A99999999NFDPEEZCWVYLKZGSLCQNOFUSENIXRHWWTZFBXMPSQHEDFWZULBZFEOMNLRNIDQKDNNIELAOXOVMYEI9PGTKORV9IKTJZQUBQAWTKBKZ9NEZHBFIMCLV9TTNJNQZUIJDFPTTCTKBJRHAITVSKUCUEMD9M9SQJ999999TKORV9IKTJZQUBQAWTKBKZ9NEZHBFIMCLV9TTNJNQZUIJDFPTTCTKBJRHAITVSKUCUEMD9M9SQJ999999999999999999999999999999999999999999999999999999999999999999999999999999999999999']}