From 812acb55d3780669873ce1ee8f0daef2bec16a8a Mon Sep 17 00:00:00 2001
From: Christian Lehmann
Date: Fri, 18 Jan 2019 22:23:07 +0100
Subject: [PATCH 01/67] Removed MessageQ dependency and added
MessageQueueProvider.
---
src/main/java/com/iota/iri/Iota.java | 19 ++-
.../iri/controllers/TransactionViewModel.java | 7 +-
src/main/java/com/iota/iri/network/Node.java | 22 ++-
.../iri/network/TransactionRequester.java | 7 +-
.../impl/LatestMilestoneTrackerImpl.java | 13 +-
.../impl/LatestSolidMilestoneTrackerImpl.java | 14 +-
.../milestone/impl/MilestoneServiceImpl.java | 14 +-
.../tipselection/impl/WalkerAlpha.java | 8 +-
.../java/com/iota/iri/storage/Tangle.java | 56 ++++----
src/main/java/com/iota/iri/zmq/MessageQ.java | 30 ++--
.../iota/iri/zmq/MessageQueueProvider.java | 10 ++
.../ZmqMessageQueueProvider.java} | 129 ++----------------
.../iota/iri/TransactionValidatorTest.java | 5 +-
.../controllers/TransactionRequesterTest.java | 8 +-
.../tipselection/impl/WalkerAlphaTest.java | 4 +-
15 files changed, 102 insertions(+), 244 deletions(-)
create mode 100644 src/main/java/com/iota/iri/zmq/MessageQueueProvider.java
rename src/main/java/com/iota/iri/{storage/ZmqPublishProvider.java => zmq/ZmqMessageQueueProvider.java} (53%)
diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java
index 9c3dc2e51a..23bffd4ec8 100644
--- a/src/main/java/com/iota/iri/Iota.java
+++ b/src/main/java/com/iota/iri/Iota.java
@@ -26,11 +26,11 @@
import com.iota.iri.storage.*;
import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider;
import com.iota.iri.utils.Pair;
-import com.iota.iri.zmq.MessageQ;
import java.security.SecureRandom;
import java.util.List;
+import com.iota.iri.zmq.ZmqMessageQueueProvider;
import org.apache.commons.lang3.NotImplementedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -104,7 +104,6 @@ public class Iota {
public final Replicator replicator;
public final IotaConfig configuration;
public final TipsViewModel tipsViewModel;
- public final MessageQ messageQ;
public final TipSelector tipsSelector;
/**
@@ -138,12 +137,11 @@ public Iota(IotaConfig configuration) throws TransactionPruningException, Snapsh
// legacy code
tangle = new Tangle();
- messageQ = MessageQ.createWith(configuration);
tipsViewModel = new TipsViewModel();
- transactionRequester = new TransactionRequester(tangle, snapshotProvider, messageQ);
+ transactionRequester = new TransactionRequester(tangle, snapshotProvider);
transactionValidator = new TransactionValidator(tangle, snapshotProvider, tipsViewModel, transactionRequester);
node = new Node(tangle, snapshotProvider, transactionValidator, transactionRequester, tipsViewModel,
- latestMilestoneTracker, messageQ, configuration);
+ latestMilestoneTracker, configuration);
replicator = new Replicator(node, configuration);
udpReceiver = new UDPReceiver(node, configuration);
tipsSolidifier = new TipsSolidifier(tangle, transactionValidator, tipsViewModel, configuration);
@@ -205,11 +203,11 @@ private void injectDependencies() throws SnapshotException, TransactionPruningEx
if (localSnapshotManager != null) {
localSnapshotManager.init(snapshotProvider, snapshotService, transactionPruner, configuration);
}
- milestoneService.init(tangle, snapshotProvider, snapshotService, messageQ, configuration);
+ milestoneService.init(tangle, snapshotProvider, snapshotService, configuration);
latestMilestoneTracker.init(tangle, snapshotProvider, milestoneService, milestoneSolidifier,
- messageQ, configuration);
+ configuration);
latestSolidMilestoneTracker.init(tangle, snapshotProvider, milestoneService, ledgerService,
- latestMilestoneTracker, messageQ);
+ latestMilestoneTracker);
seenMilestonesRetriever.init(tangle, snapshotProvider, transactionRequester);
milestoneSolidifier.init(snapshotProvider, transactionValidator);
ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService);
@@ -270,7 +268,6 @@ public void shutdown() throws Exception {
replicator.shutdown();
transactionValidator.shutdown();
tangle.shutdown();
- messageQ.shutdown();
// free the resources of the snapshot provider last because all other instances need it
snapshotProvider.shutdown();
@@ -293,7 +290,7 @@ private void initializeTangle() {
}
}
if (configuration.isZmqEnabled()) {
- tangle.addPersistenceProvider(new ZmqPublishProvider(messageQ));
+ tangle.addMessageQueueProvider(new ZmqMessageQueueProvider(configuration));
}
}
@@ -302,7 +299,7 @@ private TipSelector createTipSelector(TipSelConfig config) {
latestMilestoneTracker);
RatingCalculator ratingCalculator = new CumulativeWeightCalculator(tangle, snapshotProvider);
TailFinder tailFinder = new TailFinderImpl(tangle);
- Walker walker = new WalkerAlpha(tailFinder, tangle, messageQ, new SecureRandom(), config);
+ Walker walker = new WalkerAlpha(tailFinder, tangle, new SecureRandom(), config);
return new TipSelectorImpl(tangle, snapshotProvider, ledgerService, entryPointSelector, ratingCalculator,
walker, config);
}
diff --git a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java
index ed53da8dba..b46193c8e2 100644
--- a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java
+++ b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java
@@ -258,10 +258,9 @@ public static TransactionViewModel first(Tangle tangle) throws Exception {
* @param tangle The tangle reference for the database
* @param initialSnapshot snapshot that acts as genesis
* @param item The string identifying the purpose of the update
- * @return True if the update was successful, False if it failed
* @throws Exception Thrown if any of the metadata fails to fetch, or if the database update fails
*/
- public boolean update(Tangle tangle, Snapshot initialSnapshot, String item) throws Exception {
+ public void update(Tangle tangle, Snapshot initialSnapshot, String item) throws Exception {
getAddressHash();
getTrunkTransactionHash();
getBranchTransactionHash();
@@ -271,9 +270,9 @@ public boolean update(Tangle tangle, Snapshot initialSnapshot, String item) thro
setAttachmentData();
setMetadata();
if (initialSnapshot.hasSolidEntryPoint(hash)) {
- return false;
+ return;
}
- return tangle.update(transaction, hash, item);
+ tangle.update(transaction, hash, item);
}
/**
diff --git a/src/main/java/com/iota/iri/network/Node.java b/src/main/java/com/iota/iri/network/Node.java
index cc34b84619..86fcdbd19a 100644
--- a/src/main/java/com/iota/iri/network/Node.java
+++ b/src/main/java/com/iota/iri/network/Node.java
@@ -11,7 +11,6 @@
import com.iota.iri.service.milestone.LatestMilestoneTracker;
import com.iota.iri.service.snapshot.SnapshotProvider;
import com.iota.iri.storage.Tangle;
-import com.iota.iri.zmq.MessageQ;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
@@ -72,7 +71,6 @@ public class Node {
private final TransactionValidator transactionValidator;
private final LatestMilestoneTracker latestMilestoneTracker;
private final TransactionRequester transactionRequester;
- private final MessageQ messageQ;
private static final SecureRandom rnd = new SecureRandom();
@@ -99,11 +97,10 @@ public class Node {
* @param transactionRequester Contains a set of transaction hashes to be requested from peers.
* @param tipsViewModel Contains a hash of solid and non solid tips
* @param latestMilestoneTracker Tracks milestones issued from the coordinator
- * @param messageQ Responsible for publishing events on zeroMQ
* @param configuration Contains all the config.
*
*/
- public Node(final Tangle tangle, SnapshotProvider snapshotProvider, final TransactionValidator transactionValidator, final TransactionRequester transactionRequester, final TipsViewModel tipsViewModel, final LatestMilestoneTracker latestMilestoneTracker, final MessageQ messageQ, final NodeConfig configuration
+ public Node(final Tangle tangle, SnapshotProvider snapshotProvider, final TransactionValidator transactionValidator, final TransactionRequester transactionRequester, final TipsViewModel tipsViewModel, final LatestMilestoneTracker latestMilestoneTracker, final NodeConfig configuration
) {
this.configuration = configuration;
this.tangle = tangle;
@@ -112,7 +109,6 @@ public Node(final Tangle tangle, SnapshotProvider snapshotProvider, final Transa
this.transactionRequester = transactionRequester;
this.tipsViewModel = tipsViewModel;
this.latestMilestoneTracker = latestMilestoneTracker ;
- this.messageQ = messageQ;
this.reqHashSize = configuration.getRequestHashSize();
int packetSize = configuration.getTransactionPacketSize();
this.sendingPacket = new DatagramPacket(new byte[packetSize], packetSize);
@@ -189,7 +185,7 @@ private Runnable spawnNeighborDNSRefresherThread() {
final String hostname = n.getAddress().getHostString();
checkIp(hostname).ifPresent(ip -> {
log.info("DNS Checker: Validating DNS Address '{}' with '{}'", hostname, ip);
- messageQ.publish("dnscv %s %s", hostname, ip);
+ tangle.publish("dnscv %s %s", hostname, ip);
final String neighborAddress = neighborIpCache.get(hostname);
if (neighborAddress == null) {
@@ -197,11 +193,11 @@ private Runnable spawnNeighborDNSRefresherThread() {
} else {
if (neighborAddress.equals(ip)) {
log.info("{} seems fine.", hostname);
- messageQ.publish("dnscc %s", hostname);
+ tangle.publish("dnscc %s", hostname);
} else {
if (configuration.isDnsRefresherEnabled()) {
log.info("IP CHANGED for {}! Updating...", hostname);
- messageQ.publish("dnscu %s", hostname);
+ tangle.publish("dnscu %s", hostname);
String protocol = (n instanceof TCPNeighbor) ? "tcp://" : "udp://";
String port = ":" + n.getAddress().getPort();
@@ -360,7 +356,7 @@ public void preProcessReceivedData(byte[] receivedData, SocketAddress senderAddr
}
if (((hitCount + missCount) % 50000L == 0)) {
log.info("RecentSeenBytes cache hit/miss ratio: " + hitCount + "/" + missCount);
- messageQ.publish("hmr %d/%d", hitCount, missCount);
+ tangle.publish("hmr %d/%d", hitCount, missCount);
recentSeenBytesMissCount.set(0L);
recentSeenBytesHitCount.set(0L);
}
@@ -375,7 +371,7 @@ public void preProcessReceivedData(byte[] receivedData, SocketAddress senderAddr
String uriString = uriScheme + ":/" + senderAddress.toString();
if (Neighbor.getNumPeers() < maxPeersAllowed) {
log.info("Adding non-tethered neighbor: " + uriString);
- messageQ.publish("antn %s", uriString);
+ tangle.publish("antn %s", uriString);
try {
final URI uri = new URI(uriString);
// 3rd parameter false (not tcp), 4th parameter true (configured tethering)
@@ -392,7 +388,7 @@ public void preProcessReceivedData(byte[] receivedData, SocketAddress senderAddr
// Avoid ever growing list in case of an attack.
rejectedAddresses.clear();
} else if (rejectedAddresses.add(uriString)) {
- messageQ.publish("rntn %s %s", uriString, String.valueOf(maxPeersAllowed));
+ tangle.publish("rntn %s %s", uriString, String.valueOf(maxPeersAllowed));
log.info("Refused non-tethered neighbor: " + uriString +
" (max-peers = " + String.valueOf(maxPeersAllowed) + ")");
}
@@ -655,7 +651,7 @@ private Runnable spawnTipRequesterThread() {
long now = System.currentTimeMillis();
if ((now - lastTime) > 10000L) {
lastTime = now;
- messageQ.publish("rstat %d %d %d %d %d",
+ tangle.publish("rstat %d %d %d %d %d",
getReceiveQueueSize(), getBroadcastQueueSize(),
transactionRequester.numberOfTransactionsToRequest(), getReplyQueueSize(),
TransactionViewModel.getNumberOfStoredTransactions(tangle));
@@ -837,7 +833,7 @@ private void parseNeighborsConfig() {
.map(u -> newNeighbor(u, true))
.peek(u -> {
log.info("-> Adding neighbor : {} ", u.getAddress());
- messageQ.publish("-> Adding Neighbor : %s", u.getAddress());
+ tangle.publish("-> Adding Neighbor : %s", u.getAddress());
}).forEach(neighbors::add);
}
diff --git a/src/main/java/com/iota/iri/network/TransactionRequester.java b/src/main/java/com/iota/iri/network/TransactionRequester.java
index e3d1db8b61..e6a739c6f5 100644
--- a/src/main/java/com/iota/iri/network/TransactionRequester.java
+++ b/src/main/java/com/iota/iri/network/TransactionRequester.java
@@ -3,7 +3,6 @@
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.model.Hash;
import com.iota.iri.service.snapshot.SnapshotProvider;
-import com.iota.iri.zmq.MessageQ;
import com.iota.iri.storage.Tangle;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
@@ -18,7 +17,6 @@
public class TransactionRequester {
private static final Logger log = LoggerFactory.getLogger(TransactionRequester.class);
- private final MessageQ messageQ;
private final Set milestoneTransactionsToRequest = new LinkedHashSet<>();
private final Set transactionsToRequest = new LinkedHashSet<>();
@@ -32,10 +30,9 @@ public class TransactionRequester {
private final Tangle tangle;
private final SnapshotProvider snapshotProvider;
- public TransactionRequester(Tangle tangle, SnapshotProvider snapshotProvider, MessageQ messageQ) {
+ public TransactionRequester(Tangle tangle, SnapshotProvider snapshotProvider) {
this.tangle = tangle;
this.snapshotProvider = snapshotProvider;
- this.messageQ = messageQ;
}
public void init(double pRemoveRequest) {
@@ -120,7 +117,7 @@ public Hash transactionToRequest(boolean milestone) throws Exception {
if (TransactionViewModel.exists(tangle, hash)) {
// ... dump a log message ...
log.info("Removed existing tx from request list: " + hash);
- messageQ.publish("rtl %s", hash);
+ tangle.publish("rtl %s", hash);
// ... and continue to the next element in the set
continue;
diff --git a/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java
index bf82a87149..5941d3a33e 100644
--- a/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java
+++ b/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java
@@ -17,7 +17,6 @@
import com.iota.iri.utils.log.interval.IntervalLogger;
import com.iota.iri.utils.thread.DedicatedScheduledExecutorService;
import com.iota.iri.utils.thread.SilentScheduledExecutorService;
-import com.iota.iri.zmq.MessageQ;
import java.util.ArrayDeque;
import java.util.Deque;
@@ -70,11 +69,6 @@ public class LatestMilestoneTrackerImpl implements LatestMilestoneTracker {
*/
private MilestoneSolidifier milestoneSolidifier;
- /**
- * Holds a reference to the ZeroMQ interface that allows us to emit messages for external recipients.
- */
- private MessageQ messageQ;
-
/**
* Holds the coordinator address which is used to filter possible milestone candidates.
*/
@@ -135,19 +129,16 @@ public class LatestMilestoneTrackerImpl implements LatestMilestoneTracker {
* @param snapshotProvider manager for the snapshots that allows us to retrieve the relevant snapshots of this node
* @param milestoneService contains the important business logic when dealing with milestones
* @param milestoneSolidifier manager that takes care of solidifying milestones
- * @param messageQ ZeroMQ interface that allows us to emit messages for external recipients
* @param config configuration object which allows us to determine the important config parameters of the node
* @return the initialized instance itself to allow chaining
*/
public LatestMilestoneTrackerImpl init(Tangle tangle, SnapshotProvider snapshotProvider,
- MilestoneService milestoneService, MilestoneSolidifier milestoneSolidifier, MessageQ messageQ,
- IotaConfig config) {
+ MilestoneService milestoneService, MilestoneSolidifier milestoneSolidifier, IotaConfig config) {
this.tangle = tangle;
this.snapshotProvider = snapshotProvider;
this.milestoneService = milestoneService;
this.milestoneSolidifier = milestoneSolidifier;
- this.messageQ = messageQ;
coordinatorAddress = HashFactory.ADDRESS.create(config.getCoordinator());
@@ -164,7 +155,7 @@ public LatestMilestoneTrackerImpl init(Tangle tangle, SnapshotProvider snapshotP
*/
@Override
public void setLatestMilestone(Hash latestMilestoneHash, int latestMilestoneIndex) {
- messageQ.publish("lmi %d %d", this.latestMilestoneIndex, latestMilestoneIndex);
+ tangle.publish("lmi %d %d", this.latestMilestoneIndex, latestMilestoneIndex);
log.delegate().info("Latest milestone has changed from #" + this.latestMilestoneIndex + " to #" +
latestMilestoneIndex);
diff --git a/src/main/java/com/iota/iri/service/milestone/impl/LatestSolidMilestoneTrackerImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/LatestSolidMilestoneTrackerImpl.java
index 52ba12b367..dd45cc57db 100644
--- a/src/main/java/com/iota/iri/service/milestone/impl/LatestSolidMilestoneTrackerImpl.java
+++ b/src/main/java/com/iota/iri/service/milestone/impl/LatestSolidMilestoneTrackerImpl.java
@@ -14,7 +14,6 @@
import com.iota.iri.utils.log.interval.IntervalLogger;
import com.iota.iri.utils.thread.DedicatedScheduledExecutorService;
import com.iota.iri.utils.thread.SilentScheduledExecutorService;
-import com.iota.iri.zmq.MessageQ;
import java.util.concurrent.TimeUnit;
@@ -65,11 +64,6 @@ public class LatestSolidMilestoneTrackerImpl implements LatestSolidMilestoneTrac
*/
private LedgerService ledgerService;
- /**
- * Holds a reference to the ZeroMQ interface that allows us to emit messages for external recipients.
- */
- private MessageQ messageQ;
-
/**
* Holds a reference to the manager of the background worker.
*/
@@ -108,19 +102,17 @@ public class LatestSolidMilestoneTrackerImpl implements LatestSolidMilestoneTrac
* @param milestoneService contains the important business logic when dealing with milestones
* @param ledgerService the manager for
* @param latestMilestoneTracker the manager that keeps track of the latest milestone
- * @param messageQ ZeroMQ interface that allows us to emit messages for external recipients
* @return the initialized instance itself to allow chaining
*/
public LatestSolidMilestoneTrackerImpl init(Tangle tangle, SnapshotProvider snapshotProvider,
MilestoneService milestoneService, LedgerService ledgerService,
- LatestMilestoneTracker latestMilestoneTracker, MessageQ messageQ) {
+ LatestMilestoneTracker latestMilestoneTracker) {
this.tangle = tangle;
this.snapshotProvider = snapshotProvider;
this.milestoneService = milestoneService;
this.ledgerService = ledgerService;
this.latestMilestoneTracker = latestMilestoneTracker;
- this.messageQ = messageQ;
return this;
}
@@ -277,8 +269,8 @@ private void logChange(int prevSolidMilestoneIndex) {
if (prevSolidMilestoneIndex != latestMilestoneIndex) {
log.info("Latest SOLID milestone index changed from #" + prevSolidMilestoneIndex + " to #" + latestMilestoneIndex);
- messageQ.publish("lmsi %d %d", prevSolidMilestoneIndex, latestMilestoneIndex);
- messageQ.publish("lmhs %s", latestMilestoneHash);
+ tangle.publish("lmsi %d %d", prevSolidMilestoneIndex, latestMilestoneIndex);
+ tangle.publish("lmhs %s", latestMilestoneHash);
}
}
diff --git a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java
index 562c548b0a..951137176e 100644
--- a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java
+++ b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java
@@ -20,7 +20,6 @@
import com.iota.iri.storage.Tangle;
import com.iota.iri.utils.Converter;
import com.iota.iri.utils.dag.DAGHelper;
-import com.iota.iri.zmq.MessageQ;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -59,11 +58,6 @@ public class MilestoneServiceImpl implements MilestoneService {
*/
private SnapshotService snapshotService;
- /**
- * Holds the ZeroMQ interface that allows us to emit messages for external recipients.
- */
- private MessageQ messageQ;
-
/**
* Holds the config with important milestone specific settings.
*/
@@ -83,17 +77,15 @@ public class MilestoneServiceImpl implements MilestoneService {
*
* @param tangle Tangle object which acts as a database interface
* @param snapshotProvider snapshot provider which gives us access to the relevant snapshots
- * @param messageQ ZeroMQ interface that allows us to emit messages for external recipients
* @param config config with important milestone specific settings
* @return the initialized instance itself to allow chaining
*/
public MilestoneServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotService snapshotService,
- MessageQ messageQ, ConsensusConfig config) {
+ ConsensusConfig config) {
this.tangle = tangle;
this.snapshotProvider = snapshotProvider;
this.snapshotService = snapshotService;
- this.messageQ = messageQ;
this.config = config;
return this;
@@ -418,8 +410,8 @@ private void updateMilestoneIndexOfSingleTransaction(TransactionViewModel transa
throw new MilestoneException("error while updating the snapshotIndex of " + transaction, e);
}
- messageQ.publish("%s %s %d sn", transaction.getAddressHash(), transaction.getHash(), index);
- messageQ.publish("sn %d %s %s %s %s %s", index, transaction.getHash(), transaction.getAddressHash(),
+ tangle.publish("%s %s %d sn", transaction.getAddressHash(), transaction.getHash(), index);
+ tangle.publish("sn %d %s %s %s %s %s", index, transaction.getHash(), transaction.getAddressHash(),
transaction.getTrunkTransactionHash(), transaction.getBranchTransactionHash(),
transaction.getBundleHash());
}
diff --git a/src/main/java/com/iota/iri/service/tipselection/impl/WalkerAlpha.java b/src/main/java/com/iota/iri/service/tipselection/impl/WalkerAlpha.java
index 982b4b2c75..52440977ae 100644
--- a/src/main/java/com/iota/iri/service/tipselection/impl/WalkerAlpha.java
+++ b/src/main/java/com/iota/iri/service/tipselection/impl/WalkerAlpha.java
@@ -9,7 +9,6 @@
import com.iota.iri.service.tipselection.Walker;
import com.iota.iri.storage.Tangle;
import com.iota.iri.utils.collections.interfaces.UnIterableMap;
-import com.iota.iri.zmq.MessageQ;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,7 +30,6 @@ public class WalkerAlpha implements Walker {
private final Random random;
private final Tangle tangle;
- private final MessageQ messageQ;
private final Logger log = LoggerFactory.getLogger(Walker.class);
private final TailFinder tailFinder;
@@ -41,13 +39,11 @@ public class WalkerAlpha implements Walker {
*
* @param tailFinder instance of tailFinder, used to step from tail to tail in random walk.
* @param tangle Tangle object which acts as a database interface
- * @param messageQ ZMQ handle to publish telemetrics.
* @param random a source of randomness.
* @param config configurations to set internal parameters.
*/
- public WalkerAlpha(TailFinder tailFinder, Tangle tangle, MessageQ messageQ, Random random, TipSelConfig config) {
+ public WalkerAlpha(TailFinder tailFinder, Tangle tangle, Random random, TipSelConfig config) {
this.tangle = tangle;
- this.messageQ = messageQ;
this.tailFinder = tailFinder;
this.random = random;
this.alpha = config.getAlpha();
@@ -84,7 +80,7 @@ public Hash walk(Hash entryPoint, UnIterableMap ratings, WalkVa
} while (nextStep.isPresent());
log.debug("{} tails traversed to find tip", traversedTails.size());
- messageQ.publish("mctn %d", traversedTails.size());
+ tangle.publish("mctn %d", traversedTails.size());
return traversedTails.getLast();
}
diff --git a/src/main/java/com/iota/iri/storage/Tangle.java b/src/main/java/com/iota/iri/storage/Tangle.java
index 5e444bb173..2d4ee3ad54 100644
--- a/src/main/java/com/iota/iri/storage/Tangle.java
+++ b/src/main/java/com/iota/iri/storage/Tangle.java
@@ -8,6 +8,7 @@
import java.util.function.Function;
import java.util.stream.Collectors;
+import com.iota.iri.zmq.MessageQueueProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,23 +34,29 @@ public class Tangle {
new AbstractMap.SimpleImmutableEntry<>("transaction-metadata", Transaction.class);
private final List persistenceProviders = new ArrayList<>();
-
+ private final List messageQueueProviders = new ArrayList<>();
public void addPersistenceProvider(PersistenceProvider provider) {
this.persistenceProviders.add(provider);
}
+ public void addMessageQueueProvider(MessageQueueProvider provider) {
+ this.messageQueueProviders.add(provider);
+ }
+
public void init() throws Exception {
for(PersistenceProvider provider: this.persistenceProviders) {
provider.init();
}
}
-
public void shutdown() throws Exception {
log.info("Shutting down Tangle Persistence Providers... ");
this.persistenceProviders.forEach(PersistenceProvider::shutdown);
this.persistenceProviders.clear();
+ log.info("Shutting down Tangle MessageQueue Providers... ");
+ this.messageQueueProviders.forEach(MessageQueueProvider::shutdown);
+ this.messageQueueProviders.clear();
}
public Persistable load(Class> model, Indexable index) throws Exception {
@@ -107,16 +114,27 @@ public Pair getLatest(Class> model, Class> index) th
return latest;
}
- public Boolean update(Persistable model, Indexable index, String item) throws Exception {
- boolean success = false;
- for(PersistenceProvider provider: this.persistenceProviders) {
- if(success) {
- provider.update(model, index, item);
- } else {
- success = provider.update(model, index, item);
- }
- }
- return success;
+ public void update(Persistable model, Indexable index, String item) throws Exception {
+ updatePersistenceProvider(model, index, item);
+ updateMessageQueueProvider(model, index, item);
+ }
+
+ private void updatePersistenceProvider(Persistable model, Indexable index, String item) throws Exception {
+ for(PersistenceProvider provider: this.persistenceProviders) {
+ provider.update(model, index, item);
+ }
+ }
+
+ private void updateMessageQueueProvider(Persistable model, Indexable index, String item) {
+ for(MessageQueueProvider provider: this.messageQueueProviders) {
+ provider.update(model, index, item);
+ }
+ }
+
+ public void publish(String message, Object... objects) {
+ for(MessageQueueProvider provider: this.messageQueueProviders) {
+ provider.publish(message, objects);
+ }
}
public Set keysWithMissingReferences(Class> modelClass, Class> referencedClass) throws Exception {
@@ -237,18 +255,4 @@ public void clearMetadata(Class> column) throws Exception {
provider.clearMetadata(column);
}
}
-
- /*
- public boolean merge(Persistable model, Indexable index) throws Exception {
- boolean exists = false;
- for(PersistenceProvider provider: persistenceProviders) {
- if(exists) {
- provider.save(model, index);
- } else {
- exists = provider.merge(model, index);
- }
- }
- return exists;
- }
- */
}
diff --git a/src/main/java/com/iota/iri/zmq/MessageQ.java b/src/main/java/com/iota/iri/zmq/MessageQ.java
index 7f5d4e35c9..c1fdc3eed8 100644
--- a/src/main/java/com/iota/iri/zmq/MessageQ.java
+++ b/src/main/java/com/iota/iri/zmq/MessageQ.java
@@ -31,17 +31,16 @@
* For a complete list and detailed topic specification please refer to the README.md.
*
*/
-public class MessageQ {
+class MessageQ {
private final static Logger LOG = LoggerFactory.getLogger(MessageQ.class);
private final ZMQ.Context context;
private final ZMQ.Socket publisher;
- private boolean enabled = false;
private final ExecutorService publisherService = Executors.newSingleThreadExecutor();
public static MessageQ createWith(ZMQConfig config) {
- return new MessageQ(config.getZmqPort(), config.getZmqIpc(), config.getZmqThreads(), config.isZmqEnabled());
+ return new MessageQ(config.getZmqPort(), config.getZmqIpc(), config.getZmqThreads());
}
/**
@@ -50,20 +49,13 @@ public static MessageQ createWith(ZMQConfig config) {
* @param port port the publisher will be bound to
* @param ipc IPC socket the publisher will be bound to
* @param nthreads number of threads used by the ZMQ publisher
- * @param enabled boolean enable flag; by default the publisher will not be started
*/
- private MessageQ(int port, String ipc, int nthreads, boolean enabled) {
- if (enabled) {
- context = ZMQ.context(nthreads);
- publisher = context.socket(ZMQ.PUB);
- publisher.bind(String.format("tcp://*:%d", port));
- if (ipc != null) {
- publisher.bind(ipc);
- }
- this.enabled = true;
- } else {
- context = null;
- publisher = null;
+ private MessageQ(int port, String ipc, int nthreads) {
+ context = ZMQ.context(nthreads);
+ publisher = context.socket(ZMQ.PUB);
+ publisher.bind(String.format("tcp://*:%d", port));
+ if (ipc != null) {
+ publisher.bind(ipc);
}
}
@@ -74,10 +66,8 @@ private MessageQ(int port, String ipc, int nthreads, boolean enabled) {
* @param objects arguments referenced by the message body, similar to a format string
*/
public void publish(String message, Object... objects) {
- if (enabled) {
- String toSend = String.format(message, objects);
- publisherService.submit(() -> publisher.send(toSend));
- }
+ String toSend = String.format(message, objects);
+ publisherService.submit(() -> publisher.send(toSend));
}
/**
diff --git a/src/main/java/com/iota/iri/zmq/MessageQueueProvider.java b/src/main/java/com/iota/iri/zmq/MessageQueueProvider.java
new file mode 100644
index 0000000000..fa300bccf1
--- /dev/null
+++ b/src/main/java/com/iota/iri/zmq/MessageQueueProvider.java
@@ -0,0 +1,10 @@
+package com.iota.iri.zmq;
+
+import com.iota.iri.storage.Indexable;
+import com.iota.iri.storage.Persistable;
+
+public interface MessageQueueProvider {
+ void publish(String message, Object... objects);
+ boolean update(Persistable model, Indexable index, String item);
+ void shutdown();
+}
diff --git a/src/main/java/com/iota/iri/storage/ZmqPublishProvider.java b/src/main/java/com/iota/iri/zmq/ZmqMessageQueueProvider.java
similarity index 53%
rename from src/main/java/com/iota/iri/storage/ZmqPublishProvider.java
rename to src/main/java/com/iota/iri/zmq/ZmqMessageQueueProvider.java
index 9425805933..0e65989c74 100644
--- a/src/main/java/com/iota/iri/storage/ZmqPublishProvider.java
+++ b/src/main/java/com/iota/iri/zmq/ZmqMessageQueueProvider.java
@@ -1,55 +1,26 @@
-package com.iota.iri.storage;
+package com.iota.iri.zmq;
+import com.iota.iri.conf.ZMQConfig;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.model.Hash;
import com.iota.iri.model.persistables.Transaction;
+import com.iota.iri.storage.Indexable;
+import com.iota.iri.storage.Persistable;
import com.iota.iri.utils.Converter;
-import com.iota.iri.utils.Pair;
-import com.iota.iri.zmq.MessageQ;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ZmqPublishProvider implements PersistenceProvider {
+public class ZmqMessageQueueProvider implements MessageQueueProvider {
- private static final Logger log = LoggerFactory.getLogger(ZmqPublishProvider.class);
+ private static final Logger log = LoggerFactory.getLogger(ZmqMessageQueueProvider.class);
private final MessageQ messageQ;
- public ZmqPublishProvider( MessageQ messageQ ) {
- this.messageQ = messageQ;
- }
-
- @Override
- public void init() throws Exception {
-
- }
-
- @Override
- public boolean isAvailable() {
- return false;
- }
-
- @Override
- public void shutdown() {
-
- }
-
- @Override
- public boolean save(Persistable model, Indexable index) throws Exception {
- return false;
- }
-
- @Override
- public void delete(Class> model, Indexable index) throws Exception {
-
+ public ZmqMessageQueueProvider(ZMQConfig configuration) {
+ this.messageQ = MessageQ.createWith(configuration);
}
@Override
- public boolean update(Persistable model, Indexable index, String item) throws Exception {
+ public boolean update(Persistable model, Indexable index, String item) {
if(!(model instanceof Transaction)) {
return false;
}
@@ -74,7 +45,7 @@ private void publishTx(TransactionViewModel transactionViewModel) {
txStringBuilder.append(transactionViewModel.getHash()); txStringBuilder.append(" ");
txStringBuilder.append(transactionViewModel.getAddressHash()); txStringBuilder.append(" ");
txStringBuilder.append(String.valueOf(transactionViewModel.value())); txStringBuilder.append(" ");
- txStringBuilder.append(transactionViewModel.getObsoleteTagValue().toString().substring(0,27)); txStringBuilder.append(" ");
+ txStringBuilder.append(transactionViewModel.getObsoleteTagValue().toString(), 0, 27); txStringBuilder.append(" ");
txStringBuilder.append(String.valueOf(transactionViewModel.getTimestamp())); txStringBuilder.append(" ");
txStringBuilder.append(String.valueOf(transactionViewModel.getCurrentIndex())); txStringBuilder.append(" ");
txStringBuilder.append(String.valueOf(transactionViewModel.lastIndex())); txStringBuilder.append(" ");
@@ -82,7 +53,7 @@ private void publishTx(TransactionViewModel transactionViewModel) {
txStringBuilder.append(transactionViewModel.getTrunkTransactionHash()); txStringBuilder.append(" ");
txStringBuilder.append(transactionViewModel.getBranchTransactionHash()); txStringBuilder.append(" ");
txStringBuilder.append(String.valueOf(transactionViewModel.getArrivalTime())); txStringBuilder.append(" ");
- txStringBuilder.append(transactionViewModel.getTagValue().toString().substring(0,27));
+ txStringBuilder.append(transactionViewModel.getTagValue().toString(), 0, 27);
messageQ.publish(txStringBuilder.toString());
} catch (Exception e) {
@@ -107,82 +78,12 @@ private void publishTxTrytes(TransactionViewModel transactionViewModel) {
}
@Override
- public boolean exists(Class> model, Indexable key) throws Exception {
- return false;
+ public void publish(String message, Object... objects) {
+ this.messageQ.publish(message, objects);
}
@Override
- public Pair latest(Class> model, Class> indexModel) throws Exception {
- return null;
- }
-
- @Override
- public Set keysWithMissingReferences(Class> modelClass, Class> otherClass) throws Exception {
- return null;
- }
-
- @Override
- public Persistable get(Class> model, Indexable index) throws Exception {
- return null;
- }
-
- @Override
- public boolean mayExist(Class> model, Indexable index) throws Exception {
- return false;
- }
-
- @Override
- public long count(Class> model) throws Exception {
- return 0;
- }
-
- @Override
- public Set keysStartingWith(Class> modelClass, byte[] value) {
- return null;
- }
-
- @Override
- public Persistable seek(Class> model, byte[] key) throws Exception {
- return null;
- }
-
- @Override
- public Pair next(Class> model, Indexable index) throws Exception {
- return null;
- }
-
- @Override
- public Pair previous(Class> model, Indexable index) throws Exception {
- return null;
- }
-
- @Override
- public Pair first(Class> model, Class> indexModel) throws Exception {
- return null;
- }
-
- @Override
- public boolean saveBatch(List> models) throws Exception {
- return false;
- }
-
- @Override
- public void deleteBatch(Collection>> models) throws Exception {
-
- }
-
- @Override
- public void clear(Class> column) throws Exception {
-
- }
-
- @Override
- public void clearMetadata(Class> column) throws Exception {
-
- }
-
- @Override
- public List loadAllKeysFromTable(Class extends Persistable> model) {
- return null;
+ public void shutdown() {
+ this.messageQ.shutdown();
}
}
\ No newline at end of file
diff --git a/src/test/java/com/iota/iri/TransactionValidatorTest.java b/src/test/java/com/iota/iri/TransactionValidatorTest.java
index fcf9929464..20f21a1fac 100644
--- a/src/test/java/com/iota/iri/TransactionValidatorTest.java
+++ b/src/test/java/com/iota/iri/TransactionValidatorTest.java
@@ -11,12 +11,10 @@
import com.iota.iri.storage.Tangle;
import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider;
import com.iota.iri.utils.Converter;
-import com.iota.iri.zmq.MessageQ;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.mockito.Mockito;
import static com.iota.iri.controllers.TransactionViewModelTest.*;
import static org.junit.Assert.assertFalse;
@@ -42,8 +40,7 @@ public static void setUp() throws Exception {
dbFolder.getRoot().getAbsolutePath(), logFolder.getRoot().getAbsolutePath(),1000, Tangle.COLUMN_FAMILIES, Tangle.METADATA_COLUMN_FAMILY));
tangle.init();
TipsViewModel tipsViewModel = new TipsViewModel();
- MessageQ messageQ = Mockito.mock(MessageQ.class);
- TransactionRequester txRequester = new TransactionRequester(tangle, snapshotProvider, messageQ);
+ TransactionRequester txRequester = new TransactionRequester(tangle, snapshotProvider);
txValidator = new TransactionValidator(tangle, snapshotProvider, tipsViewModel, txRequester);
txValidator.setMwm(false, MAINNET_MWM);
}
diff --git a/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java b/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java
index 23f1e5320c..8d1d404eb0 100644
--- a/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java
+++ b/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java
@@ -6,7 +6,6 @@
import com.iota.iri.service.snapshot.SnapshotProvider;
import com.iota.iri.service.snapshot.impl.SnapshotProviderImpl;
import com.iota.iri.storage.Tangle;
-import com.iota.iri.zmq.MessageQ;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -19,7 +18,6 @@
public class TransactionRequesterTest {
private static Tangle tangle = new Tangle();
private static SnapshotProvider snapshotProvider;
- private MessageQ mq;
@Before
public void setUp() throws Exception {
@@ -78,7 +76,7 @@ public void instance() throws Exception {
@Test
public void nonMilestoneCapacityLimited() throws Exception {
- TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq);
+ TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider);
int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE;
//fill tips list
for (int i = 0; i < capacity * 2 ; i++) {
@@ -91,7 +89,7 @@ public void nonMilestoneCapacityLimited() throws Exception {
@Test
public void milestoneCapacityNotLimited() throws Exception {
- TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq);
+ TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider);
int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE;
//fill tips list
for (int i = 0; i < capacity * 2 ; i++) {
@@ -104,7 +102,7 @@ public void milestoneCapacityNotLimited() throws Exception {
@Test
public void mixedCapacityLimited() throws Exception {
- TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq);
+ TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider);
int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE;
//fill tips list
for (int i = 0; i < capacity * 4 ; i++) {
diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java
index 0ebbc6184c..d210050b06 100644
--- a/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java
+++ b/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java
@@ -11,7 +11,6 @@
import com.iota.iri.storage.Tangle;
import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider;
import com.iota.iri.utils.collections.interfaces.UnIterableMap;
-import com.iota.iri.zmq.MessageQ;
import java.util.HashMap;
import java.util.Map;
@@ -58,11 +57,10 @@ public static void setUp() throws Exception {
Tangle.COLUMN_FAMILIES, Tangle.METADATA_COLUMN_FAMILY));
tangle.init();
- MessageQ messageQ = Mockito.mock(MessageQ.class);
TailFinder tailFinder = Mockito.mock(TailFinder.class);
Mockito.when(tailFinder.findTail(Mockito.any(Hash.class)))
.then(args -> Optional.of(args.getArgumentAt(0, Hash.class)));
- walker = new WalkerAlpha(tailFinder, tangle, messageQ, new Random(1), new MainnetConfig());
+ walker = new WalkerAlpha(tailFinder, tangle, new Random(1), new MainnetConfig());
}
From 4de17a01e336fee068c26fd0ae425f5111338a3b Mon Sep 17 00:00:00 2001
From: Christian Lehmann
Date: Tue, 22 Jan 2019 19:12:17 +0100
Subject: [PATCH 02/67] Will fix #1231: ZMQ publishes to TCP and IPC instead of
just one of them
---
.../com/iota/iri/conf/BaseIotaConfig.java | 45 ++++++++++-
.../java/com/iota/iri/conf/ZMQConfig.java | 8 +-
src/main/java/com/iota/iri/zmq/MessageQ.java | 18 ++---
.../java/com/iota/iri/conf/ZMQConfigTest.java | 76 +++++++++++++++++++
4 files changed, 133 insertions(+), 14 deletions(-)
create mode 100644 src/test/java/com/iota/iri/conf/ZMQConfigTest.java
diff --git a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
index edb1dd038b..c814b4a84c 100644
--- a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
+++ b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
@@ -68,7 +68,8 @@ public abstract class BaseIotaConfig implements IotaConfig {
protected double pPropagateRequest = Defaults.P_PROPAGATE_REQUEST;
//ZMQ
- protected boolean zmqEnabled = Defaults.ZMQ_ENABLED;
+ protected boolean zmqEnableTcp = Defaults.ZMQ_ENABLE_TCP;
+ protected boolean zmqEnableIpc = Defaults.ZMQ_ENABLE_IPC;
protected int zmqPort = Defaults.ZMQ_PORT;
protected int zmqThreads = Defaults.ZMQ_THREADS;
protected String zmqIpc = Defaults.ZMQ_IPC;
@@ -616,15 +617,48 @@ protected void setSpentAddressesDbLogPath(String spentAddressesDbLogPath) {
this.spentAddressesDbLogPath = spentAddressesDbLogPath;
}
+ /**
+ * Checks if ZMQ is enabled.
+ * @return true if zmqEnableTcp or zmqEnableIpc is set.
+ */
@Override
public boolean isZmqEnabled() {
- return zmqEnabled;
+ return zmqEnableTcp || zmqEnableIpc;
}
+ /**
+ * Activates ZMQ to listen on TCP and IPC.
+ * @deprecated Use {@link #setZmqEnableTcp(boolean) and/or {@link #setZmqEnableIpc(boolean)}} instead.
+ * @param zmqEnabled true if ZMQ should listen in TCP and IPC.
+ */
+ @Deprecated
@JsonProperty
@Parameter(names = "--zmq-enabled", description = ZMQConfig.Descriptions.ZMQ_ENABLED)
protected void setZmqEnabled(boolean zmqEnabled) {
- this.zmqEnabled = zmqEnabled;
+ this.zmqEnableTcp = zmqEnabled;
+ this.zmqEnableIpc = zmqEnabled;
+ }
+
+ @Override
+ public boolean isZmqEnableTcp() {
+ return zmqEnableTcp;
+ }
+
+ @JsonProperty
+ @Parameter(names = "--zmq-enable-tcp", description = ZMQConfig.Descriptions.ZMQ_ENABLE_TCP)
+ public void setZmqEnableTcp(boolean zmqEnableTcp) {
+ this.zmqEnableTcp = zmqEnableTcp;
+ }
+
+ @Override
+ public boolean isZmqEnableIpc() {
+ return zmqEnableIpc;
+ }
+
+ @JsonProperty
+ @Parameter(names = "--zmq-enable-ipc", description = ZMQConfig.Descriptions.ZMQ_ENABLE_IPC)
+ public void setZmqEnableIpc(boolean zmqEnableIpc) {
+ this.zmqEnableIpc = zmqEnableIpc;
}
@Override
@@ -636,6 +670,7 @@ public int getZmqPort() {
@Parameter(names = "--zmq-port", description = ZMQConfig.Descriptions.ZMQ_PORT)
protected void setZmqPort(int zmqPort) {
this.zmqPort = zmqPort;
+ this.zmqEnableTcp = true;
}
@Override
@@ -658,6 +693,7 @@ public String getZmqIpc() {
@Parameter(names = "--zmq-ipc", description = ZMQConfig.Descriptions.ZMQ_IPC)
protected void setZmqIpc(String zmqIpc) {
this.zmqIpc = zmqIpc;
+ this.zmqEnableIpc = true;
}
@Override
@@ -807,8 +843,9 @@ public interface Defaults {
//Zmq
int ZMQ_THREADS = 1;
+ boolean ZMQ_ENABLE_IPC = false;
String ZMQ_IPC = "ipc://iri";
- boolean ZMQ_ENABLED = false;
+ boolean ZMQ_ENABLE_TCP = false;
int ZMQ_PORT = 5556;
//TipSel
diff --git a/src/main/java/com/iota/iri/conf/ZMQConfig.java b/src/main/java/com/iota/iri/conf/ZMQConfig.java
index b3a9b304b7..f877e761c4 100644
--- a/src/main/java/com/iota/iri/conf/ZMQConfig.java
+++ b/src/main/java/com/iota/iri/conf/ZMQConfig.java
@@ -4,6 +4,10 @@ public interface ZMQConfig extends Config {
boolean isZmqEnabled();
+ boolean isZmqEnableTcp();
+
+ boolean isZmqEnableIpc();
+
int getZmqPort();
int getZmqThreads();
@@ -11,8 +15,10 @@ public interface ZMQConfig extends Config {
String getZmqIpc();
interface Descriptions {
- String ZMQ_ENABLED = "Enabling zmq channels.";
String ZMQ_PORT = "The port used to connect to the ZMQ feed";
String ZMQ_IPC = "The path that is used to communicate with ZMQ in IPC";
+ String ZMQ_ENABLED = "Enabling zmq channels (deprecated). Use --zmq-enable-tcp or --zmq-enable-ipc instead";
+ String ZMQ_ENABLE_TCP = "Enabling zmq channels on tcp port 5556. Use --zmq-port=[PORT] to override.";
+ String ZMQ_ENABLE_IPC = "Enabling zmq channels on ipc://iri. Use --zmq-ipc=[SOCKET] to override.";
}
}
diff --git a/src/main/java/com/iota/iri/zmq/MessageQ.java b/src/main/java/com/iota/iri/zmq/MessageQ.java
index c1fdc3eed8..eacbd391d0 100644
--- a/src/main/java/com/iota/iri/zmq/MessageQ.java
+++ b/src/main/java/com/iota/iri/zmq/MessageQ.java
@@ -40,22 +40,22 @@ class MessageQ {
private final ExecutorService publisherService = Executors.newSingleThreadExecutor();
public static MessageQ createWith(ZMQConfig config) {
- return new MessageQ(config.getZmqPort(), config.getZmqIpc(), config.getZmqThreads());
+ return new MessageQ(config);
}
/**
* Creates and starts a ZMQ publisher.
*
- * @param port port the publisher will be bound to
- * @param ipc IPC socket the publisher will be bound to
- * @param nthreads number of threads used by the ZMQ publisher
+ * @param config {@link ZMQConfig} that should be used.
*/
- private MessageQ(int port, String ipc, int nthreads) {
- context = ZMQ.context(nthreads);
+ private MessageQ(ZMQConfig config) {
+ context = ZMQ.context(config.getZmqThreads());
publisher = context.socket(ZMQ.PUB);
- publisher.bind(String.format("tcp://*:%d", port));
- if (ipc != null) {
- publisher.bind(ipc);
+ if (config.isZmqEnableTcp()) {
+ publisher.bind(String.format("tcp://*:%d", config.getZmqPort()));
+ }
+ if (config.isZmqEnableIpc()) {
+ publisher.bind(config.getZmqIpc());
}
}
diff --git a/src/test/java/com/iota/iri/conf/ZMQConfigTest.java b/src/test/java/com/iota/iri/conf/ZMQConfigTest.java
new file mode 100644
index 0000000000..d709be7962
--- /dev/null
+++ b/src/test/java/com/iota/iri/conf/ZMQConfigTest.java
@@ -0,0 +1,76 @@
+package com.iota.iri.conf;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ZMQConfigTest {
+
+ @Test
+ public void isZmqEnabled() {
+ String[] args = {
+ "--zmq-enable-tcp", "true",
+ "--zmq-enable-ipc", "true",
+ };
+ IotaConfig config = ConfigFactory.createIotaConfig(false);
+ config.parseConfigFromArgs(args);
+ assertTrue("ZMQ must be globally enabled", config.isZmqEnabled());
+ assertTrue("ZMQ TCP must be enabled", config.isZmqEnableTcp());
+ assertTrue("ZMQ IPC must be enabled", config.isZmqEnableIpc());
+ }
+
+ @Test
+ public void isZmqEnableTcp() {
+ String[] args = {
+ "--zmq-enable-tcp", "true"
+ };
+ IotaConfig config = ConfigFactory.createIotaConfig(false);
+ config.parseConfigFromArgs(args);
+ assertEquals("ZMQ port must be the default port", 5556, config.getZmqPort());
+ assertTrue("ZMQ TCP must be enabled", config.isZmqEnableTcp());
+ }
+
+ @Test
+ public void isZmqEnableIpc() {
+ String[] args = {
+ "--zmq-enable-ipc", "true"
+ };
+ IotaConfig config = ConfigFactory.createIotaConfig(false);
+ config.parseConfigFromArgs(args);
+ assertEquals("ZMQ ipc must be the default ipc", "ipc://iri", config.getZmqIpc());
+ assertTrue("ZMQ IPC must be enabled", config.isZmqEnableIpc());
+ }
+
+ @Test
+ public void getZmqPort() {
+ String[] args = {
+ "--zmq-port", "8899"
+ };
+ IotaConfig config = ConfigFactory.createIotaConfig(false);
+ config.parseConfigFromArgs(args);
+ assertTrue("ZMQ TCP must be enabled", config.isZmqEnableTcp());
+ assertEquals("ZMQ port must be overridden", 8899, config.getZmqPort());
+ }
+
+ @Test
+ public void getZmqThreads() {
+ String[] args = {
+ "--zmq-threads", "5"
+ };
+ IotaConfig config = ConfigFactory.createIotaConfig(false);
+ config.parseConfigFromArgs(args);
+ assertEquals("ZMQ threads must be overridden", 5, config.getZmqThreads());
+ }
+
+ @Test
+ public void getZmqIpc() {
+ String[] args = {
+ "--zmq-ipc", "ipc://test"
+ };
+ IotaConfig config = ConfigFactory.createIotaConfig(false);
+ config.parseConfigFromArgs(args);
+ assertTrue("ZMQ IPC must be enabled", config.isZmqEnableIpc());
+ assertEquals("ZMQ ipc must be overridden", "ipc://test", config.getZmqIpc());
+ }
+}
\ No newline at end of file
From 5941d8bc30f31000fc58c58cdb2ac447f860dd1d Mon Sep 17 00:00:00 2001
From: Christian Lehmann
Date: Tue, 22 Jan 2019 19:44:56 +0100
Subject: [PATCH 03/67] Added test for legacy parameter --zmqenabled
---
src/main/java/com/iota/iri/conf/BaseIotaConfig.java | 7 ++++++-
src/test/java/com/iota/iri/conf/ZMQConfigTest.java | 12 ++++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
index c814b4a84c..de877b9791 100644
--- a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
+++ b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
@@ -75,7 +75,12 @@ public abstract class BaseIotaConfig implements IotaConfig {
protected String zmqIpc = Defaults.ZMQ_IPC;
protected int qSizeNode = Defaults.QUEUE_SIZE;
protected int cacheSizeBytes = Defaults.CACHE_SIZE_BYTES;
-
+ /**
+ * @deprecated This field was replaced by {@link #zmqEnableTcp} and {@link #zmqEnableIpc}. It is only needed
+ * for backward compatibility to --zmq-enabled parameter with JCommander.
+ */
+ @Deprecated
+ private boolean zmqEnabled;
//Tip Selection
protected int maxDepth = Defaults.MAX_DEPTH;
diff --git a/src/test/java/com/iota/iri/conf/ZMQConfigTest.java b/src/test/java/com/iota/iri/conf/ZMQConfigTest.java
index d709be7962..e2caee1feb 100644
--- a/src/test/java/com/iota/iri/conf/ZMQConfigTest.java
+++ b/src/test/java/com/iota/iri/conf/ZMQConfigTest.java
@@ -7,6 +7,18 @@
public class ZMQConfigTest {
+ @Test
+ public void isZmqEnabledLegacy() {
+ String[] args = {
+ "--zmq-enabled", "true",
+ };
+ IotaConfig config = ConfigFactory.createIotaConfig(false);
+ config.parseConfigFromArgs(args);
+ assertTrue("ZMQ must be globally enabled", config.isZmqEnabled());
+ assertTrue("ZMQ TCP must be enabled", config.isZmqEnableTcp());
+ assertTrue("ZMQ IPC must be enabled", config.isZmqEnableIpc());
+ }
+
@Test
public void isZmqEnabled() {
String[] args = {
From 6cd8d239a936e8f7c393b9c2d3805eac4ce6ad9a Mon Sep 17 00:00:00 2001
From: Christian Lehmann
Date: Tue, 22 Jan 2019 21:11:50 +0100
Subject: [PATCH 04/67] Implemented @kweg20 review comments.
---
.../com/iota/iri/conf/BaseIotaConfig.java | 6 ++---
.../java/com/iota/iri/conf/ZMQConfig.java | 25 ++++++++++++++++---
2 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
index de877b9791..b4d42542fe 100644
--- a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
+++ b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
@@ -650,7 +650,7 @@ public boolean isZmqEnableTcp() {
}
@JsonProperty
- @Parameter(names = "--zmq-enable-tcp", description = ZMQConfig.Descriptions.ZMQ_ENABLE_TCP)
+ @Parameter(names = "--zmq-enable-tcp", description = ZMQConfig.Descriptions.ZMQ_ENABLE_TCP, arity = 1)
public void setZmqEnableTcp(boolean zmqEnableTcp) {
this.zmqEnableTcp = zmqEnableTcp;
}
@@ -661,7 +661,7 @@ public boolean isZmqEnableIpc() {
}
@JsonProperty
- @Parameter(names = "--zmq-enable-ipc", description = ZMQConfig.Descriptions.ZMQ_ENABLE_IPC)
+ @Parameter(names = "--zmq-enable-ipc", description = ZMQConfig.Descriptions.ZMQ_ENABLE_IPC, arity = 1)
public void setZmqEnableIpc(boolean zmqEnableIpc) {
this.zmqEnableIpc = zmqEnableIpc;
}
@@ -684,7 +684,7 @@ public int getZmqThreads() {
}
@JsonProperty
- @Parameter(names = "--zmq-threads", description = ZMQConfig.Descriptions.ZMQ_PORT)
+ @Parameter(names = "--zmq-threads", description = ZMQConfig.Descriptions.ZMQ_THREADS)
protected void setZmqThreads(int zmqThreads) {
this.zmqThreads = zmqThreads;
}
diff --git a/src/main/java/com/iota/iri/conf/ZMQConfig.java b/src/main/java/com/iota/iri/conf/ZMQConfig.java
index f877e761c4..d5593ee7e5 100644
--- a/src/main/java/com/iota/iri/conf/ZMQConfig.java
+++ b/src/main/java/com/iota/iri/conf/ZMQConfig.java
@@ -2,23 +2,42 @@
public interface ZMQConfig extends Config {
+ /**
+ * @return Descriptions#ZMQ_ENABLED
+ */
boolean isZmqEnabled();
+ /**
+ * @return Descriptions#ZMQ_ENABLE_TCP
+ */
boolean isZmqEnableTcp();
+ /**
+ * @return Descriptions#ZMQ_ENABLE_IPC
+ */
boolean isZmqEnableIpc();
+ /**
+ * @return Descriptions#ZMQ_PORT
+ */
int getZmqPort();
+ /**
+ * @return Descriptions#ZMQ_THREADS
+ */
int getZmqThreads();
+ /**
+ * @return Descriptions#ZMQ_IPC
+ */
String getZmqIpc();
interface Descriptions {
String ZMQ_PORT = "The port used to connect to the ZMQ feed";
String ZMQ_IPC = "The path that is used to communicate with ZMQ in IPC";
- String ZMQ_ENABLED = "Enabling zmq channels (deprecated). Use --zmq-enable-tcp or --zmq-enable-ipc instead";
- String ZMQ_ENABLE_TCP = "Enabling zmq channels on tcp port 5556. Use --zmq-port=[PORT] to override.";
- String ZMQ_ENABLE_IPC = "Enabling zmq channels on ipc://iri. Use --zmq-ipc=[SOCKET] to override.";
+ String ZMQ_ENABLED = "Enable zmq channels (deprecated). Use --zmq-enable-tcp or --zmq-enable-ipc instead";
+ String ZMQ_ENABLE_TCP = "Enable zmq channels on tcp port 5556. Use --zmq-port=[PORT] to override.";
+ String ZMQ_ENABLE_IPC = "Enable zmq channels on ipc://iri. Use --zmq-ipc=[SOCKET] to override.";
+ String ZMQ_THREADS = "The threads used by ZMQ publisher";
}
}
From 2ae4db5f4c299b5c42fe64ad5078218cff169473 Mon Sep 17 00:00:00 2001
From: Christian Lehmann
Date: Wed, 23 Jan 2019 15:18:24 +0100
Subject: [PATCH 05/67] Added codacy comments.
---
.../iri/network/TransactionRequester.java | 7 +++++
.../java/com/iota/iri/storage/Tangle.java | 31 +++++++++++++++++--
.../iota/iri/zmq/MessageQueueProvider.java | 27 +++++++++++++++-
.../iota/iri/zmq/ZmqMessageQueueProvider.java | 17 +++++++++-
4 files changed, 78 insertions(+), 4 deletions(-)
diff --git a/src/main/java/com/iota/iri/network/TransactionRequester.java b/src/main/java/com/iota/iri/network/TransactionRequester.java
index e6a739c6f5..c7e71a2131 100644
--- a/src/main/java/com/iota/iri/network/TransactionRequester.java
+++ b/src/main/java/com/iota/iri/network/TransactionRequester.java
@@ -2,6 +2,7 @@
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.model.Hash;
+import com.iota.iri.service.snapshot.Snapshot;
import com.iota.iri.service.snapshot.SnapshotProvider;
import com.iota.iri.storage.Tangle;
import org.apache.commons.lang3.ArrayUtils;
@@ -30,6 +31,12 @@ public class TransactionRequester {
private final Tangle tangle;
private final SnapshotProvider snapshotProvider;
+ /**
+ * Create {@link TransactionRequester} for receiving transactions from the tangle.
+ *
+ * @param tangle used to request transaction
+ * @param snapshotProvider that allows to retrieve the {@link Snapshot} instances that are relevant for the node
+ */
public TransactionRequester(Tangle tangle, SnapshotProvider snapshotProvider) {
this.tangle = tangle;
this.snapshotProvider = snapshotProvider;
diff --git a/src/main/java/com/iota/iri/storage/Tangle.java b/src/main/java/com/iota/iri/storage/Tangle.java
index 2d4ee3ad54..b88ab25c0f 100644
--- a/src/main/java/com/iota/iri/storage/Tangle.java
+++ b/src/main/java/com/iota/iri/storage/Tangle.java
@@ -1,7 +1,14 @@
package com.iota.iri.storage;
+import com.iota.iri.model.Hash;
import com.iota.iri.model.StateDiff;
-import com.iota.iri.model.persistables.*;
+import com.iota.iri.model.persistables.Address;
+import com.iota.iri.model.persistables.Approvee;
+import com.iota.iri.model.persistables.Bundle;
+import com.iota.iri.model.persistables.Milestone;
+import com.iota.iri.model.persistables.ObsoleteTag;
+import com.iota.iri.model.persistables.Tag;
+import com.iota.iri.model.persistables.Transaction;
import com.iota.iri.utils.Pair;
import java.util.*;
@@ -40,6 +47,10 @@ public void addPersistenceProvider(PersistenceProvider provider) {
this.persistenceProviders.add(provider);
}
+ /**
+ * Adds {@link com.iota.iri.storage.MessageQueueProvider} that should be notified.
+ * @param provider that should be notified.
+ */
public void addMessageQueueProvider(MessageQueueProvider provider) {
this.messageQueueProviders.add(provider);
}
@@ -114,6 +125,14 @@ public Pair getLatest(Class> model, Class> index) th
return latest;
}
+ /**
+ * Updates all {@link PersistenceProvider} and publishes message to all {@link com.iota.iri.storage.MessageQueueProvider}.
+ *
+ * @param model with transaction data
+ * @param index {@link Hash} identifier of the {@link Transaction} set
+ * @param item identifying the purpose of the update
+ * @throws Exception when updating the {@link PersistenceProvider} fails
+ */
public void update(Persistable model, Indexable index, String item) throws Exception {
updatePersistenceProvider(model, index, item);
updateMessageQueueProvider(model, index, item);
@@ -127,10 +146,18 @@ private void updatePersistenceProvider(Persistable model, Indexable index, Strin
private void updateMessageQueueProvider(Persistable model, Indexable index, String item) {
for(MessageQueueProvider provider: this.messageQueueProviders) {
- provider.update(model, index, item);
+ provider.publishTransaction(model, index, item);
}
}
+ /**
+ * Notifies all registered {@link com.iota.iri.storage.MessageQueueProvider} and publishes message to MessageQueue.
+ *
+ * @param message that can be formatted by {@link String#format(String, Object...)}
+ * @param objects that should replace the placeholder in message.
+ * @see com.iota.iri.zmq.ZmqMessageQueueProvider#publish(String, Object...)
+ * @see String#format(String, Object...)
+ */
public void publish(String message, Object... objects) {
for(MessageQueueProvider provider: this.messageQueueProviders) {
provider.publish(message, objects);
diff --git a/src/main/java/com/iota/iri/zmq/MessageQueueProvider.java b/src/main/java/com/iota/iri/zmq/MessageQueueProvider.java
index fa300bccf1..065b92a7c7 100644
--- a/src/main/java/com/iota/iri/zmq/MessageQueueProvider.java
+++ b/src/main/java/com/iota/iri/zmq/MessageQueueProvider.java
@@ -1,10 +1,35 @@
package com.iota.iri.zmq;
+import com.iota.iri.model.Hash;
+import com.iota.iri.model.persistables.Transaction;
import com.iota.iri.storage.Indexable;
import com.iota.iri.storage.Persistable;
+/**
+ * Publish messages to the MessageQueue.
+ */
public interface MessageQueueProvider {
+ /**
+ * Publishes the message to the MessageQueue.
+ *
+ * @param message that can be formatted by {@link String#format(String, Object...)}
+ * @param objects that should replace the placeholder in message.
+ * @see String#format(String, Object...)
+ */
void publish(String message, Object... objects);
- boolean update(Persistable model, Indexable index, String item);
+
+ /**
+ * Publishes the transaction details to the MessageQueue.
+ *
+ * @param model with Transaction details send to the MessageQueue.
+ * @param index {@link Hash} identifier of the {@link Transaction} set
+ * @param item identifying the purpose of the update
+ * @return true when message was send to the MessageQueue
+ */
+ boolean publishTransaction(Persistable model, Indexable index, String item);
+
+ /**
+ * Shutdown the MessageQueue.
+ */
void shutdown();
}
diff --git a/src/main/java/com/iota/iri/zmq/ZmqMessageQueueProvider.java b/src/main/java/com/iota/iri/zmq/ZmqMessageQueueProvider.java
index 0e65989c74..289bce9a6a 100644
--- a/src/main/java/com/iota/iri/zmq/ZmqMessageQueueProvider.java
+++ b/src/main/java/com/iota/iri/zmq/ZmqMessageQueueProvider.java
@@ -10,17 +10,25 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * Use zeromq to create a MessageQueue that publishes messages.
+ */
public class ZmqMessageQueueProvider implements MessageQueueProvider {
private static final Logger log = LoggerFactory.getLogger(ZmqMessageQueueProvider.class);
private final MessageQ messageQ;
+ /**
+ * Factory method to create a new ZmqMessageQueue with the given configuration.
+ *
+ * @param configuration with the zmq properties used to create MessageQueue
+ */
public ZmqMessageQueueProvider(ZMQConfig configuration) {
this.messageQ = MessageQ.createWith(configuration);
}
@Override
- public boolean update(Persistable model, Indexable index, String item) {
+ public boolean publishTransaction(Persistable model, Indexable index, String item) {
if(!(model instanceof Transaction)) {
return false;
}
@@ -77,6 +85,13 @@ private void publishTxTrytes(TransactionViewModel transactionViewModel) {
}
}
+ /**
+ * Publishes the message to the MessageQueue.
+ *
+ * @param message that can be formatted by {@link String#format(String, Object...)}
+ * @param objects that should replace the placeholder in message.
+ * @see String#format(String, Object...)
+ */
@Override
public void publish(String message, Object... objects) {
this.messageQ.publish(message, objects);
From c3ed671c527e1a4dd54dc82a1a216fdf3ca554e4 Mon Sep 17 00:00:00 2001
From: Gal Rogozinski
Date: Sun, 17 Feb 2019 13:06:01 +0200
Subject: [PATCH 06/67] Repair #1297 merge that didn't resolve conflicts
properly
---
.../com/iota/iri/network/TransactionRequesterTest.java | 4 ++--
.../network/impl/TransactionRequesterWorkerImplTest.java | 7 +------
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/src/test/java/com/iota/iri/network/TransactionRequesterTest.java b/src/test/java/com/iota/iri/network/TransactionRequesterTest.java
index a84bb622a7..e8d6c7e5fb 100644
--- a/src/test/java/com/iota/iri/network/TransactionRequesterTest.java
+++ b/src/test/java/com/iota/iri/network/TransactionRequesterTest.java
@@ -79,7 +79,7 @@ public void instance() throws Exception {
@Test
public void popEldestTransactionToRequest() throws Exception {
- TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq);
+ TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider);
// Add some Txs to the pool and see if the method pops the eldest one
Hash eldest = getRandomTransactionHash();
txReq.requestTransaction(eldest, false);
@@ -100,7 +100,7 @@ public void transactionRequestedFreshness() throws Exception {
getRandomTransactionHash(),
getRandomTransactionHash()
));
- TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq);
+ TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider);
int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE;
//fill tips list
for (int i = 0; i < 3; i++) {
diff --git a/src/test/java/com/iota/iri/network/impl/TransactionRequesterWorkerImplTest.java b/src/test/java/com/iota/iri/network/impl/TransactionRequesterWorkerImplTest.java
index 91060e854b..e98cc53546 100644
--- a/src/test/java/com/iota/iri/network/impl/TransactionRequesterWorkerImplTest.java
+++ b/src/test/java/com/iota/iri/network/impl/TransactionRequesterWorkerImplTest.java
@@ -20,10 +20,8 @@
import com.iota.iri.model.persistables.Transaction;
import com.iota.iri.network.Node;
import com.iota.iri.network.TransactionRequester;
-import com.iota.iri.network.impl.TransactionRequesterWorkerImpl;
import com.iota.iri.service.snapshot.SnapshotProvider;
import com.iota.iri.storage.Tangle;
-import com.iota.iri.zmq.MessageQ;
import static com.iota.iri.TransactionTestUtils.getRandomTransaction;
import static com.iota.iri.TransactionTestUtils.get9Transaction;
@@ -53,9 +51,6 @@ public class TransactionRequesterWorkerImplTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private static SnapshotProvider snapshotProvider;
- @Mock
- private static MessageQ messageQ;
-
private static TransactionRequester requester;
private static TransactionRequesterWorkerImpl worker;
@@ -70,7 +65,7 @@ public class TransactionRequesterWorkerImplTest {
@Before
public void before() {
- requester = new TransactionRequester(tangle, snapshotProvider, messageQ);
+ requester = new TransactionRequester(tangle, snapshotProvider);
worker = new TransactionRequesterWorkerImpl();
worker.init(tangle, requester, tipsVM, node);
From 650bf8da4357431f87b4f276c41387eff1ffbb8a Mon Sep 17 00:00:00 2001
From: Nicolas Meylan
Date: Sun, 17 Feb 2019 15:53:50 +0100
Subject: [PATCH 07/67] Test: Add benchmarks for Curl and PearlDiver (#1331)
---
.../iota/iri/benchmarks/BenchmarkRunner.java | 15 +++++
.../iri/benchmarks/crypto/CurlBenchmark.java | 62 +++++++++++++++++++
.../crypto/PearlDiverBenchmark.java | 23 +++++++
3 files changed, 100 insertions(+)
create mode 100644 src/test/java/com/iota/iri/benchmarks/crypto/CurlBenchmark.java
create mode 100644 src/test/java/com/iota/iri/benchmarks/crypto/PearlDiverBenchmark.java
diff --git a/src/test/java/com/iota/iri/benchmarks/BenchmarkRunner.java b/src/test/java/com/iota/iri/benchmarks/BenchmarkRunner.java
index a5954a2ad6..ef4a22af9c 100644
--- a/src/test/java/com/iota/iri/benchmarks/BenchmarkRunner.java
+++ b/src/test/java/com/iota/iri/benchmarks/BenchmarkRunner.java
@@ -28,4 +28,19 @@ public void launchDbBenchmarks() throws RunnerException {
//possible to do assertions over run results
new Runner(opts).run();
}
+
+ @Test
+ public void launchCryptoBenchmark() throws RunnerException {
+ Options opts = new OptionsBuilder()
+ .include(this.getClass().getPackage().getName() + ".crypto")
+ .mode(Mode.Throughput)
+ .timeUnit(TimeUnit.SECONDS)
+ .warmupIterations(5)
+ .forks(1)
+ .measurementIterations(10)
+ .shouldFailOnError(true)
+ .shouldDoGC(false)
+ .build();
+ new Runner(opts).run();
+ }
}
diff --git a/src/test/java/com/iota/iri/benchmarks/crypto/CurlBenchmark.java b/src/test/java/com/iota/iri/benchmarks/crypto/CurlBenchmark.java
new file mode 100644
index 0000000000..341ab3d23a
--- /dev/null
+++ b/src/test/java/com/iota/iri/benchmarks/crypto/CurlBenchmark.java
@@ -0,0 +1,62 @@
+package com.iota.iri.benchmarks.crypto;
+
+import com.iota.iri.crypto.Curl;
+import com.iota.iri.crypto.SpongeFactory;
+import com.iota.iri.utils.Converter;
+import com.iota.iri.utils.Pair;
+import org.junit.Assert;
+import org.openjdk.jmh.annotations.Benchmark;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class CurlBenchmark {
+ private final static String TRYTES = "RSWWSFXPQJUBJROQBRQZWZXZJWMUBVIVMHPPTYSNW9YQIQQF9RCSJJCVZG9ZWITXNCSBBDHEEKDRBHVTWCZ9SZOOZHVBPCQNPKTWFNZAWGCZ9QDIMKRVINMIRZBPKRKQAIPGOHBTHTGYXTBJLSURDSPEOJ9UKJECUKCCPVIQQHDUYKVKISCEIEGVOQWRBAYXWGSJUTEVG9RPQLPTKYCRAJ9YNCUMDVDYDQCKRJOAPXCSUDAJGETALJINHEVNAARIPONBWXUOQUFGNOCUSSLYWKOZMZUKLNITZIFXFWQAYVJCVMDTRSHORGNSTKX9Z9DLWNHZSMNOYTU9AUCGYBVIITEPEKIXBCOFCMQPBGXYJKSHPXNUKFTXIJVYRFILAVXEWTUICZCYYPCEHNTK9SLGVL9RLAMYTAEPONCBHDXSEQZOXO9XCFUCPPMKEBR9IEJGQOPPILHFXHMIULJYXZJASQEGCQDVYFOM9ETXAGVMSCHHQLFPATWOSMZIDL9AHMSDCE9UENACG9OVFAEIPPQYBCLXDMXXA9UBJFQQBCYKETPNKHNOUKCSSYLWZDLKUARXNVKKKHNRBVSTVKQCZL9RY9BDTDTPUTFUBGRMSTOTXLWUHDMSGYRDSZLIPGQXIDMNCNBOAOI9WFUCXSRLJFIVTIPIAZUK9EDUJJ9B9YCJEZQQELLHVCWDNRH9FUXDGZRGOVXGOKORTCQQA9JXNROLETYCNLRMBGXBL9DQKMOAZCBJGWLNJLGRSTYBKLGFVRUF9QOPZVQFGMDJA9TBVGFJDBAHEVOLW9GNU9NICLCQJBOAJBAHHBZJGOFUCQMBGYQLCWNKSZPPBQMSJTJLM9GXOZHTNDLGIRCSIJAZTENQVQDHFSOQM9WVNWQQJNOPZMEISSCLOADMRNWALBBSLSWNCTOSNHNLWZBVCFIOGFPCPRKQSRGKFXGTWUSCPZSKQNLQJGKDLOXSBJMEHQPDZGSENUKWAHRNONDTBLHNAKGLOMCFYRCGMDOVANPFHMQRFCZIQHCGVORJJNYMTORDKPJPLA9LWAKAWXLIFEVLKHRKCDG9QPQCPGVKIVBENQJTJGZKFTNZHIMQISVBNLHAYSSVJKTIELGTETKPVRQXNAPWOBGQGFRMMK9UQDWJHSQMYQQTCBMVQKUVGJEAGTEQDN9TCRRAZHDPSPIYVNKPGJSJZASZQBM9WXEDWGAOQPPZFLAMZLEZGXPYSOJRWL9ZH9NOJTUKXNTCRRDO9GKULXBAVDRIZBOKJYVJUSHIX9F9O9ACYCAHUKBIEPVZWVJAJGSDQNZNWLIWVSKFJUMOYDMVUFLUXT9CEQEVRFBJVPCTJQCORM9JHLYFSMUVMFDXZFNCUFZZIKREIUIHUSHRPPOUKGFKWX9COXBAZMQBBFRFIBGEAVKBWKNTBMLPHLOUYOXPIQIZQWGOVUWQABTJT9ZZPNBABQFYRCQLXDHDEX9PULVTCQLWPTJLRSVZQEEYVBVY9KCNEZXQLEGADSTJBYOXEVGVTUFKNCNWMEDKDUMTKCMRPGKDCCBDHDVVSMPOPUBZOMZTXJSQNVVGXNPPBVSBL9WWXWQNMHRMQFEQYKWNCSW9URI9FYPT9UZMAFMMGUKFYTWPCQKVJ9DIHRJFMXRZUGI9TMTFUQHGXNBITDSORZORQIAMKY9VRYKLEHNRNFSEFBHF9KXIQAEZEJNQOENJVMWLMHI9GNZPXYUIFAJIVCLAGKUZIKTJKGNQVTXJORWIQDHUPBBPPYOUPFAABBVMMYATXERQHPECDVYGWDGXFJKOMOBXKRZD9MCQ9LGDGGGMYGUAFGMQTUHZOAPLKPNPCIKUNEMQIZOCM9COAOMZSJ9GVWZBZYXMCNALENZ9PRYMHENPWGKX9ULUIGJUJRKFJPBTTHCRZQKEAHT9DC9GSWQEGDTZFHACZMLFYDVOWZADBNMEM9XXEOMHCNJMDSUAJRQTBUWKJF9RZHK9ACGUNI9URFIHLXBXCEODONPXBSCWP9WNAEYNALKQHGULUQGAFL9LB9NBLLCACLQFGQMXRHGBTMI9YKAJKVELRWWKJAPKMSYMJTDYMZ9PJEEYIRXRMMFLRSFSHIXUL9NEJABLRUGHJFL9RASMSKOI9VCFRZ9GWTMODUUESIJBHWWHZYCLDENBFSJQPIOYC9MBGOOXSWEMLVU9L9WJXKZKVDBDMFSVHHISSSNILUMWULMVMESQUIHDGBDXROXGH9MTNFSLWJZRAPOKKRGXAAQBFPYPAAXLSTMNSNDTTJQSDQORNJS9BBGQ9KQJZYPAQ9JYQZJ9B9KQDAXUACZWRUNGMBOQLQZUHFNCKVQGORRZGAHES9PWJUKZWUJSBMNZFILBNBQQKLXITCTQDDBV9UDAOQOUPWMXTXWFWVMCXIXLRMRWMAYYQJPCEAAOFEOGZQMEDAGYGCTKUJBS9AGEXJAFHWWDZRYEN9DN9HVCMLFURISLYSWKXHJKXMHUWZXUQARMYPGKRKQMHVR9JEYXJRPNZINYNCGZHHUNHBAIJHLYZIZGGIDFWVNXZQADLEDJFTIUTQWCQSX9QNGUZXGXJYUUTFSZPQKXBA9DFRQRLTLUJENKESDGTZRGRSLTNYTITXRXRGVLWBTEWPJXZYLGHLQBAVYVOSABIVTQYQM9FIQKCBRRUEMVVTMERLWOK";
+ private final static String HASH = "TIXEPIEYMGURTQ9ABVYVQSWMNGCVQFASMFAEQWUZCLIWLCDIGYVXOEJBBEMZOIHAYSUQMEFOGZBXUMHQW";
+
+ /**
+ * Benchmark absorb and squeeze methods of Curl 81 hash function.
+ */
+ @Benchmark
+ public void curl() {
+ int size = 8019;
+ byte[] in_trits = new byte[size];
+ byte[] hash_trits = new byte[Curl.HASH_LENGTH];
+ Converter.trits(TRYTES, in_trits, 0);
+ Curl curl = (Curl) SpongeFactory.create(SpongeFactory.Mode.CURLP81);
+ curl.absorb(in_trits, 0, in_trits.length);
+ curl.squeeze(hash_trits, 0, Curl.HASH_LENGTH);
+ String out_trytes = Converter.trytes(hash_trits);
+ Assert.assertEquals(HASH, out_trytes);
+ }
+
+ /**
+ * Benchmark absorb and squeeze methods of pair Curl 81 hash function.
+ */
+ @Benchmark
+ public void pairCurl() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
+ int size = 8019;
+ byte[] in_trits = new byte[size];
+ Pair hashPair = new Pair<>(new long[Curl.HASH_LENGTH], new long[Curl.HASH_LENGTH]);
+ Converter.trits(TRYTES, in_trits, 0);
+ // Using reflection to benchmark private, non-production code.
+ // Reflection doesn't have impact on benchmark result (this has been tested)
+ // Please remove this code when method are public
+ Class curlClass = Curl.class;
+ Constructor curlConstructor = curlClass.getDeclaredConstructor(boolean.class, SpongeFactory.Mode.class);
+ curlConstructor.setAccessible(true);
+ Curl curl = curlConstructor.newInstance(true, SpongeFactory.Mode.CURLP81);
+ Method pairAbsorb = curlClass.getDeclaredMethod("absorb", Pair.class, int.class, int.class);
+ Method pairSqueeze = curlClass.getDeclaredMethod("squeeze", Pair.class, int.class, int.class);
+ pairAbsorb.setAccessible(true);
+ pairSqueeze.setAccessible(true);
+
+ pairAbsorb.invoke(curl, Converter.longPair(in_trits), 0, in_trits.length);
+ pairSqueeze.invoke(curl, hashPair, 0, Curl.HASH_LENGTH);
+ byte[] hash_trits = Converter.trits(hashPair.low, hashPair.hi);
+ String out_trytes = Converter.trytes(hash_trits);
+ Assert.assertEquals(HASH, out_trytes);
+ }
+
+}
diff --git a/src/test/java/com/iota/iri/benchmarks/crypto/PearlDiverBenchmark.java b/src/test/java/com/iota/iri/benchmarks/crypto/PearlDiverBenchmark.java
new file mode 100644
index 0000000000..9d36eb1ed3
--- /dev/null
+++ b/src/test/java/com/iota/iri/benchmarks/crypto/PearlDiverBenchmark.java
@@ -0,0 +1,23 @@
+package com.iota.iri.benchmarks.crypto;
+
+import com.iota.iri.crypto.PearlDiver;
+import com.iota.iri.utils.Converter;
+import org.openjdk.jmh.annotations.Benchmark;
+
+public class PearlDiverBenchmark {
+ private static final int MIN_WEIGHT_MAGNITUDE = 9;
+ private static final int NUM_CORES = -1; // use n-1 cores
+ private static final String TRYTES = "ZMGYJPAAYHGRDGKCUEAIZDERGWUJH9QIHEESTUAZIQDQGMREKOVZCQRKHUZPXQ9PPIBGARFTZBZCYGAWIEUGPMIEEMKVSLCCJPITK9A9VLHCWLRZTRYDPGPFGEEFMVMVQGGM9NFPEKLVQHMOPDTJHDIEHBFGBBFOHPLHX9RJVEIQC9WAOFPHP9GGFRYCJEX9UVUGVJLBCBNQCLKSLULGZUHCVUJZMILSROKHYAA9RLF9XQXKXCULHJIUGXDWBKJLGLHXXR9BNKDKG9JMJCTAGMKWGXPPKVBVNROUADXHUJWYDWZDSIUIVUTKKBTVJ9CMKWVFMCYMCVFMDPLQ9YFIHUMRTBZLIT9LIQRFXF9AKDMTXMHKCSFQDJIFQRFBHSIKVKQBUKFDPOLWQXBUZKNGGHJW9IVLG9GTIW9IRWGCM9A9YT99JUDKQQLWBFXGYVDYSRYSAWVRBFXIKMQRGCIHLWOUTKFWWDFRQZMCAUXSSK9LWQRCNNCXBJOIKVMLTMRENT9YLUOHACKHYYUERSBKWSCJTCJBCDCARABPVNNKIXXSVLELPILCTRQCEGYITPVODUMUEYFRJBOKAVVIFURHZO9SYVHZGZZIRYGYJDCOWIEEZNBDBHMVDFCGDCYSJFEGCYDIGTPTAZTM9MJXKCCRGJQTXTQYOGLGBAWAUAPVMJAAUB9QOOO9FPQVCQVZQLAICIWUXZAVVRZJFKMXSJEL9KGDSGPKEIZHDXQWZFYWPLVOSSOAETKPMSDDQEZDOCNCOJCEWLSA9SSQODHAEUEERDCOTSHNEOVGCFGXQRRIVYJQUUMRVCAEMMPFYZAEWRFVBNWSGTGEWZUCOWKMEIEAGXCHPVTABBXUEWCLHTEOJZ9JPRQP9CCJQMUNQYMMHCVPSABZ9XVRLWFOMYOGVVYSYMYWCXQFUBHUVPONPSZUCYAWHWOEGAFCUXUWBRTBTYWVXNSSIZ9LHLX9UNN9FUJDUVBNLJSD9OG9FACSRNELL9DMTYITWDZPOHZSUSNUWJ9CEKQOXOXVZJVZZPTWJIFXXXRAVTDLLETTKPGNLWQCWERXFRZOPBZTDWNURWXDFKKP9SIG9IPGUSEHAGPQTCRAMMMPUWPRQKJDAKFUTR9YVVCXJQC9ZWZBOEAQISNTDHUQRKCXASYNZLQIJMZMMO9TWTHPMJIJKPTU9DMMCPYXOSPNSDQPID9YSIOFDNIJBQUEB9JTSRTWLMXUP9WIQFSZNINQLNS9NJWTLDLZYABHFKCZOBHQQQVWWVTWCKMFSYVPZHRHNJZWWFUHMCHYKLMCBXGAVRJSYLXOSYUHHTB9VMQT9NYPRDVAWWYREFVGFEJZKGPVEOQXWZD9LQNUFTREMBT9NETURCGYBWLGNMUPDO9ZSSLZJR9AEUPYLGEEIKUTHTCIXHSBCEKATIEQYBTYACTFWUJQCCTYHNTMFXHZKEFIEOXFQTKYDY9BDSLS9HXC9ANNQONFKIBZHREPYLSZWCR99HBYTLLISHGDXNLSRKNBSFCPHAFEUYLQWISLWOZRKKP9GSYFMXUBXJLMARNGJHORAVNZVDBKAZESOBFVBFSOZQGZ9IOJGCDUPYRDGEJVOPNLEFXTNXUBDSEDIYLUQBTDJONKRMBUFWYIJINZGIZOVPRKCTYFILHJPGAUSS9QPANDZQFXWTSLLEMDXMKZYWTIRUZIXWMJRRIUINDORRFTNSHASDZRGADSIRZYQAGJUVDCCRWOCMKRL9IPEEUJKLZMGLBSLXFVEHT9LWIKZYMNZUIQMJBRJETVQVWIENTSF9HLAOMKRNHHTRTPFHGWYVODZMNFPJRHLJXTKXWSSWXSTILWWJHQFFZLIZZGVAMZ9AGTM9RXQRZENESFBYZDJAQQFEHIYWYNKNYIXIRFGMMMXUXQ9TMCHIMATYHNBAVGKOXLIIVPOEFLUJCYURXQLEXSBKCVGCPOZGTVFZVSMOE9DEHTYVT9BDXHEIZFAQFZGSEDQCJAMZTWTLACGTTQKFDWFH9DLTOSCPGFJVKEFOCDLNDOKJK9MNUEFXMOLXMXBMAYO9QZOJEYWPPBOFZMJQTLULEGEPECTHHCAOHSHLIIBNIELLLXIGMUPFLCEBHUSQ9ZEYGXVDOSZTECCJHATQODTZNLPGUVOXYZTHAQELTYYTTJTICZYRGLRROICEKMWXDEUCKFRKOQFCMPITOMWVPYGPMLVWNQAV99SYHJYZJTKXQICGYHKQJ9QAOFACDNHRYYHDMCSHPLMVLWEXUEZDDTQJUBMBVJCO9RNASPTYVHVLHAYYGYUCRBWZZESBHEUEZIHJDNOVOFCEAZTKDXACSUPBFYCIRDVPPJTSWHM9BAVSPPGMBVZKIJBAGWZEIIUNUYOHEVEO9FYMJZXJFLXXIYFBIZYKTHNQWBJYHJPTYJPPUWDBQWJAWEYSGSFVLQYIB9TNYUSNWVVJPFVDQUONBBPQJINWXAKXHFSUQKYJBFGXCQWT9TYHMAFIWQPQI9IBEMDCRIFOZN9KFGBGH99ZSKFTLOWMNYJDBREEYQBVSMZPDVGRYNDRPERXXISDEYTLJBNTJSVTSSTMHG9HCC9PIAHWZAMDGRMZFNQKEJCW9NBFRTNRRXTOTUAJS9DKRAUZWCIUYXTUHYT9SJDSFRWGCPFOBUHHNXMWNLJJRLGMVMBISRI";
+
+ /**
+ * Benchmark Pearl Diver search function.
+ */
+ @Benchmark
+ public void search() {
+ PearlDiver pearlDiver = new PearlDiver();
+ byte[] myTrits = Converter.allocateTritsForTrytes(TRYTES.length());
+ Converter.trits(TRYTES, myTrits, 0);
+ pearlDiver.search(myTrits, MIN_WEIGHT_MAGNITUDE, NUM_CORES);
+ }
+
+}
From f0be37b0722b21fc1d69aa85a228091634e54ec9 Mon Sep 17 00:00:00 2001
From: Gal Rogozinski
Date: Sun, 24 Feb 2019 15:28:31 +0100
Subject: [PATCH 08/67] Up jackson to version 2.9.8 (#1358)
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 660fbe35b6..eb6366c2d1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,7 +103,7 @@
com.fasterxml.jackson.core
jackson-databind
- 2.9.6
+ 2.9.8
From 9bb3b9282d90b3ab5d27e46c1520aa7beebe8a8e Mon Sep 17 00:00:00 2001
From: Alon Elmaliah
Date: Wed, 27 Feb 2019 15:36:54 +0200
Subject: [PATCH 09/67] refactor milestone signature parameters to be
configurable (#1322)
---
.../com/iota/iri/conf/BaseIotaConfig.java | 42 ++++++++---
.../com/iota/iri/conf/MilestoneConfig.java | 33 +++++++-
.../com/iota/iri/conf/SnapshotConfig.java | 6 --
.../java/com/iota/iri/conf/TestnetConfig.java | 75 ++++++++++++++-----
src/main/java/com/iota/iri/service/API.java | 2 +-
.../service/milestone/MilestoneService.java | 7 +-
.../impl/LatestMilestoneTrackerImpl.java | 5 +-
.../milestone/impl/MilestoneServiceImpl.java | 51 +++++++------
.../impl/SpentAddressesServiceImpl.java | 2 +-
.../com/iota/iri/conf/ConfigFactoryTest.java | 6 +-
.../java/com/iota/iri/conf/ConfigTest.java | 4 +-
.../iri/service/NodeIntegrationTests.java | 2 +-
12 files changed, 163 insertions(+), 72 deletions(-)
diff --git a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
index b4d42542fe..8d2217639b 100644
--- a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
+++ b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
@@ -1,19 +1,20 @@
package com.iota.iri.conf;
-import com.iota.iri.IRI;
-import com.iota.iri.utils.IotaUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
-
+import com.iota.iri.IRI;
+import com.iota.iri.crypto.SpongeFactory;
+import com.iota.iri.model.Hash;
+import com.iota.iri.model.HashFactory;
+import com.iota.iri.utils.IotaUtils;
import org.apache.commons.lang3.ArrayUtils;
+import java.util.ArrayList;
+import java.util.List;
+
/*
Note: the fields in this class are being deserialized from Jackson so they must follow Java Bean convention.
Meaning that every field must have a getter that is prefixed with `get` unless it is a boolean and then it should be
@@ -595,6 +596,11 @@ public int getMilestoneStartIndex() {
return Defaults.MILESTONE_START_INDEX;
}
+ @Override
+ public int getMaxMilestoneIndex() {
+ return Defaults.MAX_MILESTONE_INDEX;
+ }
+
@Override
public int getNumberOfKeysInMilestone() {
return Defaults.NUM_KEYS_IN_MILESTONE;
@@ -735,10 +741,20 @@ protected void setCacheSizeBytes(int cacheSizeBytes) {
}
@Override
- public String getCoordinator() {
+ public Hash getCoordinator() {
return Defaults.COORDINATOR_ADDRESS;
}
+ @Override
+ public int getCoordinatorSecurityLevel() {
+ return Defaults.COORDINATOR_SECURITY_LEVEL;
+ }
+
+ @Override
+ public SpongeFactory.Mode getCoordinatorSignatureMode() {
+ return Defaults.COORDINATOR_SIGNATURE_MODE;
+ }
+
@Override
public boolean isDontValidateTestnetMilestoneSig() {
return false;
@@ -864,8 +880,12 @@ public interface Defaults {
int POW_THREADS = 0;
//Coo
- String COORDINATOR_ADDRESS =
- "KPWCHICGJZXKE9GSUDXZYUAPLHAKAHYHDXNPHENTERYMMBQOPSQIDENXKLKCEYCPVTZQLEEJVYJZV9BWU";
+ Hash COORDINATOR_ADDRESS = HashFactory.ADDRESS.create(
+ "KPWCHICGJZXKE9GSUDXZYUAPLHAKAHYHDXNPHENTERYMMBQOPSQIDENXKLKCEYCPVTZQLEEJVYJZV9BWU");
+ int COORDINATOR_SECURITY_LEVEL = 1;
+ SpongeFactory.Mode COORDINATOR_SIGNATURE_MODE = SpongeFactory.Mode.CURLP27;
+ int NUM_KEYS_IN_MILESTONE = 20;
+ int MAX_MILESTONE_INDEX = 1 << NUM_KEYS_IN_MILESTONE;
//Snapshot
boolean LOCAL_SNAPSHOTS_ENABLED = true;
@@ -888,7 +908,7 @@ public interface Defaults {
"/previousEpochsSpentAddresses3.txt";
long GLOBAL_SNAPSHOT_TIME = 1545469620;
int MILESTONE_START_INDEX = 933_210;
- int NUM_KEYS_IN_MILESTONE = 20;
int MAX_ANALYZED_TXS = 20_000;
+
}
}
diff --git a/src/main/java/com/iota/iri/conf/MilestoneConfig.java b/src/main/java/com/iota/iri/conf/MilestoneConfig.java
index 94afaf28b2..540509ff51 100644
--- a/src/main/java/com/iota/iri/conf/MilestoneConfig.java
+++ b/src/main/java/com/iota/iri/conf/MilestoneConfig.java
@@ -1,5 +1,8 @@
package com.iota.iri.conf;
+import com.iota.iri.crypto.SpongeFactory;
+import com.iota.iri.model.Hash;
+
/**
* Configs that should be used for tracking milestones
*/
@@ -8,16 +11,44 @@ public interface MilestoneConfig extends Config {
/**
* @return Descriptions#COORDINATOR
*/
- String getCoordinator();
+ Hash getCoordinator();
/**
* @return {@value Descriptions#DONT_VALIDATE_TESTNET_MILESTONE_SIG}
*/
boolean isDontValidateTestnetMilestoneSig();
+ /**
+ * Default Value: {@value BaseIotaConfig.Defaults#NUM_KEYS_IN_MILESTONE}
+ * @return {@value Descriptions#NUMBER_OF_KEYS_IN_A_MILESTONE}
+ */
+ int getNumberOfKeysInMilestone();
+
+ /**
+ * This is a meta-config. Its value depends on {@link #getNumberOfKeysInMilestone()}
+ * @return the maximal amount of possible milestones that can be issued
+ */
+ int getMaxMilestoneIndex();
+
+ /**
+ * Default Value: {@value BaseIotaConfig.Defaults#COORDINATOR_SECURITY_LEVEL}
+ * @return {@value Descriptions#COORDINATOR_SECURITY_LEVEL}
+ */
+ int getCoordinatorSecurityLevel();
+
+ /**
+ * Default Value: {@link BaseIotaConfig.Defaults#COORDINATOR_SIGNATURE_MODE}
+ * @return {@value Descriptions#COORDINATOR_SIGNATURE_MODE}
+ */
+ SpongeFactory.Mode getCoordinatorSignatureMode();
+
interface Descriptions {
String COORDINATOR = "The address of the coordinator";
+ String COORDINATOR_SECURITY_LEVEL = "The security level used in coordinator signatures";
+ String COORDINATOR_SIGNATURE_MODE = "The signature mode used in coordinator signatures";
String DONT_VALIDATE_TESTNET_MILESTONE_SIG = "Disable coordinator validation on testnet";
+ String NUMBER_OF_KEYS_IN_A_MILESTONE = "The depth of the Merkle tree which in turn determines the number of" +
+ "leaves (private keys) that the coordinator can use to sign a message.";
}
}
diff --git a/src/main/java/com/iota/iri/conf/SnapshotConfig.java b/src/main/java/com/iota/iri/conf/SnapshotConfig.java
index 5a1bb36679..33c30fb705 100644
--- a/src/main/java/com/iota/iri/conf/SnapshotConfig.java
+++ b/src/main/java/com/iota/iri/conf/SnapshotConfig.java
@@ -60,10 +60,6 @@ public interface SnapshotConfig extends Config {
*/
String getLocalSnapshotsBasePath();
- /**
- * @return {@value Descriptions#NUMBER_OF_KEYS_IN_A_MILESTONE}
- */
- int getNumberOfKeysInMilestone();
/**
* @return {@value Descriptions#PREVIOUS_EPOCH_SPENT_ADDRESSES_FILE}
@@ -94,8 +90,6 @@ interface Descriptions {
String SNAPSHOT_SIGNATURE_FILE = "Path to the file that contains a signature for the snapshot file.";
String MILESTONE_START_INDEX = "The start index of the milestones. This index is encoded in each milestone " +
"transaction by the coordinator.";
- String NUMBER_OF_KEYS_IN_A_MILESTONE = "The depth of the Merkle tree which in turn determines the number of" +
- "leaves (private keys) that the coordinator can use to sign a message.";
String PREVIOUS_EPOCH_SPENT_ADDRESSES_FILE = "The file that contains the list of all used addresses " +
"from previous epochs";
String SPENT_ADDRESSES_DB_PATH = "The folder where the spent addresses DB saves its data.";
diff --git a/src/main/java/com/iota/iri/conf/TestnetConfig.java b/src/main/java/com/iota/iri/conf/TestnetConfig.java
index 29e83a84d5..a35731f7f8 100644
--- a/src/main/java/com/iota/iri/conf/TestnetConfig.java
+++ b/src/main/java/com/iota/iri/conf/TestnetConfig.java
@@ -3,21 +3,28 @@
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.iota.iri.crypto.SpongeFactory;
+import com.iota.iri.model.Hash;
+import com.iota.iri.model.HashFactory;
import java.util.Objects;
public class TestnetConfig extends BaseIotaConfig {
- protected String coordinator = Defaults.COORDINATOR_ADDRESS;
+ protected Hash coordinator = Defaults.COORDINATOR_ADDRESS;
+ protected int numberOfKeysInMilestone = Defaults.KEYS_IN_MILESTONE;
+ protected int maxMilestoneIndex = Defaults.MAX_MILESTONE_INDEX;
+ protected int coordinatorSecurityLevel = Defaults.COORDINATOR_SECURITY_LEVEL;
+
protected boolean dontValidateTestnetMilestoneSig = Defaults.DONT_VALIDATE_MILESTONE_SIG;
protected String snapshotFile = Defaults.SNAPSHOT_FILE;
protected String snapshotSignatureFile = Defaults.SNAPSHOT_SIG;
protected long snapshotTime = Defaults.SNAPSHOT_TIME;
protected int mwm = Defaults.MWM;
protected int milestoneStartIndex = Defaults.MILESTONE_START_INDEX;
- protected int numberOfKeysInMilestone = Defaults.KEYS_IN_MILESTONE;
protected int transactionPacketSize = Defaults.PACKET_SIZE;
protected int requestHashSize = Defaults.REQUEST_HASH_SIZE;
+ protected SpongeFactory.Mode coordinatorSignatureMode = Defaults.COORDINATOR_SIGNATURE_MODE;
public TestnetConfig() {
super();
@@ -32,14 +39,14 @@ public boolean isTestnet() {
}
@Override
- public String getCoordinator() {
+ public Hash getCoordinator() {
return coordinator;
}
@JsonProperty
@Parameter(names = "--testnet-coordinator", description = MilestoneConfig.Descriptions.COORDINATOR)
protected void setCoordinator(String coordinator) {
- this.coordinator = coordinator;
+ this.coordinator = HashFactory.ADDRESS.create(coordinator);
}
@Override
@@ -53,6 +60,45 @@ protected void setDontValidateTestnetMilestoneSig(boolean dontValidateTestnetMil
this.dontValidateTestnetMilestoneSig = dontValidateTestnetMilestoneSig;
}
+ @Override
+ public int getNumberOfKeysInMilestone() {
+ return numberOfKeysInMilestone;
+ }
+
+ @JsonProperty("NUMBER_OF_KEYS_IN_A_MILESTONE")
+ @Parameter(names = "--milestone-keys", description = MilestoneConfig.Descriptions.NUMBER_OF_KEYS_IN_A_MILESTONE)
+ protected void setNumberOfKeysInMilestone(int numberOfKeysInMilestone) {
+ this.numberOfKeysInMilestone = numberOfKeysInMilestone;
+ this.maxMilestoneIndex = 1 << numberOfKeysInMilestone;
+ }
+
+ @Override
+ public int getMaxMilestoneIndex() {
+ return maxMilestoneIndex;
+ }
+
+ @Override
+ public int getCoordinatorSecurityLevel() {
+ return coordinatorSecurityLevel;
+ }
+
+ @JsonProperty("COORDINATOR_SECURITY_LEVEL")
+ @Parameter(names = "--testnet-coordinator-security-level", description = MilestoneConfig.Descriptions.COORDINATOR_SECURITY_LEVEL)
+ protected void setCoordinatorSecurityLevel(int coordinatorSecurityLevel) {
+ this.coordinatorSecurityLevel = coordinatorSecurityLevel;
+ }
+
+ @Override
+ public SpongeFactory.Mode getCoordinatorSignatureMode() {
+ return coordinatorSignatureMode;
+ }
+
+ @JsonProperty("COORDINATOR_SIGNATURE_MODE")
+ @Parameter(names = "--testnet-coordinator-signature-mode", description = MilestoneConfig.Descriptions.COORDINATOR_SIGNATURE_MODE)
+ protected void setCoordinatorSignatureMode(SpongeFactory.Mode coordinatorSignatureMode) {
+ this.coordinatorSignatureMode = coordinatorSignatureMode;
+ }
+
@Override
public String getSnapshotFile() {
return snapshotFile;
@@ -108,17 +154,6 @@ protected void setMilestoneStartIndex(int milestoneStartIndex) {
this.milestoneStartIndex = milestoneStartIndex;
}
- @Override
- public int getNumberOfKeysInMilestone() {
- return numberOfKeysInMilestone;
- }
-
- @JsonProperty("NUMBER_OF_KEYS_IN_A_MILESTONE")
- @Parameter(names = "--milestone-keys", description = SnapshotConfig.Descriptions.NUMBER_OF_KEYS_IN_A_MILESTONE)
- protected void setNumberOfKeysInMilestone(int numberOfKeysInMilestone) {
- this.numberOfKeysInMilestone = numberOfKeysInMilestone;
- }
-
@Override
public int getTransactionPacketSize() {
return transactionPacketSize;
@@ -160,8 +195,14 @@ public void setDbLogPath(String dbLogPath) {
}
public interface Defaults {
- String COORDINATOR_ADDRESS = "EQQFCZBIHRHWPXKMTOLMYUYPCN9XLMJPYZVFJSAY9FQHCCLWTOLLUGKKMXYFDBOOYFBLBI9WUEILGECYM";
+ Hash COORDINATOR_ADDRESS = HashFactory.ADDRESS.create(
+ "EQQFCZBIHRHWPXKMTOLMYUYPCN9XLMJPYZVFJSAY9FQHCCLWTOLLUGKKMXYFDBOOYFBLBI9WUEILGECYM");
boolean DONT_VALIDATE_MILESTONE_SIG = false;
+ int COORDINATOR_SECURITY_LEVEL = 1;
+ SpongeFactory.Mode COORDINATOR_SIGNATURE_MODE = SpongeFactory.Mode.CURLP27;
+ int KEYS_IN_MILESTONE = 22;
+ int MAX_MILESTONE_INDEX = 1 << KEYS_IN_MILESTONE;
+
String LOCAL_SNAPSHOTS_BASE_PATH = "testnet";
String SNAPSHOT_FILE = "/snapshotTestnet.txt";
int REQUEST_HASH_SIZE = 49;
@@ -169,9 +210,9 @@ public interface Defaults {
int SNAPSHOT_TIME = 1522306500;
int MWM = 9;
int MILESTONE_START_INDEX = 434525;
- int KEYS_IN_MILESTONE = 22;
int PACKET_SIZE = 1653;
String DB_PATH = "testnetdb";
String DB_LOG_PATH = "testnetdb.log";
+
}
}
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index fe031d928e..4a7d4b4350 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -907,7 +907,7 @@ private AbstractResponse getNodeInfoStatement() throws Exception{
instance.tipsViewModel.size(),
instance.transactionRequester.numberOfTransactionsToRequest(),
features,
- instance.configuration.getCoordinator());
+ instance.configuration.getCoordinator().toString());
}
/**
diff --git a/src/main/java/com/iota/iri/service/milestone/MilestoneService.java b/src/main/java/com/iota/iri/service/milestone/MilestoneService.java
index b334740a8d..e9a0a98c23 100644
--- a/src/main/java/com/iota/iri/service/milestone/MilestoneService.java
+++ b/src/main/java/com/iota/iri/service/milestone/MilestoneService.java
@@ -2,7 +2,6 @@
import com.iota.iri.controllers.MilestoneViewModel;
import com.iota.iri.controllers.TransactionViewModel;
-import com.iota.iri.crypto.SpongeFactory;
import com.iota.iri.model.Hash;
import java.util.Optional;
@@ -34,13 +33,11 @@ public interface MilestoneService {
*
* @param transactionViewModel transaction that shall be analyzed
* @param milestoneIndex milestone index of the transaction (see {@link #getMilestoneIndex(TransactionViewModel)})
- * @param mode mode that gets used for the signature verification
- * @param securityLevel security level that gets used for the signature verification
* @return validity status of the transaction regarding its role as a milestone
* @throws MilestoneException if anything unexpected goes wrong while validating the milestone transaction
*/
- MilestoneValidity validateMilestone(TransactionViewModel transactionViewModel, int milestoneIndex,
- SpongeFactory.Mode mode, int securityLevel) throws MilestoneException;
+ MilestoneValidity validateMilestone(TransactionViewModel transactionViewModel, int milestoneIndex)
+ throws MilestoneException;
/**
* Updates the milestone index of all transactions that belong to a milestone.
diff --git a/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java
index 5941d3a33e..6ac95b9bdb 100644
--- a/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java
+++ b/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java
@@ -4,7 +4,6 @@
import com.iota.iri.controllers.AddressViewModel;
import com.iota.iri.controllers.MilestoneViewModel;
import com.iota.iri.controllers.TransactionViewModel;
-import com.iota.iri.crypto.SpongeFactory;
import com.iota.iri.model.Hash;
import com.iota.iri.model.HashFactory;
import com.iota.iri.service.milestone.LatestMilestoneTracker;
@@ -140,7 +139,7 @@ public LatestMilestoneTrackerImpl init(Tangle tangle, SnapshotProvider snapshotP
this.milestoneService = milestoneService;
this.milestoneSolidifier = milestoneSolidifier;
- coordinatorAddress = HashFactory.ADDRESS.create(config.getCoordinator());
+ coordinatorAddress = config.getCoordinator();
bootstrapLatestMilestoneValue();
@@ -201,7 +200,7 @@ public boolean processMilestoneCandidate(TransactionViewModel transaction) throw
return true;
}
- switch (milestoneService.validateMilestone(transaction, milestoneIndex, SpongeFactory.Mode.CURLP27, 1)) {
+ switch (milestoneService.validateMilestone(transaction, milestoneIndex)) {
case VALID:
if (milestoneIndex > latestMilestoneIndex) {
setLatestMilestone(transaction.getHash(), milestoneIndex);
diff --git a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java
index fc8e789718..3e0b13cf96 100644
--- a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java
+++ b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java
@@ -1,7 +1,7 @@
package com.iota.iri.service.milestone.impl;
import com.iota.iri.BundleValidator;
-import com.iota.iri.conf.ConsensusConfig;
+import com.iota.iri.conf.MilestoneConfig;
import com.iota.iri.controllers.MilestoneViewModel;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.crypto.Curl;
@@ -20,17 +20,17 @@
import com.iota.iri.storage.Tangle;
import com.iota.iri.utils.Converter;
import com.iota.iri.utils.dag.DAGHelper;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
-import java.util.*;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
import static com.iota.iri.controllers.TransactionViewModel.OBSOLETE_TAG_TRINARY_OFFSET;
-import static com.iota.iri.service.milestone.MilestoneValidity.INCOMPLETE;
-import static com.iota.iri.service.milestone.MilestoneValidity.INVALID;
-import static com.iota.iri.service.milestone.MilestoneValidity.VALID;
+import static com.iota.iri.service.milestone.MilestoneValidity.*;
/**
* Creates a service instance that allows us to perform milestone specific operations.
@@ -59,9 +59,10 @@ public class MilestoneServiceImpl implements MilestoneService {
private SnapshotService snapshotService;
/**
- * Holds the config with important milestone specific settings.
+ * Configurations for milestone
*/
- private ConsensusConfig config;
+ private MilestoneConfig config;
+
private BundleValidator bundleValidator;
@@ -76,14 +77,14 @@ public class MilestoneServiceImpl implements MilestoneService {
* allows us to still instantiate, initialize and assign in one line - see Example:
*
* {@code milestoneService = new MilestoneServiceImpl().init(...);}
- *
+ *te
* @param tangle Tangle object which acts as a database interface
* @param snapshotProvider snapshot provider which gives us access to the relevant snapshots
* @param config config with important milestone specific settings
* @return the initialized instance itself to allow chaining
*/
public MilestoneServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotService snapshotService,
- BundleValidator bundleValidator, ConsensusConfig config) {
+ BundleValidator bundleValidator, MilestoneConfig config) {
this.tangle = tangle;
this.snapshotProvider = snapshotProvider;
@@ -155,10 +156,10 @@ public void resetCorruptedMilestone(int index) throws MilestoneException {
}
@Override
- public MilestoneValidity validateMilestone(TransactionViewModel transactionViewModel, int milestoneIndex,
- SpongeFactory.Mode mode, int securityLevel) throws MilestoneException {
+ public MilestoneValidity validateMilestone(TransactionViewModel transactionViewModel, int milestoneIndex)
+ throws MilestoneException {
- if (milestoneIndex < 0 || milestoneIndex >= 0x200000) {
+ if (milestoneIndex < 0 || milestoneIndex >= config.getMaxMilestoneIndex()) {
return INVALID;
}
@@ -179,32 +180,34 @@ public MilestoneValidity validateMilestone(TransactionViewModel transactionViewM
if (tail.getHash().equals(transactionViewModel.getHash())) {
//the signed transaction - which references the confirmed transactions and contains
// the Merkle tree siblings.
- final TransactionViewModel siblingsTx = bundleTransactionViewModels.get(securityLevel);
+ int coordinatorSecurityLevel = config.getCoordinatorSecurityLevel();
+ final TransactionViewModel siblingsTx =
+ bundleTransactionViewModels.get(coordinatorSecurityLevel);
- if (isMilestoneBundleStructureValid(bundleTransactionViewModels, securityLevel)) {
+ if (isMilestoneBundleStructureValid(bundleTransactionViewModels, coordinatorSecurityLevel)) {
//milestones sign the normalized hash of the sibling transaction.
byte[] signedHash = ISS.normalizedBundle(siblingsTx.getHash().trits());
//validate leaf signature
- ByteBuffer bb = ByteBuffer.allocate(Curl.HASH_LENGTH * securityLevel);
+ ByteBuffer bb = ByteBuffer.allocate(Curl.HASH_LENGTH * coordinatorSecurityLevel);
byte[] digest = new byte[Curl.HASH_LENGTH];
- for (int i = 0; i < securityLevel; i++) {
- ISSInPlace.digest(mode, signedHash, ISS.NUMBER_OF_FRAGMENT_CHUNKS * i,
+ SpongeFactory.Mode coordinatorSignatureMode = config.getCoordinatorSignatureMode();
+ for (int i = 0; i < coordinatorSecurityLevel; i++) {
+ ISSInPlace.digest(coordinatorSignatureMode, signedHash,
+ ISS.NUMBER_OF_FRAGMENT_CHUNKS * i,
bundleTransactionViewModels.get(i).getSignature(), 0, digest);
bb.put(digest);
}
byte[] digests = bb.array();
- byte[] address = ISS.address(mode, digests);
+ byte[] address = ISS.address(coordinatorSignatureMode, digests);
//validate Merkle path
- byte[] merkleRoot = ISS.getMerkleRoot(mode, address,
+ byte[] merkleRoot = ISS.getMerkleRoot(coordinatorSignatureMode, address,
siblingsTx.trits(), 0, milestoneIndex, config.getNumberOfKeysInMilestone());
- if ((config.isTestnet() && config.isDontValidateTestnetMilestoneSig()) ||
- (HashFactory.ADDRESS.create(merkleRoot)).equals(
- HashFactory.ADDRESS.create(config.getCoordinator()))) {
-
+ boolean skipValidation = config.isTestnet() && config.isDontValidateTestnetMilestoneSig();
+ if (skipValidation || config.getCoordinator().equals(HashFactory.ADDRESS.create(merkleRoot))) {
MilestoneViewModel newMilestoneViewModel = new MilestoneViewModel(milestoneIndex,
transactionViewModel.getHash());
newMilestoneViewModel.store(tangle);
diff --git a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java
index ad95531959..cb3d7025c7 100644
--- a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java
+++ b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java
@@ -182,7 +182,7 @@ private boolean wasAddressSpentFrom(Hash addressHash, Collection checkedAd
}
private Set getInitialUnspentAddresses() {
- return Stream.of(Hash.NULL_HASH, HashFactory.ADDRESS.create(config.getCoordinator()))
+ return Stream.of(Hash.NULL_HASH, config.getCoordinator())
.collect(Collectors.toSet());
}
}
diff --git a/src/test/java/com/iota/iri/conf/ConfigFactoryTest.java b/src/test/java/com/iota/iri/conf/ConfigFactoryTest.java
index ff705a6de8..aac121837f 100644
--- a/src/test/java/com/iota/iri/conf/ConfigFactoryTest.java
+++ b/src/test/java/com/iota/iri/conf/ConfigFactoryTest.java
@@ -1,5 +1,8 @@
package com.iota.iri.conf;
+import com.iota.iri.model.Hash;
+import com.iota.iri.model.HashFactory;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -113,7 +116,8 @@ public void createFromFileTestnetWithTestnetFalseAndFalse() throws IOException {
public void createFromFileTestnetWithTrailingSpaces() throws IOException {
File configFile = createTestnetConfigFile("true");
IotaConfig iotaConfig = ConfigFactory.createFromFile(configFile, true);
- String expected = "NPCRMHDOMU9QHFFBKFCWFHFJNNQDRNDOGVPEVDVGWKHFUFEXLWJBHXDJFKQGYFRDZBQIFDSJMUCCQVICI";
+ Hash expected = HashFactory.ADDRESS.create(
+ "NPCRMHDOMU9QHFFBKFCWFHFJNNQDRNDOGVPEVDVGWKHFUFEXLWJBHXDJFKQGYFRDZBQIFDSJMUCCQVICI");
assertEquals("Expected that leading and trailing spaces were trimmed.", expected, iotaConfig.getCoordinator());
}
diff --git a/src/test/java/com/iota/iri/conf/ConfigTest.java b/src/test/java/com/iota/iri/conf/ConfigTest.java
index a4ef561dd3..f865147f8e 100644
--- a/src/test/java/com/iota/iri/conf/ConfigTest.java
+++ b/src/test/java/com/iota/iri/conf/ConfigTest.java
@@ -3,6 +3,8 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+
+import com.iota.iri.model.HashFactory;
import com.iota.iri.utils.IotaUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;
@@ -177,7 +179,7 @@ public void testArgsParsingTestnet() {
Assert.assertEquals("db path", "/db", iotaConfig.getDbPath());
Assert.assertEquals("zmq enabled", true, iotaConfig.isZmqEnabled());
Assert.assertEquals("mwm", 4, iotaConfig.getMwm());
- Assert.assertEquals("coo", "TTTTTTTTT", iotaConfig.getCoordinator());
+ Assert.assertEquals("coo", HashFactory.ADDRESS.create("TTTTTTTTT"), iotaConfig.getCoordinator());
Assert.assertEquals("--testnet-no-coo-validation", true,
iotaConfig.isDontValidateTestnetMilestoneSig());
}
diff --git a/src/test/java/com/iota/iri/service/NodeIntegrationTests.java b/src/test/java/com/iota/iri/service/NodeIntegrationTests.java
index 7ae9976f2c..47c93ad2c2 100644
--- a/src/test/java/com/iota/iri/service/NodeIntegrationTests.java
+++ b/src/test/java/com/iota/iri/service/NodeIntegrationTests.java
@@ -140,7 +140,7 @@ private void newMilestone(API api, List tips, long index) throws Exception
transactions.add(new byte[TRINARY_SIZE]);
Converter.copyTrits(index, transactions.get(0), OBSOLETE_TAG_TRINARY_OFFSET, OBSOLETE_TAG_TRINARY_SIZE);
transactions.add(Arrays.copyOf(transactions.get(0), TRINARY_SIZE));
- Hash coordinator = HashFactory.ADDRESS.create(new TestnetConfig().getCoordinator());
+ Hash coordinator = new TestnetConfig().getCoordinator();
System.arraycopy(coordinator.trits(), 0, transactions.get(0), ADDRESS_TRINARY_OFFSET, ADDRESS_TRINARY_SIZE);
setBundleHash(transactions, null);
List elements = api.attachToTangleStatement(tips.get(0), tips.get(0), 13, transactions.stream().map(Converter::trytes).collect(Collectors.toList()));
From bfcce42dd8e39629f7ded88c19058a05c78aad4a Mon Sep 17 00:00:00 2001
From: Jakub Cech
Date: Wed, 27 Feb 2019 14:49:03 +0100
Subject: [PATCH 10/67] Documentation: readme links fix (#1355)
---
README.md | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 66057653b9..1f1af79861 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ The IRI repository is the main IOTA Reference Implementation and the embodiment
This is a full-featured [[IOTA]](https://iota.org/) node with a convenient JSON-REST HTTP interface.
It allows users to become part of the [[IOTA]](https://iota.org) network as both a transaction relay
-and network information provider through the easy-to-use [[API]](https://iota.readme.io/reference).
+and network information provider through the easy-to-use [[API]](https://docs.iota.org/docs/iri/0.1/references/api-reference).
It is specially designed for users seeking a fast, efficient and fully-compatible network setup.
@@ -27,8 +27,8 @@ The IOTA network is an independent peer-to-peer network with a first-user, frien
- As a 'friend-to-friend' network, you have the privilege of joining new users into the network through your node
by adding them to your approved neighbors list — ensuring that you both broadcast to them and also receive their broadcasts.
-You can **find neighbors** quickly at both our [[Discord Community]](https://discord.gg/7Gu2mG5) and [[forum.iota.org]](https://forum.iota.org/).
-
+You can **find neighbors** on the #nodesharing channel of our [[Discord server]](https://discord.gg/7Gu2mG5).
+
Everyone will be welcoming and very happy to help you get connected.
If you want to get tokens for your testcase, please just ask in one of the communication channels.
@@ -39,7 +39,13 @@ please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
# Documentation
-This page contains basic instructions for setting up an IRI node. You can find the full documentation on our [documentation website](https://docs.iota.org/iri). Also see the [IRI API refernece](https://iota.readme.io/reference).
+This page contains basic instructions for setting up an IRI node. You can find the full documentation on:
+- Our [documentation website](https://docs.iota.org/docs/iri/0.1/introduction/overview)
+- [IRI API refernece](https://docs.iota.org/docs/iri/0.1/references/api-reference)
+
+You can also use one of these great community guides:
+- [IOTA Partners guide](https://iota.partners/)
+- [IRI Playbook](https://iri-playbook.readthedocs.io/en/master/index.html)
# Installing
@@ -61,7 +67,7 @@ $ mvn package
This will create a `target` directory in which you will find the executable jar file that you can use.
-### How to run IRI
+### How to run IRI
#### Locally
From 74ec4578bbaeb3d7ca9919ff843fd48d7e5f7545 Mon Sep 17 00:00:00 2001
From: Mathieu Viossat
Date: Wed, 27 Feb 2019 15:17:54 +0100
Subject: [PATCH 11/67] Fix: unknown IXI command response (#1327)
* Fix unknown IXI command response
* Add unknown IXI command test
---
src/main/java/com/iota/iri/IXI.java | 2 +-
src/test/java/com/iota/iri/IXITest.java | 24 ++++++++++++++++++++++--
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/src/main/java/com/iota/iri/IXI.java b/src/main/java/com/iota/iri/IXI.java
index 94b7e9ff79..05d4f3c79a 100644
--- a/src/main/java/com/iota/iri/IXI.java
+++ b/src/main/java/com/iota/iri/IXI.java
@@ -196,7 +196,7 @@ public AbstractResponse processCommand(final String command, Map
if (matcher.find()) {
Map> ixiMap = ixiAPI.get(matcher.group(1));
- if (ixiMap != null) {
+ if (ixiMap != null && ixiMap.containsKey(matcher.group(2))) {
return ixiMap.get(matcher.group(2)).call(request);
}
}
diff --git a/src/test/java/com/iota/iri/IXITest.java b/src/test/java/com/iota/iri/IXITest.java
index 0efa3070a4..b8a4de59fc 100644
--- a/src/test/java/com/iota/iri/IXITest.java
+++ b/src/test/java/com/iota/iri/IXITest.java
@@ -1,5 +1,6 @@
package com.iota.iri;
+import com.iota.iri.service.CallableRequest;
import com.iota.iri.service.dto.AbstractResponse;
import com.iota.iri.service.dto.ErrorResponse;
import org.hamcrest.CoreMatchers;
@@ -8,6 +9,10 @@
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -27,6 +32,12 @@ public static void setUp() throws Exception {
ixiDir.create();
ixi = new IXI();
ixi.init(ixiDir.getRoot().getAbsolutePath());
+
+ Field ixiApiField = ixi.getClass().getDeclaredField("ixiAPI");
+ ixiApiField.setAccessible(true);
+ Map>> ixiAPI =
+ (Map>>) ixiApiField.get(ixi);
+ ixiAPI.put("IXI", new HashMap<>());
}
/**
@@ -70,7 +81,7 @@ public void processCommandEmpty() {
}
/**
- * If an does not match the command pattern, expect an unknown command error message.
+ * If the given command does not exist, expect an unknown command error message.
*/
@Test
public void processCommandUnknown() {
@@ -79,4 +90,13 @@ public void processCommandUnknown() {
assertTrue("Wrong error message returned in response", response.toString().contains("Command [unknown] is unknown"));
}
-}
\ No newline at end of file
+ /**
+ * If an IXI module does not have the given command, expect an unknown command error message.
+ */
+ @Test
+ public void processIXICommandUnknown() {
+ AbstractResponse response = ixi.processCommand("IXI.unknown", null);
+ assertThat("Wrong type of response", response, CoreMatchers.instanceOf(ErrorResponse.class));
+ assertTrue("Wrong error message returned in response", response.toString().contains("Command [IXI.unknown] is unknown"));
+ }
+}
From 8f815199931e5be7a8d62e23f0ae0c3f6dd3b36e Mon Sep 17 00:00:00 2001
From: legacycode
Date: Wed, 27 Feb 2019 17:09:14 +0100
Subject: [PATCH 12/67] Feature: upgrade RocksDB to version 5.17.2 (#1206)
---
pom.xml | 4 +-
.../rocksDB/RocksDBPersistenceProvider.java | 75 ++++++++-----------
2 files changed, 35 insertions(+), 44 deletions(-)
diff --git a/pom.xml b/pom.xml
index eb6366c2d1..18995e36c7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -89,7 +89,7 @@
org.rocksdb
rocksdbjni
- 5.7.3
+ 5.17.2
@@ -339,7 +339,7 @@
commons-io:commons-io:2.5:jar:null:compile:2852e6e05fbb95076fc091f6d1780f1f8fe35e0f
- org.rocksdb:rocksdbjni:5.7.3:jar:null:compile:421b44ad957a2b6cce5adedc204db551831b553d
+ org.rocksdb:rocksdbjni:5.17.2:jar:null:compile:bca52276cabe91a3b97cc18e50fa2eabc2986f58
com.google.code.gson:gson:2.8.1:jar:null:compile:02a8e0aa38a2e21cb39e2f5a7d6704cbdc941da0
diff --git a/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java b/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java
index af50a58af5..f4d96fdbdb 100644
--- a/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java
+++ b/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java
@@ -10,13 +10,38 @@
import java.io.File;
import java.nio.file.Paths;
import java.security.SecureRandom;
-import java.util.*;
-import java.util.stream.Collectors;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.SystemUtils;
-import org.rocksdb.*;
+import org.rocksdb.BackupEngine;
+import org.rocksdb.BackupableDBOptions;
+import org.rocksdb.BlockBasedTableConfig;
+import org.rocksdb.BloomFilter;
+import org.rocksdb.ColumnFamilyDescriptor;
+import org.rocksdb.ColumnFamilyHandle;
+import org.rocksdb.ColumnFamilyOptions;
+import org.rocksdb.DBOptions;
+import org.rocksdb.Env;
+import org.rocksdb.MergeOperator;
+import org.rocksdb.RestoreOptions;
+import org.rocksdb.RocksDB;
+import org.rocksdb.RocksDBException;
+import org.rocksdb.RocksEnv;
+import org.rocksdb.RocksIterator;
+import org.rocksdb.StringAppendOperator;
+import org.rocksdb.WriteBatch;
+import org.rocksdb.WriteOptions;
import org.rocksdb.util.SizeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -296,16 +321,16 @@ public void deleteBatch(Collection {
+ for (Pair> entry : models) {
Indexable indexable = entry.low;
byte[] keyBytes = indexable.bytes();
ColumnFamilyHandle handle = classTreeMap.get(entry.hi);
- writeBatch.remove(handle, keyBytes);
+ writeBatch.delete(handle, keyBytes);
ColumnFamilyHandle metadataHandle = metadataReference.get(entry.hi);
if (metadataHandle != null) {
- writeBatch.remove(metadataHandle, keyBytes);
+ writeBatch.delete(metadataHandle, keyBytes);
}
- });
+ }
WriteOptions writeOptions = new WriteOptions()
//We are explicit about what happens if the node reboots before a flush to the db
@@ -350,7 +375,7 @@ private void flushHandle(ColumnFamilyHandle handle) throws RocksDBException {
itemsToDelete.add(iterator.key());
}
}
- if (itemsToDelete.size() > 0) {
+ if (!itemsToDelete.isEmpty()) {
log.info("Amount to delete: " + itemsToDelete.size());
}
int counter = 0;
@@ -505,38 +530,4 @@ private void initClassTreeMap(List columnFamilyDescripto
classTreeMap = MapUtils.unmodifiableMap(classMap);
}
- // 2018 March 28 - Unused Code
- private void fillMissingColumns(List familyDescriptors, String path) throws Exception {
-
- List columnFamilies = RocksDB.listColumnFamilies(new Options().setCreateIfMissing(true), path)
- .stream()
- .map(b -> new ColumnFamilyDescriptor(b, new ColumnFamilyOptions()))
- .collect(Collectors.toList());
-
- columnFamilies.add(0, familyDescriptors.get(0));
-
- List missingFromDatabase = familyDescriptors.stream().filter(d -> columnFamilies.stream().filter(desc -> new String(desc.columnFamilyName()).equals(new String(d.columnFamilyName()))).toArray().length == 0).collect(Collectors.toList());
- List missingFromDescription = columnFamilies.stream().filter(d -> familyDescriptors.stream().filter(desc -> new String(desc.columnFamilyName()).equals(new String(d.columnFamilyName()))).toArray().length == 0).collect(Collectors.toList());
-
- if (missingFromDatabase.size() != 0) {
- missingFromDatabase.remove(familyDescriptors.get(0));
-
- try (RocksDB rocksDB = db = RocksDB.open(options, path, columnFamilies, columnFamilyHandles)) {
- for (ColumnFamilyDescriptor description : missingFromDatabase) {
- addColumnFamily(description.columnFamilyName(), rocksDB);
- }
- }
- }
- if (missingFromDescription.size() != 0) {
- familyDescriptors.addAll(missingFromDescription);
- }
- }
-
- // 2018 March 28 - Unused Code
- private void addColumnFamily(byte[] familyName, RocksDB db) throws RocksDBException {
- final ColumnFamilyHandle columnFamilyHandle = db.createColumnFamily(
- new ColumnFamilyDescriptor(familyName, new ColumnFamilyOptions()));
-
- assert (columnFamilyHandle != null);
- }
}
From fea7867194c6a2259dcf5a68c992feb2dff4644d Mon Sep 17 00:00:00 2001
From: legacycode
Date: Wed, 27 Feb 2019 17:23:16 +0100
Subject: [PATCH 13/67] Feature: Add configuration parameter for remote trusted
api hosts (#1203)
* Will add new configuration option to allow trusted hosts full api access, by ignoring --remote-limit-api.
* Make the configuration more type safe and output the trusted hosts, when starting iri.
* Minor rework on documentation.
* Added command line validation and converter for JCommander for REMOTE_TRUSTED_API_HOSTS.
* Fixed unused import and added test for giving parameter in config file.
* Removed JCommander validation for new trusted hosts parameter.
* Added review comments.
---
.../java/com/iota/iri/conf/APIConfig.java | 12 +++++-
.../com/iota/iri/conf/BaseIotaConfig.java | 38 ++++++++++++++++++-
src/main/java/com/iota/iri/service/API.java | 2 +-
.../java/com/iota/iri/conf/ConfigTest.java | 21 +++++++++-
4 files changed, 68 insertions(+), 5 deletions(-)
diff --git a/src/main/java/com/iota/iri/conf/APIConfig.java b/src/main/java/com/iota/iri/conf/APIConfig.java
index 58fc960b6e..7a7844bca2 100644
--- a/src/main/java/com/iota/iri/conf/APIConfig.java
+++ b/src/main/java/com/iota/iri/conf/APIConfig.java
@@ -1,5 +1,6 @@
package com.iota.iri.conf;
+import java.net.InetAddress;
import java.util.List;
/**
@@ -24,6 +25,11 @@ public interface APIConfig extends Config {
*/
List getRemoteLimitApi();
+ /**
+ * @return {@value Descriptions#REMOTE_TRUSTED_API_HOSTS}
+ */
+ List getRemoteTrustedApiHosts();
+
/**
* @return {@value Descriptions#MAX_FIND_TRANSACTIONS}
*/
@@ -49,11 +55,15 @@ public interface APIConfig extends Config {
*/
String getRemoteAuth();
+ /**
+ * These descriptions are used by JCommander when you enter java iri.jar --help
at the command line.
+ */
interface Descriptions {
String PORT = "The port that will be used by the API.";
String API_HOST = "The host on which the API will listen to. Set to 0.0.0.0 to accept any host.";
String REMOTE_LIMIT_API = "Commands that should be ignored by API.";
- String REMOTE_AUTH = "A string in the form of :. Used to access the API";
+ String REMOTE_TRUSTED_API_HOSTS = "Open the API interface to defined hosts. You can specify multiple hosts in a comma separated list \"--remote-trusted-api-hosts 192.168.0.55,10.0.0.10\". You must also provide the \"--remote\" parameter. Warning: \"--remote-limit-api\" will have no effect for these hosts.";
+ String REMOTE_AUTH = "A string in the form of :. Used to access the API. You can provide a clear text or an hashed password.";
String MAX_FIND_TRANSACTIONS = "The maximal number of transactions that may be returned by the \"findTransactions\" API call. If the number of transactions found exceeds this number an error will be returned.";
String MAX_REQUESTS_LIST = "The maximal number of parameters one can place in an API call. If the number parameters exceeds this number an error will be returned";
String MAX_GET_TRYTES = "The maximal number of trytes that may be returned by the \"getTrytes\" API call. If the number of transactions found exceeds this number an error will be returned.";
diff --git a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
index 8d2217639b..40841628dc 100644
--- a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
+++ b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
@@ -12,17 +12,21 @@
import com.iota.iri.utils.IotaUtils;
import org.apache.commons.lang3.ArrayUtils;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
-/*
+/**
Note: the fields in this class are being deserialized from Jackson so they must follow Java Bean convention.
Meaning that every field must have a getter that is prefixed with `get` unless it is a boolean and then it should be
prefixed with `is`.
*/
public abstract class BaseIotaConfig implements IotaConfig {
- protected static final String SPLIT_STRING_TO_LIST_REGEX = ",| ";
+ public static final String SPLIT_STRING_TO_LIST_REGEX = ",| ";
private boolean help;
@@ -30,6 +34,7 @@ public abstract class BaseIotaConfig implements IotaConfig {
protected int port = Defaults.API_PORT;
protected String apiHost = Defaults.API_HOST;
protected List remoteLimitApi = Defaults.REMOTE_LIMIT_API;
+ protected List remoteTrustedApiHosts = Defaults.REMOTE_LIMIT_API_HOSTS;
protected int maxFindTransactions = Defaults.MAX_FIND_TRANSACTIONS;
protected int maxRequestsList = Defaults.MAX_REQUESTS_LIST;
protected int maxGetTrytes = Defaults.MAX_GET_TRYTES;
@@ -176,6 +181,30 @@ protected void setRemoteLimitApi(String remoteLimitApi) {
this.remoteLimitApi = IotaUtils.splitStringToImmutableList(remoteLimitApi, SPLIT_STRING_TO_LIST_REGEX);
}
+ @Override
+ public List getRemoteTrustedApiHosts() {
+ return remoteTrustedApiHosts;
+ }
+
+ @JsonProperty
+ @Parameter(names = {"--remote-trusted-api-hosts"}, description = APIConfig.Descriptions.REMOTE_TRUSTED_API_HOSTS)
+ public void setRemoteTrustedApiHosts(String remoteTrustedApiHosts) {
+ List addresses = IotaUtils.splitStringToImmutableList(remoteTrustedApiHosts, SPLIT_STRING_TO_LIST_REGEX);
+ List inetAddresses = addresses.stream().map(host -> {
+ try {
+ return InetAddress.getByName(host.trim());
+ } catch (UnknownHostException e) {
+ throw new ParameterException("Invalid value for --remote-trusted-api-hosts address: ", e);
+ }
+ }).collect(Collectors.toList());
+
+ // always make sure that localhost exists as trusted host
+ if(!inetAddresses.contains(Defaults.REMOTE_LIMIT_API_DEFAULT_HOST)) {
+ inetAddresses.add(Defaults.REMOTE_LIMIT_API_DEFAULT_HOST);
+ }
+ this.remoteTrustedApiHosts = Collections.unmodifiableList(inetAddresses);
+ }
+
@Override
public int getMaxFindTransactions() {
return maxFindTransactions;
@@ -816,11 +845,16 @@ protected void setPowThreads(int powThreads) {
this.powThreads = powThreads;
}
+ /**
+ * Represents the default values primarily used by the {@link BaseIotaConfig} field initialisation.
+ */
public interface Defaults {
//API
int API_PORT = 14265;
String API_HOST = "localhost";
List REMOTE_LIMIT_API = IotaUtils.createImmutableList("addNeighbors", "getNeighbors", "removeNeighbors", "attachToTangle", "interruptAttachingToTangle");
+ InetAddress REMOTE_LIMIT_API_DEFAULT_HOST = InetAddress.getLoopbackAddress();
+ List REMOTE_LIMIT_API_HOSTS = IotaUtils.createImmutableList(REMOTE_LIMIT_API_DEFAULT_HOST);
int MAX_FIND_TRANSACTIONS = 100_000;
int MAX_REQUESTS_LIST = 1_000;
int MAX_GET_TRYTES = 10_000;
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index 4a7d4b4350..618b6f5972 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -348,7 +348,7 @@ private AbstractResponse process(final String requestString, InetSocketAddress s
// Is this command allowed to be run from this request address?
// We check the remote limit API configuration.
if (instance.configuration.getRemoteLimitApi().contains(command) &&
- !sourceAddress.getAddress().isLoopbackAddress()) {
+ !instance.configuration.getRemoteTrustedApiHosts().contains(sourceAddress.getAddress())) {
return AccessLimitedResponse.create("COMMAND " + command + " is not available on this node");
}
diff --git a/src/test/java/com/iota/iri/conf/ConfigTest.java b/src/test/java/com/iota/iri/conf/ConfigTest.java
index f865147f8e..0ffcb08bc9 100644
--- a/src/test/java/com/iota/iri/conf/ConfigTest.java
+++ b/src/test/java/com/iota/iri/conf/ConfigTest.java
@@ -19,8 +19,11 @@
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -51,7 +54,7 @@ public static void tearDownAfterClass() throws IOException {
Test that iterates over common configs. It also attempts to check different types of types (double, boolean, string)
*/
@Test
- public void testArgsParsingMainnet() {
+ public void testArgsParsingMainnet() throws UnknownHostException {
String[] args = {
"-p", "14000",
"-u", "13000",
@@ -59,6 +62,7 @@ public void testArgsParsingMainnet() {
"-n", "udp://neighbor1 neighbor, tcp://neighbor2",
"--api-host", "1.1.1.1",
"--remote-limit-api", "call1 call2, call3",
+ "--remote-trusted-api-hosts", "192.168.0.55, 10.0.0.10",
"--max-find-transactions", "500",
"--max-requests-list", "1000",
"--max-get-trytes", "4000",
@@ -92,6 +96,13 @@ public void testArgsParsingMainnet() {
Assert.assertEquals("api host", "1.1.1.1", iotaConfig.getApiHost());
Assert.assertEquals("remote limit api", Arrays.asList("call1", "call2", "call3"),
iotaConfig.getRemoteLimitApi());
+
+ List expectedTrustedApiHosts = Arrays.asList(
+ InetAddress.getByName("192.168.0.55"),
+ InetAddress.getByName("10.0.0.10"),
+ InetAddress.getByName("127.0.0.1"));
+ Assert.assertEquals("remote trusted api hosts", expectedTrustedApiHosts, iotaConfig.getRemoteTrustedApiHosts());
+
Assert.assertEquals("max find transactions", 500, iotaConfig.getMaxFindTransactions());
Assert.assertEquals("max requests list", 1000, iotaConfig.getMaxRequestsList());
Assert.assertEquals("max get trytes", 4000, iotaConfig.getMaxGetTrytes());
@@ -190,6 +201,7 @@ public void testIniParsingMainnet() throws Exception {
.append("[IRI]").append(System.lineSeparator())
.append("PORT = 17000").append(System.lineSeparator())
.append("NEIGHBORS = udp://neighbor1 neighbor, tcp://neighbor2").append(System.lineSeparator())
+ .append("REMOTE_TRUSTED_API_HOSTS = 192.168.0.55, 10.0.0.10").append(System.lineSeparator())
.append("ZMQ_ENABLED = true").append(System.lineSeparator())
.append("P_REMOVE_REQUEST = 0.4").append(System.lineSeparator())
.append("MWM = 4").append(System.lineSeparator())
@@ -206,6 +218,13 @@ public void testIniParsingMainnet() throws Exception {
Assert.assertEquals("PORT", 17000, iotaConfig.getPort());
Assert.assertEquals("NEIGHBORS", Arrays.asList("udp://neighbor1", "neighbor", "tcp://neighbor2"),
iotaConfig.getNeighbors());
+
+ List expectedTrustedApiHosts = Arrays.asList(
+ InetAddress.getByName("192.168.0.55"),
+ InetAddress.getByName("10.0.0.10"),
+ BaseIotaConfig.Defaults.REMOTE_LIMIT_API_DEFAULT_HOST);
+ Assert.assertEquals("REMOTE_TRUSTED_API_HOSTS", expectedTrustedApiHosts, iotaConfig.getRemoteTrustedApiHosts());
+
Assert.assertEquals("ZMQ_ENABLED", true, iotaConfig.isZmqEnabled());
Assert.assertEquals("P_REMOVE_REQUEST", 0.4d, iotaConfig.getpRemoveRequest(), 0);
Assert.assertNotEquals("MWM", 4, iotaConfig.getMwm());
From cb08095541f9899551ac66dcd3926251251082eb Mon Sep 17 00:00:00 2001
From: lhf <45928776+lhfbc@users.noreply.github.com>
Date: Mon, 4 Mar 2019 17:17:05 +0800
Subject: [PATCH 14/67] Change: print out line number in log info. (#1362)
---
src/main/resources/logback.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 0ce840ac49..85a08b9c40 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -24,7 +24,7 @@
DENY
- %d{MM/dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+ %d{MM/dd HH:mm:ss.SSS} [%thread] %-5level %logger{0}:%L - %msg%n
@@ -37,7 +37,7 @@
DENY
- %d{MM/dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+ %d{MM/dd HH:mm:ss.SSS} [%thread] %-5level %logger{0}:%L - %msg%n
@@ -46,4 +46,4 @@
-
\ No newline at end of file
+
From d4f2a50324eb8ad9a79a54f6bfb7f7089e056db9 Mon Sep 17 00:00:00 2001
From: Gal Rogozinski
Date: Wed, 13 Mar 2019 09:09:38 +0100
Subject: [PATCH 15/67] Change: Prepare IRI to have its version injected by CI
(#1359)
* Parse version from jar manifest
* Parse version from pom when running directly from IDE
* update pom version
* add commit hash to version
* get correct version wiht travis
* delete unneccessary comment
* change travis.yml to release properly
* add doc and delete unused imports
---
.travis.yml | 10 +++---
DOCKER.md | 6 ++--
pom.xml | 33 ++++++++++++++++++-
src/main/java/com/iota/iri/IRI.java | 5 +--
.../com/iota/iri/conf/BaseIotaConfig.java | 2 +-
src/main/java/com/iota/iri/service/API.java | 5 +--
.../java/com/iota/iri/utils/IotaUtils.java | 29 ++++++++++++++++
7 files changed, 77 insertions(+), 13 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 057c21907e..a5ef1a5bef 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,10 +29,12 @@ script:
- git checkout -f $TRAVIS_BRANCH
- git checkout $build_head
- git merge $TRAVIS_BRANCH
- - mvn integration-test -Dlogging-level=INFO
- #run jar sanity tests
- - VERSION=$(mvn help:evaluate -Dexpression=project.version | grep -E '^[0-9.]+')
+ - VERSION=$(mvn git-commit-id:revision help:evaluate -Dexpression=project.version | grep -E '^[0-9.]+')
+ - VERSION=${VERSION/DEV*/RELEASE}
- echo $VERSION
+ - mvn versions:set -DnewVersion=$VERSION
+ #run jar sanity tests
+ - mvn integration-test -Dlogging-level=INFO
#jacoco prep (appends to mvn run)
- JACOCO_V=$(mvn help:evaluate -Dexpression=jacoco.version | grep -E '^[0-9.]+')
- export _JAVA_OPTIONS="$_JAVA_OPTIONS -javaagent:$HOME/.m2/repository/org/jacoco/org.jacoco.agent/$JACOCO_V/org.jacoco.agent-$JACOCO_V-runtime.jar=destfile=$PWD/target/jacoco.exec,output=file,append=true,dumponexit=true"
@@ -61,7 +63,7 @@ deploy:
api_key:
secure: "j+KolvRrsinh6OTqjli1y5I4gdPdD5MT6RFahVRrznxtcb2t7y6x41PknkI/8BksxLJKKQ1f40L307Aquzih9FmLesWcrYrCOS/B40dlCc3uV8DEZKhZF+QWqvsDpZ0EfiluGFljqGpxwngb/miNObqQzQvzpdUXBpZcuzxgqziynJD6jt3TYbNn544m/i4cWbb++b1h2Bm+jwELzJcf6/CZ1TxUh49vdM4S6yTpvnKwg4NLeH6g8s9oYEN1iwYtQokGKlH/emBZDsqDtF4KH7kXPpEKTcho4nH504XWJpanVAQ02Pha4SAnyVu4rKiLH4e6v99xZOBLVTH/qkvlwzvkNYr2xS5q9T9G7DZBRE+oc2MIbcnuSkhHwrUcmf2Hc0zCIP4RLCXAUuVlNEmIQUKgTezVzaqaEQLsa+EFIoosS+8pT/kVnaLbwxrITUmgM246+gvzMcVg8SmyPvVj6iRxfR6spTd7V2M0ePdqWlCL8P9RY4YrtxR2yJ6/3C0XNf29t+s/r1jat8fwWp8Gf7GiCaJEYG9VO24Chhi+MZCKBJnmx5G/98zvLw3f6a1FsEGZrw9RhF2vJKK6sD3rIvNkUsYmf0OhM7bf0NceFMGTJ/mFzM/JX0OHMtIyAvUfyyPVbQkIHNcGBgQMZasx07K3fjcn2lqv4FbHVi3HbdM="
file_glob: true
- file: target/*.jar*
+ file: target/*RELEASE.jar*
skip_cleanup: true
before_deploy: openssl aes-256-cbc -K $encrypted_5a15fa813cca_key -iv $encrypted_5a15fa813cca_iv -in codesigning.asc.enc -out codesigning.asc -d && gpg --fast-import codesigning.asc
deploy: mvn package -P build-extras -Dlogging-level=INFO
diff --git a/DOCKER.md b/DOCKER.md
index 14e92f860b..be0b0b8198 100644
--- a/DOCKER.md
+++ b/DOCKER.md
@@ -2,13 +2,13 @@
Run the official iotaledger/iri container, passing the mandatory -p option:
-```docker run iotaledger/iri:v1.6.1-RELEASE -p 14265```
+```docker run iotaledger/iri:vX.X.X-RELEASE -p 14265```
This will get your a running IRI with its API listening on port 14265, no neighbours and an empty database. The IRI Docker container by default expects data at /iri/data. Use the `-v` option of the `docker run` command to mount volumes so to have persistent data. You can also pass more command line options to the docker run command and those will be passed to IRI.
If you want to use a iri.ini file with the docker container, supposing it's stored under /path/to/conf/iri.ini on your docker host, then pass `-v /path/to/conf:/iri/conf` and add -c /iri/conf/iri.ini as docker run arguments. So for example the `docker run` command above would become:
-```docker run -v /path/to/conf:/iri/conf -v /path/to/data:/iri/data iotaledger/iri:v1.6.1-RELEASE -p 14265 -c /iri/conf/iri.ini```
+```docker run -v /path/to/conf:/iri/conf -v /path/to/data:/iri/data iotaledger/iri:vX.X.X-RELEASE -p 14265 -c /iri/conf/iri.ini```
Please refer to the IRI documentation for further command line options and iri.ini options.
@@ -61,7 +61,7 @@ ExecStart=/usr/bin/docker run \
-p 14265:14265 \
-p 15600:15600 \
-p 14600:14600/udp \
-iotaledger/iri:v1.6.1-RELEASE \
+iotaledger/iri:vX.X.X-RELEASE \
-p 14265 \
--zmq-enabled \
--testnet
diff --git a/pom.xml b/pom.xml
index 18995e36c7..b89a416d51 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.iota
iri
- 1.6.1-RELEASE
+ 1.6.1-DEV-${version.number}
IRI
IOTA Reference Implementation
@@ -22,6 +22,7 @@
3.0.0
1.4.26.Final
0.7.9
+ ${git.commit.id.abbrev}
@@ -218,6 +219,36 @@
false
+
+ pl.project13.maven
+ git-commit-id-plugin
+ 2.2.4
+
+
+ validate
+
+ revision
+
+
+
+
+ ${project.basedir}/.git
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.1
+
+
+
+ true
+
+
+
+
diff --git a/src/main/java/com/iota/iri/IRI.java b/src/main/java/com/iota/iri/IRI.java
index bd84b60d52..27d9d88786 100644
--- a/src/main/java/com/iota/iri/IRI.java
+++ b/src/main/java/com/iota/iri/IRI.java
@@ -5,6 +5,7 @@
import com.iota.iri.conf.ConfigFactory;
import com.iota.iri.conf.IotaConfig;
import com.iota.iri.service.API;
+import com.iota.iri.utils.IotaUtils;
import java.io.File;
import java.io.IOException;
@@ -42,7 +43,6 @@ public class IRI {
public static final String MAINNET_NAME = "IRI";
public static final String TESTNET_NAME = "IRI Testnet";
- public static final String VERSION = "1.6.1-RELEASE";
/**
* The entry point of IRI.
@@ -114,7 +114,8 @@ private static class IRILauncher {
*/
public static void main(String [] args) throws Exception {
IotaConfig config = createConfiguration(args);
- log.info("Welcome to {} {}", config.isTestnet() ? TESTNET_NAME : MAINNET_NAME, VERSION);
+ String version = IotaUtils.getIriVersion();
+ log.info("Welcome to {} {}", config.isTestnet() ? TESTNET_NAME : MAINNET_NAME, version);
iota = new Iota(config);
ixi = new IXI(iota);
diff --git a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
index 40841628dc..3756f622a8 100644
--- a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
+++ b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java
@@ -123,7 +123,7 @@ public JCommander parseConfigFromArgs(String[] args) throws ParameterException {
.acceptUnknownOptions(true)
.allowParameterOverwriting(true)
//This is the first line of JCommander Usage
- .programName("java -jar iri-" + IRI.VERSION + ".jar")
+ .programName("java -jar iri-" + IotaUtils.getIriVersion() + ".jar")
.build();
if (ArrayUtils.isNotEmpty(args)) {
jCommander.parse(args);
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index 618b6f5972..9f345899ae 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -22,6 +22,7 @@
import com.iota.iri.service.tipselection.impl.WalkValidatorImpl;
import com.iota.iri.utils.Converter;
import com.iota.iri.utils.IotaIOUtils;
+import com.iota.iri.utils.IotaUtils;
import com.iota.iri.utils.MapIdentityManager;
import io.undertow.Undertow;
import io.undertow.security.api.AuthenticationMechanism;
@@ -884,8 +885,8 @@ private AbstractResponse getNodeInfoStatement() throws Exception{
MilestoneViewModel milestone = MilestoneViewModel.first(instance.tangle);
return GetNodeInfoResponse.create(
- name,
- IRI.VERSION,
+ name,
+ IotaUtils.getIriVersion(),
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().freeMemory(),
System.getProperty("java.version"),
diff --git a/src/main/java/com/iota/iri/utils/IotaUtils.java b/src/main/java/com/iota/iri/utils/IotaUtils.java
index 7a751e4d65..56b6fcf4c1 100644
--- a/src/main/java/com/iota/iri/utils/IotaUtils.java
+++ b/src/main/java/com/iota/iri/utils/IotaUtils.java
@@ -1,9 +1,15 @@
package com.iota.iri.utils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.iota.iri.IRI;
import com.iota.iri.model.Hash;
+import java.io.FileReader;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -17,6 +23,29 @@
public class IotaUtils {
+ private static final Logger log = LoggerFactory.getLogger(IotaUtils.class);
+
+ /**
+ * Returns the current version IRI is running by reading the Jar manifest.
+ * If we run not from a jar or the manifest is missing we read straight from the pom
+ *
+ * @return the implementation version of IRI
+ */
+ public static String getIriVersion() {
+ String implementationVersion = IRI.class.getPackage().getImplementationVersion();
+ //If not in manifest (can happen when running from IDE)
+ if (implementationVersion == null) {
+ MavenXpp3Reader reader = new MavenXpp3Reader();
+ try {
+ Model model = reader.read(new FileReader("pom.xml"));
+ implementationVersion = model.getVersion();
+ } catch (Exception e) {
+ log.error("Failed to parse version from pom", e);
+ }
+ }
+ return implementationVersion;
+ }
+
public static List splitStringToImmutableList(String string, String regexSplit) {
return Arrays.stream(string.split(regexSplit))
.filter(StringUtils::isNoneBlank)
From 16ae9afaf6267ce64b455d145f4d696c287afd7f Mon Sep 17 00:00:00 2001
From: Brord van Wierst
Date: Tue, 12 Mar 2019 20:51:43 +0100
Subject: [PATCH 16/67] initial add
---
pom.xml | 13 ++
.../java/com/iota/iri/service/ApiHandler.java | 125 ++++++++++++++++++
.../iota/iri/service/restserver/ApiCall.java | 49 +++++++
.../com/iota/iri/service/restserver/Root.java | 20 +++
.../com/iota/iri/service/restserver/Test.java | 44 ++++++
.../com/iota/iri/service/ApiHandlerTest.java | 80 +++++++++++
6 files changed, 331 insertions(+)
create mode 100644 src/main/java/com/iota/iri/service/ApiHandler.java
create mode 100644 src/main/java/com/iota/iri/service/restserver/ApiCall.java
create mode 100644 src/main/java/com/iota/iri/service/restserver/Root.java
create mode 100644 src/main/java/com/iota/iri/service/restserver/Test.java
create mode 100644 src/test/java/com/iota/iri/service/ApiHandlerTest.java
diff --git a/pom.xml b/pom.xml
index b89a416d51..5de901745b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -125,6 +125,19 @@
undertow-websockets-jsr
${undertow.version}
+
+
+
+ org.jboss.resteasy
+ resteasy-undertow
+ 3.6.3.Final
+
+
+
+ org.jboss.resteasy
+ resteasy-jackson-provider
+ 3.6.3.Final
+
diff --git a/src/main/java/com/iota/iri/service/ApiHandler.java b/src/main/java/com/iota/iri/service/ApiHandler.java
new file mode 100644
index 0000000000..26a4a75b0c
--- /dev/null
+++ b/src/main/java/com/iota/iri/service/ApiHandler.java
@@ -0,0 +1,125 @@
+package com.iota.iri.service;
+
+import static io.undertow.Handlers.path;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.iota.iri.conf.APIConfig;
+import com.iota.iri.service.restserver.Root;
+import com.iota.iri.utils.MapIdentityManager;
+
+import io.undertow.Undertow;
+import io.undertow.Undertow.Builder;
+import io.undertow.security.api.AuthenticationMechanism;
+import io.undertow.security.api.AuthenticationMechanismFactory;
+import io.undertow.security.api.AuthenticationMode;
+import io.undertow.security.handlers.AuthenticationCallHandler;
+import io.undertow.security.handlers.AuthenticationConstraintHandler;
+import io.undertow.security.handlers.AuthenticationMechanismsHandler;
+import io.undertow.security.handlers.SecurityInitialHandler;
+import io.undertow.security.idm.IdentityManager;
+import io.undertow.security.impl.BasicAuthenticationMechanism;
+import io.undertow.server.HandlerWrapper;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.util.Headers;
+import io.undertow.util.HttpString;
+import io.undertow.util.Methods;
+import io.undertow.util.MimeMappings;
+import io.undertow.util.StatusCodes;
+
+public class ApiHandler extends Application {
+
+ private static final Logger log = LoggerFactory.getLogger(ApiHandler.class);
+
+ private UndertowJaxrsServer server;
+ private APIConfig configuration;
+
+ public ApiHandler(APIConfig configuration) {
+ this.configuration = configuration;
+ }
+
+ public void init() throws IOException {
+ final int apiPort = configuration.getPort();
+ final String apiHost = configuration.getApiHost();
+
+ log.debug("Binding JSON-REST API Undertow server on {}:{}", apiHost, apiPort);
+
+ Undertow.Builder builder = Undertow.builder()
+ .addHttpListener(apiPort, apiHost);
+
+ server = new UndertowJaxrsServer().start(builder);
+ DeploymentInfo info = server.undertowDeployment(Root.class);
+ info.setDisplayName("Iota Realm");
+ info.setDeploymentName("Iota Realm");
+ info.setContextPath("/");
+
+ info.addSecurityWrapper(new HandlerWrapper() {
+ @Override
+ public HttpHandler wrap(HttpHandler toWrap) {
+ String credentials = configuration.getRemoteAuth();
+ if (credentials == null || credentials.isEmpty()) {
+ return toWrap;
+ }
+
+ final Map users = new HashMap<>(2);
+ users.put(credentials.split(":")[0], credentials.split(":")[1].toCharArray());
+
+ IdentityManager identityManager = new MapIdentityManager(users);
+ HttpHandler handler = toWrap;
+ handler = new AuthenticationCallHandler(handler);
+ handler = new AuthenticationConstraintHandler(handler);
+ final List mechanisms =
+ Collections.singletonList(new BasicAuthenticationMechanism("Iota Realm"));
+
+ handler = new AuthenticationMechanismsHandler(handler, mechanisms);
+ handler = new SecurityInitialHandler(AuthenticationMode.PRO_ACTIVE, identityManager, handler);
+ return handler;
+ }
+ });
+
+ server.deploy(info);
+ }
+
+ public void stop() {
+ server.stop();
+ }
+
+ private void handleRequest(final HttpServerExchange exchange) throws Exception {
+ HttpString requestMethod = exchange.getRequestMethod();
+ if (Methods.OPTIONS.equals(requestMethod)) {
+ String allowedMethods = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH";
+ //return list of allowed methods in response headers
+ exchange.setStatusCode(StatusCodes.OK);
+ exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt"));
+ exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, 0);
+ exchange.getResponseHeaders().put(Headers.ALLOW, allowedMethods);
+ exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Origin"), "*");
+ exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Headers"), "User-Agent, Origin, X-Requested-With, Content-Type, Accept, X-IOTA-API-Version");
+ exchange.getResponseSender().close();
+ return;
+ }
+
+ if (exchange.isInIoThread()) {
+ //exchange.dispatch(this);
+ return;
+ }
+
+ //processRequest(exchange);
+ }
+}
diff --git a/src/main/java/com/iota/iri/service/restserver/ApiCall.java b/src/main/java/com/iota/iri/service/restserver/ApiCall.java
new file mode 100644
index 0000000000..5ff43575e7
--- /dev/null
+++ b/src/main/java/com/iota/iri/service/restserver/ApiCall.java
@@ -0,0 +1,49 @@
+package com.iota.iri.service.restserver;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonSyntaxException;
+import com.iota.iri.service.dto.AbstractResponse;
+import com.iota.iri.service.dto.ErrorResponse;
+
+public abstract class ApiCall {
+
+ private static final Gson gson = new GsonBuilder().create();
+
+ private long start;
+
+ protected Map request;
+
+ public AbstractResponse pre(String requestString) {
+ start = System.currentTimeMillis();
+
+ try {
+ request = gson.fromJson(requestString, Map.class);
+ } catch(JsonSyntaxException jsonSyntaxException) {
+ return ErrorResponse.create("Invalid JSON syntax: " + jsonSyntaxException.getMessage());
+ }
+ if (request == null) {
+ return ErrorResponse.create("Invalid request payload: '" + requestString + "'");
+ }
+
+ // Did the requester ask for a command?
+ final String command = (String) request.get("command");
+ if (command == null) {
+ return ErrorResponse.create("COMMAND parameter has not been specified in the request.");
+ }
+ return null;
+ }
+
+ public AbstractResponse post(AbstractResponse response) {
+ response.setDuration((int) (System.currentTimeMillis() - start));
+
+ return response;
+ }
+
+ public String getCommand() {
+ return request.get("command").toString();
+ }
+}
diff --git a/src/main/java/com/iota/iri/service/restserver/Root.java b/src/main/java/com/iota/iri/service/restserver/Root.java
new file mode 100644
index 0000000000..a458da5939
--- /dev/null
+++ b/src/main/java/com/iota/iri/service/restserver/Root.java
@@ -0,0 +1,20 @@
+package com.iota.iri.service.restserver;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+@Path("")
+public class Root extends Application {
+
+ @Override
+ public Set> getClasses(){
+ HashSet> classes = new HashSet>();
+ classes.add(Test.class);
+ return classes;
+ }
+
+
+}
diff --git a/src/main/java/com/iota/iri/service/restserver/Test.java b/src/main/java/com/iota/iri/service/restserver/Test.java
new file mode 100644
index 0000000000..a0bd7abe79
--- /dev/null
+++ b/src/main/java/com/iota/iri/service/restserver/Test.java
@@ -0,0 +1,44 @@
+package com.iota.iri.service.restserver;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.iota.iri.service.dto.AbstractResponse;
+
+@Path("")
+public class Test extends ApiCall {
+
+ @GET
+ @Path("/ping")
+ @Produces(MediaType.TEXT_PLAIN)
+ public String ping() {
+ return "pong";
+ }
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response apiCall(String request) throws JsonParseException, JsonMappingException, IOException {
+ AbstractResponse response = pre(request);
+ if (response == null) {
+ response = process();
+ }
+
+ return Response.ok(post(response)).build();
+ }
+
+ private AbstractResponse process() {
+ return AbstractResponse.createEmptyResponse();
+ }
+}
diff --git a/src/test/java/com/iota/iri/service/ApiHandlerTest.java b/src/test/java/com/iota/iri/service/ApiHandlerTest.java
new file mode 100644
index 0000000000..7f339df5b3
--- /dev/null
+++ b/src/test/java/com/iota/iri/service/ApiHandlerTest.java
@@ -0,0 +1,80 @@
+package com.iota.iri.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.test.TestPortProvider;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import com.iota.iri.conf.APIConfig;
+
+public class ApiHandlerTest {
+
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private APIConfig apiconfig;
+
+ private ApiHandler server;
+
+ @Before
+ public void setup() {
+ Mockito.when(apiconfig.getPort()).thenReturn(TestPortProvider.getPort());
+ Mockito.when(apiconfig.getApiHost()).thenReturn(TestPortProvider.getHost());
+ //Mockito.when(apiconfig.getRemoteAuth()).thenReturn("user:pass");
+
+ this.server = new ApiHandler(apiconfig);
+
+ try {
+ this.server.init();
+ } catch (IOException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @After
+ public void tearDown() {
+ this.server.stop();
+ }
+
+ @Test
+ public void test() {
+ Client client = ClientBuilder.newClient();
+ String val = client.target(TestPortProvider.generateURL("/ping"))
+ .request().get(String.class);
+ assertEquals("pong", val);
+ }
+
+ @Test
+ public void test2() {
+ Client client = ClientBuilder.newClient();
+ String val = client.target(TestPortProvider.generateURL(""))
+ .request().get(String.class);
+ System.out.println(val);
+ }
+
+ @Test
+ public void nodeInfo() {
+ Client client = ClientBuilder.newClient();
+ String jsonString = "{\"command\": \"getNodeInfo\"}";
+ Response val = client.target(TestPortProvider.generateURL(""))
+ .request().post(Entity.entity(jsonString, MediaType.APPLICATION_JSON));
+ System.out.println(val.getEntity());
+ }
+}
From 63f8c57c475b5aa2ae01b3894eb22f6bacf7d298 Mon Sep 17 00:00:00 2001
From: Brord van Wierst
Date: Wed, 13 Mar 2019 16:02:58 +0100
Subject: [PATCH 17/67] Rest connector
---
src/main/java/com/iota/iri/IRI.java | 18 +-
src/main/java/com/iota/iri/service/API.java | 264 +++++++++++-------
.../iri/service/restserver/RestConnector.java | 14 +
.../RestEasy.java} | 71 ++---
.../com/iota/iri/service/restserver/Root.java | 3 -
.../com/iota/iri/service/restserver/Test.java | 8 +-
.../iota/iri/service/APIIntegrationTests.java | 9 +-
.../com/iota/iri/service/ApiHandlerTest.java | 40 +--
.../iri/service/NodeIntegrationTests.java | 11 +-
9 files changed, 262 insertions(+), 176 deletions(-)
create mode 100644 src/main/java/com/iota/iri/service/restserver/RestConnector.java
rename src/main/java/com/iota/iri/service/{ApiHandler.java => restserver/RestEasy.java} (56%)
diff --git a/src/main/java/com/iota/iri/IRI.java b/src/main/java/com/iota/iri/IRI.java
index 27d9d88786..e1a65d3947 100644
--- a/src/main/java/com/iota/iri/IRI.java
+++ b/src/main/java/com/iota/iri/IRI.java
@@ -4,8 +4,18 @@
import com.iota.iri.conf.Config;
import com.iota.iri.conf.ConfigFactory;
import com.iota.iri.conf.IotaConfig;
+import com.iota.iri.controllers.TipsViewModel;
+import com.iota.iri.network.Node;
+import com.iota.iri.network.TransactionRequester;
import com.iota.iri.service.API;
import com.iota.iri.utils.IotaUtils;
+import com.iota.iri.service.ledger.LedgerService;
+import com.iota.iri.service.milestone.LatestMilestoneTracker;
+import com.iota.iri.service.restserver.RestEasy;
+import com.iota.iri.service.snapshot.SnapshotProvider;
+import com.iota.iri.service.spentaddresses.SpentAddressesService;
+import com.iota.iri.service.tipselection.TipSelector;
+import com.iota.iri.storage.Tangle;
import java.io.File;
import java.io.IOException;
@@ -119,14 +129,18 @@ public static void main(String [] args) throws Exception {
iota = new Iota(config);
ixi = new IXI(iota);
- api = new API(iota, ixi);
+ api = new API(iota.configuration, ixi, iota.transactionRequester,
+ iota.spentAddressesService, iota.tangle, iota.bundleValidator,
+ iota.snapshotProvider, iota.ledgerService, iota.node, iota.tipsSelector,
+ iota.tipsViewModel, iota.transactionValidator,
+ iota.latestMilestoneTracker);
shutdownHook();
try {
iota.init();
- api.init();
//TODO redundant parameter but we will touch this when we refactor IXI
ixi.init(config.getIxiDir());
+ api.init(new RestEasy(iota.configuration));
log.info("IOTA Node initialised correctly.");
} catch (Exception e) {
log.error("Exception during IOTA node initialisation: ", e);
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index 9f345899ae..e39157e36e 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -7,7 +7,10 @@
import com.iota.iri.IRI;
import com.iota.iri.IXI;
import com.iota.iri.Iota;
+import com.iota.iri.TransactionValidator;
import com.iota.iri.conf.APIConfig;
+import com.iota.iri.conf.IotaConfig;
+import com.iota.iri.conf.NetworkConfig;
import com.iota.iri.controllers.*;
import com.iota.iri.crypto.Curl;
import com.iota.iri.crypto.PearlDiver;
@@ -17,9 +20,17 @@
import com.iota.iri.model.HashFactory;
import com.iota.iri.model.persistables.Transaction;
import com.iota.iri.network.Neighbor;
+import com.iota.iri.network.Node;
+import com.iota.iri.network.TransactionRequester;
import com.iota.iri.service.dto.*;
+import com.iota.iri.service.ledger.LedgerService;
+import com.iota.iri.service.milestone.LatestMilestoneTracker;
+import com.iota.iri.service.restserver.RestConnector;
+import com.iota.iri.service.snapshot.SnapshotProvider;
+import com.iota.iri.service.spentaddresses.SpentAddressesService;
import com.iota.iri.service.tipselection.TipSelector;
import com.iota.iri.service.tipselection.impl.WalkValidatorImpl;
+import com.iota.iri.storage.Tangle;
import com.iota.iri.utils.Converter;
import com.iota.iri.utils.IotaIOUtils;
import com.iota.iri.utils.IotaUtils;
@@ -77,67 +88,116 @@
@SuppressWarnings("unchecked")
public class API {
+ private static final Logger log = LoggerFactory.getLogger(API.class);
+
+ //region [CONSTANTS] ///////////////////////////////////////////////////////////////////////////////
+
public static final String REFERENCE_TRANSACTION_NOT_FOUND = "reference transaction not found";
public static final String REFERENCE_TRANSACTION_TOO_OLD = "reference transaction is too old";
public static final String INVALID_SUBTANGLE = "This operation cannot be executed: "
+ "The subtangle has not been updated yet.";
+
+ private static final String overMaxErrorMessage = "Could not complete request";
+ private static final String invalidParams = "Invalid parameters";
- private static final Logger log = LoggerFactory.getLogger(API.class);
- private final IXI ixi;
-
- private Undertow server;
+ private static final char ZERO_LENGTH_ALLOWED = 'Y';
+ private static final char ZERO_LENGTH_NOT_ALLOWED = 'N';
+
+ private static final int HASH_SIZE = 81;
+ private static final int TRYTES_SIZE = 2673;
- private final Gson gson = new GsonBuilder().create();
- private volatile PearlDiver pearlDiver = new PearlDiver();
-
- private final AtomicInteger counter = new AtomicInteger(0);
-
- private Pattern trytesPattern = Pattern.compile("[9A-Z]*");
-
- private final static int HASH_SIZE = 81;
- private final static int TRYTES_SIZE = 2673;
-
- private final static long MAX_TIMESTAMP_VALUE = (long) (Math.pow(3, 27) - 1) / 2; // max positive 27-trits value
+ private static final long MAX_TIMESTAMP_VALUE = (long) (Math.pow(3, 27) - 1) / 2; // max positive 27-trits value
+ //endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////
+
private static int counterGetTxToApprove = 0;
private static long ellapsedTime_getTxToApprove = 0L;
private static int counter_PoW = 0;
private static long ellapsedTime_PoW = 0L;
+
+ //region [CONSTRUCTOR_FIELDS] ///////////////////////////////////////////////////////////////////////////////
+ private final IotaConfig configuration;
+ private final IXI ixi;
+ private final TransactionRequester transactionRequester;
+ private final SpentAddressesService spentAddressesService;
+ private final Tangle tangle;
+ private final BundleValidator bundleValidator;
+ private final SnapshotProvider snapshotProvider;
+ private final LedgerService ledgerService;
+ private final Node node;
+ private final TipSelector tipsSelector;
+ private final TipsViewModel tipsViewModel;
+ private final TransactionValidator transactionValidator;
+ private final LatestMilestoneTracker latestMilestoneTracker;
+
private final int maxFindTxs;
private final int maxRequestList;
private final int maxGetTrytes;
private final int maxBodyLength;
private final boolean testNet;
- private final static String overMaxErrorMessage = "Could not complete request";
- private final static String invalidParams = "Invalid parameters";
+ private final String[] features;
+
+ //endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ private Undertow server;
+
+ private final Gson gson = new GsonBuilder().create();
+ private volatile PearlDiver pearlDiver = new PearlDiver();
- private final static char ZERO_LENGTH_ALLOWED = 'Y';
- private final static char ZERO_LENGTH_NOT_ALLOWED = 'N';
- private Iota instance;
+ private final AtomicInteger counter = new AtomicInteger(0);
- private final String[] features;
+ private Pattern trytesPattern = Pattern.compile("[9A-Z]*");
+
+ private RestConnector connector;
/**
* Starts loading the IOTA API, parameters do not have to be initialized.
- *
- * @param instance The data source we interact with during any API call.
+ *
+ * @param configuration
* @param ixi If a command is not in the standard API,
* we try to process it as a Nashorn JavaScript module through {@link IXI}
+ * @param transactionRequester
+ * @param spentAddressesService
+ * @param tangle
+ * @param bundleValidator
+ * @param snapshotProvider
+ * @param ledgerService
+ * @param node
+ * @param tipsSelector
+ * @param tipsViewModel
+ * @param transactionValidator
+ * @param latestMilestoneTracker
*/
- public API(Iota instance, IXI ixi) {
- this.instance = instance;
+ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRequester,
+ SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator,
+ SnapshotProvider snapshotProvider, LedgerService ledgerService, Node node, TipSelector tipsSelector,
+ TipsViewModel tipsViewModel, TransactionValidator transactionValidator,
+ LatestMilestoneTracker latestMilestoneTracker) {
+ this.configuration = configuration;
this.ixi = ixi;
- APIConfig configuration = instance.configuration;
+
+ this.transactionRequester = transactionRequester;
+ this.spentAddressesService = spentAddressesService;
+ this.tangle = tangle;
+ this.bundleValidator = bundleValidator;
+ this.snapshotProvider = snapshotProvider;
+ this.ledgerService = ledgerService;
+ this.node = node;
+ this.tipsSelector = tipsSelector;
+ this.tipsViewModel = tipsViewModel;
+ this.transactionValidator = transactionValidator;
+ this.latestMilestoneTracker = latestMilestoneTracker;
+
maxFindTxs = configuration.getMaxFindTransactions();
maxRequestList = configuration.getMaxRequestsList();
maxGetTrytes = configuration.getMaxGetTrytes();
maxBodyLength = configuration.getMaxBodyLength();
testNet = configuration.isTestnet();
- features = Feature.calculateFeatureNames(instance.configuration);
+ features = Feature.calculateFeatureNames(configuration);
}
/**
@@ -165,13 +225,15 @@ public API(Iota instance, IXI ixi) {
*
*
*/
- public void init() throws IOException {
- APIConfig configuration = instance.configuration;
+ public void init(RestConnector connector) throws IOException {
final int apiPort = configuration.getPort();
final String apiHost = configuration.getApiHost();
log.debug("Binding JSON-REST API Undertow server on {}:{}", apiHost, apiPort);
+ this.connector = connector;
+ connector.init(apiPort, apiHost, this::process);
+
server = Undertow.builder().addHttpListener(apiPort, apiHost)
.setHandler(path().addPrefixPath("/", addSecurity(new HttpHandler() {
@Override
@@ -197,6 +259,8 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
processRequest(exchange);
}
}))).build();
+
+ connector.start();
server.start();
}
@@ -287,7 +351,7 @@ private void processRequest(final HttpServerExchange exchange) throws IOExceptio
} else if (body.length() > maxBodyLength) {
response = ErrorResponse.create("Request too long");
} else {
- response = process(body, exchange.getSourceAddress());
+ response = process(body); //, exchange.getSourceAddress()
}
sendResponse(exchange, response, beginningTime);
}
@@ -324,8 +388,7 @@ private void processRequest(final HttpServerExchange exchange) throws IOExceptio
* @throws UnsupportedEncodingException If the requestString cannot be parsed into a Map.
Currently caught and turned into a {@link ExceptionResponse}.
*/
- private AbstractResponse process(final String requestString, InetSocketAddress sourceAddress)
- throws UnsupportedEncodingException {
+ private AbstractResponse process(final String requestString){
try {
// Request JSON data into map
@@ -348,10 +411,12 @@ private AbstractResponse process(final String requestString, InetSocketAddress s
// Is this command allowed to be run from this request address?
// We check the remote limit API configuration.
- if (instance.configuration.getRemoteLimitApi().contains(command) &&
- !instance.configuration.getRemoteTrustedApiHosts().contains(sourceAddress.getAddress())) {
+
+ //TODO , InetSocketAddress sourceAddress process elsewhere
+ /*if (configuration.getRemoteLimitApi().contains(command) &&
+ !configuration.getRemoteTrustedApiHosts().contains(sourceAddress.getAddress())) {
return AccessLimitedResponse.create("COMMAND " + command + " is not available on this node");
- }
+ }*/
log.debug("# {} -> Requesting command '{}'", counter.incrementAndGet(), command);
@@ -456,8 +521,8 @@ private AbstractResponse process(final String requestString, InetSocketAddress s
}
case "getMissingTransactions": {
//TransactionRequester.instance().rescanTransactionsToRequest();
- synchronized (instance.transactionRequester) {
- List missingTx = Arrays.stream(instance.transactionRequester.getRequestedTransactions())
+ synchronized (transactionRequester) {
+ List missingTx = Arrays.stream(transactionRequester.getRequestedTransactions())
.map(Hash::toString)
.collect(Collectors.toList());
return GetTipsResponse.create(missingTx);
@@ -509,7 +574,7 @@ private AbstractResponse wereAddressesSpentFromStatement(List addresses)
int index = 0;
for (Hash address : addressesHash) {
- states[index++] = instance.spentAddressesService.wasAddressSpentFrom(address);
+ states[index++] = spentAddressesService.wasAddressSpentFrom(address);
}
return WereAddressesSpentFrom.create(states);
}
@@ -523,7 +588,7 @@ private AbstractResponse wereAddressesSpentFromStatement(List addresses)
* @throws Exception When a model could not be loaded.
*/
private Hash findTail(Hash hash) throws Exception {
- TransactionViewModel tx = TransactionViewModel.fromHash(instance.tangle, hash);
+ TransactionViewModel tx = TransactionViewModel.fromHash(tangle, hash);
final Hash bundleHash = tx.getBundleHash();
long index = tx.getCurrentIndex();
boolean foundApprovee = false;
@@ -531,9 +596,9 @@ private Hash findTail(Hash hash) throws Exception {
// As long as the index is bigger than 0 and we are still traversing the same bundle
// If the hash we asked about is already a tail, this loop never starts
while (index-- > 0 && tx.getBundleHash().equals(bundleHash)) {
- Set approvees = tx.getApprovers(instance.tangle).getHashes();
+ Set approvees = tx.getApprovers(tangle).getHashes();
for (Hash approvee : approvees) {
- TransactionViewModel nextTx = TransactionViewModel.fromHash(instance.tangle, approvee);
+ TransactionViewModel nextTx = TransactionViewModel.fromHash(tangle, approvee);
if (nextTx.getBundleHash().equals(bundleHash)) {
tx = nextTx;
foundApprovee = true;
@@ -574,7 +639,7 @@ private AbstractResponse checkConsistencyStatement(List transactionsList
// Check if the transactions themselves are valid
for (Hash transaction : transactions) {
- TransactionViewModel txVM = TransactionViewModel.fromHash(instance.tangle, transaction);
+ TransactionViewModel txVM = TransactionViewModel.fromHash(tangle, transaction);
if (txVM.getType() == TransactionViewModel.PREFILLED_SLOT) {
return ErrorResponse.create("Invalid transaction, missing: " + transaction);
}
@@ -587,7 +652,7 @@ private AbstractResponse checkConsistencyStatement(List transactionsList
state = false;
info = "tails are not solid (missing a referenced tx): " + transaction;
break;
- } else if (instance.bundleValidator.validate(instance.tangle, instance.snapshotProvider.getInitialSnapshot(), txVM.getHash()).size() == 0) {
+ } else if (bundleValidator.validate(tangle, snapshotProvider.getInitialSnapshot(), txVM.getHash()).size() == 0) {
state = false;
info = "tails are not consistent (bundle is invalid): " + transaction;
break;
@@ -596,10 +661,9 @@ private AbstractResponse checkConsistencyStatement(List transactionsList
// Transactions are valid, lets check ledger consistency
if (state) {
- instance.snapshotProvider.getLatestSnapshot().lockRead();
+ snapshotProvider.getLatestSnapshot().lockRead();
try {
- WalkValidatorImpl walkValidator = new WalkValidatorImpl(instance.tangle, instance.snapshotProvider, instance.ledgerService,
- instance.configuration);
+ WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, configuration);
for (Hash transaction : transactions) {
if (!walkValidator.isValid(transaction)) {
state = false;
@@ -608,7 +672,7 @@ private AbstractResponse checkConsistencyStatement(List transactionsList
}
}
} finally {
- instance.snapshotProvider.getLatestSnapshot().unlockRead();
+ snapshotProvider.getLatestSnapshot().unlockRead();
}
}
@@ -622,7 +686,7 @@ private AbstractResponse checkConsistencyStatement(List transactionsList
* @return false if we received at least a solid milestone, otherwise true
*/
public boolean invalidSubtangleStatus() {
- return (instance.snapshotProvider.getLatestSnapshot().getIndex() == instance.snapshotProvider.getInitialSnapshot().getIndex());
+ return (snapshotProvider.getLatestSnapshot().getIndex() == snapshotProvider.getInitialSnapshot().getIndex());
}
/**
@@ -632,7 +696,7 @@ public boolean invalidSubtangleStatus() {
* @return {@link com.iota.iri.service.dto.GetNeighborsResponse}
**/
private AbstractResponse getNeighborsStatement() {
- return GetNeighborsResponse.create(instance.node.getNeighbors());
+ return GetNeighborsResponse.create(node.getNeighbors());
}
/**
@@ -652,9 +716,9 @@ private AbstractResponse addNeighborsStatement(List uris) {
try {
for (final String uriString : uris) {
log.info("Adding neighbor: " + uriString);
- final Neighbor neighbor = instance.node.newNeighbor(new URI(uriString), true);
- if (!instance.node.getNeighbors().contains(neighbor)) {
- instance.node.getNeighbors().add(neighbor);
+ final Neighbor neighbor = node.newNeighbor(new URI(uriString), true);
+ if (!node.getNeighbors().contains(neighbor)) {
+ node.getNeighbors().add(neighbor);
numberOfAddedNeighbors++;
}
}
@@ -682,7 +746,7 @@ private AbstractResponse removeNeighborsStatement(List uris) {
try {
for (final String uriString : uris) {
log.info("Removing neighbor: " + uriString);
- if (instance.node.removeNeighbor(new URI(uriString),true)) {
+ if (node.removeNeighbor(new URI(uriString),true)) {
numberOfRemovedNeighbors++;
}
}
@@ -703,7 +767,7 @@ private AbstractResponse removeNeighborsStatement(List uris) {
private synchronized AbstractResponse getTrytesStatement(List hashes) throws Exception {
final List elements = new LinkedList<>();
for (final String hash : hashes) {
- final TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(instance.tangle, HashFactory.TRANSACTION.create(hash));
+ final TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(tangle, HashFactory.TRANSACTION.create(hash));
if (transactionViewModel != null) {
elements.add(Converter.trytes(transactionViewModel.trits()));
}
@@ -763,7 +827,7 @@ private static void incEllapsedTimeGetTxToApprove(long ellapsedTime) {
* @throws Exception When tip selection has failed. Currently caught and returned as an {@link ErrorResponse}.
**/
private synchronized AbstractResponse getTransactionsToApproveStatement(int depth, Optional reference) throws Exception {
- if (depth < 0 || depth > instance.configuration.getMaxDepth()) {
+ if (depth < 0 || depth > configuration.getMaxDepth()) {
return ErrorResponse.create("Invalid depth input");
}
@@ -792,7 +856,7 @@ List getTransactionToApproveTips(int depth, Optional reference) thro
throw new IllegalStateException(INVALID_SUBTANGLE);
}
- List tips = instance.tipsSelector.getTransactionsToApprove(depth, reference);
+ List tips = tipsSelector.getTransactionsToApprove(depth, reference);
if (log.isDebugEnabled()) {
gatherStatisticsOnTipSelection();
@@ -828,7 +892,7 @@ private void gatherStatisticsOnTipSelection() {
* @return {@link com.iota.iri.service.dto.GetTipsResponse}
**/
private synchronized AbstractResponse getTipsStatement() throws Exception {
- return GetTipsResponse.create(instance.tipsViewModel.getTips()
+ return GetTipsResponse.create(tipsViewModel.getTips()
.stream()
.map(Hash::toString)
.collect(Collectors.toList()));
@@ -848,18 +912,18 @@ public void storeTransactionsStatement(List trytes) throws Exception {
for (final String trytesPart : trytes) {
//validate all trytes
Converter.trits(trytesPart, txTrits, 0);
- final TransactionViewModel transactionViewModel = instance.transactionValidator.validateTrits(txTrits,
- instance.transactionValidator.getMinWeightMagnitude());
+ final TransactionViewModel transactionViewModel = transactionValidator.validateTrits(txTrits,
+ transactionValidator.getMinWeightMagnitude());
elements.add(transactionViewModel);
}
for (final TransactionViewModel transactionViewModel : elements) {
//store transactions
- if(transactionViewModel.store(instance.tangle, instance.snapshotProvider.getInitialSnapshot())) {
+ if(transactionViewModel.store(tangle, snapshotProvider.getInitialSnapshot())) {
transactionViewModel.setArrivalTime(System.currentTimeMillis() / 1000L);
- instance.transactionValidator.updateStatus(transactionViewModel);
+ transactionValidator.updateStatus(transactionViewModel);
transactionViewModel.updateSender("local");
- transactionViewModel.update(instance.tangle, instance.snapshotProvider.getInitialSnapshot(), "sender");
+ transactionViewModel.update(tangle, snapshotProvider.getInitialSnapshot(), "sender");
}
}
}
@@ -881,8 +945,8 @@ private AbstractResponse interruptAttachingToTangleStatement(){
* @throws Exception When we cant find the first milestone in the database
**/
private AbstractResponse getNodeInfoStatement() throws Exception{
- String name = instance.configuration.isTestnet() ? IRI.TESTNET_NAME : IRI.MAINNET_NAME;
- MilestoneViewModel milestone = MilestoneViewModel.first(instance.tangle);
+ String name = configuration.isTestnet() ? IRI.TESTNET_NAME : IRI.MAINNET_NAME;
+ MilestoneViewModel milestone = MilestoneViewModel.first(tangle);
return GetNodeInfoResponse.create(
name,
@@ -893,22 +957,22 @@ private AbstractResponse getNodeInfoStatement() throws Exception{
Runtime.getRuntime().maxMemory(),
Runtime.getRuntime().totalMemory(),
- instance.latestMilestoneTracker.getLatestMilestoneHash(),
- instance.latestMilestoneTracker.getLatestMilestoneIndex(),
+ latestMilestoneTracker.getLatestMilestoneHash(),
+ latestMilestoneTracker.getLatestMilestoneIndex(),
- instance.snapshotProvider.getLatestSnapshot().getHash(),
- instance.snapshotProvider.getLatestSnapshot().getIndex(),
+ snapshotProvider.getLatestSnapshot().getHash(),
+ snapshotProvider.getLatestSnapshot().getIndex(),
milestone != null ? milestone.index() : -1,
- instance.snapshotProvider.getLatestSnapshot().getInitialIndex(),
+ snapshotProvider.getLatestSnapshot().getInitialIndex(),
- instance.node.howManyNeighbors(),
- instance.node.queuedTransactionsSize(),
+ node.howManyNeighbors(),
+ node.queuedTransactionsSize(),
System.currentTimeMillis(),
- instance.tipsViewModel.size(),
- instance.transactionRequester.numberOfTransactionsToRequest(),
+ tipsViewModel.size(),
+ transactionRequester.numberOfTransactionsToRequest(),
features,
- instance.configuration.getCoordinator().toString());
+ configuration.getCoordinator().toString());
}
/**
@@ -917,7 +981,7 @@ private AbstractResponse getNodeInfoStatement() throws Exception{
* @return {@link GetNodeAPIConfigurationResponse}
*/
private AbstractResponse getNodeAPIConfigurationStatement() {
- return GetNodeAPIConfigurationResponse.create(instance.configuration);
+ return GetNodeAPIConfigurationResponse.create(configuration);
}
/**
@@ -955,7 +1019,7 @@ private AbstractResponse getInclusionStatesStatement(
List tipsIndex = new LinkedList<>();
{
for(Hash tip: tps) {
- TransactionViewModel tx = TransactionViewModel.fromHash(instance.tangle, tip);
+ TransactionViewModel tx = TransactionViewModel.fromHash(tangle, tip);
if (tx.getType() != TransactionViewModel.PREFILLED_SLOT) {
tipsIndex.add(tx.snapshotIndex());
}
@@ -979,7 +1043,7 @@ private AbstractResponse getInclusionStatesStatement(
// Sets to 1 if the transaction index is below the max index of tips (included).
for(Hash hash: trans) {
- TransactionViewModel transaction = TransactionViewModel.fromHash(instance.tangle, hash);
+ TransactionViewModel transaction = TransactionViewModel.fromHash(tangle, hash);
if(transaction.getType() == TransactionViewModel.PREFILLED_SLOT || transaction.snapshotIndex() == 0) {
inclusionStates[count] = -1;
} else if(transaction.snapshotIndex() > maxTipsIndex) {
@@ -997,7 +1061,7 @@ private AbstractResponse getInclusionStatesStatement(
// Sorts all tips per snapshot index. Stops if a tip is not in our database, or just as a hash.
for (final Hash tip : tps) {
- TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(instance.tangle, tip);
+ TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(tangle, tip);
if (transactionViewModel.getType() == TransactionViewModel.PREFILLED_SLOT){
return ErrorResponse.create("One of the tips is absent");
}
@@ -1009,7 +1073,7 @@ private AbstractResponse getInclusionStatesStatement(
// Loop over all transactions without a state, and counts the amount per snapshot index
for(int i = 0; i < inclusionStates.length; i++) {
if(inclusionStates[i] == 0) {
- TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(instance.tangle, trans.get(i));
+ TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(tangle, trans.get(i));
int snapshotIndex = transactionViewModel.snapshotIndex();
sameIndexTransactionCount.putIfAbsent(snapshotIndex, 0);
sameIndexTransactionCount.put(snapshotIndex, sameIndexTransactionCount.get(snapshotIndex) + 1);
@@ -1073,7 +1137,7 @@ private boolean exhaustiveSearchWithinIndex(
// Check if the transactions have indeed this index. Otherwise ignore.
// Starts off with the tips in nonAnalyzedTransactions, but transaction trunk & branch gets added.
- final TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(instance.tangle, pointer);
+ final TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(tangle, pointer);
if (transactionViewModel.snapshotIndex() == index) {
// Do we have the complete transaction?
if (transactionViewModel.getType() == TransactionViewModel.PREFILLED_SLOT) {
@@ -1130,7 +1194,7 @@ private synchronized AbstractResponse findTransactionsStatement(final Map bundles = getParameterAsSet(request,"bundles",HASH_SIZE);
for (final String bundle : bundles) {
bundlesTransactions.addAll(
- BundleViewModel.load(instance.tangle, HashFactory.BUNDLE.create(bundle))
+ BundleViewModel.load(tangle, HashFactory.BUNDLE.create(bundle))
.getHashes());
}
foundTransactions.addAll(bundlesTransactions);
@@ -1142,7 +1206,7 @@ private synchronized AbstractResponse findTransactionsStatement(final Map addresses = getParameterAsSet(request,"addresses",HASH_SIZE);
for (final String address : addresses) {
addressesTransactions.addAll(
- AddressViewModel.load(instance.tangle, HashFactory.ADDRESS.create(address))
+ AddressViewModel.load(tangle, HashFactory.ADDRESS.create(address))
.getHashes());
}
foundTransactions.addAll(addressesTransactions);
@@ -1155,14 +1219,14 @@ private synchronized AbstractResponse findTransactionsStatement(final Map approvees = getParameterAsSet(request,"approvees",HASH_SIZE);
for (final String approvee : approvees) {
approveeTransactions.addAll(
- TransactionViewModel.fromHash(instance.tangle, HashFactory.TRANSACTION.create(approvee))
- .getApprovers(instance.tangle)
+ TransactionViewModel.fromHash(tangle, HashFactory.TRANSACTION.create(approvee))
+ .getApprovers(tangle)
.getHashes());
}
foundTransactions.addAll(approveeTransactions);
@@ -1267,15 +1331,15 @@ public void broadcastTransactionsStatement(List trytes) {
for (final String tryte : trytes) {
//validate all trytes
Converter.trits(tryte, txTrits, 0);
- final TransactionViewModel transactionViewModel = instance.transactionValidator.validateTrits(
- txTrits, instance.transactionValidator.getMinWeightMagnitude());
+ final TransactionViewModel transactionViewModel = transactionValidator.validateTrits(
+ txTrits, transactionValidator.getMinWeightMagnitude());
elements.add(transactionViewModel);
}
for (final TransactionViewModel transactionViewModel : elements) {
//push first in line to broadcast
transactionViewModel.weightMagnitude = Curl.HASH_LENGTH;
- instance.node.broadcast(transactionViewModel);
+ node.broadcast(transactionViewModel);
}
}
@@ -1312,11 +1376,11 @@ private AbstractResponse getBalancesStatement(List addresses,
List hashes;
final Map balances = new HashMap<>();
- instance.snapshotProvider.getLatestSnapshot().lockRead();
- final int index = instance.snapshotProvider.getLatestSnapshot().getIndex();
+ snapshotProvider.getLatestSnapshot().lockRead();
+ final int index = snapshotProvider.getLatestSnapshot().getIndex();
if (tips == null || tips.isEmpty()) {
- hashes = Collections.singletonList(instance.snapshotProvider.getLatestSnapshot().getHash());
+ hashes = Collections.singletonList(snapshotProvider.getLatestSnapshot().getHash());
} else {
hashes = tips.stream()
.map(tip -> (HashFactory.TRANSACTION.create(tip)))
@@ -1326,7 +1390,7 @@ private AbstractResponse getBalancesStatement(List addresses,
try {
// Get the balance for each address at the last snapshot
for (final Hash address : addressList) {
- Long value = instance.snapshotProvider.getLatestSnapshot().getBalance(address);
+ Long value = snapshotProvider.getLatestSnapshot().getBalance(address);
if (value == null) {
value = 0L;
}
@@ -1339,10 +1403,10 @@ private AbstractResponse getBalancesStatement(List addresses,
// Calculate the difference created by the non-verified transactions which tips approve.
// This difference is put in a map with address -> value changed
for (Hash tip : hashes) {
- if (!TransactionViewModel.exists(instance.tangle, tip)) {
+ if (!TransactionViewModel.exists(tangle, tip)) {
return ErrorResponse.create("Tip not found: " + tip.toString());
}
- if (!instance.ledgerService.isBalanceDiffConsistent(visitedHashes, diff, tip)) {
+ if (!ledgerService.isBalanceDiffConsistent(visitedHashes, diff, tip)) {
return ErrorResponse.create("Tips are not consistent");
}
}
@@ -1350,7 +1414,7 @@ private AbstractResponse getBalancesStatement(List addresses,
// Update the found balance according to 'diffs' balance changes
diff.forEach((key, value) -> balances.computeIfPresent(key, (hash, aLong) -> value + aLong));
} finally {
- instance.snapshotProvider.getLatestSnapshot().unlockRead();
+ snapshotProvider.getLatestSnapshot().unlockRead();
}
final List elements = addressList.stream()
@@ -1475,13 +1539,13 @@ public synchronized List attachToTangleStatement(Hash trunkTransaction,
TransactionViewModel.ATTACHMENT_TIMESTAMP_UPPER_BOUND_TRINARY_OFFSET,
TransactionViewModel.ATTACHMENT_TIMESTAMP_UPPER_BOUND_TRINARY_SIZE);
- if (!pearlDiver.search(transactionTrits, minWeightMagnitude, instance.configuration.getPowThreads())) {
+ if (!pearlDiver.search(transactionTrits, minWeightMagnitude, configuration.getPowThreads())) {
transactionViewModels.clear();
break;
}
//validate PoW - throws exception if invalid
- final TransactionViewModel transactionViewModel = instance.transactionValidator.validateTrits(
- transactionTrits, instance.transactionValidator.getMinWeightMagnitude());
+ final TransactionViewModel transactionViewModel = transactionValidator.validateTrits(
+ transactionTrits, transactionValidator.getMinWeightMagnitude());
transactionViewModels.add(transactionViewModel);
prevTransaction = transactionViewModel.getHash();
@@ -1640,7 +1704,7 @@ private static void setupResponseHeaders(HttpServerExchange exchange) {
* @return The updated handler
*/
private HttpHandler addSecurity(HttpHandler toWrap) {
- String credentials = instance.configuration.getRemoteAuth();
+ String credentials = configuration.getRemoteAuth();
if (credentials == null || credentials.isEmpty()) {
return toWrap;
}
diff --git a/src/main/java/com/iota/iri/service/restserver/RestConnector.java b/src/main/java/com/iota/iri/service/restserver/RestConnector.java
new file mode 100644
index 0000000000..1f23b8c2c3
--- /dev/null
+++ b/src/main/java/com/iota/iri/service/restserver/RestConnector.java
@@ -0,0 +1,14 @@
+package com.iota.iri.service.restserver;
+
+import java.util.function.Function;
+
+import com.iota.iri.service.dto.AbstractResponse;
+
+public interface RestConnector {
+
+ void init(Function processFunction);
+
+ void start();
+
+ void stop();
+}
diff --git a/src/main/java/com/iota/iri/service/ApiHandler.java b/src/main/java/com/iota/iri/service/restserver/RestEasy.java
similarity index 56%
rename from src/main/java/com/iota/iri/service/ApiHandler.java
rename to src/main/java/com/iota/iri/service/restserver/RestEasy.java
index 26a4a75b0c..97f162309d 100644
--- a/src/main/java/com/iota/iri/service/ApiHandler.java
+++ b/src/main/java/com/iota/iri/service/restserver/RestEasy.java
@@ -1,31 +1,31 @@
-package com.iota.iri.service;
-
-import static io.undertow.Handlers.path;
+package com.iota.iri.service.restserver;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
+import java.util.function.Function;
+import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
+import javax.ws.rs.POST;
import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.iota.iri.conf.APIConfig;
-import com.iota.iri.service.restserver.Root;
+import com.iota.iri.service.dto.AbstractResponse;
import com.iota.iri.utils.MapIdentityManager;
import io.undertow.Undertow;
-import io.undertow.Undertow.Builder;
import io.undertow.security.api.AuthenticationMechanism;
-import io.undertow.security.api.AuthenticationMechanismFactory;
import io.undertow.security.api.AuthenticationMode;
import io.undertow.security.handlers.AuthenticationCallHandler;
import io.undertow.security.handlers.AuthenticationConstraintHandler;
@@ -35,36 +35,30 @@
import io.undertow.security.impl.BasicAuthenticationMechanism;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.DeploymentInfo;
-import io.undertow.util.Headers;
-import io.undertow.util.HttpString;
-import io.undertow.util.Methods;
-import io.undertow.util.MimeMappings;
-import io.undertow.util.StatusCodes;
-public class ApiHandler extends Application {
+public class RestEasy extends Application implements RestConnector {
- private static final Logger log = LoggerFactory.getLogger(ApiHandler.class);
+ private static final Logger log = LoggerFactory.getLogger(RestEasy.class);
private UndertowJaxrsServer server;
private APIConfig configuration;
+
+ private DeploymentInfo info;
- public ApiHandler(APIConfig configuration) {
+ public RestEasy(APIConfig configuration) {
this.configuration = configuration;
}
- public void init() throws IOException {
- final int apiPort = configuration.getPort();
- final String apiHost = configuration.getApiHost();
-
- log.debug("Binding JSON-REST API Undertow server on {}:{}", apiHost, apiPort);
+ @Override
+ public void init(Function processFunction) {
+ log.debug("Binding JSON-REST API Undertow server on {}:{}", configuration.getApiHost(), configuration.getPort());
Undertow.Builder builder = Undertow.builder()
- .addHttpListener(apiPort, apiHost);
+ .addHttpListener(configuration.getPort(), configuration.getApiHost());
server = new UndertowJaxrsServer().start(builder);
- DeploymentInfo info = server.undertowDeployment(Root.class);
+ info = server.undertowDeployment(Root.class);
info.setDisplayName("Iota Realm");
info.setDeploymentName("Iota Realm");
info.setContextPath("/");
@@ -92,34 +86,15 @@ public HttpHandler wrap(HttpHandler toWrap) {
return handler;
}
});
-
+ }
+
+ @Override
+ public void start() {
server.deploy(info);
}
+ @Override
public void stop() {
server.stop();
}
-
- private void handleRequest(final HttpServerExchange exchange) throws Exception {
- HttpString requestMethod = exchange.getRequestMethod();
- if (Methods.OPTIONS.equals(requestMethod)) {
- String allowedMethods = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH";
- //return list of allowed methods in response headers
- exchange.setStatusCode(StatusCodes.OK);
- exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt"));
- exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, 0);
- exchange.getResponseHeaders().put(Headers.ALLOW, allowedMethods);
- exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Origin"), "*");
- exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Headers"), "User-Agent, Origin, X-Requested-With, Content-Type, Accept, X-IOTA-API-Version");
- exchange.getResponseSender().close();
- return;
- }
-
- if (exchange.isInIoThread()) {
- //exchange.dispatch(this);
- return;
- }
-
- //processRequest(exchange);
- }
}
diff --git a/src/main/java/com/iota/iri/service/restserver/Root.java b/src/main/java/com/iota/iri/service/restserver/Root.java
index a458da5939..52741c9efb 100644
--- a/src/main/java/com/iota/iri/service/restserver/Root.java
+++ b/src/main/java/com/iota/iri/service/restserver/Root.java
@@ -6,7 +6,6 @@
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
-@Path("")
public class Root extends Application {
@Override
@@ -15,6 +14,4 @@ public Set> getClasses(){
classes.add(Test.class);
return classes;
}
-
-
}
diff --git a/src/main/java/com/iota/iri/service/restserver/Test.java b/src/main/java/com/iota/iri/service/restserver/Test.java
index a0bd7abe79..9d22d0720e 100644
--- a/src/main/java/com/iota/iri/service/restserver/Test.java
+++ b/src/main/java/com/iota/iri/service/restserver/Test.java
@@ -19,9 +19,12 @@
@Path("")
public class Test extends ApiCall {
+ public Test() {
+ System.out.println("Constructor");
+ }
+
@GET
- @Path("/ping")
- @Produces(MediaType.TEXT_PLAIN)
+ @Path("ping")
public String ping() {
return "pong";
}
@@ -30,6 +33,7 @@ public String ping() {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response apiCall(String request) throws JsonParseException, JsonMappingException, IOException {
+ System.out.println("here1");
AbstractResponse response = pre(request);
if (response == null) {
response = process();
diff --git a/src/test/java/com/iota/iri/service/APIIntegrationTests.java b/src/test/java/com/iota/iri/service/APIIntegrationTests.java
index 73152876a8..54ebdf2473 100644
--- a/src/test/java/com/iota/iri/service/APIIntegrationTests.java
+++ b/src/test/java/com/iota/iri/service/APIIntegrationTests.java
@@ -10,6 +10,7 @@
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.crypto.SpongeFactory;
import com.iota.iri.model.TransactionHash;
+import com.iota.iri.service.restserver.RestEasy;
import com.iota.iri.utils.Converter;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.builder.ResponseSpecBuilder;
@@ -90,13 +91,17 @@ public static void setUp() throws Exception {
//create node
iota = new Iota(configuration);
ixi = new IXI(iota);
- api = new API(iota, ixi);
+ api = new API(iota.configuration, ixi, iota.transactionRequester,
+ iota.spentAddressesService, iota.tangle, iota.bundleValidator,
+ iota.snapshotProvider, iota.ledgerService, iota.node, iota.tipsSelector,
+ iota.tipsViewModel, iota.transactionValidator,
+ iota.latestMilestoneTracker);
//init
try {
iota.init();
iota.snapshotProvider.getInitialSnapshot().setTimestamp(0);
- api.init();
+ api.init(new RestEasy());
ixi.init(IXIConfig.IXI_DIR);
} catch (final Exception e) {
log.error("Exception during IOTA node initialisation: ", e);
diff --git a/src/test/java/com/iota/iri/service/ApiHandlerTest.java b/src/test/java/com/iota/iri/service/ApiHandlerTest.java
index 7f339df5b3..08e020156e 100644
--- a/src/test/java/com/iota/iri/service/ApiHandlerTest.java
+++ b/src/test/java/com/iota/iri/service/ApiHandlerTest.java
@@ -22,35 +22,36 @@
import org.mockito.junit.MockitoRule;
import com.iota.iri.conf.APIConfig;
+import com.iota.iri.service.restserver.RestEasy;
public class ApiHandlerTest {
-
@Rule
- public MockitoRule mockitoRule = MockitoJUnit.rule();
-
- @Mock
- private APIConfig apiconfig;
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private APIConfig apiconfig;
- private ApiHandler server;
+
+ private RestEasy server;
@Before
public void setup() {
Mockito.when(apiconfig.getPort()).thenReturn(TestPortProvider.getPort());
Mockito.when(apiconfig.getApiHost()).thenReturn(TestPortProvider.getHost());
//Mockito.when(apiconfig.getRemoteAuth()).thenReturn("user:pass");
-
- this.server = new ApiHandler(apiconfig);
-
- try {
- this.server.init();
- } catch (IOException e) {
- fail(e.getMessage());
- }
+
+
+ this.server = new RestEasy(apiconfig);
+ this.server.init((String param) -> {
+ System.out.println("called!");
+ return null;
+ });
+ this.server.start();
}
@After
public void tearDown() {
- this.server.stop();
+ //this.server.stop();
}
@Test
@@ -64,8 +65,13 @@ public void test() {
@Test
public void test2() {
Client client = ClientBuilder.newClient();
- String val = client.target(TestPortProvider.generateURL(""))
+ String val = client.target(TestPortProvider.generateURL("/ping"))
.request().get(String.class);
+ String val2 = client.target(TestPortProvider.generateURL("/ping"))
+ .request().get(String.class);
+
+ String val3 = client.target(TestPortProvider.generateURL("/ping"))
+ .request().get(String.class);
System.out.println(val);
}
@@ -73,7 +79,7 @@ public void test2() {
public void nodeInfo() {
Client client = ClientBuilder.newClient();
String jsonString = "{\"command\": \"getNodeInfo\"}";
- Response val = client.target(TestPortProvider.generateURL(""))
+ Response val = client.target(TestPortProvider.generateURL("/"))
.request().post(Entity.entity(jsonString, MediaType.APPLICATION_JSON));
System.out.println(val.getEntity());
}
diff --git a/src/test/java/com/iota/iri/service/NodeIntegrationTests.java b/src/test/java/com/iota/iri/service/NodeIntegrationTests.java
index 47c93ad2c2..c8e92a0b5e 100644
--- a/src/test/java/com/iota/iri/service/NodeIntegrationTests.java
+++ b/src/test/java/com/iota/iri/service/NodeIntegrationTests.java
@@ -12,6 +12,7 @@
import com.iota.iri.model.Hash;
import com.iota.iri.model.HashFactory;
import com.iota.iri.network.Node;
+import com.iota.iri.service.restserver.RestEasy;
import com.iota.iri.utils.Converter;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.After;
@@ -52,8 +53,14 @@ public void testGetsSolid() throws Exception {
iotaNodes[i] = newNode(i, folders[i*2], folders[i*2+1]);
ixi[i] = new IXI(iotaNodes[i]);
ixi[i].init(IXIConfig.IXI_DIR);
- api[i] = new API(iotaNodes[i], ixi[i]);
- api[i].init();
+
+ api[i] = new API(iotaNodes[i].configuration, ixi[i], iotaNodes[i].transactionRequester,
+ iotaNodes[i].spentAddressesService, iotaNodes[i].tangle, iotaNodes[i].bundleValidator,
+ iotaNodes[i].snapshotProvider, iotaNodes[i].ledgerService, iotaNodes[i].node,
+ iotaNodes[i].tipsSelector, iotaNodes[i].tipsViewModel, iotaNodes[i].transactionValidator,
+ iotaNodes[i].latestMilestoneTracker);
+
+ api[i].init(new RestEasy());
}
Node.uri("udp://localhost:14701").ifPresent(uri -> iotaNodes[0].node.addNeighbor(iotaNodes[0].node.newNeighbor(uri, true)));
//Node.uri("udp://localhost:14700").ifPresent(uri -> iotaNodes[1].node.addNeighbor(iotaNodes[1].node.newNeighbor(uri, true)));
From 68874ccb925453fb4465f5ed4f528566ba82b15c Mon Sep 17 00:00:00 2001
From: Brord van Wierst
Date: Wed, 13 Mar 2019 19:43:32 +0100
Subject: [PATCH 18/67] Reverted to non-rest ish way
---
src/main/java/com/iota/iri/service/API.java | 226 +-----------------
.../iota/iri/service/restserver/ApiCall.java | 3 +-
.../iri/service/restserver/ApiProcessor.java | 10 +
.../iri/service/restserver/RestConnector.java | 6 +-
.../iota/iri/service/restserver/RestEasy.java | 207 ++++++++++++++--
.../com/iota/iri/service/restserver/Root.java | 5 +-
.../com/iota/iri/service/restserver/Test.java | 22 +-
.../com/iota/iri/service/ApiHandlerTest.java | 6 +-
8 files changed, 243 insertions(+), 242 deletions(-)
create mode 100644 src/main/java/com/iota/iri/service/restserver/ApiProcessor.java
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index e39157e36e..fcde71968d 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -6,11 +6,9 @@
import com.iota.iri.BundleValidator;
import com.iota.iri.IRI;
import com.iota.iri.IXI;
-import com.iota.iri.Iota;
import com.iota.iri.TransactionValidator;
import com.iota.iri.conf.APIConfig;
import com.iota.iri.conf.IotaConfig;
-import com.iota.iri.conf.NetworkConfig;
import com.iota.iri.controllers.*;
import com.iota.iri.crypto.Curl;
import com.iota.iri.crypto.PearlDiver;
@@ -32,6 +30,7 @@
import com.iota.iri.service.tipselection.impl.WalkValidatorImpl;
import com.iota.iri.storage.Tangle;
import com.iota.iri.utils.Converter;
+
import com.iota.iri.utils.IotaIOUtils;
import com.iota.iri.utils.IotaUtils;
import com.iota.iri.utils.MapIdentityManager;
@@ -47,19 +46,15 @@
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.*;
+
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.xnio.channels.StreamSinkChannel;
-import org.xnio.streams.ChannelInputStream;
-
import java.io.IOException;
import java.io.UnsupportedEncodingException;
-import java.net.InetSocketAddress;
+import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
@@ -68,8 +63,6 @@
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import static io.undertow.Handlers.path;
-
/**
*
* The API makes it possible to interact with the node by requesting information or actions to be taken.
@@ -135,14 +128,11 @@ public class API {
private final int maxFindTxs;
private final int maxRequestList;
private final int maxGetTrytes;
- private final int maxBodyLength;
private final boolean testNet;
private final String[] features;
//endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- private Undertow server;
private final Gson gson = new GsonBuilder().create();
private volatile PearlDiver pearlDiver = new PearlDiver();
@@ -194,168 +184,18 @@ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRe
maxFindTxs = configuration.getMaxFindTransactions();
maxRequestList = configuration.getMaxRequestsList();
maxGetTrytes = configuration.getMaxGetTrytes();
- maxBodyLength = configuration.getMaxBodyLength();
testNet = configuration.isTestnet();
features = Feature.calculateFeatureNames(configuration);
}
- /**
- * Prepares the IOTA API for usage. Until this method is called, no HTTP requests can be made.
- * The order of loading is as follows
- *
- * -
- * Read the spend addresses from the previous epoch. Used in {@link #wasAddressSpentFrom(Hash)}.
- * This only happens if {@link APIConfig#isTestnet()} is false
- * If reading from the previous epoch fails, a log is printed. The API will continue to initialize.
- *
- * -
- * Get the {@link APIConfig} from the {@link Iota} instance,
- * and read {@link APIConfig#getPort()} and {@link APIConfig#getApiHost()}
- *
- * -
- * Builds a secure {@link Undertow} server with the port and host.
- * If {@link APIConfig#getRemoteAuth()} is defined, remote authentication is blocked for anyone except
- * those defined in {@link APIConfig#getRemoteAuth()} or localhost.
- * This is done with {@link BasicAuthenticationMechanism} in a {@link AuthenticationMode#PRO_ACTIVE} mode.
- * By default, this authentication is disabled.
- *
- * -
- * Starts the server, opening it for HTTP API requests
- *
- *
- */
+
public void init(RestConnector connector) throws IOException {
- final int apiPort = configuration.getPort();
- final String apiHost = configuration.getApiHost();
-
- log.debug("Binding JSON-REST API Undertow server on {}:{}", apiHost, apiPort);
-
this.connector = connector;
- connector.init(apiPort, apiHost, this::process);
-
- server = Undertow.builder().addHttpListener(apiPort, apiHost)
- .setHandler(path().addPrefixPath("/", addSecurity(new HttpHandler() {
- @Override
- public void handleRequest(final HttpServerExchange exchange) throws Exception {
- HttpString requestMethod = exchange.getRequestMethod();
- if (Methods.OPTIONS.equals(requestMethod)) {
- String allowedMethods = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH";
- //return list of allowed methods in response headers
- exchange.setStatusCode(StatusCodes.OK);
- exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt"));
- exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, 0);
- exchange.getResponseHeaders().put(Headers.ALLOW, allowedMethods);
- exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Origin"), "*");
- exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Headers"), "User-Agent, Origin, X-Requested-With, Content-Type, Accept, X-IOTA-API-Version");
- exchange.getResponseSender().close();
- return;
- }
-
- if (exchange.isInIoThread()) {
- exchange.dispatch(this);
- return;
- }
- processRequest(exchange);
- }
- }))).build();
-
+ connector.init(this::process);
connector.start();
- server.start();
- }
-
- /**
- * Sends the API response back as JSON to the requester.
- * Status code of the HTTP request is also set according to the type of response.
- *
- * - {@link ErrorResponse}: 400
- * - {@link AccessLimitedResponse}: 401
- * - {@link ExceptionResponse}: 500
- * - Default: 200
- *
- *
- * @param exchange Contains information about what the client sent to us
- * @param res The response of the API.
- * See {@link #processRequest(HttpServerExchange)}
- * and {@link #process(String, InetSocketAddress)} for the different responses in each case.
- * @param beginningTime The time when we received the request, in milliseconds.
- * This will be used to set the response duration in {@link AbstractResponse#setDuration(Integer)}
- * @throws IOException When connection to client has been lost - Currently being caught.
- */
- private void sendResponse(HttpServerExchange exchange, AbstractResponse res, long beginningTime) throws IOException {
- res.setDuration((int) (System.currentTimeMillis() - beginningTime));
- final String response = gson.toJson(res);
-
- if (res instanceof ErrorResponse) {
- // bad request or invalid parameters
- exchange.setStatusCode(400);
- } else if (res instanceof AccessLimitedResponse) {
- // API method not allowed
- exchange.setStatusCode(401);
- } else if (res instanceof ExceptionResponse) {
- // internal error
- exchange.setStatusCode(500);
- }
-
- setupResponseHeaders(exchange);
-
- ByteBuffer responseBuf = ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8));
- exchange.setResponseContentLength(responseBuf.array().length);
- StreamSinkChannel sinkChannel = exchange.getResponseChannel();
- sinkChannel.getWriteSetter().set( channel -> {
- if (responseBuf.remaining() > 0) {
- try {
- sinkChannel.write(responseBuf);
- if (responseBuf.remaining() == 0) {
- exchange.endExchange();
- }
- } catch (IOException e) {
- log.error("Lost connection to client - cannot send response");
- exchange.endExchange();
- sinkChannel.getWriteSetter().set(null);
- }
- }
- else {
- exchange.endExchange();
- }
- });
- sinkChannel.resumeWrites();
- }
-
- /**
- *
- * Processes an API HTTP request.
- * No checks have been done until now, except that it is not an OPTIONS request.
- * We can be sure that we are in a thread that allows blocking.
- *
- *
- * The request process duration is recorded.
- * During this the request gets verified. If it is incorrect, an {@link ErrorResponse} is made.
- * Otherwise it is processed in {@link #process(String, InetSocketAddress)}.
- * The result is sent back to the requester.
- *
- *
- * @param exchange Contains the data the client sent to us
- * @throws IOException If the body of this HTTP request cannot be read
- */
- private void processRequest(final HttpServerExchange exchange) throws IOException {
- final ChannelInputStream cis = new ChannelInputStream(exchange.getRequestChannel());
- exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json");
-
- final long beginningTime = System.currentTimeMillis();
- final String body = IotaIOUtils.toString(cis, StandardCharsets.UTF_8);
- AbstractResponse response;
-
- if (!exchange.getRequestHeaders().contains("X-IOTA-API-Version")) {
- response = ErrorResponse.create("Invalid API Version");
- } else if (body.length() > maxBodyLength) {
- response = ErrorResponse.create("Request too long");
- } else {
- response = process(body); //, exchange.getSourceAddress()
- }
- sendResponse(exchange, response, beginningTime);
}
-
+
/**
* Handles an API request body.
* Its returned {@link AbstractResponse} is created using the following logic
@@ -388,7 +228,7 @@ private void processRequest(final HttpServerExchange exchange) throws IOExceptio
* @throws UnsupportedEncodingException If the requestString cannot be parsed into a Map.
Currently caught and turned into a {@link ExceptionResponse}.
*/
- private AbstractResponse process(final String requestString){
+ private AbstractResponse process(final String requestString, InetAddress netAddress){
try {
// Request JSON data into map
@@ -411,12 +251,10 @@ private AbstractResponse process(final String requestString){
// Is this command allowed to be run from this request address?
// We check the remote limit API configuration.
-
- //TODO , InetSocketAddress sourceAddress process elsewhere
- /*if (configuration.getRemoteLimitApi().contains(command) &&
- !configuration.getRemoteTrustedApiHosts().contains(sourceAddress.getAddress())) {
+ if (configuration.getRemoteLimitApi().contains(command) &&
+ !configuration.getRemoteTrustedApiHosts().contains(netAddress)) {
return AccessLimitedResponse.create("COMMAND " + command + " is not available on this node");
- }*/
+ }
log.debug("# {} -> Requesting command '{}'", counter.incrementAndGet(), command);
@@ -1684,53 +1522,13 @@ private boolean validTrytes(String trytes, int length, char zeroAllowed) {
return matcher.matches();
}
- /**
- * Updates the {@link HttpServerExchange} {@link HeaderMap} with the proper response settings.
- * @param exchange Contains information about what the client has send to us
- */
- private static void setupResponseHeaders(HttpServerExchange exchange) {
- final HeaderMap headerMap = exchange.getResponseHeaders();
- headerMap.add(new HttpString("Access-Control-Allow-Origin"),"*");
- headerMap.add(new HttpString("Keep-Alive"), "timeout=500, max=100");
- }
-
- /**
- * Sets up the {@link HttpHandler} to have correct security settings.
- * Remote authentication is blocked for anyone except
- * those defined in {@link APIConfig#getRemoteAuth()} or localhost.
- * This is done with {@link BasicAuthenticationMechanism} in a {@link AuthenticationMode#PRO_ACTIVE} mode.
- *
- * @param toWrap the path handler used in creating the server.
- * @return The updated handler
- */
- private HttpHandler addSecurity(HttpHandler toWrap) {
- String credentials = configuration.getRemoteAuth();
- if (credentials == null || credentials.isEmpty()) {
- return toWrap;
- }
-
- final Map users = new HashMap<>(2);
- users.put(credentials.split(":")[0], credentials.split(":")[1].toCharArray());
-
- IdentityManager identityManager = new MapIdentityManager(users);
- HttpHandler handler = toWrap;
- handler = new AuthenticationCallHandler(handler);
- handler = new AuthenticationConstraintHandler(handler);
- final List mechanisms =
- Collections.singletonList(new BasicAuthenticationMechanism("Iota Realm"));
-
- handler = new AuthenticationMechanismsHandler(handler, mechanisms);
- handler = new SecurityInitialHandler(AuthenticationMode.PRO_ACTIVE, identityManager, handler);
- return handler;
- }
-
/**
* If a server is running, stops the server from accepting new incoming requests.
* Does not remove the instance, so the server may be restarted without having to recreate it.
*/
public void shutDown() {
- if (server != null) {
- server.stop();
+ if (connector != null) {
+ connector.stop();
}
}
diff --git a/src/main/java/com/iota/iri/service/restserver/ApiCall.java b/src/main/java/com/iota/iri/service/restserver/ApiCall.java
index 5ff43575e7..8ba7fcffcd 100644
--- a/src/main/java/com/iota/iri/service/restserver/ApiCall.java
+++ b/src/main/java/com/iota/iri/service/restserver/ApiCall.java
@@ -30,8 +30,7 @@ public AbstractResponse pre(String requestString) {
}
// Did the requester ask for a command?
- final String command = (String) request.get("command");
- if (command == null) {
+ if (getCommand() == null) {
return ErrorResponse.create("COMMAND parameter has not been specified in the request.");
}
return null;
diff --git a/src/main/java/com/iota/iri/service/restserver/ApiProcessor.java b/src/main/java/com/iota/iri/service/restserver/ApiProcessor.java
new file mode 100644
index 0000000000..c9541ba05d
--- /dev/null
+++ b/src/main/java/com/iota/iri/service/restserver/ApiProcessor.java
@@ -0,0 +1,10 @@
+package com.iota.iri.service.restserver;
+
+import java.net.InetAddress;
+
+import com.iota.iri.service.dto.AbstractResponse;
+
+public interface ApiProcessor {
+
+ AbstractResponse processFunction(String request, InetAddress inetAddress);
+}
diff --git a/src/main/java/com/iota/iri/service/restserver/RestConnector.java b/src/main/java/com/iota/iri/service/restserver/RestConnector.java
index 1f23b8c2c3..a3783ae36f 100644
--- a/src/main/java/com/iota/iri/service/restserver/RestConnector.java
+++ b/src/main/java/com/iota/iri/service/restserver/RestConnector.java
@@ -1,12 +1,8 @@
package com.iota.iri.service.restserver;
-import java.util.function.Function;
-
-import com.iota.iri.service.dto.AbstractResponse;
-
public interface RestConnector {
- void init(Function processFunction);
+ void init(ApiProcessor processFunction);
void start();
diff --git a/src/main/java/com/iota/iri/service/restserver/RestEasy.java b/src/main/java/com/iota/iri/service/restserver/RestEasy.java
index 97f162309d..f535e2c8db 100644
--- a/src/main/java/com/iota/iri/service/restserver/RestEasy.java
+++ b/src/main/java/com/iota/iri/service/restserver/RestEasy.java
@@ -1,29 +1,35 @@
package com.iota.iri.service.restserver;
+import static io.undertow.Handlers.path;
+
import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.function.Function;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
+import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.xnio.channels.StreamSinkChannel;
+import org.xnio.streams.ChannelInputStream;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.iota.iri.Iota;
import com.iota.iri.conf.APIConfig;
import com.iota.iri.service.dto.AbstractResponse;
+import com.iota.iri.service.dto.AccessLimitedResponse;
+import com.iota.iri.service.dto.ErrorResponse;
+import com.iota.iri.service.dto.ExceptionResponse;
+import com.iota.iri.utils.IotaIOUtils;
import com.iota.iri.utils.MapIdentityManager;
+import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.security.api.AuthenticationMechanism;
import io.undertow.security.api.AuthenticationMode;
@@ -35,29 +41,68 @@
import io.undertow.security.impl.BasicAuthenticationMechanism;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.util.HeaderMap;
+import io.undertow.util.Headers;
+import io.undertow.util.HttpString;
+import io.undertow.util.Methods;
+import io.undertow.util.MimeMappings;
+import io.undertow.util.StatusCodes;
+@ApplicationPath("")
public class RestEasy extends Application implements RestConnector {
private static final Logger log = LoggerFactory.getLogger(RestEasy.class);
+
+ private final Gson gson = new GsonBuilder().create();
private UndertowJaxrsServer server;
private APIConfig configuration;
private DeploymentInfo info;
+
+ private ApiProcessor processFunction;
+
+ private int maxBodyLength;
public RestEasy(APIConfig configuration) {
this.configuration = configuration;
+ maxBodyLength = configuration.getMaxBodyLength();
}
+ /**
+ * Prepares the IOTA API for usage. Until this method is called, no HTTP requests can be made.
+ * The order of loading is as follows
+ *
+ * -
+ * Read the spend addresses from the previous epoch. Used in {@link #wasAddressSpentFrom(Hash)}.
+ * This only happens if {@link APIConfig#isTestnet()} is false
+ * If reading from the previous epoch fails, a log is printed. The API will continue to initialize.
+ *
+ * -
+ * Get the {@link APIConfig} from the {@link Iota} instance,
+ * and read {@link APIConfig#getPort()} and {@link APIConfig#getApiHost()}
+ *
+ * -
+ * Builds a secure {@link Undertow} server with the port and host.
+ * If {@link APIConfig#getRemoteAuth()} is defined, remote authentication is blocked for anyone except
+ * those defined in {@link APIConfig#getRemoteAuth()} or localhost.
+ * This is done with {@link BasicAuthenticationMechanism} in a {@link AuthenticationMode#PRO_ACTIVE} mode.
+ * By default, this authentication is disabled.
+ *
+ * -
+ * Starts the server, opening it for HTTP API requests
+ *
+ *
+ */
@Override
- public void init(Function processFunction) {
+ public void init(ApiProcessor processFunction) {
log.debug("Binding JSON-REST API Undertow server on {}:{}", configuration.getApiHost(), configuration.getPort());
-
- Undertow.Builder builder = Undertow.builder()
- .addHttpListener(configuration.getPort(), configuration.getApiHost());
+ this.processFunction = processFunction;
+
+ server = new UndertowJaxrsServer();
- server = new UndertowJaxrsServer().start(builder);
info = server.undertowDeployment(Root.class);
info.setDisplayName("Iota Realm");
info.setDeploymentName("Iota Realm");
@@ -86,10 +131,40 @@ public HttpHandler wrap(HttpHandler toWrap) {
return handler;
}
});
+
+ info.addInitialHandlerChainWrapper(handler -> {
+ return Handlers.path().addPrefixPath("/", new HttpHandler() {
+ @Override
+ public void handleRequest(final HttpServerExchange exchange) throws Exception {
+ HttpString requestMethod = exchange.getRequestMethod();
+ if (Methods.OPTIONS.equals(requestMethod)) {
+ String allowedMethods = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH";
+ //return list of allowed methods in response headers
+ exchange.setStatusCode(StatusCodes.OK);
+ exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt"));
+ exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, 0);
+ exchange.getResponseHeaders().put(Headers.ALLOW, allowedMethods);
+ exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Origin"), "*");
+ exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Headers"), "User-Agent, Origin, X-Requested-With, Content-Type, Accept, X-IOTA-API-Version");
+ exchange.getResponseSender().close();
+ return;
+ }
+
+ if (exchange.isInIoThread()) {
+ exchange.dispatch(this);
+ return;
+ }
+ processRequest(exchange);
+ }
+ });
+ });
}
@Override
public void start() {
+ Undertow.Builder builder = Undertow.builder()
+ .addHttpListener(configuration.getPort(), configuration.getApiHost());
+ server.start(builder);
server.deploy(info);
}
@@ -97,4 +172,106 @@ public void start() {
public void stop() {
server.stop();
}
+
+ /**
+ * Sends the API response back as JSON to the requester.
+ * Status code of the HTTP request is also set according to the type of response.
+ *
+ * - {@link ErrorResponse}: 400
+ * - {@link AccessLimitedRprocessRequestesponse}: 401
+ * - {@link ExceptionResponse}: 500
+ * - Default: 200
+ *
+ *
+ * @param exchange Contains information about what the client sent to us
+ * @param res The response of the API.
+ * See {@link #processRequest(HttpServerExchange)}
+ * and {@link #process(String, InetSocketAddress)} for the different responses in each case.
+ * @param beginningTime The time when we received the request, in milliseconds.
+ * This will be used to set the response duration in {@link AbstractResponse#setDuration(Integer)}
+ * @throws IOException When connection to client has been lost - Currently being caught.
+ */
+ private void sendResponse(HttpServerExchange exchange, AbstractResponse res, long beginningTime) throws IOException {
+ res.setDuration((int) (System.currentTimeMillis() - beginningTime));
+ final String response = gson.toJson(res);
+
+ if (res instanceof ErrorResponse) {
+ // bad request or invalid parameters
+ exchange.setStatusCode(400);
+ } else if (res instanceof AccessLimitedResponse) {
+ // API method not allowed
+ exchange.setStatusCode(401);
+ } else if (res instanceof ExceptionResponse) {
+ // internal error
+ exchange.setStatusCode(500);
+ }
+
+ setupResponseHeaders(exchange);
+
+ ByteBuffer responseBuf = ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8));
+ exchange.setResponseContentLength(responseBuf.array().length);
+ StreamSinkChannel sinkChannel = exchange.getResponseChannel();
+ sinkChannel.getWriteSetter().set( channel -> {
+ if (responseBuf.remaining() > 0) {
+ try {
+ sinkChannel.write(responseBuf);
+ if (responseBuf.remaining() == 0) {
+ exchange.endExchange();
+ }
+ } catch (IOException e) {
+ log.error("Lost connection to client - cannot send response");
+ exchange.endExchange();
+ sinkChannel.getWriteSetter().set(null);
+ }
+ }
+ else {
+ exchange.endExchange();
+ }
+ });
+ sinkChannel.resumeWrites();
+ }
+
+ /**
+ *
+ * Processes an API HTTP request.
+ * No checks have been done until now, except that it is not an OPTIONS request.
+ * We can be sure that we are in a thread that allows blocking.
+ *
+ *
+ * The request process duration is recorded.
+ * During this the request gets verified. If it is incorrect, an {@link ErrorResponse} is made.
+ * Otherwise it is processed in {@link #process(String, InetSocketAddress)}.
+ * The result is sent back to the requester.
+ *
+ *
+ * @param exchange Contains the data the client sent to us
+ * @throws IOException If the body of this HTTP request cannot be read
+ */
+ private void processRequest(final HttpServerExchange exchange) throws IOException {
+ final ChannelInputStream cis = new ChannelInputStream(exchange.getRequestChannel());
+ exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json");
+
+ final long beginningTime = System.currentTimeMillis();
+ final String body = IotaIOUtils.toString(cis, StandardCharsets.UTF_8);
+ AbstractResponse response;
+
+ if (!exchange.getRequestHeaders().contains("X-IOTA-API-Version")) {
+ response = ErrorResponse.create("Invalid API Version");
+ } else if (body.length() > maxBodyLength) {
+ response = ErrorResponse.create("Request too long");
+ } else {
+ response = this.processFunction.processFunction(body, exchange.getSourceAddress().getAddress());
+ }
+ sendResponse(exchange, response, beginningTime);
+ }
+
+ /**
+ * Updates the {@link HttpServerExchange} {@link HeaderMap} with the proper response settings.
+ * @param exchange Contains information about what the client has send to us
+ */
+ private static void setupResponseHeaders(HttpServerExchange exchange) {
+ final HeaderMap headerMap = exchange.getResponseHeaders();
+ headerMap.add(new HttpString("Access-Control-Allow-Origin"),"*");
+ headerMap.add(new HttpString("Keep-Alive"), "timeout=500, max=100");
+ }
}
diff --git a/src/main/java/com/iota/iri/service/restserver/Root.java b/src/main/java/com/iota/iri/service/restserver/Root.java
index 52741c9efb..74618cb77e 100644
--- a/src/main/java/com/iota/iri/service/restserver/Root.java
+++ b/src/main/java/com/iota/iri/service/restserver/Root.java
@@ -3,11 +3,14 @@
import java.util.HashSet;
import java.util.Set;
-import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
public class Root extends Application {
+ public Root(ApiProcessor processFunction) {
+
+ }
+
@Override
public Set> getClasses(){
HashSet> classes = new HashSet>();
diff --git a/src/main/java/com/iota/iri/service/restserver/Test.java b/src/main/java/com/iota/iri/service/restserver/Test.java
index 9d22d0720e..1db5a6adff 100644
--- a/src/main/java/com/iota/iri/service/restserver/Test.java
+++ b/src/main/java/com/iota/iri/service/restserver/Test.java
@@ -3,12 +3,17 @@
import java.io.IOException;
import java.io.InputStream;
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@@ -19,8 +24,20 @@
@Path("")
public class Test extends ApiCall {
+ @Context()
+ private ApiProcessor requestMetadata;
+
+ @Context
+ HttpHeaders requestHeaders;
+
+ @Context
+ HttpServletRequest httpRequest;
+
+ @Context
+ Request request;
+
public Test() {
- System.out.println("Constructor");
+
}
@GET
@@ -33,7 +50,8 @@ public String ping() {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response apiCall(String request) throws JsonParseException, JsonMappingException, IOException {
- System.out.println("here1");
+ System.out.println("Hello: " + this.httpRequest);
+
AbstractResponse response = pre(request);
if (response == null) {
response = process();
diff --git a/src/test/java/com/iota/iri/service/ApiHandlerTest.java b/src/test/java/com/iota/iri/service/ApiHandlerTest.java
index 08e020156e..f5e5cbdeef 100644
--- a/src/test/java/com/iota/iri/service/ApiHandlerTest.java
+++ b/src/test/java/com/iota/iri/service/ApiHandlerTest.java
@@ -4,6 +4,7 @@
import static org.junit.Assert.fail;
import java.io.IOException;
+import java.net.InetAddress;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
@@ -39,10 +40,9 @@ public void setup() {
Mockito.when(apiconfig.getPort()).thenReturn(TestPortProvider.getPort());
Mockito.when(apiconfig.getApiHost()).thenReturn(TestPortProvider.getHost());
//Mockito.when(apiconfig.getRemoteAuth()).thenReturn("user:pass");
-
-
+
this.server = new RestEasy(apiconfig);
- this.server.init((String param) -> {
+ this.server.init((String param, InetAddress address) -> {
System.out.println("called!");
return null;
});
From 0f1b6fdb6b32e7f30a03a4f9594fb308106cebb4 Mon Sep 17 00:00:00 2001
From: Brord van Wierst
Date: Wed, 13 Mar 2019 22:40:40 +0100
Subject: [PATCH 19/67] Added resteasy tests
---
src/main/java/com/iota/iri/service/API.java | 1 -
.../restserver/{Test.java => ApiPath.java} | 7 +-
.../iri/service/restserver/ApiProcessor.java | 12 ++
.../iri/service/restserver/RestConnector.java | 17 +++
.../iota/iri/service/restserver/RestEasy.java | 23 ++-
.../restserver/{Root.java => RootPath.java} | 8 +-
.../iota/iri/service/APIIntegrationTests.java | 2 +-
.../com/iota/iri/service/ApiHandlerTest.java | 86 -----------
.../iri/service/NodeIntegrationTests.java | 2 +-
.../iri/service/restserver/RestEasyTest.java | 137 ++++++++++++++++++
10 files changed, 187 insertions(+), 108 deletions(-)
rename src/main/java/com/iota/iri/service/restserver/{Test.java => ApiPath.java} (90%)
rename src/main/java/com/iota/iri/service/restserver/{Root.java => RootPath.java} (66%)
delete mode 100644 src/test/java/com/iota/iri/service/ApiHandlerTest.java
create mode 100644 src/test/java/com/iota/iri/service/restserver/RestEasyTest.java
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index fcde71968d..34c17a9ab2 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -229,7 +229,6 @@ public void init(RestConnector connector) throws IOException {
Currently caught and turned into a {@link ExceptionResponse}.
*/
private AbstractResponse process(final String requestString, InetAddress netAddress){
-
try {
// Request JSON data into map
Map request;
diff --git a/src/main/java/com/iota/iri/service/restserver/Test.java b/src/main/java/com/iota/iri/service/restserver/ApiPath.java
similarity index 90%
rename from src/main/java/com/iota/iri/service/restserver/Test.java
rename to src/main/java/com/iota/iri/service/restserver/ApiPath.java
index 1db5a6adff..a09637edeb 100644
--- a/src/main/java/com/iota/iri/service/restserver/Test.java
+++ b/src/main/java/com/iota/iri/service/restserver/ApiPath.java
@@ -1,9 +1,7 @@
package com.iota.iri.service.restserver;
import java.io.IOException;
-import java.io.InputStream;
-import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -15,14 +13,13 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.iota.iri.service.dto.AbstractResponse;
@Path("")
-public class Test extends ApiCall {
+public class ApiPath extends ApiCall {
@Context()
private ApiProcessor requestMetadata;
@@ -36,7 +33,7 @@ public class Test extends ApiCall {
@Context
Request request;
- public Test() {
+ public ApiPath() {
}
diff --git a/src/main/java/com/iota/iri/service/restserver/ApiProcessor.java b/src/main/java/com/iota/iri/service/restserver/ApiProcessor.java
index c9541ba05d..985628f833 100644
--- a/src/main/java/com/iota/iri/service/restserver/ApiProcessor.java
+++ b/src/main/java/com/iota/iri/service/restserver/ApiProcessor.java
@@ -4,7 +4,19 @@
import com.iota.iri.service.dto.AbstractResponse;
+/**
+ *
+ * Interface that defines the API call handling
+ *
+ */
public interface ApiProcessor {
+ /**
+ * Processes the request according to the
+ *
+ * @param request the request body, unprocessed
+ * @param inetAddress the address from the API caller
+ * @return The response for this request
+ */
AbstractResponse processFunction(String request, InetAddress inetAddress);
}
diff --git a/src/main/java/com/iota/iri/service/restserver/RestConnector.java b/src/main/java/com/iota/iri/service/restserver/RestConnector.java
index a3783ae36f..815b6e10e6 100644
--- a/src/main/java/com/iota/iri/service/restserver/RestConnector.java
+++ b/src/main/java/com/iota/iri/service/restserver/RestConnector.java
@@ -1,10 +1,27 @@
package com.iota.iri.service.restserver;
+/**
+ *
+ * Connector interface which contains logic for starting and stopping a REST server
+ *
+ */
public interface RestConnector {
+ /**
+ * Initializes the REST server.
+ *
+ * @param processFunction the function/class we call after dependency specific handling
+ */
void init(ApiProcessor processFunction);
+ /**
+ * Starts the server.
+ * If {@link #init(ApiProcessor)} has not been called, nothing happens
+ */
void start();
+ /**
+ * Stops the REST server, so no more API calls can be made
+ */
void stop();
}
diff --git a/src/main/java/com/iota/iri/service/restserver/RestEasy.java b/src/main/java/com/iota/iri/service/restserver/RestEasy.java
index f535e2c8db..a04d97a875 100644
--- a/src/main/java/com/iota/iri/service/restserver/RestEasy.java
+++ b/src/main/java/com/iota/iri/service/restserver/RestEasy.java
@@ -1,7 +1,5 @@
package com.iota.iri.service.restserver;
-import static io.undertow.Handlers.path;
-
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
@@ -103,7 +101,7 @@ public void init(ApiProcessor processFunction) {
server = new UndertowJaxrsServer();
- info = server.undertowDeployment(Root.class);
+ info = server.undertowDeployment(RootPath.class);
info.setDisplayName("Iota Realm");
info.setDeploymentName("Iota Realm");
info.setContextPath("/");
@@ -132,7 +130,7 @@ public HttpHandler wrap(HttpHandler toWrap) {
}
});
- info.addInitialHandlerChainWrapper(handler -> {
+ info.addInnerHandlerChainWrapper(handler -> {
return Handlers.path().addPrefixPath("/", new HttpHandler() {
@Override
public void handleRequest(final HttpServerExchange exchange) throws Exception {
@@ -160,14 +158,22 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
});
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void start() {
- Undertow.Builder builder = Undertow.builder()
- .addHttpListener(configuration.getPort(), configuration.getApiHost());
- server.start(builder);
- server.deploy(info);
+ if (info != null) {
+ Undertow.Builder builder = Undertow.builder()
+ .addHttpListener(configuration.getPort(), configuration.getApiHost());
+ server.start(builder);
+ server.deploy(info);
+ }
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void stop() {
server.stop();
@@ -262,6 +268,7 @@ private void processRequest(final HttpServerExchange exchange) throws IOExceptio
} else {
response = this.processFunction.processFunction(body, exchange.getSourceAddress().getAddress());
}
+
sendResponse(exchange, response, beginningTime);
}
diff --git a/src/main/java/com/iota/iri/service/restserver/Root.java b/src/main/java/com/iota/iri/service/restserver/RootPath.java
similarity index 66%
rename from src/main/java/com/iota/iri/service/restserver/Root.java
rename to src/main/java/com/iota/iri/service/restserver/RootPath.java
index 74618cb77e..c315452594 100644
--- a/src/main/java/com/iota/iri/service/restserver/Root.java
+++ b/src/main/java/com/iota/iri/service/restserver/RootPath.java
@@ -5,16 +5,12 @@
import javax.ws.rs.core.Application;
-public class Root extends Application {
-
- public Root(ApiProcessor processFunction) {
-
- }
+public class RootPath extends Application {
@Override
public Set> getClasses(){
HashSet> classes = new HashSet>();
- classes.add(Test.class);
+ classes.add(ApiPath.class);
return classes;
}
}
diff --git a/src/test/java/com/iota/iri/service/APIIntegrationTests.java b/src/test/java/com/iota/iri/service/APIIntegrationTests.java
index 54ebdf2473..a2e14debea 100644
--- a/src/test/java/com/iota/iri/service/APIIntegrationTests.java
+++ b/src/test/java/com/iota/iri/service/APIIntegrationTests.java
@@ -101,7 +101,7 @@ public static void setUp() throws Exception {
try {
iota.init();
iota.snapshotProvider.getInitialSnapshot().setTimestamp(0);
- api.init(new RestEasy());
+ api.init(new RestEasy(configuration));
ixi.init(IXIConfig.IXI_DIR);
} catch (final Exception e) {
log.error("Exception during IOTA node initialisation: ", e);
diff --git a/src/test/java/com/iota/iri/service/ApiHandlerTest.java b/src/test/java/com/iota/iri/service/ApiHandlerTest.java
deleted file mode 100644
index f5e5cbdeef..0000000000
--- a/src/test/java/com/iota/iri/service/ApiHandlerTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.iota.iri.service;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.net.InetAddress;
-
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import org.jboss.resteasy.test.TestPortProvider;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import com.iota.iri.conf.APIConfig;
-import com.iota.iri.service.restserver.RestEasy;
-
-public class ApiHandlerTest {
- @Rule
- public MockitoRule mockitoRule = MockitoJUnit.rule();
-
- @Mock
- private APIConfig apiconfig;
-
-
- private RestEasy server;
-
- @Before
- public void setup() {
- Mockito.when(apiconfig.getPort()).thenReturn(TestPortProvider.getPort());
- Mockito.when(apiconfig.getApiHost()).thenReturn(TestPortProvider.getHost());
- //Mockito.when(apiconfig.getRemoteAuth()).thenReturn("user:pass");
-
- this.server = new RestEasy(apiconfig);
- this.server.init((String param, InetAddress address) -> {
- System.out.println("called!");
- return null;
- });
- this.server.start();
- }
-
- @After
- public void tearDown() {
- //this.server.stop();
- }
-
- @Test
- public void test() {
- Client client = ClientBuilder.newClient();
- String val = client.target(TestPortProvider.generateURL("/ping"))
- .request().get(String.class);
- assertEquals("pong", val);
- }
-
- @Test
- public void test2() {
- Client client = ClientBuilder.newClient();
- String val = client.target(TestPortProvider.generateURL("/ping"))
- .request().get(String.class);
- String val2 = client.target(TestPortProvider.generateURL("/ping"))
- .request().get(String.class);
-
- String val3 = client.target(TestPortProvider.generateURL("/ping"))
- .request().get(String.class);
- System.out.println(val);
- }
-
- @Test
- public void nodeInfo() {
- Client client = ClientBuilder.newClient();
- String jsonString = "{\"command\": \"getNodeInfo\"}";
- Response val = client.target(TestPortProvider.generateURL("/"))
- .request().post(Entity.entity(jsonString, MediaType.APPLICATION_JSON));
- System.out.println(val.getEntity());
- }
-}
diff --git a/src/test/java/com/iota/iri/service/NodeIntegrationTests.java b/src/test/java/com/iota/iri/service/NodeIntegrationTests.java
index c8e92a0b5e..16af51b88f 100644
--- a/src/test/java/com/iota/iri/service/NodeIntegrationTests.java
+++ b/src/test/java/com/iota/iri/service/NodeIntegrationTests.java
@@ -60,7 +60,7 @@ public void testGetsSolid() throws Exception {
iotaNodes[i].tipsSelector, iotaNodes[i].tipsViewModel, iotaNodes[i].transactionValidator,
iotaNodes[i].latestMilestoneTracker);
- api[i].init(new RestEasy());
+ api[i].init(new RestEasy(iotaNodes[i].configuration));
}
Node.uri("udp://localhost:14701").ifPresent(uri -> iotaNodes[0].node.addNeighbor(iotaNodes[0].node.newNeighbor(uri, true)));
//Node.uri("udp://localhost:14700").ifPresent(uri -> iotaNodes[1].node.addNeighbor(iotaNodes[1].node.newNeighbor(uri, true)));
diff --git a/src/test/java/com/iota/iri/service/restserver/RestEasyTest.java b/src/test/java/com/iota/iri/service/restserver/RestEasyTest.java
new file mode 100644
index 0000000000..01d68e8c67
--- /dev/null
+++ b/src/test/java/com/iota/iri/service/restserver/RestEasyTest.java
@@ -0,0 +1,137 @@
+package com.iota.iri.service.restserver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.Base64;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.bouncycastle.util.encoders.Base64Encoder;
+import org.jboss.resteasy.test.TestPortProvider;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import com.iota.iri.conf.APIConfig;
+import com.iota.iri.service.dto.ErrorResponse;
+import com.iota.iri.service.dto.GetNodeInfoResponse;
+import com.iota.iri.service.restserver.RestEasy;
+
+public class RestEasyTest {
+
+ private static final String USER_PASS = "user:pass";
+
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private APIConfig apiconfig;
+
+
+ private RestEasy server;
+
+ @Before
+ public void setup() {
+ Mockito.when(apiconfig.getPort()).thenReturn(TestPortProvider.getPort());
+ Mockito.when(apiconfig.getApiHost()).thenReturn(TestPortProvider.getHost());
+ Mockito.when(apiconfig.getMaxBodyLength()).thenReturn(Integer.MAX_VALUE);
+ }
+
+ @After
+ public void tearDown() {
+ this.server.stop();
+ }
+
+ @Test
+ public void nodeInfoMissingApiVersion() {
+ this.server = new RestEasy(apiconfig);
+ this.server.init((String param, InetAddress address) -> {
+ return GetNodeInfoResponse.createEmptyResponse();
+ });
+ this.server.start();
+
+ Client client = ClientBuilder.newClient();
+ String jsonString = "{\"command\": \"getNodeInfo\"}";
+ Response val = client.target(TestPortProvider.generateURL("/"))
+ .request()
+ .post(Entity.entity(jsonString, MediaType.APPLICATION_JSON));
+ ErrorResponse response = val.readEntity(ErrorResponse.class);
+ assertEquals("Invalid API Version", response.getError());
+ }
+
+ @Test
+ public void nodeInfoValid() {
+ this.server = new RestEasy(apiconfig);
+ this.server.init((String param, InetAddress address) -> {
+ return GetNodeInfoResponse.createEmptyResponse();
+ });
+ this.server.start();
+
+ Client client = ClientBuilder.newClient();
+ String jsonString = "{\"command\": \"getNodeInfo\"}";
+ Response val = client.target(TestPortProvider.generateURL("/"))
+ .request()
+ .header("X-IOTA-API-Version", "1")
+ .post(Entity.entity(jsonString, MediaType.APPLICATION_JSON));
+
+ GetNodeInfoResponse response = val.readEntity(GetNodeInfoResponse.class);
+ assertNotNull(response);
+ }
+
+ @Test
+ public void notAllowed() {
+ Mockito.when(apiconfig.getRemoteAuth()).thenReturn(USER_PASS);
+
+ this.server = new RestEasy(apiconfig);
+ this.server.init((String param, InetAddress address) -> {
+ return GetNodeInfoResponse.createEmptyResponse();
+ });
+ this.server.start();
+
+ Client client = ClientBuilder.newClient();
+ String jsonString = "{\"command\": \"getNodeInfo\"}";
+ Response val = client.target(TestPortProvider.generateURL("/"))
+ .request()
+ .header("X-IOTA-API-Version", "1")
+ .post(Entity.entity(jsonString, MediaType.APPLICATION_JSON));
+
+ assertEquals(Response.Status.UNAUTHORIZED, val.getStatusInfo());
+ }
+
+ @Test
+ public void allowed() {
+ Mockito.when(apiconfig.getRemoteAuth()).thenReturn(USER_PASS);
+
+ this.server = new RestEasy(apiconfig);
+ this.server.init((String param, InetAddress address) -> {
+ return GetNodeInfoResponse.createEmptyResponse();
+ });
+ this.server.start();
+
+ Client client = ClientBuilder.newClient();
+ String jsonString = "{\"command\": \"getNodeInfo\"}";
+
+ String encoded = Base64.getEncoder().encodeToString(USER_PASS.getBytes());
+
+ Response val = client.target(TestPortProvider.generateURL("/"))
+ .request()
+ .header("X-IOTA-API-Version", "1")
+ .header("Authorization", "Basic " + encoded)
+ .post(Entity.entity(jsonString, MediaType.APPLICATION_JSON));
+
+ assertEquals(Response.Status.OK, val.getStatusInfo());
+ }
+}
From fde4252432a235f8d3c0440726742ca38f7fd3b0 Mon Sep 17 00:00:00 2001
From: Brord van Wierst
Date: Thu, 14 Mar 2019 00:18:54 +0100
Subject: [PATCH 20/67] Added functional routing, TODO error handling
---
src/main/java/com/iota/iri/service/API.java | 314 +++++++++++---------
1 file changed, 181 insertions(+), 133 deletions(-)
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index 34c17a9ab2..d589cf1d90 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -58,6 +58,7 @@
import java.security.InvalidAlgorithmParameterException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -141,6 +142,28 @@ public class API {
private Pattern trytesPattern = Pattern.compile("[9A-Z]*");
+ private final Map, AbstractResponse>> commandRoute
+ = new HashMap, AbstractResponse>>() {{
+ put("addNeighbors", addNeighbors());
+ put("attachToTangle", attachToTangle());
+ put("broadcastTransactions", broadcastTransactions());
+ put("findTransactions", findTransactions());
+ put("getBalances", getBalances());
+ put("getInclusionStates", getInclusionStates());
+ put("getNeighbors", getNeighbors());
+ put("getNodeInfo", getNodeInfo());
+ put("getTips", getTips());
+ put("getTransactionsToApprove", getTransactionsToApprove());
+ put("getTrytes", getTrytes());
+ put("interruptAttachingToTangle", interruptAttachingToTangle());
+ put("removeNeighbors", removeNeighbors());
+ put("storeTransactions", storeTransactions());
+ put("getMissingTransactions", getMissingTransactions());
+ put("checkConsistency", checkConsistency());
+ put("wereAddressesSpentFrom", wereAddressesSpentFrom());
+ }};
+
+
private RestConnector connector;
/**
@@ -257,141 +280,21 @@ private AbstractResponse process(final String requestString, InetAddress netAddr
log.debug("# {} -> Requesting command '{}'", counter.incrementAndGet(), command);
- switch (command) {
- case "storeMessage": {
- if (!testNet) {
- return AccessLimitedResponse.create("COMMAND storeMessage is only available on testnet");
- }
-
- if (!request.containsKey("address") || !request.containsKey("message")) {
- return ErrorResponse.create("Invalid params");
- }
-
- String address = (String) request.get("address");
- String message = (String) request.get("message");
- return storeMessageStatement(address, message);
- }
-
- case "addNeighbors": {
- List uris = getParameterAsList(request,"uris",0);
- log.debug("Invoking 'addNeighbors' with {}", uris);
- return addNeighborsStatement(uris);
- }
- case "attachToTangle": {
- final Hash trunkTransaction = HashFactory.TRANSACTION.create(getParameterAsStringAndValidate(request,"trunkTransaction", HASH_SIZE));
- final Hash branchTransaction = HashFactory.TRANSACTION.create(getParameterAsStringAndValidate(request,"branchTransaction", HASH_SIZE));
- final int minWeightMagnitude = getParameterAsInt(request,"minWeightMagnitude");
-
- final List trytes = getParameterAsList(request,"trytes", TRYTES_SIZE);
-
- List elements = attachToTangleStatement(trunkTransaction, branchTransaction, minWeightMagnitude, trytes);
- return AttachToTangleResponse.create(elements);
- }
- case "broadcastTransactions": {
- final List trytes = getParameterAsList(request,"trytes", TRYTES_SIZE);
- broadcastTransactionsStatement(trytes);
- return AbstractResponse.createEmptyResponse();
- }
- case "findTransactions": {
- return findTransactionsStatement(request);
- }
- case "getBalances": {
- final List addresses = getParameterAsList(request,"addresses", HASH_SIZE);
- final List tips = request.containsKey("tips") ?
- getParameterAsList(request,"tips", HASH_SIZE):
- null;
- final int threshold = getParameterAsInt(request, "threshold");
- return getBalancesStatement(addresses, tips, threshold);
- }
- case "getInclusionStates": {
- if (invalidSubtangleStatus()) {
- return ErrorResponse.create(INVALID_SUBTANGLE);
- }
- final List transactions = getParameterAsList(request,"transactions", HASH_SIZE);
- final List tips = getParameterAsList(request,"tips", HASH_SIZE);
-
- return getInclusionStatesStatement(transactions, tips);
- }
- case "getNeighbors": {
- return getNeighborsStatement();
- }
- case "getNodeInfo": {
- return getNodeInfoStatement();
- }
- case "getNodeAPIConfiguration": {
- return getNodeAPIConfigurationStatement();
- }
- case "getTips": {
- return getTipsStatement();
- }
- case "getTransactionsToApprove": {
- Optional reference = request.containsKey("reference") ?
- Optional.of(HashFactory.TRANSACTION.create(getParameterAsStringAndValidate(request,"reference", HASH_SIZE)))
- : Optional.empty();
- int depth = getParameterAsInt(request, "depth");
-
- return getTransactionsToApproveStatement(depth, reference);
- }
- case "getTrytes": {
- final List hashes = getParameterAsList(request,"hashes", HASH_SIZE);
- return getTrytesStatement(hashes);
- }
-
- case "interruptAttachingToTangle": {
- return interruptAttachingToTangleStatement();
- }
- case "removeNeighbors": {
- List uris = getParameterAsList(request,"uris",0);
- log.debug("Invoking 'removeNeighbors' with {}", uris);
- return removeNeighborsStatement(uris);
- }
-
- case "storeTransactions": {
- try {
- final List trytes = getParameterAsList(request,"trytes", TRYTES_SIZE);
- storeTransactionsStatement(trytes);
- return AbstractResponse.createEmptyResponse();
- } catch (RuntimeException e) {
- //transaction not valid
- return ErrorResponse.create("Invalid trytes input");
- }
- }
- case "getMissingTransactions": {
- //TransactionRequester.instance().rescanTransactionsToRequest();
- synchronized (transactionRequester) {
- List missingTx = Arrays.stream(transactionRequester.getRequestedTransactions())
- .map(Hash::toString)
- .collect(Collectors.toList());
- return GetTipsResponse.create(missingTx);
- }
- }
- case "checkConsistency": {
- if (invalidSubtangleStatus()) {
- return ErrorResponse.create(INVALID_SUBTANGLE);
- }
- final List transactions = getParameterAsList(request,"tails", HASH_SIZE);
- return checkConsistencyStatement(transactions);
- }
- case "wereAddressesSpentFrom": {
- final List addresses = getParameterAsList(request,"addresses", HASH_SIZE);
- return wereAddressesSpentFromStatement(addresses);
- }
- default: {
- AbstractResponse response = ixi.processCommand(command, request);
- return response == null ?
- ErrorResponse.create("Command [" + command + "] is unknown") :
- response;
+ if (commandRoute.containsKey(command)) {
+ return commandRoute.get(command).apply(request);
+ } else {
+ AbstractResponse response = ixi.processCommand(command, request);
+ if (response == null) {
+ return ErrorResponse.create("Command [" + command + "] is unknown");
+ } else {
+ return response;
}
}
-
- } catch (final ValidationException e) {
- log.info("API Validation failed: " + e.getLocalizedMessage());
- return ErrorResponse.create(e.getLocalizedMessage());
- } catch (final InvalidAlgorithmParameterException e) {
- log.info("API InvalidAlgorithmParameter passed: " + e.getLocalizedMessage());
- return ErrorResponse.create(e.getLocalizedMessage());
- } catch (final Exception e) {
- log.error("API Exception: {}", e.getLocalizedMessage(), e);
+ } catch (final IllegalArgumentException e) {
+ log.error("API Validation failed: " + e.getLocalizedMessage());
+ return ExceptionResponse.create(e.getLocalizedMessage());
+ } catch (final IllegalStateException e) {
+ log.error("API Exception: " + e.getLocalizedMessage());
return ExceptionResponse.create(e.getLocalizedMessage());
}
}
@@ -1609,4 +1512,149 @@ private synchronized AbstractResponse storeMessageStatement(String address, Stri
broadcastTransactionsStatement(powResult);
return AbstractResponse.createEmptyResponse();
}
+
+ //
+ // FUNCTIONAL COMMAND ROUTES
+ //
+ private Function