@pinpong fixed getNewAddress (#4)

* implemented toTrytes and toStrings

* added TrytesConverterTest

* WIP

* added checksum calculation, pls review

* updated checksum

* updated tests

* fixed getnewaddress
This commit is contained in:
Gianluigi Davassi
2016-12-02 23:57:44 +01:00
committed by GitHub
parent f8111a0997
commit f697dac422
21 changed files with 511 additions and 242 deletions
@@ -1,86 +1,15 @@
package jota.dto.response;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import jota.model.Transaction;
import java.util.ArrayList;
import java.util.List;
public class AnalyzeTransactionResponse extends AbstractResponse {
private Transactions[] transactions;
private List<Transaction> transactions = new ArrayList<Transaction>();
public Transactions[] getTransactions() {
public List<Transaction> getTransactions() {
return transactions;
}
static class Transactions {
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 address;
private String value;
private String bundle;
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
public String getValue() {
return value;
}
public String getDigest() {
return digest;
}
public String getTrunkTransaction() {
return trunkTransaction;
}
public String getTimestamp() {
return timestamp;
}
public String getSignatureNonce() {
return signatureNonce;
}
public String getType() {
return type;
}
public String getAddress() {
return address;
}
public String getApprovalNonce() {
return approvalNonce;
}
public String getBranchTransaction() {
return branchTransaction;
}
public String getBundle() {
return bundle;
}
public String getHash() {
return hash;
}
public String getIndex() {
return index;
}
public String getSignatureMessageChunk() {
return signatureMessageChunk;
}
}
}
@@ -1,93 +1,15 @@
package jota.dto.response;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import jota.model.Transaction;
import java.util.ArrayList;
import java.util.List;
public class GetBundleResponse extends AbstractResponse {
public Transactions[] transactions;
private List<Transaction> transactions = new ArrayList<>();
private String warning;
public String getWarning() {
return warning;
}
public Transactions[] getTransactions() {
public List<Transaction> getTransactions() {
return transactions;
}
public static class Transactions {
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 address;
private String value;
private String bundle;
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
public String getAddress() {
return address;
}
public String getApprovalNonce() {
return approvalNonce;
}
public String getBranchTransaction() {
return branchTransaction;
}
public String getBundle() {
return bundle;
}
public String getDigest() {
return digest;
}
public String getHash() {
return hash;
}
public String getIndex() {
return index;
}
public String getSignatureMessageChunk() {
return signatureMessageChunk;
}
public String getSignatureNonce() {
return signatureNonce;
}
public String getTimestamp() {
return timestamp;
}
public String getType() {
return type;
}
public String getTrunkTransaction() {
return trunkTransaction;
}
public String getValue() {
return value;
}
}
}
@@ -1,34 +1,15 @@
package jota.dto.response;
import jota.model.Neighbor;
import java.util.ArrayList;
import java.util.List;
public class GetNeighborsResponse extends AbstractResponse {
public Neighbors[] neighbors;
private List<Neighbor> neighbors = new ArrayList<>();
public Neighbors[] getNeighbors() {
public List<Neighbor> getNeighbors() {
return neighbors;
}
public static class Neighbors {
private String address;
private Integer numberOfAllTransactions;
private Integer numberOfInvalidTransactions;
private Integer numberOfNewTransactions;
public String getAddress() {
return address;
}
public Integer getNumberOfAllTransactions() {
return numberOfAllTransactions;
}
public Integer getNumberOfInvalidTransactions() {
return numberOfInvalidTransactions;
}
public Integer getNumberOfNewTransactions() {
return numberOfNewTransactions;
}
}
}
@@ -1,46 +1,15 @@
package jota.dto.response;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import jota.model.Transfer;
import java.util.ArrayList;
import java.util.List;
public class GetTransfersResponse extends AbstractResponse {
private Transfers[] transfers;
private List<Transfer> transfers = new ArrayList<>();
public Transfers[] getTransfers() {
public List<Transfer> getTransfers() {
return transfers;
}
public static class Transfers {
private String timestamp;
private String address;
private String hash;
private Integer persistence;
private long value;
public String getAddress() {
return address;
}
public String getHash() {
return hash;
}
public Integer getPersistence() {
return persistence;
}
public String getTimestamp() {
return timestamp;
}
public long getValue() {
return value;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}
}
+36
View File
@@ -0,0 +1,36 @@
package jota.model;
/**
* Created by pinpong on 02.12.16.
*/
public class Neighbor {
private String address;
private Integer numberOfAllTransactions;
private Integer numberOfInvalidTransactions;
private Integer numberOfNewTransactions;
public Neighbor(String address, Integer numberOfAllTransactions, Integer numberOfInvalidTransactions, Integer numberOfNewTransactions) {
this.address = address;
this.numberOfAllTransactions = numberOfAllTransactions;
this.numberOfInvalidTransactions = numberOfInvalidTransactions;
this.numberOfNewTransactions = numberOfNewTransactions;
}
public String getAddress() {
return address;
}
public Integer getNumberOfAllTransactions() {
return numberOfAllTransactions;
}
public Integer getNumberOfInvalidTransactions() {
return numberOfInvalidTransactions;
}
public Integer getNumberOfNewTransactions() {
return numberOfNewTransactions;
}
}
+99
View File
@@ -0,0 +1,99 @@
package jota.model;
import org.apache.commons.lang3.builder.ToStringBuilder;
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 address;
private String value;
private String bundle;
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) {
this.hash = hash;
this.type = type;
this.signatureMessageChunk = signatureMessageChunk;
this.digest = digest;
this.address = address;
this.value = value;
this.timestamp = timestamp;
this.index = index;
this.bundle = bundle;
this.signatureNonce = signatureNonce;
this.approvalNonce = approvalNonce;
this.trunkTransaction = trunkTransaction;
this.branchTransaction = branchTransaction;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
public String getValue() {
return value;
}
public String getDigest() {
return digest;
}
public String getTrunkTransaction() {
return trunkTransaction;
}
public String getTimestamp() {
return timestamp;
}
public String getSignatureNonce() {
return signatureNonce;
}
public String getType() {
return type;
}
public String getAddress() {
return address;
}
public String getApprovalNonce() {
return approvalNonce;
}
public String getBranchTransaction() {
return branchTransaction;
}
public String getBundle() {
return bundle;
}
public String getHash() {
return hash;
}
public String getIndex() {
return index;
}
public String getSignatureMessageChunk() {
return signatureMessageChunk;
}
}
+50
View File
@@ -0,0 +1,50 @@
package jota.model;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* Created by pinpong on 02.12.16.
*/
public class Transfer {
private String timestamp;
private String address;
private String hash;
private Integer persistence;
private long value;
public Transfer(String timestamp, String address, String hash, Integer persistence, long value) {
this.timestamp = timestamp;
this.address = address;
this.hash = hash;
this.persistence = persistence;
this.value = value;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
public String getAddress() {
return address;
}
public String getHash() {
return hash;
}
public Integer getPersistence() {
return persistence;
}
public String getTimestamp() {
return timestamp;
}
public long getValue() {
return value;
}
}
+42
View File
@@ -0,0 +1,42 @@
package jota.utils;
/**
* Created by pinpong on 02.12.16.
*/
public class Checksum {
public static String addChecksum(String address) {
InputValidator.checkAddress(address);
String addressWithChecksum = address;
addressWithChecksum += calculateChecksum(address);
return addressWithChecksum;
}
public static String removeChecksum(String addressWithChecksum) {
if (isAddressWithChecksum(addressWithChecksum)) {
return getAddress(addressWithChecksum);
} else return "";
}
private static String getAddress(String addressWithChecksum) {
return addressWithChecksum.substring(0, Constants.addressLengthWithoutChecksum);
}
public static boolean isValidChecksum(String addressWithChecksum) {
String addressWithoutChecksum = removeChecksum(addressWithChecksum);
String addressWithRecalculateChecksum = addressWithChecksum += calculateChecksum(addressWithoutChecksum);
return addressWithRecalculateChecksum.equals(addressWithChecksum);
}
private static boolean isAddressWithChecksum(String addressWithChecksum) {
return InputValidator.checkAddress(addressWithChecksum) && addressWithChecksum.length() == Constants.addressLengthWithChecksum;
}
public static String calculateChecksum(String address) {
Curl curl = new Curl();
curl.reset();
curl.setState(Converter.copyTrits(address, curl.getState()));
curl.transform();
return Converter.trytes(curl.getState()).substring(0, 9);
}
}
+13
View File
@@ -0,0 +1,13 @@
package jota.utils;
/**
* Created by pinpong on 02.12.16.
*/
public class Constants {
public static final String TRYTE_ALPHABET = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static int addressLengthWithoutChecksum = 81;
public static int addressLengthWithChecksum = 90;
}
+13 -4
View File
@@ -9,7 +9,6 @@ public class Converter {
public static final int NUMBER_OF_TRITS_IN_A_BYTE = 5;
public static final int NUMBER_OF_TRITS_IN_A_TRYTE = 3;
public static final String TRYTE_ALPHABET = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static final int[][] BYTE_TO_TRITS_MAPPINGS = new int[243][];
static final int[][] TRYTE_TO_TRITS_MAPPINGS = new int[27][];
@@ -63,7 +62,7 @@ public class Converter {
final int[] trits = new int[trytes.length() * NUMBER_OF_TRITS_IN_A_TRYTE];
for (int i = 0; i < trytes.length(); i++) {
System.arraycopy(TRYTE_TO_TRITS_MAPPINGS[TRYTE_ALPHABET.indexOf(trytes.charAt(i))], 0, trits, i * NUMBER_OF_TRITS_IN_A_TRYTE, NUMBER_OF_TRITS_IN_A_TRYTE);
System.arraycopy(TRYTE_TO_TRITS_MAPPINGS[Constants.TRYTE_ALPHABET.indexOf(trytes.charAt(i))], 0, trits, i * NUMBER_OF_TRITS_IN_A_TRYTE, NUMBER_OF_TRITS_IN_A_TRYTE);
}
return trits;
@@ -92,6 +91,16 @@ 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 + 1] = TRYTE_TO_TRITS_MAPPINGS[index][1];
destination[i * 3 + 2] = TRYTE_TO_TRITS_MAPPINGS[index][2];
}
return destination;
}
public static String trytes(final int[] trits, final int offset, final int size) {
StringBuilder trytes = new StringBuilder();
@@ -100,9 +109,9 @@ public class Converter {
int j = trits[offset + i * 3] + trits[offset + i * 3 + 1] * 3 + trits[offset + i * 3 + 2] * 9;
if (j < 0) {
j += TRYTE_ALPHABET.length();
j += Constants.TRYTE_ALPHABET.length();
}
trytes.append(TRYTE_ALPHABET.charAt(j));
trytes.append(Constants.TRYTE_ALPHABET.charAt(j));
}
return trytes.toString();
}
+3 -2
View File
@@ -13,7 +13,7 @@ public class Curl {
private static final int NUMBER_OF_ROUNDS = 27;
private static final int[] TRUTH_TABLE = {1, 0, -1, 1, -1, 0, -1, 1, 0};
private final int[] state = new int[STATE_LENGTH];
private int[] state = new int[STATE_LENGTH];
public void absorb(final int[] trits, int offset, int length) {
@@ -35,7 +35,7 @@ public class Curl {
return state;
}
private void transform() {
public void transform() {
final int[] scratchpad = new int[STATE_LENGTH];
int scratchpadIndex = 0;
@@ -56,4 +56,5 @@ public class Curl {
public int[] getState() {
return state;
}
public void setState(int[] state) { this.state = state; }
}
@@ -0,0 +1,22 @@
package jota.utils;
/**
* Created by pinpong on 02.12.16.
*/
public class InputValidator {
public static boolean isAddress(String address) {
return (address.length() == Constants.addressLengthWithoutChecksum ||
address.length() == Constants.addressLengthWithChecksum) && isTrytes(address, address.length());
}
public static boolean checkAddress(String address) {
if (!isAddress(address))
throw new RuntimeException("Invalid address: " + address);
return true;
}
public static boolean isTrytes(String trytes, int length) {
return trytes.matches("^[A-Z9]{" + (length == 0 ? "0," : length) + "}$");
}
}
@@ -18,8 +18,13 @@ public class IotaAPIUtils {
public static GetNewAddressResponse getNewAddress(final String seed, final int index) {
final int[] key = Signing.key(Converter.trits(seed), index, 2);
System.out.println("Length = "+ key.length );
final int[] digests = Signing.digests(key);
System.out.println("Length = "+ digests.length );
final int[] addressTrits = Signing.address(digests);
System.out.println("Length = "+ addressTrits.length );
final String address = Converter.trytes(addressTrits);
return GetNewAddressResponse.create(address);
+11 -7
View File
@@ -21,8 +21,10 @@ public class Signing {
}
Curl curl = new Curl();
//curl.absorb(subseed, state);
//curl.squeeze(subseed, state);
curl.reset();
curl.absorb(subseed, 0, subseed.length);
curl.squeeze(subseed, 0, subseed.length);
curl.reset();
curl.absorb(subseed, 0, subseed.length);
List<Integer> key = new ArrayList<>();
@@ -33,7 +35,7 @@ public class Signing {
for (int i = 0; i < 27; i++) {
curl.squeeze(buffer, 0, buffer.length);
curl.squeeze(buffer, offset, buffer.length);
for (int j = 0; j < 243; j++) {
key.add(buffer[j]);
}
@@ -54,8 +56,8 @@ public class Signing {
public static int[] digests(int[] key) {
final Curl curl = new Curl();
int[] digests = new int[key.length];
int[] buffer = new int[key.length];
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);
@@ -64,7 +66,7 @@ public class Signing {
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);
}
@@ -74,6 +76,7 @@ public class Signing {
}
}
curl.reset();
curl.absorb(keyFragment, 0, keyFragment.length);
curl.squeeze(buffer, 0, buffer.length);
@@ -86,7 +89,8 @@ public class Signing {
public static int[] address(int[] digests) {
final Curl curl = new Curl();
int[] address = new int[digests.length];
int[] address = new int[243];
curl.reset();
curl.absorb(digests, 0, digests.length);
curl.squeeze(address, 0, address.length);
return address;
@@ -0,0 +1,90 @@
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
* <p>
* How the conversion works:
* 2 Trytes === 1 Byte
* There are a total of 27 different tryte values: 9ABCDEFGHIJKLMNOPQRSTUVWXYZ
* <p>
* 1. We get the decimal value of an individual ASCII character
* 2. From the decimal value, we then derive the two tryte values by basically calculating the tryte equivalent (e.g. 100 === 19 + 3 * 27)
* a. The first tryte value is the decimal value modulo 27 (27 trytes)
* 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
* <p>
* EXAMPLES
* 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:
* a. First value: 90 modulo 27 is 9. This is now our first value
* b. Second value: (90 - 9) / 27 is 3. This is our second value.
* 3. Our two values are now 9 and 3. To get the tryte value now we simply insert it as indices into '9ABCDEFGHIJKLMNOPQRSTUVWXYZ'
* a. The first tryte value is '9ABCDEFGHIJKLMNOPQRSTUVWXYZ'[9] === "I"
* b. The second tryte value is '9ABCDEFGHIJKLMNOPQRSTUVWXYZ'[3] === "C"
* Our tryte pair is "IC"
* <p>
* RESULT:
* The ASCII char "Z" is represented as "IC" in trytes.
*/
public static String toTrytes(String inputString) {
String trytes = "";
for (int i = 0; i < inputString.length(); i++) {
char asciiValue = inputString.charAt(i);
// If not recognizable ASCII character, replace with space
if (asciiValue > 255) {
asciiValue = 32;
}
int firstValue = asciiValue % 27;
int secondValue = (asciiValue - firstValue) / 27;
String trytesValue = String.valueOf(Constants.TRYTE_ALPHABET.charAt(firstValue) + String.valueOf(Constants.TRYTE_ALPHABET.charAt(secondValue)));
trytes += trytesValue;
}
return trytes;
}
/**
* Trytes to bytes
* Reverse operation from the byteToTrytes function in send.js
* 2 Trytes == 1 Byte
* We assume that the trytes are a JSON encoded object thus for our encoding:
* First character = {
* Last character = }
* Everything after that is 9's padding
*/
public static String toString(String inputTrytes) {
String string = "";
for (int i = 0; i < inputTrytes.length(); i += 2) {
// get a trytes pair
int firstValue = Constants.TRYTE_ALPHABET.indexOf(inputTrytes.charAt(i));
int secondValue = Constants.TRYTE_ALPHABET.indexOf(inputTrytes.charAt(i + 1));
int decimalValue = firstValue + secondValue * 27;
String character = Character.toString((char) decimalValue);
string += character;
}
return string;
}
}