From 84b60fbe6974cbb9e022f363ddbcdf5160d9fb74 Mon Sep 17 00:00:00 2001 From: AZ Date: Fri, 9 Dec 2016 18:29:22 +0100 Subject: [PATCH] [WIP ]tryfix getInputs, start to finish the god damn java lib --- src/main/java/jota/IotaAPIProxy.java | 466 ++++++++++++++---- .../dto/response/GetNewAddressResponse.java | 2 +- .../java/jota/error/ArgumentException.java | 14 + src/main/java/jota/error/BaseException.java | 51 ++ .../jota/error/NotEnoughBalanceException.java | 10 + src/main/java/jota/model/Input.java | 48 ++ src/main/java/jota/model/Inputs.java | 39 ++ src/main/java/jota/model/Transaction.java | 53 ++ src/main/java/jota/utils/IotaAPIUtils.java | 71 +++ 9 files changed, 660 insertions(+), 94 deletions(-) create mode 100644 src/main/java/jota/error/ArgumentException.java create mode 100644 src/main/java/jota/error/BaseException.java create mode 100644 src/main/java/jota/error/NotEnoughBalanceException.java create mode 100644 src/main/java/jota/model/Input.java create mode 100644 src/main/java/jota/model/Inputs.java diff --git a/src/main/java/jota/IotaAPIProxy.java b/src/main/java/jota/IotaAPIProxy.java index 617de2e..6a77143 100644 --- a/src/main/java/jota/IotaAPIProxy.java +++ b/src/main/java/jota/IotaAPIProxy.java @@ -2,8 +2,12 @@ package jota; import jota.dto.request.*; import jota.dto.response.*; +import jota.error.ArgumentException; +import jota.error.NotEnoughBalanceException; +import jota.model.*; import jota.utils.IotaAPIUtils; import okhttp3.OkHttpClient; +import org.apache.commons.lang3.NotImplementedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import retrofit2.Call; @@ -14,21 +18,18 @@ import retrofit2.converter.gson.GsonConverterFactory; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Properties; +import java.util.*; import java.util.concurrent.TimeUnit; /** * IotaAPIProxy Builder. Usage: - * + *

* IotaApiProxy api = IotaApiProxy.Builder * .protocol("http") * .nodeAddress("localhost") * .port(12345) * .build(); - * + *

* GetNodeInfoResponse response = api.getNodeInfo(); * * @author davassi @@ -196,7 +197,7 @@ public class IotaAPIProxy { 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 @@ -208,117 +209,396 @@ public class IotaAPIProxy { * @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 */ - public GetNewAddressResponse getNewAddress(final String seed, final int index, final boolean checksum, final int total, final boolean returnAll) { final List allAddresses = new ArrayList<>(); - // Case 1: total - // + // If total number of addresses to generate is supplied, simply generate // and return the list of all addresses - if (total != 0) { - // Increase index with each iteration for (int i = index; i < index + total; i++) { allAddresses.add(IotaAPIUtils.newAddress(seed, i, checksum)); } return GetNewAddressResponse.create(allAddresses); } - - // Case 2: no total provided - // - // Continue calling findTransactions to see if address was already created - // if null, return list of addresses - + // No total provided: Continue calling findTransactions to see if address was + // already created if null, return list of addresses for (int i = index; ; i++) { - String newAddress = IotaAPIUtils.newAddress(seed, i, checksum); + final String newAddress = IotaAPIUtils.newAddress(seed, i, checksum); final FindTransactionResponse response = findTransactionsByAddresses(new String[]{newAddress}); - + allAddresses.add(newAddress); - if (response.getHashes().length == 0) { break; } } - // If returnAll, return list of allAddresses - // else return only the last address that was generated + // If !returnAll return only the last address that was generated if (!returnAll) { - allAddresses.subList(0, allAddresses.size()-1).clear(); + allAddresses.subList(0, allAddresses.size() - 1).clear(); } - - return GetNewAddressResponse.create(allAddresses); + return GetNewAddressResponse.create(allAddresses); } - public static class Builder { + public Transaction[] sendTrytes(String[] trytes, int depth, int minWeightMagnitude) { + GetTransactionsToApproveResponse transactionsToApproveResponse = getTransactionsToApprove(depth); - String protocol, host, port; + GetAttachToTangleResponse attachToTangleResponse = + attachToTangle(transactionsToApproveResponse.getTrunkTransaction(), + transactionsToApproveResponse.getBranchTransactionToApprove(), minWeightMagnitude, trytes); - public IotaAPIProxy build() { - - if (protocol == null || host == null || port == null) { - - // check properties files. - if (!checkPropertiesFiles()) { - - // last resort: best effort on enviroment variable, - // before assigning default values. - checkEnviromentVariables(); - } - } - - return new IotaAPIProxy(this); - } - - private boolean checkPropertiesFiles() { - - try { - - FileReader fileReader = new FileReader("node_config.properties"); - BufferedReader bufferedReader = new BufferedReader(fileReader); - - final Properties nodeConfig = new Properties(); - nodeConfig.load(bufferedReader); - - if (nodeConfig.getProperty("iota.node.protocol") != null) { - protocol = nodeConfig.getProperty("iota.node.protocol"); - } - - if (nodeConfig.getProperty("iota.node.host") != null) { - host = nodeConfig.getProperty("iota.node.host"); - } - - if (nodeConfig.getProperty("iota.node.port") != null) { - port = nodeConfig.getProperty("iota.node.port"); - } - - } catch (IOException e1) { - log.debug("node_config.properties not found. Rolling back for another solution..."); - } - return (port != null && protocol != null && host != null); - } - - private void checkEnviromentVariables() { - protocol = env("IOTA_NODE_PROTOCOL", "http"); - host = env("IOTA_NODE_HOST", "localhost"); - port = env("IOTA_NODE_PORT", "14265"); - } - - public Builder host(String host) { - this.host = host; - return this; - } - - public Builder port(String port) { - this.port = port; - return this; - } - - public Builder protocol(String protocol) { - this.protocol = protocol; - return this; - } + broadcastTransactions(attachToTangleResponse.getTrytes()); + return analyzeTransactions(attachToTangleResponse.getTrytes()); } -} + + private Transaction[] analyzeTransactions(String[] trytes) { + throw new NotImplementedException("MISSING"); + } + + /* + public Transaction[] SendTransfer(String seed, int depth, int minWeightMagnitude, Transfer[] transactions, int[] inputs, String address) { + // todo: check what to do with the optional arguments + String[] trytes = prepareTransfers(seed, transactions, inputs, address); + return sendTrytes(trytes, depth, minWeightMagnitude); + }*/ + + public Inputs GetInputs(String seed, Integer start, Integer end, int threshold) throws ArgumentException, NotEnoughBalanceException { + if (start < 0) + start = 0; + + if (end < 0) + end = 0; + + // If start value bigger than end, return error + if (start > end) + throw new ArgumentException(); + + // or if difference between end and start is bigger than 500 keys + if (end - start > 500) + throw new ArgumentException(); + + // Case 1: start and end + // + // If start and end is defined by the user, simply iterate through the keys + // and call getBalances + if (end != 0) { + String[] addresses = new String[end - start]; + + for (int i = start; i < end; i++) { + String address = IotaAPIUtils.newAddress(seed, i, false); + addresses[i] = address; + } + + return getBalancesaAndFormat(addresses, start, end, threshold); + } + + // Case 2: iterate till threshold || end + // + // Either start from index: 0 or start (if defined) until threshold is reached. + // Calls getNewAddress and deterministically generates and returns all addresses + // We then do getBalance, format the output and return it + else { + List addressList = getNewAddress(seed, start, true, 0,true).getAddresses(); + String[] addresses = addressList.toArray(new String[addressList.size()]); + return getBalancesaAndFormat(addresses, start, end, threshold); + } + } + + private Inputs getBalancesaAndFormat(String[] addresses) throws NotEnoughBalanceException{ + return getBalancesaAndFormat(addresses, null, null, null); + } + + private Inputs getBalancesaAndFormat(String[] addresses, Integer start, Integer end, Integer threshold) throws NotEnoughBalanceException { + GetBalancesResponse getBalancesResponse = getBalances(threshold, addresses); + + String[] balances = getBalancesResponse.getBalances(); + + Inputs inputs = new Inputs(new ArrayList(), 0); + + boolean threshholdReached = false; + + for (int i = 0; i < addresses.length; i++) { + if (Long.parseLong(balances[i]) > 0) { + inputs.getInputsList().add(new Input(addresses[i], Long.parseLong(balances[i]), start + i)); + inputs.setTotalBalance(inputs.getTotalBalance() + inputs.getInputsList().get(i).getBalance()); + + if (inputs.getTotalBalance() >= threshold) { + threshholdReached = true; + break; + } + } + } + + if (threshholdReached) + return inputs; + else { + throw new NotEnoughBalanceException(); + } + } + + //public String[] prepareTransfers(String seed, Transfer[] transfers, int[] inputs, String remainderAddress) { + //InputValidator.checkTransferArray(transfers); +/* + // If message or tag is not supplied, provide it + + for (Transfer transfer : transfers) { + + if (transfer.getAddress() == null) + transfer.getMessage().isEmpty(); + if (transfer.getTag() == null) + transfer.getMessage().isEmpty(); + } + + // Create a new bundle + Bundle bundle = new Bundle(); + long totalValue = 0; + List signatureFragments = new ArrayList(); + String tag = ""; + // + // Iterate over all transfers, get totalValue + // and prepare the signatureFragments, message and tag + // + for (Transfer transfer : transfers) { + int signatureMessageLength = 1; + + + // If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction) + if (transfer.getMessage().length() > 2187) { + // Get total length, message / maxLength (2187 trytes) + signatureMessageLength += (int) Math.floor(((double) transfer.getMessage().length() / 2187)); + + String msgCopy = transfer.getMessage(); + + // While there is still a message, copy it + while (msgCopy != null) { + String fragment = msgCopy.substring(0, 2187); + msgCopy = msgCopy.substring(2187, msgCopy.length()); + + // Pad remainder of fragment + for (int j = 0; fragment.length() < 2187; j++) { + fragment += '9'; + } + + signatureFragments.add(fragment); + } + } else { + // Else, get single fragment with 2187 of 9's trytes + String fragment = ""; + + if (transfer.getMessage() != null) { + fragment = transfer.getMessage().substring(0, 2187); + } + + for (int j = 0; fragment.length() < 2187; j++) { + fragment += '9'; + } + + signatureFragments.add(fragment); + } + + // get current timestamp in seconds + // var timestamp = Math.floor(Date.now() / 1000); + long millis = System.currentTimeMillis() / 1000; + + // If no tag defined, get 27 tryte tag. + tag = transfer.getTag() != null ? transfer.getTag() : "999999999999999999999999999"; + + // Pad for required 27 tryte length + for (int j = 0; tag.length() < 27; j++) { + tag += '9'; + } + + // Add first entries to the bundle + // Slice the address in case the user provided a checksummed one + bundle.addEntry(signatureMessageLength, transfer.getAddress().substring(0, 81), transfer.getValue(), tag, millis); + // Sum up total value + totalValue += transfer.getValue(); + } + + // Get inputs if we are sending tokens + if (totalValue != 0) { + // Case 1: user provided inputs + // + // Validate the inputs by calling getBalances + if (inputs != null) { + // Get list if addresses of the provided inputs + + List inputAddresses = new ArrayList(); + for (int input : inputs) { + inputAddresses.add(input); + } + + GetBalancesResponse balances = getBalances(100, inputAddresses); + + List confirmedInputs = new ArrayList(); + + long totalBalance = 0; + for (int i = 0; i < balances.getBalances().length; i++) { + long thisBalance = Long.parseLong(balances.getBalances()[i]); + totalBalance += thisBalance; + + // If input has balance, add it to confirmedInputs + if (thisBalance > 0) { + long inputEl = inputs[i]; + inputEl = thisBalance; + + confirmedInputs.add(inputEl); + } + } + + // Return not enough balance error + if (totalValue > totalBalance) { + throw new NotEnoughBalanceException(totalBalance, totalValue); + } + + addRemainder(seed, confirmedInputs, totalValue, bundle, tag, remainderAddress, signatureFragments); + } + + // Case 2: Get inputs deterministically + // + // If no inputs provided, derive the addresses from the seed and + // confirm that the inputs exceed the threshold + else { + // todo getInputs should trow an exception if not enough balance + addRemainder(seed, GetInputs(seed, null, null, (int) totalValue).InputsList, + totalValue, bundle, tag, remainderAddress, signatureFragments); + } + } else { + // If no input required, don't sign and simply finalize the bundle + bundle.finalize(); + bundle.addTrytes(signatureFragments); + + List bundleTrytes = null; + // todo not sure what to add here + bundle.getLength().forEach(); + tx =>bundleTrytes.add(null); + + bundleTrytes.Reverse(); + return bundleTrytes.toArray(); + } + + // todo not sure what to return here too + return null; + } + + + private void addRemainder(String seed, List inputs, long totalValue, Bundle bundle, String tag, + String remainderAddress, List signatureFragments) { + for (Input input : inputs) { + long thisBalance = input.getBalance(); + long totalTransferValue = totalValue; + long toSubtract = 0 - thisBalance; + long timestamp = (new Date()).getTime(); + + // Add input as bundle entry + bundle.addEntry(2, input.getAddress(), toSubtract, tag, timestamp); + // If there is a remainder value + // Add extra output to send remaining funds to + + if (thisBalance >= totalTransferValue) { + long remainder = thisBalance - totalTransferValue; + + // If user has provided remainder address + // Use it to send remaining funds to + if (remainder > 0 && remainderAddress != null) { + // Remainder bundle entry + bundle.addEntry(1, remainderAddress, remainder, tag, timestamp); + + // Final function for signing inputs + IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments); + } else if (remainder > 0) { + // Generate a new Address by calling getNewAddress + String address = getNewAddress(seed, 0, false, 0, false).getAddresses().get(0); + // Remainder bundle entry + bundle.addEntry(1, address, remainder, tag, timestamp); + + // Final function for signing inputs + IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments); + } else { + // If there is no remainder, do not add transaction to bundle + // simply sign and return + IotaAPIUtils.signInputsAndReturn(seed, inputs, bundle, signatureFragments); + } + + // If multiple inputs provided, subtract the totalTransferValue by + // the inputs balance + } else { + totalTransferValue -= thisBalance; + } + } + } + */ + + public static class Builder { + + String protocol, host, port; + + public IotaAPIProxy build() { + + if (protocol == null || host == null || port == null) { + + // check properties files. + if (!checkPropertiesFiles()) { + + // last resort: best effort on enviroment variable, + // before assigning default values. + checkEnviromentVariables(); + } + } + + return new IotaAPIProxy(this); + } + + private boolean checkPropertiesFiles() { + + try { + + FileReader fileReader = new FileReader("node_config.properties"); + BufferedReader bufferedReader = new BufferedReader(fileReader); + + final Properties nodeConfig = new Properties(); + nodeConfig.load(bufferedReader); + + if (nodeConfig.getProperty("iota.node.protocol") != null) { + protocol = nodeConfig.getProperty("iota.node.protocol"); + } + + if (nodeConfig.getProperty("iota.node.host") != null) { + host = nodeConfig.getProperty("iota.node.host"); + } + + if (nodeConfig.getProperty("iota.node.port") != null) { + port = nodeConfig.getProperty("iota.node.port"); + } + + } catch (IOException e1) { + log.debug("node_config.properties not found. Rolling back for another solution..."); + } + return (port != null && protocol != null && host != null); + } + + private void checkEnviromentVariables() { + protocol = env("IOTA_NODE_PROTOCOL", "http"); + host = env("IOTA_NODE_HOST", "localhost"); + port = env("IOTA_NODE_PORT", "14265"); + } + + public Builder host(String host) { + this.host = host; + return this; + } + + public Builder port(String port) { + this.port = port; + return this; + } + + public Builder protocol(String protocol) { + this.protocol = protocol; + return this; + } + + } + } \ No newline at end of file diff --git a/src/main/java/jota/dto/response/GetNewAddressResponse.java b/src/main/java/jota/dto/response/GetNewAddressResponse.java index b54384d..484e17e 100644 --- a/src/main/java/jota/dto/response/GetNewAddressResponse.java +++ b/src/main/java/jota/dto/response/GetNewAddressResponse.java @@ -12,7 +12,7 @@ public class GetNewAddressResponse extends AbstractResponse { return res; } - public List getAddress() { + public List getAddresses() { return addresses; } } diff --git a/src/main/java/jota/error/ArgumentException.java b/src/main/java/jota/error/ArgumentException.java new file mode 100644 index 0000000..38eefb4 --- /dev/null +++ b/src/main/java/jota/error/ArgumentException.java @@ -0,0 +1,14 @@ +package jota.error; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; + +/** + * 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/Input.java b/src/main/java/jota/model/Input.java new file mode 100644 index 0000000..8e3a53e --- /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; + } +} diff --git a/src/main/java/jota/model/Inputs.java b/src/main/java/jota/model/Inputs.java new file mode 100644 index 0000000..521d51d --- /dev/null +++ b/src/main/java/jota/model/Inputs.java @@ -0,0 +1,39 @@ +package jota.model; + +import com.google.gson.Gson; + +import java.util.List; + +/** + * Created by Adrian on 09.12.2016. + */ +public class Inputs { + private List inputsList; + private long totalBalance; + + public Inputs(List inputsList, long totalBalance) { + this.inputsList = inputsList; + this.totalBalance = totalBalance; + } + + @Override + public String toString() { + return new Gson().toJson(this); + } + + public List getInputsList() { + return inputsList; + } + + public void setInputsList(List inputsList) { + inputsList = inputsList; + } + + public long getTotalBalance() { + return totalBalance; + } + + public void setTotalBalance(long totalBalance) { + totalBalance = totalBalance; + } +} diff --git a/src/main/java/jota/model/Transaction.java b/src/main/java/jota/model/Transaction.java index 4cf5e89..90e7231 100644 --- a/src/main/java/jota/model/Transaction.java +++ b/src/main/java/jota/model/Transaction.java @@ -44,6 +44,59 @@ public class Transaction { return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); } + public void setSignatureMessageChunk(String signatureMessageChunk) { + + this.signatureMessageChunk = signatureMessageChunk; + } + + public void setIndex(String index) { + this.index = index; + } + + public void setApprovalNonce(String approvalNonce) { + this.approvalNonce = approvalNonce; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public void setDigest(String digest) { + this.digest = digest; + } + + public void setType(String type) { + this.type = type; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + public void setTrunkTransaction(String trunkTransaction) { + this.trunkTransaction = trunkTransaction; + } + + public void setBranchTransaction(String branchTransaction) { + this.branchTransaction = branchTransaction; + } + + public void setSignatureNonce(String signatureNonce) { + this.signatureNonce = signatureNonce; + } + + public void setAddress(String address) { + this.address = address; + } + + public void setValue(String value) { + this.value = value; + } + + public void setBundle(String bundle) { + this.bundle = bundle; + } + public String getValue() { return value; } diff --git a/src/main/java/jota/utils/IotaAPIUtils.java b/src/main/java/jota/utils/IotaAPIUtils.java index 3da5c2d..7444800 100644 --- a/src/main/java/jota/utils/IotaAPIUtils.java +++ b/src/main/java/jota/utils/IotaAPIUtils.java @@ -1,10 +1,14 @@ package jota.utils; import jota.dto.response.GetBundleResponse; +import jota.model.Bundle; +import jota.model.Input; import org.apache.commons.lang3.NotImplementedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; + /** * Client Side computation service * @@ -39,5 +43,72 @@ public class IotaAPIUtils { public static GetBundleResponse getBundle(final String transaction) { throw new NotImplementedException("Not yet implemented"); } + + /* + 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 + var firstFragment = key.Take(6561); + + // Get the normalized bundle hash + String normalizedBundleHash = bundle.normalizedBundle(bundleHash); + /* + // First bundle fragment uses 27 trytes + var firstBundleFragment = normalizedBundleHash(27); + + // Calculate the new signatureFragment with the first bundle fragment + var firstSignedFragment = Signing.signatureFragment(firstBundleFragment, firstFragment); + + // Convert signature to trytes and assign the new signatureFragment + bundle.Transactions[i].signatureMessageFragment = Converter.trytes(firstSignedFragment); + + // Because the signature is > 2187 trytes, we need to + // find the second transaction to add the remainder of the signature + for (var j = 0; j < bundle.Transactions.Count; j++) + { + // Same address as well as value = 0 (as we already spent the input) + if (bundle.Transactions[j].Address == thisAddress && bundle.Transactions[j].Value == 0) + { + // Use the second 6562 trits + var secondFragment = key.Skip(6561).Take(6561); + + // The second 27 to 54 trytes of the bundle hash + var secondBundleFragment = normalizedBundleHash.slice(27, 27*2); + + // Calculate the new signature + var secondSignedFragment = Signing.signatureFragment(secondBundleFragment, secondFragment); + + // Convert signature to trytes and assign it again to this bundle entry + bundle.Transactions[j].signatureMessageFragment = Converter.trytes(secondSignedFragment); + } + }*/ + // } + // } }