mirror of
https://github.com/gosticks/iota.lib.java.git
synced 2025-10-16 11:45:37 +00:00
first GetNewAddress implementation
This commit is contained in:
parent
87bbf9c474
commit
b7b45bb74f
@ -11,6 +11,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import jota.dto.request.*;
|
||||
import jota.dto.response.*;
|
||||
import jota.utils.IotaAPIUtils;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -7,4 +7,10 @@ public class GetNewAddressResponse extends AbstractResponse {
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public static GetNewAddressResponse create(String address) {
|
||||
GetNewAddressResponse res = new GetNewAddressResponse();
|
||||
res.address = address;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
130
src/main/java/jota/utils/Converter.java
Normal file
130
src/main/java/jota/utils/Converter.java
Normal file
@ -0,0 +1,130 @@
|
||||
package jota.utils;
|
||||
|
||||
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;
|
||||
|
||||
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][];
|
||||
|
||||
public static final String TRYTE_ALPHABET = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
static {
|
||||
|
||||
final int[] trits = new int[NUMBER_OF_TRITS_IN_A_BYTE];
|
||||
|
||||
for (int i = 0; i < 243; i++) {
|
||||
BYTE_TO_TRITS_MAPPINGS[i] = Arrays.copyOf(trits, NUMBER_OF_TRITS_IN_A_BYTE);
|
||||
increment(trits, NUMBER_OF_TRITS_IN_A_BYTE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 27; i++) {
|
||||
TRYTE_TO_TRITS_MAPPINGS[i] = Arrays.copyOf(trits, NUMBER_OF_TRITS_IN_A_TRYTE);
|
||||
increment(trits, NUMBER_OF_TRITS_IN_A_TRYTE);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] bytes(final int[] trits, final int offset, final int size) {
|
||||
|
||||
final byte[] bytes = new byte[(size + NUMBER_OF_TRITS_IN_A_BYTE - 1) / NUMBER_OF_TRITS_IN_A_BYTE];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
|
||||
int value = 0;
|
||||
for (int j = (size - i * NUMBER_OF_TRITS_IN_A_BYTE) < 5 ? (size - i * NUMBER_OF_TRITS_IN_A_BYTE) : NUMBER_OF_TRITS_IN_A_BYTE; j-- > 0; ) {
|
||||
value = value * RADIX + trits[offset + i * NUMBER_OF_TRITS_IN_A_BYTE + j];
|
||||
}
|
||||
bytes[i] = (byte)value;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static byte[] bytes(final int[] trits) {
|
||||
return bytes(trits, 0, trits.length);
|
||||
}
|
||||
|
||||
public static void getTrits(final byte[] bytes, final int[] trits) {
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < bytes.length && offset < trits.length; i++) {
|
||||
System.arraycopy(BYTE_TO_TRITS_MAPPINGS[bytes[i] < 0 ? (bytes[i] + BYTE_TO_TRITS_MAPPINGS.length) : bytes[i]], 0, trits, offset, trits.length - offset < NUMBER_OF_TRITS_IN_A_BYTE ? (trits.length - offset) : NUMBER_OF_TRITS_IN_A_BYTE);
|
||||
offset += NUMBER_OF_TRITS_IN_A_BYTE;
|
||||
}
|
||||
while (offset < trits.length) {
|
||||
trits[offset++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] trits(final String trytes) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return trits;
|
||||
}
|
||||
|
||||
public static void copyTrits(final long value, final int[] destination, final int offset, final int size) {
|
||||
|
||||
long absoluteValue = value < 0 ? -value : value;
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
||||
int remainder = (int)(absoluteValue % RADIX);
|
||||
absoluteValue /= RADIX;
|
||||
if (remainder > MAX_TRIT_VALUE) {
|
||||
|
||||
remainder = MIN_TRIT_VALUE;
|
||||
absoluteValue++;
|
||||
}
|
||||
destination[offset + i] = remainder;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
destination[offset + i] = -destination[offset + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String trytes(final int[] trits, final int offset, final int size) {
|
||||
|
||||
StringBuilder trytes = new StringBuilder();
|
||||
for (int i = 0; i < (size + NUMBER_OF_TRITS_IN_A_TRYTE - 1) / NUMBER_OF_TRITS_IN_A_TRYTE; i++) {
|
||||
|
||||
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();
|
||||
}
|
||||
trytes.append(TRYTE_ALPHABET.charAt(j));
|
||||
}
|
||||
return trytes.toString();
|
||||
}
|
||||
|
||||
public static String trytes(final int[] trits) {
|
||||
return trytes(trits, 0, trits.length);
|
||||
}
|
||||
|
||||
public static int tryteValue(final int[] trits, final int offset) {
|
||||
return trits[offset] + trits[offset + 1] * 3 + trits[offset + 2] * 9;
|
||||
}
|
||||
|
||||
public static void increment(final int[] trits, final int size) {
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (++trits[i] > Converter.MAX_TRIT_VALUE) {
|
||||
trits[i] = Converter.MIN_TRIT_VALUE;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/main/java/jota/utils/Curl.java
Normal file
60
src/main/java/jota/utils/Curl.java
Normal file
@ -0,0 +1,60 @@
|
||||
package jota.utils;
|
||||
|
||||
/**
|
||||
* (c) 2016 Come-from-Beyond
|
||||
*
|
||||
* Curl belongs to the sponge function family.
|
||||
*
|
||||
*/
|
||||
public class Curl {
|
||||
|
||||
public static final int HASH_LENGTH = 243;
|
||||
private static final int STATE_LENGTH = 3 * HASH_LENGTH;
|
||||
|
||||
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];
|
||||
|
||||
public void absorb(final int[] trits, int offset, int length) {
|
||||
|
||||
do {
|
||||
System.arraycopy(trits, offset, state, 0, length < HASH_LENGTH ? length : HASH_LENGTH);
|
||||
transform();
|
||||
offset += HASH_LENGTH;
|
||||
} while ((length -= HASH_LENGTH) > 0);
|
||||
}
|
||||
|
||||
public int [] squeeze(final int[] trits, int offset, int length) {
|
||||
|
||||
do {
|
||||
System.arraycopy(state, 0, trits, offset, length < HASH_LENGTH ? length : HASH_LENGTH);
|
||||
transform();
|
||||
offset += HASH_LENGTH;
|
||||
} while ((length -= HASH_LENGTH) > 0);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private void transform() {
|
||||
|
||||
final int[] scratchpad = new int[STATE_LENGTH];
|
||||
int scratchpadIndex = 0;
|
||||
for (int round = 0; round < NUMBER_OF_ROUNDS; round++) {
|
||||
System.arraycopy(state, 0, scratchpad, 0, STATE_LENGTH);
|
||||
for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) {
|
||||
state[stateIndex] = TRUTH_TABLE[scratchpad[scratchpadIndex] + scratchpad[scratchpadIndex += (scratchpadIndex < 365 ? 364 : -365)] * 3 + 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
for (int stateIndex = 0; stateIndex < STATE_LENGTH; stateIndex++) {
|
||||
state[stateIndex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getState() {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package jota;
|
||||
package jota.utils;
|
||||
|
||||
import jota.dto.response.GetBundleResponse;
|
||||
import jota.dto.response.GetNewAddressResponse;
|
||||
@ -15,8 +15,14 @@ public class IotaAPIUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(IotaAPIUtils.class);
|
||||
|
||||
public static GetNewAddressResponse getNewAddress(final String seed, final Integer securityLevel) {
|
||||
throw new NotImplementedException("Not yet implemented");
|
||||
public static GetNewAddressResponse getNewAddress(final String seed, final int index) {
|
||||
|
||||
final int [] key = Signing.key(Converter.trits(seed), index, 2);
|
||||
final int [] digests = Signing.digests(key);
|
||||
final int [] addressTrits = Signing.address(digests);
|
||||
final String address = Converter.trytes(addressTrits);
|
||||
|
||||
return GetNewAddressResponse.create(address);
|
||||
}
|
||||
|
||||
public static GetBundleResponse getBundle(final String transaction) {
|
||||
93
src/main/java/jota/utils/Signing.java
Normal file
93
src/main/java/jota/utils/Signing.java
Normal file
@ -0,0 +1,93 @@
|
||||
package jota.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Signing {
|
||||
|
||||
static int[] key(int [] seed, int index, int length) {
|
||||
|
||||
final int[] subseed = seed;
|
||||
|
||||
for (int i = 0; i < index; i++) {
|
||||
for (int j = 0; j < 243; j++) {
|
||||
if (++subseed[j] > 1) {
|
||||
subseed[j] = -1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Curl curl = new Curl();
|
||||
//curl.absorb(subseed, state);
|
||||
//curl.squeeze(subseed, state);
|
||||
curl.absorb(subseed, 0, subseed.length);
|
||||
|
||||
List<Integer> key = new ArrayList<>();
|
||||
int [] buffer = new int[subseed.length];
|
||||
int offset = 0;
|
||||
|
||||
while (length-- > 0) {
|
||||
|
||||
for (int i = 0; i < 27; i++) {
|
||||
|
||||
curl.squeeze(buffer, 0, buffer.length);
|
||||
for (int j = 0; j < 243; j++) {
|
||||
key.add(buffer[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return to(key);
|
||||
}
|
||||
|
||||
private static int[] to(List<Integer> key) {
|
||||
int a [] = new int[key.size()]; int i = 0;
|
||||
for (Integer v : key) {
|
||||
a[i++] = v;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
public static int [] digests(int [] key) {
|
||||
final Curl curl = new Curl();
|
||||
|
||||
int[] digests = new int[key.length];
|
||||
int[] buffer = new int[key.length];
|
||||
|
||||
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.absorb(buffer, 0, buffer.length);
|
||||
curl.squeeze(buffer, 0, buffer.length);
|
||||
}
|
||||
for (int k = 0; k < 243; k++) {
|
||||
|
||||
keyFragment[j * 243 + k] = buffer[k];
|
||||
}
|
||||
}
|
||||
|
||||
curl.absorb(keyFragment, 0, keyFragment.length);
|
||||
curl.squeeze(buffer, 0, buffer.length);
|
||||
|
||||
for (int j = 0; j < 243; j++) {
|
||||
digests[i * 243 + j] = buffer[j];
|
||||
}
|
||||
}
|
||||
return digests;
|
||||
}
|
||||
|
||||
public static int [] address(int [] digests) {
|
||||
final Curl curl = new Curl();
|
||||
int [] address = new int[digests.length];
|
||||
curl.absorb(digests, 0, digests.length);
|
||||
curl.squeeze(address, 0, address.length);
|
||||
return address;
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import static org.junit.Assert.assertThat;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import jota.dto.response.*;
|
||||
import jota.utils.IotaAPIUtils;
|
||||
import org.hamcrest.core.IsNull;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
@ -122,5 +123,11 @@ public class IotaAPIProxyTest {
|
||||
IotaAPIProxy proxy = new IotaAPIProxy.Builder().build();
|
||||
assertThat(proxy, IsNull.notNullValue());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldCreateANewAddress() {
|
||||
GetNewAddressResponse res = IotaAPIUtils.getNewAddress(TEST_SEED, 2);
|
||||
System.err.println(res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user