Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit a8ca265

Browse files
authored
Merge pull request #38 from schierlm/local-pow
Add support for local PoW - thx to @schierlm
2 parents f758fc2 + 2a39e03 commit a8ca265

File tree

5 files changed

+278
-0
lines changed

5 files changed

+278
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
package cfb.pearldiver;
2+
3+
/**
4+
* (c) 2016 Come-from-Beyond.
5+
*
6+
* See <https://github.com/iotaledger/PearlDiver>.
7+
*/
8+
public class PearlDiver {
9+
10+
public static final int TRANSACTION_LENGTH = 8019;
11+
12+
private static final int CURL_HASH_LENGTH = 243;
13+
private static final int CURL_STATE_LENGTH = CURL_HASH_LENGTH * 3;
14+
15+
private static final int RUNNING = 0;
16+
private static final int CANCELLED = 1;
17+
private static final int COMPLETED = 2;
18+
19+
private volatile int state;
20+
21+
public synchronized void cancel() {
22+
23+
state = CANCELLED;
24+
25+
notifyAll();
26+
}
27+
28+
public synchronized boolean search(final int[] transactionTrits, final int minWeightMagnitude, int numberOfThreads) {
29+
30+
if (transactionTrits.length != TRANSACTION_LENGTH) {
31+
32+
throw new RuntimeException("Invalid transaction trits length: " + transactionTrits.length);
33+
}
34+
if (minWeightMagnitude < 0 || minWeightMagnitude > CURL_HASH_LENGTH) {
35+
36+
throw new RuntimeException("Invalid min weight magnitude: " + minWeightMagnitude);
37+
}
38+
39+
state = RUNNING;
40+
41+
final long[] midCurlStateLow = new long[CURL_STATE_LENGTH], midCurlStateHigh = new long[CURL_STATE_LENGTH];
42+
43+
{
44+
for (int i = CURL_HASH_LENGTH; i < CURL_STATE_LENGTH; i++) {
45+
46+
midCurlStateLow[i] = 0b1111111111111111111111111111111111111111111111111111111111111111L;
47+
midCurlStateHigh[i] = 0b1111111111111111111111111111111111111111111111111111111111111111L;
48+
}
49+
50+
int offset = 0;
51+
final long[] curlScratchpadLow = new long[CURL_STATE_LENGTH], curlScratchpadHigh = new long[CURL_STATE_LENGTH];
52+
for (int i = (TRANSACTION_LENGTH - CURL_HASH_LENGTH) / CURL_HASH_LENGTH; i-- > 0; ) {
53+
54+
for (int j = 0; j < CURL_HASH_LENGTH; j++) {
55+
56+
switch (transactionTrits[offset++]) {
57+
58+
case 0: {
59+
60+
midCurlStateLow[j] = 0b1111111111111111111111111111111111111111111111111111111111111111L;
61+
midCurlStateHigh[j] = 0b1111111111111111111111111111111111111111111111111111111111111111L;
62+
63+
} break;
64+
65+
case 1: {
66+
67+
midCurlStateLow[j] = 0b0000000000000000000000000000000000000000000000000000000000000000L;
68+
midCurlStateHigh[j] = 0b1111111111111111111111111111111111111111111111111111111111111111L;
69+
70+
} break;
71+
72+
default: {
73+
74+
midCurlStateLow[j] = 0b1111111111111111111111111111111111111111111111111111111111111111L;
75+
midCurlStateHigh[j] = 0b0000000000000000000000000000000000000000000000000000000000000000L;
76+
}
77+
}
78+
}
79+
80+
transform(midCurlStateLow, midCurlStateHigh, curlScratchpadLow, curlScratchpadHigh);
81+
}
82+
83+
midCurlStateLow[0] = 0b1101101101101101101101101101101101101101101101101101101101101101L;
84+
midCurlStateHigh[0] = 0b1011011011011011011011011011011011011011011011011011011011011011L;
85+
midCurlStateLow[1] = 0b1111000111111000111111000111111000111111000111111000111111000111L;
86+
midCurlStateHigh[1] = 0b1000111111000111111000111111000111111000111111000111111000111111L;
87+
midCurlStateLow[2] = 0b0111111111111111111000000000111111111111111111000000000111111111L;
88+
midCurlStateHigh[2] = 0b1111111111000000000111111111111111111000000000111111111111111111L;
89+
midCurlStateLow[3] = 0b1111111111000000000000000000000000000111111111111111111111111111L;
90+
midCurlStateHigh[3] = 0b0000000000111111111111111111111111111111111111111111111111111111L;
91+
}
92+
93+
if (numberOfThreads <= 0) {
94+
95+
numberOfThreads = Runtime.getRuntime().availableProcessors() - 1;
96+
if (numberOfThreads < 1) {
97+
98+
numberOfThreads = 1;
99+
}
100+
}
101+
102+
while (numberOfThreads-- > 0) {
103+
104+
final int threadIndex = numberOfThreads;
105+
(new Thread(new Runnable() { public void run() {
106+
107+
final long[] midCurlStateCopyLow = new long[CURL_STATE_LENGTH], midCurlStateCopyHigh = new long[CURL_STATE_LENGTH];
108+
System.arraycopy(midCurlStateLow, 0, midCurlStateCopyLow, 0, CURL_STATE_LENGTH);
109+
System.arraycopy(midCurlStateHigh, 0, midCurlStateCopyHigh, 0, CURL_STATE_LENGTH);
110+
for (int i = threadIndex; i-- > 0; ) {
111+
112+
increment(midCurlStateCopyLow, midCurlStateCopyHigh, CURL_HASH_LENGTH / 3, (CURL_HASH_LENGTH / 3) * 2);
113+
}
114+
115+
final long[] curlStateLow = new long[CURL_STATE_LENGTH], curlStateHigh = new long[CURL_STATE_LENGTH];
116+
final long[] curlScratchpadLow = new long[CURL_STATE_LENGTH], curlScratchpadHigh = new long[CURL_STATE_LENGTH];
117+
while (state == RUNNING) {
118+
119+
increment(midCurlStateCopyLow, midCurlStateCopyHigh, (CURL_HASH_LENGTH / 3) * 2, CURL_HASH_LENGTH);
120+
System.arraycopy(midCurlStateCopyLow, 0, curlStateLow, 0, CURL_STATE_LENGTH);
121+
System.arraycopy(midCurlStateCopyHigh, 0, curlStateHigh, 0, CURL_STATE_LENGTH);
122+
transform(curlStateLow, curlStateHigh, curlScratchpadLow, curlScratchpadHigh);
123+
124+
NEXT_BIT_INDEX:
125+
for (int bitIndex = 64; bitIndex-- > 0; ) {
126+
127+
for (int i = minWeightMagnitude; i-- > 0; ) {
128+
129+
if ((((int)(curlStateLow[CURL_HASH_LENGTH - 1 - i] >> bitIndex)) & 1) != (((int)(curlStateHigh[CURL_HASH_LENGTH - 1 - i] >> bitIndex)) & 1)) {
130+
131+
continue NEXT_BIT_INDEX;
132+
}
133+
}
134+
135+
synchronized (PearlDiver.this) {
136+
137+
if (state == RUNNING) {
138+
139+
state = COMPLETED;
140+
141+
for (int i = 0; i < CURL_HASH_LENGTH; i++) {
142+
143+
transactionTrits[TRANSACTION_LENGTH - CURL_HASH_LENGTH + i] = ((((int) (midCurlStateCopyLow[i] >> bitIndex)) & 1) == 0) ? 1 : (((((int) (midCurlStateCopyHigh[i] >> bitIndex)) & 1) == 0) ? -1 : 0);
144+
}
145+
146+
PearlDiver.this.notifyAll();
147+
}
148+
}
149+
150+
break;
151+
}
152+
}
153+
154+
}})).start();
155+
}
156+
157+
try {
158+
159+
while (state == RUNNING) {
160+
161+
wait();
162+
}
163+
164+
} catch (final InterruptedException e) {
165+
166+
state = CANCELLED;
167+
}
168+
169+
return state == COMPLETED;
170+
}
171+
172+
private static void transform(final long[] curlStateLow, final long[] curlStateHigh, final long[] curlScratchpadLow, final long[] curlScratchpadHigh) {
173+
174+
int curlScratchpadIndex = 0;
175+
for (int round = 27; round-- > 0; ) {
176+
177+
System.arraycopy(curlStateLow, 0, curlScratchpadLow, 0, CURL_STATE_LENGTH);
178+
System.arraycopy(curlStateHigh, 0, curlScratchpadHigh, 0, CURL_STATE_LENGTH);
179+
180+
for (int curlStateIndex = 0; curlStateIndex < CURL_STATE_LENGTH; curlStateIndex++) {
181+
182+
final long alpha = curlScratchpadLow[curlScratchpadIndex];
183+
final long beta = curlScratchpadHigh[curlScratchpadIndex];
184+
final long gamma = curlScratchpadHigh[curlScratchpadIndex += (curlScratchpadIndex < 365 ? 364 : -365)];
185+
final long delta = (alpha | (~gamma)) & (curlScratchpadLow[curlScratchpadIndex] ^ beta);
186+
187+
curlStateLow[curlStateIndex] = ~delta;
188+
curlStateHigh[curlStateIndex] = (alpha ^ gamma) | delta;
189+
}
190+
}
191+
}
192+
193+
private static void increment(final long[] midCurlStateCopyLow, final long[] midCurlStateCopyHigh, final int fromIndex, final int toIndex) {
194+
195+
for (int i = fromIndex; i < toIndex; i++) {
196+
197+
if (midCurlStateCopyLow[i] == 0b0000000000000000000000000000000000000000000000000000000000000000L) {
198+
199+
midCurlStateCopyLow[i] = 0b1111111111111111111111111111111111111111111111111111111111111111L;
200+
midCurlStateCopyHigh[i] = 0b0000000000000000000000000000000000000000000000000000000000000000L;
201+
202+
} else {
203+
204+
if (midCurlStateCopyHigh[i] == 0b0000000000000000000000000000000000000000000000000000000000000000L) {
205+
206+
midCurlStateCopyHigh[i] = 0b1111111111111111111111111111111111111111111111111111111111111111L;
207+
208+
} else {
209+
210+
midCurlStateCopyLow[i] = 0b0000000000000000000000000000000000000000000000000000000000000000L;
211+
}
212+
213+
break;
214+
}
215+
}
216+
}
217+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package cfb.pearldiver;
2+
3+
import jota.IotaLocalPoW;
4+
import jota.utils.Converter;
5+
6+
/**
7+
* Perform local PoW using Come-from-Beyond's PearlDiver implementation.
8+
*/
9+
public class PearlDiverLocalPoW implements IotaLocalPoW {
10+
11+
PearlDiver pearlDiver = new PearlDiver();
12+
13+
@Override
14+
public String performPoW(String trytes, int minWeightMagnitude) {
15+
int[] trits = Converter.trits(trytes);
16+
if (!pearlDiver.search(trits, minWeightMagnitude, 0))
17+
throw new IllegalStateException("PearlDiver search failed");
18+
return Converter.trytes(trits);
19+
}
20+
}

src/main/java/jota/IotaAPICore.java

+25
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import jota.dto.request.*;
44
import jota.dto.response.*;
55
import jota.error.InvalidTrytesException;
6+
import jota.model.Transaction;
67
import jota.utils.InputValidator;
78
import okhttp3.OkHttpClient;
89
import org.slf4j.Logger;
@@ -31,6 +32,7 @@ public class IotaAPICore {
3132

3233
private IotaAPIService service;
3334
private String protocol, host, port;
35+
private IotaLocalPoW localPoW;
3436

3537
/**
3638
* Build the API core.
@@ -41,6 +43,7 @@ protected IotaAPICore(final Builder builder) {
4143
protocol = builder.protocol;
4244
host = builder.host;
4345
port = builder.port;
46+
localPoW = builder.localPoW;
4447
postConstruct();
4548
}
4649

@@ -193,6 +196,19 @@ public GetAttachToTangleResponse attachToTangle(String trunkTransaction, String
193196
throw new InvalidTrytesException();
194197
}
195198

199+
if (localPoW != null) {
200+
final String[] resultTrytes = new String[trytes.length];
201+
String previousTransaction = null;
202+
for (int i = 0; i < trytes.length; i++) {
203+
Transaction txn = new Transaction(trytes[i]);
204+
txn.setTrunkTransaction(previousTransaction == null ? trunkTransaction : previousTransaction);
205+
txn.setBranchTransaction(previousTransaction == null ? branchTransaction : trunkTransaction);
206+
resultTrytes[i] = localPoW.performPoW(txn.toTrytes(), minWeightMagnitude);
207+
previousTransaction = new Transaction(resultTrytes[i]).getHash();
208+
}
209+
return new GetAttachToTangleResponse(resultTrytes);
210+
}
211+
196212
final Call<GetAttachToTangleResponse> res = service.attachToTangle(IotaAttachToTangleRequest.createAttachToTangleRequest(trunkTransaction, branchTransaction, minWeightMagnitude, trytes));
197213
return wrapCheckedException(res).body();
198214
}
@@ -226,6 +242,7 @@ public static class Builder<T extends Builder<T>> {
226242
private Properties nodeConfig = null;
227243

228244
String protocol, host, port;
245+
IotaLocalPoW localPoW;
229246

230247
public IotaAPICore build() {
231248
// resolution order: builder value, configuration file, default value
@@ -307,5 +324,13 @@ public T protocol(String protocol) {
307324
return (T) this;
308325
}
309326

327+
/**
328+
* @param protocol
329+
* @return
330+
*/
331+
public T localPoW(IotaLocalPoW localPoW) {
332+
this.localPoW = localPoW;
333+
return (T) this;
334+
}
310335
}
311336
}

src/main/java/jota/IotaLocalPoW.java

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package jota;
2+
3+
/**
4+
* Interface for an implementation to perform local PoW.
5+
*/
6+
public interface IotaLocalPoW {
7+
public String performPoW(String trytes, int minWeightMagnitude);
8+
}

src/main/java/jota/dto/response/GetAttachToTangleResponse.java

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ public GetAttachToTangleResponse(long duration) {
1414
setDuration(duration);
1515
}
1616

17+
/**
18+
* Initializes a new instance of the GetAttachToTangleResponse class with the given trytes.
19+
*/
20+
public GetAttachToTangleResponse(String[] trytes) {
21+
setDuration(0L);
22+
this.trytes = trytes;
23+
}
24+
1725
/**
1826
* Gets the rytes.
1927
*

0 commit comments

Comments
 (0)