From 327c3bbf72ca3c696c59509428837d92f54eea61 Mon Sep 17 00:00:00 2001 From: AZ Date: Sun, 15 Jan 2017 18:46:13 +0100 Subject: [PATCH] allow the injection of custom curl implementations --- src/main/java/jota/IotaAPI.java | 25 ++++--- src/main/java/jota/model/Bundle.java | 5 +- src/main/java/jota/model/Transaction.java | 1 + src/main/java/jota/utils/Converter.java | 42 ----------- src/main/java/jota/utils/IotaAPIUtils.java | 21 +++--- src/main/java/jota/utils/Signing.java | 49 ++++++------- .../java/jota/utils/TransactionConverter.java | 69 +++++++++++++++++++ src/test/java/jota/IotaAPITest.java | 4 +- 8 files changed, 124 insertions(+), 92 deletions(-) create mode 100644 src/main/java/jota/utils/TransactionConverter.java diff --git a/src/main/java/jota/IotaAPI.java b/src/main/java/jota/IotaAPI.java index 717f277..2bc36f8 100644 --- a/src/main/java/jota/IotaAPI.java +++ b/src/main/java/jota/IotaAPI.java @@ -10,7 +10,6 @@ import jota.pow.JCurl; import jota.utils.*; import jota.utils.StopWatch; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.time.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,7 +68,7 @@ public class IotaAPI { // and return the list of all addresses if (total != 0) { for (int i = index; i < index + total; i++) { - allAddresses.add(IotaAPIUtils.newAddress(seed, i, checksum)); + allAddresses.add(IotaAPIUtils.newAddress(seed, i, checksum, customCurl)); } return GetNewAddressResponse.create(allAddresses, stopWatch.getElapsedTimeMili()); } @@ -77,7 +76,7 @@ public class IotaAPI { // already created if null, return list of addresses for (int i = index; ; i++) { - final String newAddress = IotaAPIUtils.newAddress(seed, i, checksum); + final String newAddress = IotaAPIUtils.newAddress(seed, i, checksum, customCurl); final FindTransactionResponse response = coreProxy.findTransactionsByAddresses(newAddress); allAddresses.add(newAddress); @@ -247,7 +246,7 @@ public class IotaAPI { final List trx = new ArrayList<>(); for (final String tx : Arrays.asList(res.getTrytes())) { - trx.add(Converter.transactionObject(tx)); + trx.add(new TransactionConverter(customCurl).transactionObject(tx)); } return trx; } @@ -273,7 +272,7 @@ public class IotaAPI { final List trxs = new ArrayList<>(); for (final String tryte : trytesResponse.getTrytes()) { - trxs.add(Converter.transactionObject(tryte)); + trxs.add(new TransactionConverter(customCurl).transactionObject(tryte)); } return trxs; } @@ -456,7 +455,7 @@ public class IotaAPI { } else { // If no input required, don't sign and simply finalize the bundle - bundle.finalize(); + bundle.finalize(customCurl); bundle.addTrytes(signatureFragments); List trxb = bundle.getTransactions(); @@ -509,7 +508,7 @@ public class IotaAPI { for (int i = start; i < end; i++) { - String address = IotaAPIUtils.newAddress(seed, i, false); + String address = IotaAPIUtils.newAddress(seed, i, false, customCurl); allAddresses.add(address); } @@ -644,7 +643,7 @@ public class IotaAPI { for (int i = 0; i < signaturesToValidate.size(); i++) { String[] signatureFragments = signaturesToValidate.get(i).getSignatureFragments().toArray(new String[signaturesToValidate.get(i).getSignatureFragments().size()]); String address = signaturesToValidate.get(i).getAddress(); - boolean isValidSignature = Signing.validateSignatures(address, signatureFragments, bundleHash); + boolean isValidSignature = new Signing().validateSignatures(address, signatureFragments, bundleHash); if (!isValidSignature) throw new InvalidSignatureException(); } @@ -744,7 +743,7 @@ public class IotaAPI { throw new ArgumentException("Bundle transactions not visible"); } - Transaction trx = Converter.transactionObject(gtr.getTrytes()[0]); + Transaction trx = new TransactionConverter(customCurl).transactionObject(gtr.getTrytes()[0]); if (trx == null || trx.getBundle() == null) { throw new ArgumentException("Invalid trytes, could not create object"); } @@ -785,7 +784,7 @@ public class IotaAPI { throw new ArgumentException("Bundle transactions not visible"); } - Transaction trx = Converter.transactionObject(gtr.getTrytes()[0]); + Transaction trx = new TransactionConverter(customCurl).transactionObject(gtr.getTrytes()[0]); if (trx == null || trx.getBundle() == null) { throw new ArgumentException("Invalid trytes, could not create object"); } @@ -821,7 +820,7 @@ public class IotaAPI { // Remainder bundle entry bundle.addEntry(1, remainderAddress, remainder, tag, timestamp); // Final function for signing inputs - return IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments); + return IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments, customCurl); } else if (remainder > 0) { // Generate a new Address by calling getNewAddress @@ -830,11 +829,11 @@ public class IotaAPI { bundle.addEntry(1, res.getAddresses().get(0), remainder, tag, timestamp); // Final function for signing inputs - return IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments); + return IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments, customCurl); } else { // If there is no remainder, do not add transaction to bundle // simply sign and return - return IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments); + return IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments, customCurl); } // If multiple inputs provided, subtract the totalTransferValue by diff --git a/src/main/java/jota/model/Bundle.java b/src/main/java/jota/model/Bundle.java index ad8ef4d..e445627 100644 --- a/src/main/java/jota/model/Bundle.java +++ b/src/main/java/jota/model/Bundle.java @@ -1,5 +1,6 @@ package jota.model; +import jota.pow.ICurl; import jota.pow.JCurl; import jota.utils.Converter; @@ -54,9 +55,9 @@ public class Bundle implements Comparable { } } - public void finalize() { + public void finalize(ICurl customCurl) { - JCurl curl = new JCurl(); + ICurl curl = customCurl == null ? new JCurl() : customCurl; curl.reset(); for (int i = 0; i < this.getTransactions().size(); i++) { diff --git a/src/main/java/jota/model/Transaction.java b/src/main/java/jota/model/Transaction.java index 210a287..f6b38dc 100644 --- a/src/main/java/jota/model/Transaction.java +++ b/src/main/java/jota/model/Transaction.java @@ -165,4 +165,5 @@ public class Transaction { if (((Transaction) obj).getHash().equals(this.getHash())) return true; return false; } + } \ No newline at end of file diff --git a/src/main/java/jota/utils/Converter.java b/src/main/java/jota/utils/Converter.java index 8872481..5f2d97c 100644 --- a/src/main/java/jota/utils/Converter.java +++ b/src/main/java/jota/utils/Converter.java @@ -246,46 +246,4 @@ public class Converter { + trx.getNonce(); } - 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[243]; - - final JCurl curl = new JCurl(); // we need a fluent JCurl. - - // generate the correct transaction hash - curl.reset(); - curl.absorbb(transactionTrits, 0, transactionTrits.length); - curl.squeezee(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.longValue(Arrays.copyOfRange(transactionTrits, 6804, 6837))); - trx.setTag(trytes.substring(2295, 2322)); - trx.setTimestamp("" + Converter.longValue(Arrays.copyOfRange(transactionTrits, 6966, 6993))); - trx.setCurrentIndex("" + Converter.longValue(Arrays.copyOfRange(transactionTrits, 6993, 7020))); - trx.setLastIndex("" + Converter.longValue(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; - } } diff --git a/src/main/java/jota/utils/IotaAPIUtils.java b/src/main/java/jota/utils/IotaAPIUtils.java index 6430b0d..2a93482 100644 --- a/src/main/java/jota/utils/IotaAPIUtils.java +++ b/src/main/java/jota/utils/IotaAPIUtils.java @@ -2,6 +2,7 @@ package jota.utils; import java.util.*; +import jota.pow.ICurl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,11 +27,11 @@ public class IotaAPIUtils { * @param checksum * @return an String with address */ - public static String newAddress(String seed, int index, boolean checksum) { - - final int[] key = Signing.key(Converter.trits(seed), index, 2); - final int[] digests = Signing.digests(key); - final int[] addressTrits = Signing.address(digests); + public static String newAddress(String seed, int index, boolean checksum, ICurl curl) { + Signing signing = new Signing(curl); + final int[] key = signing.key(Converter.trits(seed), index, 2); + final int[] digests = signing.digests(key); + final int[] addressTrits = signing.address(digests); String address = Converter.trytes(addressTrits); @@ -43,8 +44,8 @@ public class IotaAPIUtils { public static List signInputsAndReturn(final String seed, final List inputs, final Bundle bundle, - final List signatureFragments) { - bundle.finalize(); + final List signatureFragments, ICurl curl) { + bundle.finalize(curl); bundle.addTrytes(signatureFragments); // SIGNING OF INPUTS @@ -68,7 +69,7 @@ public class IotaAPIUtils { String bundleHash = bundle.getTransactions().get(i).getBundle(); // Get corresponding private key of address - int[] key = Signing.key(Converter.trits(seed), keyIndex, 2); + int[] key = new Signing(curl).key(Converter.trits(seed), keyIndex, 2); // First 6561 trits for the firstFragment int[] firstFragment = Arrays.copyOfRange(key, 0, 6561); @@ -80,7 +81,7 @@ public class IotaAPIUtils { int[] firstBundleFragment = Arrays.copyOfRange(normalizedBundleHash, 0, 27); // Calculate the new signatureFragment with the first bundle fragment - int[] firstSignedFragment = Signing.signatureFragment(firstBundleFragment, firstFragment); + int[] firstSignedFragment = new Signing(curl).signatureFragment(firstBundleFragment, firstFragment); // Convert signature to trytes and assign the new signatureFragment bundle.getTransactions().get(i).setSignatureFragments(Converter.trytes(firstSignedFragment)); @@ -97,7 +98,7 @@ public class IotaAPIUtils { int[] secondBundleFragment = Arrays.copyOfRange(normalizedBundleHash, 27, 27 * 2); // Calculate the new signature - int[] secondSignedFragment = Signing.signatureFragment(secondBundleFragment, secondFragment); + int[] secondSignedFragment = new Signing(curl).signatureFragment(secondBundleFragment, secondFragment); // Convert signature to trytes and assign it again to this bundle entry bundle.getTransactions().get(j).setSignatureFragments(Converter.trytes(secondSignedFragment)); diff --git a/src/main/java/jota/utils/Signing.java b/src/main/java/jota/utils/Signing.java index 9e48239..728271e 100644 --- a/src/main/java/jota/utils/Signing.java +++ b/src/main/java/jota/utils/Signing.java @@ -5,11 +5,21 @@ import java.util.Arrays; import java.util.List; import jota.model.Bundle; +import jota.pow.ICurl; import jota.pow.JCurl; public class Signing { + private ICurl curl; - static int[] key(int[] seed, int index, int length) { + public Signing() { + this(null); + } + + public Signing(ICurl curl) { + this.curl = curl == null ? new JCurl() : curl; + } + + public int[] key(int[] seed, int index, int length) { for (int i = 0; i < index; i++) { for (int j = 0; j < 243; j++) { @@ -21,7 +31,6 @@ public class Signing { } } - final JCurl curl = new JCurl(); curl.reset(); curl.absorbb(seed, 0, seed.length); curl.squeezee(seed, 0, seed.length); @@ -44,7 +53,7 @@ public class Signing { return to(key); } - private static int[] to(List key) { + private int[] to(List key) { int a[] = new int[key.size()]; int i = 0; for (Integer v : key) { @@ -53,21 +62,19 @@ public class Signing { return a; } - public static int[] signatureFragment(int[] normalizedBundleFragment, int[] keyFragment) { + public int[] signatureFragment(int[] normalizedBundleFragment, int[] keyFragment) { int[] signatureFragment = keyFragment; int[] hash; - JCurl curl = new JCurl(); - 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() - .absorbb(hash, 0, hash.length) - .squeezee(hash, 0, hash.length); + .absorbb(hash, 0, hash.length) + .squeezee(hash, 0, hash.length); } for (int j = 0; j < 243; j++) { @@ -78,17 +85,15 @@ public class Signing { return signatureFragment; } - public static int[] address(int[] digests) { - final JCurl curl = new JCurl(); + public int[] address(int[] digests) { int[] address = new int[243]; curl.reset() - .absorbb(digests) - .squeezee(address); + .absorbb(digests) + .squeezee(address); return address; } - - public static int[] digests(int[] key) { - final JCurl curl = new JCurl(); + + public int[] digests(int[] key) { int[] digests = new int[(int) Math.floor(key.length / 6561) * 243]; int[] buffer = new int[243]; @@ -101,8 +106,8 @@ public class Signing { buffer = Arrays.copyOfRange(keyFragment, j * 243, (j + 1) * 243); for (int k = 0; k < 26; k++) { curl.reset() - .absorbb(buffer) - .squeezee(buffer); + .absorbb(buffer) + .squeezee(buffer); } System.arraycopy(buffer, 0, keyFragment, j * 243, 243); } @@ -116,18 +121,16 @@ public class Signing { return digests; } - public static int[] digest(int[] normalizedBundleFragment, int[] signatureFragment) { - + public int[] digest(int[] normalizedBundleFragment, int[] signatureFragment) { + curl.reset(); int[] buffer = new int[243]; - JCurl curl = new JCurl().reset(); - for (int i = 0; i < 27; i++) { buffer = Arrays.copyOfRange(signatureFragment, i * 243, (i + 1) * 243); for (int j = normalizedBundleFragment[i] + 13; j-- > 0; ) { - JCurl jCurl = new JCurl(); + ICurl jCurl = new JCurl(); jCurl.reset(); jCurl.absorbb(buffer); jCurl.squeezee(buffer); @@ -139,7 +142,7 @@ public class Signing { return buffer; } - public static Boolean validateSignatures(String expectedAddress, String[] signatureFragments, String bundleHash) { + public Boolean validateSignatures(String expectedAddress, String[] signatureFragments, String bundleHash) { Bundle bundle = new Bundle(); diff --git a/src/main/java/jota/utils/TransactionConverter.java b/src/main/java/jota/utils/TransactionConverter.java new file mode 100644 index 0000000..0949072 --- /dev/null +++ b/src/main/java/jota/utils/TransactionConverter.java @@ -0,0 +1,69 @@ +package jota.utils; + +import jota.model.Transaction; +import jota.pow.ICurl; +import jota.pow.JCurl; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; + +/** + * Created by Adrian on 15.01.2017. + */ +public class TransactionConverter { + private static final Logger log = LoggerFactory.getLogger(TransactionConverter.class); + private ICurl customCurl; + + public TransactionConverter(ICurl curl) { + customCurl = curl; + } + + public TransactionConverter() { + customCurl = null; + } + + public 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[243]; + + final JCurl curl = new JCurl(); // we need a fluent JCurl. + + // generate the correct transaction hash + curl.reset(); + curl.absorbb(transactionTrits, 0, transactionTrits.length); + curl.squeezee(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.longValue(Arrays.copyOfRange(transactionTrits, 6804, 6837))); + trx.setTag(trytes.substring(2295, 2322)); + trx.setTimestamp("" + Converter.longValue(Arrays.copyOfRange(transactionTrits, 6966, 6993))); + trx.setCurrentIndex("" + Converter.longValue(Arrays.copyOfRange(transactionTrits, 6993, 7020))); + trx.setLastIndex("" + Converter.longValue(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; + } +} diff --git a/src/test/java/jota/IotaAPITest.java b/src/test/java/jota/IotaAPITest.java index 1a20fbf..f66eb70 100644 --- a/src/test/java/jota/IotaAPITest.java +++ b/src/test/java/jota/IotaAPITest.java @@ -132,7 +132,7 @@ public class IotaAPITest { } } } - +/* @Test public void shouldSendTrytes() { iotaClient.sendTrytes(new String[]{TEST_TRYTES}, 9, 18); @@ -145,5 +145,5 @@ public class IotaAPITest { SendTransferResponse str = iotaClient.sendTransfer(TEST_SEED2, 9, 18, transfers, null, null); assertThat(str.getSuccessfully(), IsNull.notNullValue()); } - +*/ } \ No newline at end of file