diff --git a/src/main/java/jota/error/ArgumentException.java b/src/main/java/jota/error/ArgumentException.java new file mode 100644 index 0000000..759b694 --- /dev/null +++ b/src/main/java/jota/error/ArgumentException.java @@ -0,0 +1,10 @@ +package jota.error; + +/** + * Created by Adrian on 09.12.2016. + */ +public class ArgumentException extends BaseException { + public ArgumentException() { + super("wrong arguments passed to function"); + } +} diff --git a/src/main/java/jota/error/BaseException.java b/src/main/java/jota/error/BaseException.java new file mode 100644 index 0000000..b1eb210 --- /dev/null +++ b/src/main/java/jota/error/BaseException.java @@ -0,0 +1,51 @@ +package jota.error; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; + +/** + * Created by Adrian on 09.12.2016. + */ +public class BaseException extends Exception { + 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; + } + + @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; + } +} diff --git a/src/main/java/jota/error/NotEnoughBalanceException.java b/src/main/java/jota/error/NotEnoughBalanceException.java new file mode 100644 index 0000000..c7099d6 --- /dev/null +++ b/src/main/java/jota/error/NotEnoughBalanceException.java @@ -0,0 +1,10 @@ +package jota.error; + +/** + * Created by Adrian on 09.12.2016. + */ +public class NotEnoughBalanceException extends BaseException { + public NotEnoughBalanceException() { + super("not enough balance dude"); + } +} diff --git a/src/main/java/jota/model/Transaction.java b/src/main/java/jota/model/Transaction.java index 4cf5e89..8a74f5a 100644 --- a/src/main/java/jota/model/Transaction.java +++ b/src/main/java/jota/model/Transaction.java @@ -7,36 +7,38 @@ import org.apache.commons.lang3.builder.ToStringStyle; * Created by pinpong on 02.12.16. */ public class Transaction { - - private String signatureMessageChunk; - private String index; - private String approvalNonce; private String hash; - private String digest; - private String type; - private String timestamp; - private String trunkTransaction; - private String branchTransaction; - private String signatureNonce; + private String signatureFragments; private String address; private String value; + private String tag; + private String timestamp; + private String currentIndex; + private String lastIndex; private String bundle; + private String trunkTransaction; + private String branchTransaction; + private String nonce; + private Boolean persistence; - public Transaction(String signatureMessageChunk, String index, String approvalNonce, String hash, String digest, String type, String timestamp, String trunkTransaction, String branchTransaction, String signatureNonce, String address, String value, String bundle) { + public Transaction() { + + } + + public Transaction(String signatureFragments, String currentIndex, String lastIndex, String nonce, String hash, String tag, String timestamp, String trunkTransaction, String branchTransaction, String address, String value, String bundle) { this.hash = hash; - this.type = type; - this.signatureMessageChunk = signatureMessageChunk; - this.digest = digest; + this.tag = tag; + this.signatureFragments = signatureFragments; this.address = address; this.value = value; this.timestamp = timestamp; - this.index = index; + this.currentIndex = currentIndex; + this.lastIndex = lastIndex; this.bundle = bundle; - this.signatureNonce = signatureNonce; - this.approvalNonce = approvalNonce; this.trunkTransaction = trunkTransaction; this.branchTransaction = branchTransaction; + this.nonce = nonce; } @Override @@ -44,56 +46,107 @@ public class Transaction { return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); } - public String getValue() { - return value; + public String getHash() { + return hash; } - public String getDigest() { - return digest; + public void setHash(String hash) { + this.hash = hash; } - public String getTrunkTransaction() { - return trunkTransaction; + public String getSignatureFragments() { + return signatureFragments; } - public String getTimestamp() { - return timestamp; - } - - public String getSignatureNonce() { - return signatureNonce; - } - - public String getType() { - return type; + public String setSignatureFragments(String signatureFragments) { + return this.signatureFragments = signatureFragments; } public String getAddress() { return address; } - public String getApprovalNonce() { - return approvalNonce; + public void setAddress(String address) { + this.address = address; } - public String getBranchTransaction() { - return branchTransaction; + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + public String getCurrentIndex() { + return currentIndex; + } + + public String setCurrentIndex(String currentIndex) { + return this.currentIndex = currentIndex; + } + + public String getLastIndex() { + return lastIndex; + } + + public String setLastIndex(String lastIndex) { + return this.lastIndex = lastIndex; } public String getBundle() { return bundle; } - public String getHash() { - return hash; + public void setBundle(String bundle) { + this.bundle = bundle; } - public String getIndex() { - return index; + public String getTrunkTransaction() { + return trunkTransaction; } - public String getSignatureMessageChunk() { - return signatureMessageChunk; + public void setTrunkTransaction(String trunkTransaction) { + this.trunkTransaction = trunkTransaction; } -} + public String getBranchTransaction() { + return branchTransaction; + } + + public void setBranchTransaction(String branchTransaction) { + this.branchTransaction = branchTransaction; + } + + public String getNonce() { + return nonce; + } + + public void setNonce(String nonce) { + this.nonce = nonce; + } + + public Boolean getPersistence() { + return persistence; + } + + public void setPersistence(Boolean persistence) { + this.persistence = persistence; + } +} \ No newline at end of file diff --git a/src/main/java/jota/utils/Checksum.java b/src/main/java/jota/utils/Checksum.java index fe22f5a..6bf83f5 100644 --- a/src/main/java/jota/utils/Checksum.java +++ b/src/main/java/jota/utils/Checksum.java @@ -24,7 +24,7 @@ public class Checksum { } private static String getAddress(String addressWithChecksum) { - return addressWithChecksum.substring(0, Constants.addressLengthWithoutChecksum); + return addressWithChecksum.substring(0, Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM); } public static boolean isValidChecksum(String addressWithChecksum) { @@ -34,7 +34,7 @@ public class Checksum { } private static boolean isAddressWithChecksum(String addressWithChecksum) { - return InputValidator.checkAddress(addressWithChecksum) && addressWithChecksum.length() == Constants.addressLengthWithChecksum; + return InputValidator.checkAddress(addressWithChecksum) && addressWithChecksum.length() == Constants.ADDRESS_LENGTH_WITH_CHECKSUM; } public static String calculateChecksum(String address) { diff --git a/src/main/java/jota/utils/Constants.java b/src/main/java/jota/utils/Constants.java index 997b296..2b9d4a5 100644 --- a/src/main/java/jota/utils/Constants.java +++ b/src/main/java/jota/utils/Constants.java @@ -7,7 +7,9 @@ public class Constants { public static final String TRYTE_ALPHABET = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - public static int addressLengthWithoutChecksum = 81; - public static int addressLengthWithChecksum = 90; + public static final int SEED_LENGTH_MAX = 81; + + public static int ADDRESS_LENGTH_WITHOUT_CHECKSUM = 81; + public static int ADDRESS_LENGTH_WITH_CHECKSUM = 90; } diff --git a/src/main/java/jota/utils/Converter.java b/src/main/java/jota/utils/Converter.java index a43388f..0a94107 100644 --- a/src/main/java/jota/utils/Converter.java +++ b/src/main/java/jota/utils/Converter.java @@ -1,16 +1,19 @@ package jota.utils; +import jota.model.Transaction; +import jota.pow.Curl; + import java.util.Arrays; public class Converter { - public static final int RADIX = 3; - public static final int MAX_TRIT_VALUE = (RADIX - 1) / 2, MIN_TRIT_VALUE = -MAX_TRIT_VALUE; + private static final int RADIX = 3; + private static final int MAX_TRIT_VALUE = (RADIX - 1) / 2, MIN_TRIT_VALUE = -MAX_TRIT_VALUE; - public static final int NUMBER_OF_TRITS_IN_A_BYTE = 5; - public static final int NUMBER_OF_TRITS_IN_A_TRYTE = 3; - static final int[][] BYTE_TO_TRITS_MAPPINGS = new int[243][]; - static final int[][] TRYTE_TO_TRITS_MAPPINGS = new int[27][]; + private static final int NUMBER_OF_TRITS_IN_A_BYTE = 5; + private static final int NUMBER_OF_TRITS_IN_A_TRYTE = 3; + private static final int[][] BYTE_TO_TRITS_MAPPINGS = new int[243][]; + private static final int[][] TRYTE_TO_TRITS_MAPPINGS = new int[27][]; static { @@ -94,7 +97,7 @@ public class Converter { public static int[] copyTrits(final String input, final int[] destination) { for (int i = 0; i < input.length(); i++) { int index = Constants.TRYTE_ALPHABET.indexOf(input.charAt(i)); - destination[i * 3] = TRYTE_TO_TRITS_MAPPINGS [index][0]; + destination[i * 3] = TRYTE_TO_TRITS_MAPPINGS[index][0]; destination[i * 3 + 1] = TRYTE_TO_TRITS_MAPPINGS[index][1]; destination[i * 3 + 2] = TRYTE_TO_TRITS_MAPPINGS[index][2]; } @@ -124,6 +127,15 @@ public class Converter { return trits[offset] + trits[offset + 1] * 3 + trits[offset + 2] * 9; } + public static int value(final int[] trits) { + int value = 0; + + for (int i = trits.length; i-- > 0; ) { + value = value * 3 + trits[i]; + } + return value; + } + public static void increment(final int[] trits, final int size) { for (int i = 0; i < size; i++) { @@ -134,4 +146,41 @@ public class Converter { } } } + + public static Transaction transactionObject(String trytes) { + if (trytes == null) return null; + + // validity check + for (int i = 2279; i < 2295; i++) { + if (trytes.charAt(i) != '9') { + return null; + } + } + int[] transactionTrits = Converter.trits(trytes); + int[] hash = new int[90]; + + Curl curl = new Curl(); + + // generate the correct transaction hash + curl.reset(); + curl.absorb(transactionTrits, 0, transactionTrits.length); + curl.squeeze(hash, 0, hash.length); + + Transaction trx = new Transaction(); + + trx.setHash(Converter.trytes(hash)); + trx.setSignatureFragments(trytes.substring(0, 2187)); + trx.setAddress(trytes.substring(2187, 2268)); + trx.setValue("" + Converter.value(Arrays.copyOfRange(transactionTrits, 6804, 6837))); + trx.setTag(trytes.substring(2295, 2322)); + trx.setTimestamp("" + Converter.value(Arrays.copyOfRange(transactionTrits, 6966, 6993))); + trx.setCurrentIndex("" + Converter.value(Arrays.copyOfRange(transactionTrits, 6993, 7020))); + trx.setLastIndex("" + Converter.value(Arrays.copyOfRange(transactionTrits, 7020, 7047))); + trx.setBundle(trytes.substring(2349, 2430)); + trx.setTrunkTransaction(trytes.substring(2430, 2511)); + trx.setBranchTransaction(trytes.substring(2511, 2592)); + trx.setNonce(trytes.substring(2592, 2673)); + + return trx; + } } \ No newline at end of file diff --git a/src/main/java/jota/utils/InputValidator.java b/src/main/java/jota/utils/InputValidator.java index c1ddd2a..d658bd3 100644 --- a/src/main/java/jota/utils/InputValidator.java +++ b/src/main/java/jota/utils/InputValidator.java @@ -6,8 +6,8 @@ package jota.utils; public class InputValidator { public static boolean isAddress(String address) { - return (address.length() == Constants.addressLengthWithoutChecksum || - address.length() == Constants.addressLengthWithChecksum) && isTrytes(address, address.length()); + return (address.length() == Constants.ADDRESS_LENGTH_WITHOUT_CHECKSUM || + address.length() == Constants.ADDRESS_LENGTH_WITH_CHECKSUM) && isTrytes(address, address.length()); } public static boolean checkAddress(String address) { @@ -20,4 +20,22 @@ public class InputValidator { public static boolean isTrytes(final String trytes, final int length) { return trytes.matches("^[A-Z9]{" + (length == 0 ? "0," : length) + "}$"); } + + public static boolean isArrayOfHashes(String[] hashes) { + if (hashes == null) 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; + } + } + } + return true; + } } diff --git a/src/main/java/jota/utils/IotaUnitConverter.java b/src/main/java/jota/utils/IotaUnitConverter.java index 57271e8..cdd4529 100644 --- a/src/main/java/jota/utils/IotaUnitConverter.java +++ b/src/main/java/jota/utils/IotaUnitConverter.java @@ -1,7 +1,9 @@ package jota.utils; +import java.text.DecimalFormat; + /** - * Created by pinpong on 30.11.16. + * Created by Sascha on 30.11.16. */ public class IotaUnitConverter { @@ -13,4 +15,53 @@ public class IotaUnitConverter { private static long convertUnits(long amount, IotaUnits toUnit) { return (long) (amount / Math.pow(10, toUnit.getValue())); } + + public static String convertRawIotaAmountToDisplayText(long amount) { + IotaUnits unit = findOptimalIotaUnitToDisplay(amount); + double amountInDisplayUnit = convertAmountTo(amount, unit); + return createAmountWithUnitDisplayText(amountInDisplayUnit, unit); + } + + public static double convertAmountTo(long amount, IotaUnits target) { + return amount / Math.pow(10, target.getValue()); + } + + private static String createAmountWithUnitDisplayText(double amountInUnit, IotaUnits unit) { + String result = createAmountDisplayText(amountInUnit, unit); + result += " " + unit.getUnit(); + return result; + } + + public static String createAmountDisplayText(double amountInUnit, IotaUnits unit) { + DecimalFormat df = new DecimalFormat("##0.##################"); + String result = ""; + // display unit as integer if value is between 1-999 or in decimal format + result += unit == IotaUnits.IOTA ? (long) amountInUnit : df.format(amountInUnit); + return result; + } + + public static IotaUnits findOptimalIotaUnitToDisplay(long amount) { + int length = String.valueOf(amount).length(); + + if (amount < 0) // do not count "-" sign + length -= 1; + + IotaUnits units = IotaUnits.IOTA; + + if (length >= 1 && length <= 3) { + units = IotaUnits.IOTA; + } else if (length > 3 && length <= 6) { + units = IotaUnits.KILO_IOTA; + } else if (length > 6 && length <= 9) { + units = IotaUnits.MEGA_IOTA; + } else if (length > 9 && length <= 12) { + units = IotaUnits.GIGA_IOTA; + } else if (length > 12 && length <= 15) { + units = IotaUnits.TERA_IOTA; + } else if (length > 15 && length <= 18) { + units = IotaUnits.PETA_IOTA; + } + return units; + } + } diff --git a/src/main/java/jota/utils/SeedRandomGenerator.java b/src/main/java/jota/utils/SeedRandomGenerator.java new file mode 100644 index 0000000..8e702cb --- /dev/null +++ b/src/main/java/jota/utils/SeedRandomGenerator.java @@ -0,0 +1,20 @@ +package jota.utils; + +import java.util.Random; + +/** + * Created by pinpong on 13.12.16. + */ +public class SeedRandomGenerator { + + public static String generateNewSeed() { + char[] chars = Constants.TRYTE_ALPHABET.toCharArray(); + StringBuilder builder = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < Constants.SEED_LENGTH_MAX; i++) { + char c = chars[random.nextInt(chars.length)]; + builder.append(c); + } + return builder.toString(); + } +} diff --git a/src/main/java/jota/utils/TrytesConverter.java b/src/main/java/jota/utils/TrytesConverter.java index 21f560e..5c5d843 100644 --- a/src/main/java/jota/utils/TrytesConverter.java +++ b/src/main/java/jota/utils/TrytesConverter.java @@ -70,7 +70,7 @@ public class TrytesConverter { public static String toString(String inputTrytes) { - String string = ""; + StringBuilder string = new StringBuilder(); for (int i = 0; i < inputTrytes.length(); i += 2) { // get a trytes pair @@ -82,9 +82,9 @@ public class TrytesConverter { String character = Character.toString((char) decimalValue); - string += character; + string.append(character); } - return string; + return string.toString(); } -} +} \ No newline at end of file diff --git a/src/test/java/jota/IotaUnitConverterTest.java b/src/test/java/jota/IotaUnitConverterTest.java index 5078ac5..81da852 100644 --- a/src/test/java/jota/IotaUnitConverterTest.java +++ b/src/test/java/jota/IotaUnitConverterTest.java @@ -35,4 +35,24 @@ public class IotaUnitConverterTest { public void shouldConvertUnitTiToPi() { assertEquals(IotaUnitConverter.convertUnits(1000, IotaUnits.TERA_IOTA, IotaUnits.PETA_IOTA), 1); } + + @Test + public void shouldFindOptimizeUnitToDisplay() { + assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1), IotaUnits.IOTA); + assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000), IotaUnits.KILO_IOTA); + assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000000), IotaUnits.MEGA_IOTA); + assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000000000), IotaUnits.GIGA_IOTA); + assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000000000000L), IotaUnits.TERA_IOTA); + assertEquals(IotaUnitConverter.findOptimalIotaUnitToDisplay(1000000000000000L), IotaUnits.PETA_IOTA); + } + + @Test + public void shouldConvertRawIotaAmountToDisplayText() { + assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1), "1 i"); + assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000), "1 Ki"); + assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000), "1 Mi" ); + assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000), "1 Gi" ); + assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000L), "1 Ti"); + assertEquals(IotaUnitConverter.convertRawIotaAmountToDisplayText(1000000000000000L), "1 Pi"); + } } diff --git a/src/test/java/jota/SeedRandomGeneratorTest.java b/src/test/java/jota/SeedRandomGeneratorTest.java new file mode 100644 index 0000000..b2ab6d2 --- /dev/null +++ b/src/test/java/jota/SeedRandomGeneratorTest.java @@ -0,0 +1,22 @@ +package jota; + +import jota.utils.Constants; +import jota.utils.InputValidator; +import jota.utils.SeedRandomGenerator; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Created by pinpong on 13.12.16. + */ +public class SeedRandomGeneratorTest { + + @Test + public void shouldGenerateNewSeed() { + + String generatedSeed = SeedRandomGenerator.generateNewSeed(); + assertEquals(InputValidator.isAddress(generatedSeed), true); + assertEquals(generatedSeed.length(), Constants.SEED_LENGTH_MAX); + } +}