mirror of
https://github.com/gosticks/iota.lib.java.git
synced 2026-08-31 05:10:25 +00:00
Implemented prepareTransfer and getInputs
This commit is contained in:
@@ -1,28 +1,60 @@
|
||||
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;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jota.dto.request.IotaAttachToTangleRequest;
|
||||
import jota.dto.request.IotaBroadcastTransactionRequest;
|
||||
import jota.dto.request.IotaCommandRequest;
|
||||
import jota.dto.request.IotaFindTransactionsRequest;
|
||||
import jota.dto.request.IotaGetBalancesRequest;
|
||||
import jota.dto.request.IotaGetInclusionStateRequest;
|
||||
import jota.dto.request.IotaGetTransactionsToApproveRequest;
|
||||
import jota.dto.request.IotaGetTrytesRequest;
|
||||
import jota.dto.request.IotaNeighborsRequest;
|
||||
import jota.dto.request.IotaStoreTransactionsRequest;
|
||||
import jota.dto.response.AddNeighborsResponse;
|
||||
import jota.dto.response.BroadcastTransactionsResponse;
|
||||
import jota.dto.response.FindTransactionResponse;
|
||||
import jota.dto.response.GetAttachToTangleResponse;
|
||||
import jota.dto.response.GetBalancesAndFormatResponse;
|
||||
import jota.dto.response.GetBalancesResponse;
|
||||
import jota.dto.response.GetBundleResponse;
|
||||
import jota.dto.response.GetInclusionStateResponse;
|
||||
import jota.dto.response.GetNeighborsResponse;
|
||||
import jota.dto.response.GetNewAddressResponse;
|
||||
import jota.dto.response.GetNodeInfoResponse;
|
||||
import jota.dto.response.GetTipsResponse;
|
||||
import jota.dto.response.GetTransactionsToApproveResponse;
|
||||
import jota.dto.response.GetTrytesResponse;
|
||||
import jota.dto.response.InterruptAttachingToTangleResponse;
|
||||
import jota.dto.response.RemoveNeighborsResponse;
|
||||
import jota.dto.response.StoreTransactionsResponse;
|
||||
import jota.model.Bundle;
|
||||
import jota.model.Input;
|
||||
import jota.model.Transaction;
|
||||
import jota.model.Transfer;
|
||||
import jota.utils.Converter;
|
||||
import jota.utils.InputValidator;
|
||||
import jota.utils.IotaAPIUtils;
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
/**
|
||||
* IotaAPIProxy Builder. Usage:
|
||||
@@ -174,6 +206,10 @@ public class IotaAPIProxy {
|
||||
final Call<GetBalancesResponse> res = service.getBalances(IotaGetBalancesRequest.createIotaGetBalancesRequest(threshold, addresses));
|
||||
return wrapCheckedException(res).body();
|
||||
}
|
||||
|
||||
public GetBalancesResponse getBalances(Integer threshold, List<String> addresses) {
|
||||
return getBalances(threshold, addresses.toArray(new String[] {}));
|
||||
}
|
||||
|
||||
public InterruptAttachingToTangleResponse interruptAttachingToTangle() {
|
||||
final Call<InterruptAttachingToTangleResponse> res = service.interruptAttachingToTangle(IotaCommandRequest.createInterruptAttachToTangleRequest());
|
||||
@@ -244,18 +280,20 @@ public class IotaAPIProxy {
|
||||
* newAddress
|
||||
* broadcastAndStore
|
||||
* sendTrytes
|
||||
*
|
||||
getTransactionsObjects
|
||||
findTransactionObjects
|
||||
getLatestInclusion
|
||||
getInputs
|
||||
prepareTransfers
|
||||
sendTransfer
|
||||
replayBundle
|
||||
broadcastBundle
|
||||
getBundle
|
||||
getTransfers
|
||||
getAccountData
|
||||
* prepareTransfers
|
||||
* getInputs
|
||||
|
||||
getTransfers
|
||||
sendTransfer
|
||||
getBundle
|
||||
|
||||
getTransactionsObjects
|
||||
findTransactionObjects
|
||||
getLatestInclusion
|
||||
|
||||
replayBundle
|
||||
broadcastBundle
|
||||
getAccountData
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -272,7 +310,6 @@ public class IotaAPIProxy {
|
||||
throw new IllegalStateException("BroadcastAndStore Illegal state Exception");
|
||||
}
|
||||
return storeTransactions(trytes);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,16 +333,288 @@ public class IotaAPIProxy {
|
||||
throw new IllegalStateException("sendTrytes Illegal state Exception");
|
||||
}
|
||||
|
||||
return Arrays.stream(res.getTrytes())
|
||||
.map(Converter::transactionObject)
|
||||
.collect(Collectors.toList());
|
||||
//return Arrays.stream(res.getTrytes()).map(Converter::transactionObject).collect(Collectors.toList());
|
||||
final List<Transaction> trx = new ArrayList<>();
|
||||
|
||||
for (final String tx : Arrays.asList(res.getTrytes())) {
|
||||
trx.add(Converter.transactionObject(tx));
|
||||
}
|
||||
return trx;
|
||||
}
|
||||
|
||||
public List<Transaction> findAndGetTxs(final String addresses) {
|
||||
|
||||
final FindTransactionResponse res = findTransactionsByAddresses(addresses);
|
||||
return getTransactionsObjects(res.getHashes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for getTrytes and transactionObjects
|
||||
* gets the trytes and transaction object from a list of transaction hashes
|
||||
*
|
||||
* @method getTransactionsObjects
|
||||
* @param {array} hashes
|
||||
* @return
|
||||
* @returns {function} callback
|
||||
* @returns {object} success
|
||||
**/
|
||||
public List<Transaction> getTransactionsObjects(String ... hashes) {
|
||||
|
||||
if (!InputValidator.isArrayOfHashes(hashes)) {
|
||||
throw new IllegalStateException("Not an Array of Hashes: " + Arrays.toString(hashes));
|
||||
}
|
||||
|
||||
final GetTrytesResponse trytesResponse = getTrytes(hashes);
|
||||
|
||||
final List<Transaction> trxs = new ArrayList<>();
|
||||
|
||||
for (final String tryte : trytesResponse.getTrytes()) {
|
||||
trxs.add(Converter.transactionObject(tryte));
|
||||
}
|
||||
return trxs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares transfer by generating bundle, finding and signing inputs
|
||||
*
|
||||
* @method prepareTransfers
|
||||
* @param {string} seed
|
||||
* @param {object} transfers
|
||||
* @param {object} options
|
||||
* @property {array} inputs Inputs used for signing. Needs to have correct keyIndex and address value
|
||||
* @property {string} address Remainder address
|
||||
* @param {function} callback
|
||||
* @return
|
||||
* @returns {array} trytes Returns bundle trytes
|
||||
**/
|
||||
public List<String> prepareTransfers(final String seed, final List<Transfer> transfers, String remainder, List<Input> inputs) {
|
||||
|
||||
// Input validation of transfers object
|
||||
if (!InputValidator.isTransfersCollectionCorrect(transfers)) {
|
||||
throw new IllegalStateException("Invalid Transfer");
|
||||
}
|
||||
|
||||
// Create a new bundle
|
||||
final Bundle bundle = new Bundle();
|
||||
final List<String> signatureFragments = new ArrayList<>();
|
||||
|
||||
int totalValue = 0;
|
||||
String tag;
|
||||
|
||||
// Iterate over all transfers, get totalValue
|
||||
// and prepare the signatureFragments, message and tag
|
||||
for (final 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 += Math.floor(transfer.getMessage().length() / 2187);
|
||||
|
||||
String msgCopy = new String(transfer.getMessage());
|
||||
|
||||
// While there is still a message, copy it
|
||||
while (!msgCopy.isEmpty()) {
|
||||
|
||||
String fragment = StringUtils.substring(msgCopy, 0, 2187);
|
||||
msgCopy = StringUtils.substring(msgCopy, 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 = StringUtils.substring(transfer.getMessage(), 0, 2187);
|
||||
|
||||
for (int j = 0; fragment.length() < 2187; j++) {
|
||||
fragment += '9';
|
||||
}
|
||||
|
||||
signatureFragments.add(fragment);
|
||||
}
|
||||
|
||||
// get current timestamp in seconds
|
||||
long timestamp = (long) Math.floor(Calendar.getInstance().getTimeInMillis() / 1000);
|
||||
|
||||
// If no tag defined, get 27 tryte tag.
|
||||
tag = transfer.getTag().isEmpty() ? "999999999999999999999999999" : transfer.getTag();
|
||||
|
||||
// Pad for required 27 tryte length
|
||||
for (int j = 0; tag.length() < 27; j++) {
|
||||
tag += '9';
|
||||
}
|
||||
|
||||
// Add first entry to the bundle
|
||||
bundle.addEntry(signatureMessageLength, transfer.getAddress(), transfer.getValue(), tag, timestamp);
|
||||
// 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.isEmpty()) {
|
||||
|
||||
// Get list if addresses of the provided inputs
|
||||
List<String> inputsAddresses = new ArrayList<>();
|
||||
for (final Input i : inputs) {
|
||||
inputsAddresses.add(i.getAddress());
|
||||
}
|
||||
|
||||
GetBalancesResponse resbalances = getBalances(100, inputsAddresses);
|
||||
String[] balances = resbalances.getBalances();
|
||||
|
||||
|
||||
List<Input> confirmedInputs = new ArrayList<>();
|
||||
int totalBalance = 0; int i = 0;
|
||||
for (String balance : balances) {
|
||||
long thisBalance = Integer.parseInt(balance);
|
||||
totalBalance += thisBalance;
|
||||
|
||||
// If input has balance, add it to confirmedInputs
|
||||
if (thisBalance > 0) {
|
||||
Input inputEl = inputs.get(i++);
|
||||
inputEl.setBalance(thisBalance);
|
||||
confirmedInputs.add(inputEl);
|
||||
}
|
||||
}
|
||||
|
||||
// Return not enough balance error
|
||||
if (totalValue > totalBalance) {
|
||||
throw new IllegalStateException("Not enough balance");
|
||||
}
|
||||
|
||||
return IotaAPIUtils.signInputsAndReturn(seed, confirmedInputs, bundle, 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 {
|
||||
|
||||
GetBalancesAndFormatResponse newinputs = getInputs(seed, Collections.EMPTY_LIST, 0, 0, totalValue);
|
||||
// If inputs with enough balance
|
||||
return IotaAPIUtils.signInputsAndReturn(seed, newinputs.getInput(), bundle, signatureFragments);
|
||||
}
|
||||
} else {
|
||||
|
||||
// If no input required, don't sign and simply finalize the bundle
|
||||
bundle.finalize();
|
||||
bundle.addTrytes(signatureFragments);
|
||||
|
||||
List<Transaction> trxb = bundle.getTransactions();
|
||||
List<String> bundleTrytes = new ArrayList<>();
|
||||
for (Transaction tx : trxb) {
|
||||
jota.utils.IotaAPIUtils.transactionTrytes(tx);
|
||||
}
|
||||
Collections.reverse(bundleTrytes);
|
||||
return bundleTrytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the inputs of a seed
|
||||
*
|
||||
* @method getInputs
|
||||
* @param {string} seed
|
||||
* @param {object} options
|
||||
* @property {int} start Starting key index
|
||||
* @property {int} end Ending key index
|
||||
* @property {int} threshold Min balance required
|
||||
* @param {function} callback
|
||||
**/
|
||||
public GetBalancesAndFormatResponse getInputs(final String seed, final List<String> balances, int start, int end, int threshold) {
|
||||
|
||||
// validate the seed
|
||||
if (!InputValidator.isTrytes(seed, 0)) {
|
||||
throw new IllegalStateException("Invalid Seed");
|
||||
}
|
||||
|
||||
// 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 inputs provided");
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
List<String> allAddresses = new ArrayList<>();
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
|
||||
String address = IotaAPIUtils.newAddress(seed, i, false);
|
||||
allAddresses.add(address);
|
||||
}
|
||||
|
||||
return getBalanceAndFormat(allAddresses, balances, threshold, start, end);
|
||||
}
|
||||
// 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 {
|
||||
|
||||
final GetNewAddressResponse res = getNewAddress(seed, start, false, 0, true);
|
||||
return getBalanceAndFormat(res.getAddresses(), balances, threshold, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls getBalances and formats the output
|
||||
// returns the final inputsObject then
|
||||
public GetBalancesAndFormatResponse getBalanceAndFormat(final List<String> addresses,
|
||||
final List<String> balances, long threshold, int start, int end) {
|
||||
|
||||
GetBalancesResponse bres = getBalances(100, addresses);
|
||||
|
||||
// If threshold defined, keep track of whether reached or not
|
||||
// else set default to true
|
||||
boolean thresholdReached = threshold != 0 ? false : true; int i = -1;
|
||||
|
||||
List<Input> inputs = new ArrayList<>();
|
||||
long totalBalance = 0;
|
||||
|
||||
for (String address : addresses) {
|
||||
|
||||
long balance = Long.parseLong(balances.get(++i));
|
||||
|
||||
if (balance > 0) {
|
||||
final Input newEntry = new Input(address, balance, start+i);
|
||||
|
||||
inputs.add(newEntry);
|
||||
// Increase totalBalance of all aggregated inputs
|
||||
totalBalance += balance;
|
||||
|
||||
if (thresholdReached == false && totalBalance >= threshold) {
|
||||
thresholdReached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thresholdReached) {
|
||||
return GetBalancesAndFormatResponse.create(inputs, totalBalance);
|
||||
}
|
||||
throw new IllegalStateException("Not enough balance");
|
||||
}
|
||||
|
||||
public GetBundleResponse getBundle(String transaction) {
|
||||
return null; //IotaAPIUtils.getBundle(transaction);
|
||||
}
|
||||
|
||||
|
||||
public static class Builder {
|
||||
|
||||
String protocol, host, port;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package jota.dto.response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jota.model.Input;
|
||||
|
||||
public class GetBalancesAndFormatResponse extends AbstractResponse {
|
||||
|
||||
private List<Input> input;
|
||||
private long totalBalance;
|
||||
|
||||
public List<Input> getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
public void setInput(List<Input> input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
public long getTotalBalance() {
|
||||
return totalBalance;
|
||||
}
|
||||
|
||||
public void setTotalBalance(long totalBalance) {
|
||||
this.totalBalance = totalBalance;
|
||||
}
|
||||
|
||||
public static GetBalancesAndFormatResponse create(List<Input> inputs, long totalBalance2) {
|
||||
GetBalancesAndFormatResponse res = new GetBalancesAndFormatResponse();
|
||||
res.setInput(inputs);
|
||||
res.setTotalBalance(totalBalance2);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ public class GetNewAddressResponse extends AbstractResponse {
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<String> getAddress() {
|
||||
public List<String> getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package jota.model;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 02.12.16.
|
||||
@@ -11,21 +10,33 @@ public class Transfer {
|
||||
private String timestamp;
|
||||
private String address;
|
||||
private String hash;
|
||||
private Integer persistence;
|
||||
private Boolean persistence;
|
||||
private long value;
|
||||
private String message;
|
||||
private String tag;
|
||||
|
||||
public Transfer(String timestamp, String address, String hash, Integer persistence, long value) {
|
||||
|
||||
public Transfer(String timestamp, String address, String hash, Boolean persistence, long value, String message,
|
||||
String tag) {
|
||||
this.timestamp = timestamp;
|
||||
this.address = address;
|
||||
this.hash = hash;
|
||||
this.persistence = persistence;
|
||||
this.value = value;
|
||||
this.message = message;
|
||||
this.tag = tag;
|
||||
|
||||
}
|
||||
|
||||
public Transfer(String address, long value, String message, String tag) {
|
||||
this.address = address;
|
||||
this.value = value;
|
||||
this.message = message;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
return new Gson().toJson(this);
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
@@ -36,7 +47,7 @@ public class Transfer {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public Integer getPersistence() {
|
||||
public Boolean getPersistence() {
|
||||
return persistence;
|
||||
}
|
||||
|
||||
@@ -47,4 +58,40 @@ public class Transfer {
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setTimestamp(String timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public void setHash(String hash) {
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
public void setPersistence(Boolean persistence) {
|
||||
this.persistence = persistence;
|
||||
}
|
||||
|
||||
public void setValue(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void setTag(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,10 @@ public class Curl {
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Curl absorb(final int[] trits) {
|
||||
return absorb(trits, 0, trits.length);
|
||||
}
|
||||
|
||||
public Curl transform() {
|
||||
|
||||
@@ -56,6 +60,10 @@ public class Curl {
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public int[] squeeze(final int[] trits) {
|
||||
return squeeze(trits, 0, trits.length);
|
||||
}
|
||||
|
||||
public int[] getState() {
|
||||
return state;
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package jota.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import jota.model.Transaction;
|
||||
import jota.model.Transfer;
|
||||
|
||||
/**
|
||||
* Created by pinpong on 02.12.16.
|
||||
*/
|
||||
@@ -20,6 +27,10 @@ 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 isValue(final String value) {
|
||||
return StringUtils.isNumeric(value);
|
||||
}
|
||||
|
||||
public static boolean isArrayOfHashes(String[] hashes) {
|
||||
if (hashes == null) return false;
|
||||
@@ -38,4 +49,40 @@ public class InputValidator {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if input is correct hash collections
|
||||
*
|
||||
* @method isTransfersArray
|
||||
* @param {array} hash
|
||||
* @returns {boolean}
|
||||
**/
|
||||
public static boolean isTransfersCollectionCorrect(final List<Transfer> transfers) {
|
||||
|
||||
for (final Transfer transfer : transfers) {
|
||||
if (!isTransfersArray(transfer)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isTransfersArray(final Transfer transfer) {
|
||||
|
||||
if (!isAddress(transfer.getAddress())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if message is correct trytes of any length
|
||||
if (!isTrytes(transfer.getMessage(), 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if tag is correct trytes of {0,27} trytes
|
||||
if (!isTrytes(transfer.getTag(), 27)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +77,10 @@ public class IotaAPIUtils {
|
||||
+ trx.getNonce();
|
||||
}
|
||||
|
||||
public static List<String> signInputsAndReturn(String seed, List<Input> inputs, Bundle bundle,
|
||||
List<String> signatureFragments) {
|
||||
public static List<String> signInputsAndReturn(final String seed,
|
||||
final List<Input> inputs,
|
||||
final Bundle bundle,
|
||||
final List<String> signatureFragments) {
|
||||
bundle.finalize();
|
||||
bundle.addTrytes(signatureFragments);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import jota.model.Bundle;
|
||||
import jota.pow.Curl;
|
||||
|
||||
public class Signing {
|
||||
@@ -52,35 +53,6 @@ public class Signing {
|
||||
return a;
|
||||
}
|
||||
|
||||
public static int[] digests(int[] key) {
|
||||
final Curl curl = new Curl();
|
||||
|
||||
int[] digests = new int[(int) Math.floor(key.length / 6561) * 243];
|
||||
int[] buffer = new int[243];
|
||||
|
||||
for (int i = 0; i < Math.floor(key.length / 6561); i++) {
|
||||
int[] keyFragment = Arrays.copyOfRange(key, i * 6561, (i + 1) * 6561);
|
||||
|
||||
for (int j = 0; j < 27; j++) {
|
||||
|
||||
buffer = Arrays.copyOfRange(keyFragment, j * 243, (j + 1) * 243);
|
||||
for (int k = 0; k < 26; k++) {
|
||||
curl.reset();
|
||||
curl.absorb(buffer, 0, buffer.length);
|
||||
curl.squeeze(buffer, 0, buffer.length);
|
||||
}
|
||||
System.arraycopy(buffer, 0, keyFragment, j * 243, 243);
|
||||
}
|
||||
|
||||
curl.reset();
|
||||
curl.absorb(keyFragment, 0, keyFragment.length);
|
||||
curl.squeeze(buffer, 0, buffer.length);
|
||||
|
||||
System.arraycopy(buffer, 0, digests, i * 243, 243);
|
||||
}
|
||||
return digests;
|
||||
}
|
||||
|
||||
public static int[] signatureFragment(int[] normalizedBundleFragment, int[] keyFragment) {
|
||||
|
||||
int[] signatureFragment = keyFragment;
|
||||
@@ -109,9 +81,91 @@ public class Signing {
|
||||
public static int[] address(int[] digests) {
|
||||
final Curl curl = new Curl();
|
||||
int[] address = new int[243];
|
||||
curl.reset();
|
||||
curl.absorb(digests, 0, digests.length);
|
||||
curl.squeeze(address, 0, address.length);
|
||||
curl.reset()
|
||||
.absorb(digests)
|
||||
.squeeze(address);
|
||||
return address;
|
||||
}
|
||||
|
||||
public static int[] digests(int[] key) {
|
||||
final Curl curl = new Curl();
|
||||
|
||||
int[] digests = new int[(int) Math.floor(key.length / 6561) * 243];
|
||||
int[] buffer = new int[243];
|
||||
|
||||
for (int i = 0; i < Math.floor(key.length / 6561); i++) {
|
||||
int[] keyFragment = Arrays.copyOfRange(key, i * 6561, (i + 1) * 6561);
|
||||
|
||||
for (int j = 0; j < 27; j++) {
|
||||
|
||||
buffer = Arrays.copyOfRange(keyFragment, j * 243, (j + 1) * 243);
|
||||
for (int k = 0; k < 26; k++) {
|
||||
curl.reset()
|
||||
.absorb(buffer)
|
||||
.squeeze(buffer);
|
||||
}
|
||||
System.arraycopy(buffer, 0, keyFragment, j * 243, 243);
|
||||
}
|
||||
|
||||
curl.reset();
|
||||
curl.absorb(keyFragment, 0, keyFragment.length);
|
||||
curl.squeeze(buffer, 0, buffer.length);
|
||||
|
||||
System.arraycopy(buffer, 0, digests, i * 243, 243);
|
||||
}
|
||||
return digests;
|
||||
}
|
||||
|
||||
public static int[] digest(int[] normalizedBundleFragment, int[] signatureFragment) {
|
||||
|
||||
int[] buffer = new int[243];
|
||||
|
||||
Curl curl = new Curl().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; ) {
|
||||
|
||||
new Curl().reset()
|
||||
.absorb(buffer)
|
||||
.squeeze(buffer);
|
||||
}
|
||||
curl.absorb(buffer);
|
||||
}
|
||||
curl.squeeze(buffer);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static Boolean validateSignatures(String expectedAddress, String[] signatureFragments, String bundleHash) {
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
int[][] normalizedBundleFragments = new int[3][27];
|
||||
int[] normalizedBundleHash = bundle.normalizedBundle(bundleHash);
|
||||
|
||||
// Split hash into 3 fragments
|
||||
for (int i = 0; i < 3; i++) {
|
||||
normalizedBundleFragments[i] = Arrays.copyOfRange(normalizedBundleHash, i * 27, (i + 1) * 27);
|
||||
}
|
||||
|
||||
// Get digests
|
||||
int[] digests = new int[signatureFragments.length * 243 + 243];
|
||||
|
||||
for (int i = 0; i < signatureFragments.length; i++) {
|
||||
|
||||
int[] digestBuffer = digest(normalizedBundleFragments[i % 3], Converter.trits(signatureFragments[i]));
|
||||
|
||||
for (int j = 0; j < 243; j++) {
|
||||
|
||||
digests[i * 243 + j] = digestBuffer[j];
|
||||
}
|
||||
}
|
||||
|
||||
String address = Converter.trytes(address(digests));
|
||||
|
||||
return (expectedAddress.equals(address));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user