diff --git a/Dockerfile b/Dockerfile
index e99687467d..c7f5575201 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,7 +5,7 @@ RUN mvn clean package
FROM openjdk:jre-slim
WORKDIR /iri
-COPY --from=builder /iri/target/iri-1.4.1.1.jar iri.jar
+COPY --from=builder /iri/target/iri-1.4.1.2.jar iri.jar
COPY logback.xml /iri
VOLUME /iri
diff --git a/changelog.txt b/changelog.txt
index 3641853e71..4e45eb697b 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,9 @@
+1.4.1.2
+ - Fixes DB over-storing transactions due to concurrency
+ - Upgrade to RocksDB 5.7.3
+ - Upgrades findTransaction logic to match spec (Multi-field intersection, Input field limits)
+ - Unified API input validation
+
1.4.1.1
- Fixes CORS issue introduced with last release
- attachToTangle no longer overwrites tag field unconditionally
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
index c0782d9fef..50ebf9c6de 100644
--- a/dependency-reduced-pom.xml
+++ b/dependency-reduced-pom.xml
@@ -4,7 +4,7 @@
com.iota
iri
IRI
- 1.4.1
+ 1.4.1.2
IOTA Reference Implementation
scm:git:git://github.com/iotaledger/iri.git
@@ -61,7 +61,7 @@
enforce
- verify
+ package
enforce
@@ -74,7 +74,7 @@
org.slf4j:slf4j-api:1.7.25:jar:null:compile:da76ca59f6a57ee3102f8f9bd9cee742973efa8a
ch.qos.logback:logback-classic:1.2.3:jar:null:compile:7c4f3c474fb2c041d8028740440937705ebb473a
commons-io:commons-io:2.5:jar:null:compile:2852e6e05fbb95076fc091f6d1780f1f8fe35e0f
- org.rocksdb:rocksdbjni:5.1.4:jar:null:compile:1924f10f010ddf724a41d2ce6903a706bda7f1b6
+ org.rocksdb:rocksdbjni:5.7.3:jar:null:compile:421b44ad957a2b6cce5adedc204db551831b553d
com.google.code.gson:gson:2.8.1:jar:null:compile:02a8e0aa38a2e21cb39e2f5a7d6704cbdc941da0
io.undertow:undertow-core:${undertow.version}:jar:null:compile:e5764e5017bfe8c2dd421dc80035e5165501bfda
io.undertow:undertow-servlet:${undertow.version}:jar:null:compile:0e2850a558e70a2d72d9a3e782c7b6fde2d9f1c7
diff --git a/pom.xml b/pom.xml
index 04eba6f128..662ce2f9c2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.iota
iri
- 1.4.1.1
+ 1.4.1.2
IRI
IOTA Reference Implementation
@@ -76,7 +76,7 @@
org.rocksdb
rocksdbjni
- 5.1.4
+ 5.7.3
@@ -212,7 +212,7 @@
enforce
- verify
+ package
enforce
@@ -232,7 +232,7 @@
org.slf4j:slf4j-api:1.7.25:jar:null:compile:da76ca59f6a57ee3102f8f9bd9cee742973efa8a
ch.qos.logback:logback-classic:1.2.3:jar:null:compile:7c4f3c474fb2c041d8028740440937705ebb473a
commons-io:commons-io:2.5:jar:null:compile:2852e6e05fbb95076fc091f6d1780f1f8fe35e0f
- org.rocksdb:rocksdbjni:5.1.4:jar:null:compile:1924f10f010ddf724a41d2ce6903a706bda7f1b6
+ org.rocksdb:rocksdbjni:5.7.3:jar:null:compile:421b44ad957a2b6cce5adedc204db551831b553d
com.google.code.gson:gson:2.8.1:jar:null:compile:02a8e0aa38a2e21cb39e2f5a7d6704cbdc941da0
io.undertow:undertow-core:${undertow.version}:jar:null:compile:e5764e5017bfe8c2dd421dc80035e5165501bfda
io.undertow:undertow-servlet:${undertow.version}:jar:null:compile:0e2850a558e70a2d72d9a3e782c7b6fde2d9f1c7
diff --git a/src/main/java/com/iota/iri/IRI.java b/src/main/java/com/iota/iri/IRI.java
index c81cd0a00e..5daaba63c9 100644
--- a/src/main/java/com/iota/iri/IRI.java
+++ b/src/main/java/com/iota/iri/IRI.java
@@ -40,7 +40,7 @@ public class IRI {
public static final String MAINNET_NAME = "IRI";
public static final String TESTNET_NAME = "IRI Testnet";
- public static final String VERSION = "1.4.1.1";
+ public static final String VERSION = "1.4.1.2";
public static Iota iota;
public static API api;
public static IXI ixi;
diff --git a/src/main/java/com/iota/iri/conf/Configuration.java b/src/main/java/com/iota/iri/conf/Configuration.java
index 848f2f692a..245da0de55 100644
--- a/src/main/java/com/iota/iri/conf/Configuration.java
+++ b/src/main/java/com/iota/iri/conf/Configuration.java
@@ -55,7 +55,9 @@ public enum DefaultConfSettings {
MIN_RANDOM_WALKS,
MAX_RANDOM_WALKS,
MAX_FIND_TRANSACTIONS,
+ MAX_REQUESTS_LIST,
MAX_GET_TRYTES,
+ MAX_BODY_LENGTH,
MAX_DEPTH,
MAINNET_MWM,
TESTNET_MWM,
@@ -104,7 +106,9 @@ public enum DefaultConfSettings {
conf.put(DefaultConfSettings.MAX_DEPTH.name(), "15");
conf.put(DefaultConfSettings.MAX_FIND_TRANSACTIONS.name(), "100000");
+ conf.put(DefaultConfSettings.MAX_REQUESTS_LIST.name(), "1000");
conf.put(DefaultConfSettings.MAX_GET_TRYTES.name(), "10000");
+ conf.put(DefaultConfSettings.MAX_BODY_LENGTH.name(), "1000000");
conf.put(DefaultConfSettings.ZMQ_ENABLED.name(), "false");
conf.put(DefaultConfSettings.ZMQ_PORT.name(), "5556");
conf.put(DefaultConfSettings.ZMQ_IPC.name(), "ipc://iri");
diff --git a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java
index e11118a5a9..4d9c8049c2 100644
--- a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java
+++ b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java
@@ -55,19 +55,22 @@ public class TransactionViewModel {
private int[] trits;
public int weightMagnitude;
- public static TransactionViewModel find(final Tangle tangle, byte[] hash) throws Exception {
- TransactionViewModel transactionViewModel = new TransactionViewModel((Transaction) tangle.find(Transaction.class, hash), new Hash(hash));
- if(!transactionViewModel.getHash().equals(Hash.NULL_HASH) && !transactionViewModel.transaction.parsed) {
+ public static void fillMetadata(final Tangle tangle, TransactionViewModel transactionViewModel) throws Exception {
+ if (transactionViewModel.getHash().equals(Hash.NULL_HASH)) { return; }
+ if(transactionViewModel.getType() == FILLED_SLOT && !transactionViewModel.transaction.parsed) {
tangle.saveBatch(transactionViewModel.getMetadataSaveBatch());
}
+ }
+
+ public static TransactionViewModel find(final Tangle tangle, byte[] hash) throws Exception {
+ TransactionViewModel transactionViewModel = new TransactionViewModel((Transaction) tangle.find(Transaction.class, hash), new Hash(hash));
+ fillMetadata(tangle, transactionViewModel);
return transactionViewModel;
}
public static TransactionViewModel fromHash(final Tangle tangle, final Hash hash) throws Exception {
TransactionViewModel transactionViewModel = new TransactionViewModel((Transaction) tangle.load(Transaction.class, hash), hash);
- if(!transactionViewModel.getHash().equals(Hash.NULL_HASH) && !transactionViewModel.transaction.parsed) {
- tangle.saveBatch(transactionViewModel.getMetadataSaveBatch());
- }
+ fillMetadata(tangle, transactionViewModel);
return transactionViewModel;
}
@@ -118,7 +121,7 @@ public boolean update(final Tangle tangle, String item) throws Exception {
if(hash.equals(Hash.NULL_HASH)) {
return false;
}
- return tangle.update(transaction, getHash(), item);
+ return tangle.update(transaction, hash, item);
}
public TransactionViewModel getBranchTransaction(final Tangle tangle) throws Exception {
@@ -149,16 +152,16 @@ public synchronized int[] trits() {
}
public void delete(Tangle tangle) throws Exception {
- tangle.delete(Transaction.class, getHash());
+ tangle.delete(Transaction.class, hash);
}
public List> getMetadataSaveBatch() throws Exception {
List> hashesList = new ArrayList<>();
- hashesList.add(new Pair<>(getAddressHash(), new Address(getHash())));
- hashesList.add(new Pair<>(getBundleHash(), new Bundle(getHash())));
- hashesList.add(new Pair<>(getBranchTransactionHash(), new Approvee(getHash())));
- hashesList.add(new Pair<>(getTrunkTransactionHash(), new Approvee(getHash())));
- hashesList.add(new Pair<>(getObsoleteTagValue(), new Tag(getHash())));
+ hashesList.add(new Pair<>(getAddressHash(), new Address(hash)));
+ hashesList.add(new Pair<>(getBundleHash(), new Bundle(hash)));
+ hashesList.add(new Pair<>(getBranchTransactionHash(), new Approvee(hash)));
+ hashesList.add(new Pair<>(getTrunkTransactionHash(), new Approvee(hash)));
+ hashesList.add(new Pair<>(getObsoleteTagValue(), new Tag(hash)));
setAttachmentData();
setMetadata();
return hashesList;
@@ -168,7 +171,7 @@ public List> getSaveBatch() throws Exception {
List> hashesList = new ArrayList<>();
hashesList.addAll(getMetadataSaveBatch());
getBytes();
- hashesList.add(new Pair<>(getHash(), transaction));
+ hashesList.add(new Pair<>(hash, transaction));
return hashesList;
}
@@ -182,7 +185,7 @@ public static TransactionViewModel first(Tangle tangle) throws Exception {
}
public TransactionViewModel next(Tangle tangle) throws Exception {
- Pair transactionPair = tangle.next(Transaction.class, getHash());
+ Pair transactionPair = tangle.next(Transaction.class, hash);
if(transactionPair != null && transactionPair.hi != null) {
return new TransactionViewModel((Transaction) transactionPair.hi, (Hash) transactionPair.low);
}
@@ -190,10 +193,15 @@ public TransactionViewModel next(Tangle tangle) throws Exception {
}
public boolean store(Tangle tangle) throws Exception {
- if(!exists(tangle, getHash()) && !getHash().equals(Hash.NULL_HASH)) {
- return tangle.saveBatch(getSaveBatch());
+ if (hash.equals(Hash.NULL_HASH) || exists(tangle, hash)) {
+ return false;
}
- return false;
+
+ List> batch = getSaveBatch();
+ if (exists(tangle, hash)) {
+ return false;
+ }
+ return tangle.saveBatch(batch);
}
public ApproveeViewModel getApprovers(Tangle tangle) throws Exception {
@@ -388,26 +396,26 @@ private void updateHeight(long height) throws Exception {
}
public void updateHeights(final Tangle tangle) throws Exception {
- TransactionViewModel transaction = this, trunk = this.getTrunkTransaction(tangle);
+ TransactionViewModel transactionVM = this, trunk = this.getTrunkTransaction(tangle);
Stack transactionViewModels = new Stack<>();
- transactionViewModels.push(transaction.getHash());
+ transactionViewModels.push(transactionVM.getHash());
while(trunk.getHeight() == 0 && trunk.getType() != PREFILLED_SLOT && !trunk.getHash().equals(Hash.NULL_HASH)) {
- transaction = trunk;
- trunk = transaction.getTrunkTransaction(tangle);
- transactionViewModels.push(transaction.getHash());
+ transactionVM = trunk;
+ trunk = transactionVM.getTrunkTransaction(tangle);
+ transactionViewModels.push(transactionVM.getHash());
}
while(transactionViewModels.size() != 0) {
- transaction = TransactionViewModel.fromHash(tangle, transactionViewModels.pop());
- if(trunk.getHash().equals(Hash.NULL_HASH) && trunk.getHeight() == 0 && !transaction.getHash().equals(Hash.NULL_HASH)) {
- transaction.updateHeight(1L);
- transaction.update(tangle, "height");
- } else if ( trunk.getType() != PREFILLED_SLOT && transaction.getHeight() == 0){
- transaction.updateHeight(1 + trunk.getHeight());
- transaction.update(tangle, "height");
+ transactionVM = TransactionViewModel.fromHash(tangle, transactionViewModels.pop());
+ if(trunk.getHash().equals(Hash.NULL_HASH) && trunk.getHeight() == 0 && !transactionVM.getHash().equals(Hash.NULL_HASH)) {
+ transactionVM.updateHeight(1L);
+ transactionVM.update(tangle, "height");
+ } else if ( trunk.getType() != PREFILLED_SLOT && transactionVM.getHeight() == 0){
+ transactionVM.updateHeight(1 + trunk.getHeight());
+ transactionVM.update(tangle, "height");
} else {
break;
}
- trunk = transaction;
+ trunk = transactionVM;
}
}
diff --git a/src/main/java/com/iota/iri/model/Transaction.java b/src/main/java/com/iota/iri/model/Transaction.java
index fd6f4566a7..26647976cd 100644
--- a/src/main/java/com/iota/iri/model/Transaction.java
+++ b/src/main/java/com/iota/iri/model/Transaction.java
@@ -30,7 +30,7 @@ public class Transaction implements Persistable {
public long attachmentTimestampUpperBound;
public int validity = 0;
- public int type = 1;
+ public int type = TransactionViewModel.PREFILLED_SLOT;
public long arrivalTime = 0;
//public boolean confirmed = false;
@@ -48,6 +48,7 @@ public void read(byte[] bytes) {
if(bytes != null) {
this.bytes = new byte[SIZE];
System.arraycopy(bytes, 0, this.bytes, 0, SIZE);
+ this.type = TransactionViewModel.FILLED_SLOT;
}
}
diff --git a/src/main/java/com/iota/iri/network/Node.java b/src/main/java/com/iota/iri/network/Node.java
index 331e7342c0..5b4d5a8587 100644
--- a/src/main/java/com/iota/iri/network/Node.java
+++ b/src/main/java/com/iota/iri/network/Node.java
@@ -82,9 +82,10 @@ public class Node {
- private final LRUHashCache recentSeenHashes = new LRUHashCache(5000);
- private final LRUByteCache recentSeenBytes = new LRUByteCache(15000);
+ private final LRUCache recentSeenHashes = new LRUCache<>(5000);
+ private final LRUCache recentSeenBytes = new LRUCache<>(15000);
+ private boolean debug;
private static AtomicLong recentSeenBytesMissCount = new AtomicLong(0L);
private static AtomicLong recentSeenBytesHitCount = new AtomicLong(0L);
@@ -120,6 +121,7 @@ public void init() throws Exception {
P_REPLY_RANDOM_TIP = configuration.doubling(Configuration.DefaultConfSettings.P_REPLY_RANDOM_TIP.name());
P_PROPAGATE_REQUEST = configuration.doubling(Configuration.DefaultConfSettings.P_PROPAGATE_REQUEST.name());
sendLimit = (long) ( (configuration.doubling(Configuration.DefaultConfSettings.SEND_LIMIT.name()) * 1000000) / (TRANSACTION_PACKET_SIZE * 8) );
+ debug = configuration.booling(Configuration.DefaultConfSettings.DEBUG);
Arrays.stream(configuration.string(Configuration.DefaultConfSettings.NEIGHBORS).split(" ")).distinct()
.filter(s -> !s.isEmpty()).map(Node::uri).map(Optional::get).peek(u -> {
@@ -152,7 +154,7 @@ public DatagramSocket getUdpSocket() {
}
private final Map neighborIpCache = new HashMap<>();
-
+
private Runnable spawnNeighborDNSRefresherThread() {
return () -> {
@@ -212,32 +214,33 @@ private Runnable spawnNeighborDNSRefresherThread() {
}
private Optional checkIp(final String dnsName) {
-
+
if (StringUtils.isEmpty(dnsName)) {
return Optional.empty();
}
-
+
InetAddress inetAddress;
try {
inetAddress = java.net.InetAddress.getByName(dnsName);
} catch (UnknownHostException e) {
return Optional.empty();
}
-
+
final String hostAddress = inetAddress.getHostAddress();
-
+
if (StringUtils.equals(dnsName, hostAddress)) { // not a DNS...
return Optional.empty();
}
-
+
return Optional.of(hostAddress);
}
public void preProcessReceivedData(byte[] receivedData, SocketAddress senderAddress, String uriScheme) {
TransactionViewModel receivedTransactionViewModel = null;
Hash receivedTransactionHash = null;
-
boolean addressMatch = false;
+ boolean cached = false;
+
for (final Neighbor neighbor : getNeighbors()) {
addressMatch = neighbor.matches(senderAddress);
if (addressMatch) {
@@ -251,42 +254,29 @@ public void preProcessReceivedData(byte[] receivedData, SocketAddress senderAddr
//Transaction bytes
- //final int byteHash = ByteBuffer.wrap(receivedData, 0, TransactionViewModel.SIZE).hashCode();
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(receivedData, 0, TransactionViewModel.SIZE);
ByteBuffer byteHash = ByteBuffer.wrap(digest.digest());
-
+
//check if cached
synchronized (recentSeenBytes) {
- receivedTransactionHash = recentSeenBytes.get(byteHash);
+ cached = (receivedTransactionHash = recentSeenBytes.get(byteHash)) != null;
}
- if (receivedTransactionHash == null) {
+ if (!cached) {
//if not, then validate
receivedTransactionViewModel = TransactionValidator.validate(receivedData, transactionValidator.getMinWeightMagnitude());
receivedTransactionHash = receivedTransactionViewModel.getHash();
- //if valid - add to receive queue (receivedTransactionViewModel, neighbor)
- addReceivedDataToReceiveQueue(receivedTransactionViewModel, neighbor);
-
synchronized (recentSeenBytes) {
- recentSeenBytes.set(byteHash, receivedTransactionHash);
+ recentSeenBytes.put(byteHash, receivedTransactionHash);
}
- recentSeenBytesMissCount.getAndIncrement();
+ //if valid - add to receive queue (receivedTransactionViewModel, neighbor)
+ addReceivedDataToReceiveQueue(receivedTransactionViewModel, neighbor);
}
- else {
- recentSeenBytesHitCount.getAndIncrement();
- }
- if (((recentSeenBytesMissCount.get() + recentSeenBytesHitCount.get()) % 50000L == 0)) {
- log.info("RecentSeenBytes cache hit/miss ratio: "+recentSeenBytesHitCount.get()+"/"+recentSeenBytesMissCount.get());
- messageQ.publish("hmr %d/%d",recentSeenBytesHitCount.get(), recentSeenBytesMissCount.get());
- recentSeenBytesMissCount.set(0L);
- recentSeenBytesHitCount.set(0L);
- }
-
} catch (NoSuchAlgorithmException e) {
log.error("MessageDigest: "+e);
} catch (final RuntimeException e) {
@@ -307,6 +297,25 @@ public void preProcessReceivedData(byte[] receivedData, SocketAddress senderAddr
addReceivedDataToReplyQueue(requestedHash, neighbor);
+ //recentSeenBytes statistics
+
+ if (debug) {
+ long hitCount, missCount;
+ if(cached) {
+ hitCount = recentSeenBytesHitCount.incrementAndGet();
+ missCount = recentSeenBytesMissCount.get();
+ } else {
+ hitCount = recentSeenBytesHitCount.get();
+ missCount = recentSeenBytesMissCount.getAndIncrement();
+ }
+ if (((hitCount + missCount) % 50000L == 0)) {
+ log.info("RecentSeenBytes cache hit/miss ratio: " + hitCount + "/" + missCount);
+ messageQ.publish("hmr %d/%d", hitCount, missCount);
+ recentSeenBytesMissCount.set(0L);
+ recentSeenBytesHitCount.set(0L);
+ }
+ }
+
break;
}
}
@@ -334,7 +343,7 @@ public void preProcessReceivedData(byte[] receivedData, SocketAddress senderAddr
log.error("Invalid URI string: " + uriString);
}
}
- else {
+ else {
if ( rejectedAddresses.size() > 20 ) {
// Avoid ever growing list in case of an attack.
rejectedAddresses.clear();
@@ -342,7 +351,7 @@ public void preProcessReceivedData(byte[] receivedData, SocketAddress senderAddr
else if ( rejectedAddresses.add(uriString) ) {
messageQ.publish("rntn %s %s", uriString, String.valueOf(maxPeersAllowed));
log.info("Refused non-tethered neighbor: " + uriString +
- " (max-peers = "+ String.valueOf(maxPeersAllowed) + ")");
+ " (max-peers = "+ String.valueOf(maxPeersAllowed) + ")");
}
}
}
@@ -385,18 +394,14 @@ public void processReceivedData(TransactionViewModel receivedTransactionViewMode
//store new transaction
try {
- //first check if Hash seen recently
+ //first check if Hash seen recently & update seen.
synchronized (recentSeenHashes) {
- cached = recentSeenHashes.get(receivedTransactionViewModel.getHash());
+ cached = (recentSeenHashes.put(receivedTransactionViewModel.getHash(), true)) != null;
+
}
- if (cached) {
- stored = false;
- } else {
- //if not, store tx. & update recentSeenHashes
+ if (!cached) {
+ //if not, store tx.
stored = receivedTransactionViewModel.store(tangle);
- synchronized (recentSeenHashes) {
- recentSeenHashes.set(receivedTransactionViewModel.getHash(), true);
- }
}
} catch (Exception e) {
log.error("Error accessing persistence store.", e);
@@ -410,7 +415,6 @@ public void processReceivedData(TransactionViewModel receivedTransactionViewMode
transactionValidator.updateStatus(receivedTransactionViewModel);
receivedTransactionViewModel.updateSender(neighbor.getAddress().toString());
receivedTransactionViewModel.update(tangle, "arrivalTime|sender");
-
} catch (Exception e) {
log.error("Error updating transactions.", e);
}
@@ -546,7 +550,7 @@ private Runnable spawnTipRequesterThread() {
System.arraycopy(transactionViewModel.getBytes(), 0, tipRequestingPacket.getData(), 0, TransactionViewModel.SIZE);
System.arraycopy(transactionViewModel.getHash().bytes(), 0, tipRequestingPacket.getData(), TransactionViewModel.SIZE,
TransactionRequester.REQUEST_HASH_SIZE);
- //Hash.SIZE_IN_BYTES);
+ //Hash.SIZE_IN_BYTES);
neighbors.forEach(n -> n.send(tipRequestingPacket));
@@ -697,7 +701,7 @@ public Neighbor newNeighbor(final URI uri, boolean isConfigured) {
}
return neighbor;
}
-
+
public static Optional uri(final String uri) {
try {
return Optional.of(new URI(uri));
@@ -731,67 +735,35 @@ public int getReplyQueueSize() {
return replyQueue.size();
}
- public class LRUHashCache {
-
- private int capacity;
- private LinkedHashMap map;
-
- public LRUHashCache(int capacity) {
- this.capacity = capacity;
- this.map = new LinkedHashMap<>();
- }
-
- public Boolean get(Hash key) {
- Boolean value = this.map.get(key);
- if (value == null) {
- value = false;
- } else {
- this.set(key, value);
- }
- return value;
- }
-
- public void set(Hash key, Boolean value) {
- if (this.map.containsKey(key)) {
- this.map.remove(key);
- } else if (this.map.size() == this.capacity) {
- Iterator it = this.map.keySet().iterator();
- it.next();
- it.remove();
- }
- map.put(key, value);
- }
- }
-
- public class LRUByteCache {
+ public class LRUCache {
private int capacity;
- private LinkedHashMap map;
+ private LinkedHashMap map;
- public LRUByteCache(int capacity) {
+ public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new LinkedHashMap<>();
}
- public Hash get(ByteBuffer key) {
- Hash value = this.map.get(key);
+ public V get(K key) {
+ V value = this.map.get(key);
if (value == null) {
value = null;
} else {
- this.set(key, value);
+ this.put(key, value);
}
return value;
}
- public void set(ByteBuffer key, Hash value) {
+ public V put(K key, V value) {
if (this.map.containsKey(key)) {
this.map.remove(key);
} else if (this.map.size() == this.capacity) {
- Iterator it = this.map.keySet().iterator();
+ Iterator it = this.map.keySet().iterator();
it.next();
it.remove();
}
- map.put(key, value);
+ return map.put(key, value);
}
}
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index 85f244353a..e2f84ce929 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -76,6 +76,8 @@
import io.undertow.util.MimeMappings;
import io.undertow.util.StatusCodes;
+import javax.xml.bind.ValidationException;
+
@SuppressWarnings("unchecked")
public class API {
@@ -99,7 +101,11 @@ public class API {
private final int minRandomWalks;
private final int maxRandomWalks;
private final int maxFindTxs;
+ private final int maxRequestList;
private final int maxGetTrytes;
+ private final int maxBodyLength;
+ private final static String overMaxErrorMessage = "Could not complete request";
+ private final static String invalidParams = "Invalid parameters";
private final static char ZERO_LENGTH_ALLOWED = 'Y';
private final static char ZERO_LENGTH_NOT_ALLOWED = 'N';
@@ -111,15 +117,16 @@ public API(Iota instance, IXI ixi) {
minRandomWalks = instance.configuration.integer(DefaultConfSettings.MIN_RANDOM_WALKS);
maxRandomWalks = instance.configuration.integer(DefaultConfSettings.MAX_RANDOM_WALKS);
maxFindTxs = instance.configuration.integer(DefaultConfSettings.MAX_FIND_TRANSACTIONS);
+ maxRequestList = instance.configuration.integer(DefaultConfSettings.MAX_REQUESTS_LIST);
maxGetTrytes = instance.configuration.integer(DefaultConfSettings.MAX_GET_TRYTES);
-
+ maxBodyLength = instance.configuration.integer(DefaultConfSettings.MAX_BODY_LENGTH);
}
public void init() throws IOException {
final int apiPort = instance.configuration.integer(DefaultConfSettings.PORT);
final String apiHost = instance.configuration.string(DefaultConfSettings.API_HOST);
- log.debug("Binding JSON-REST API Undertown server on {}:{}", apiHost, apiPort);
+ log.debug("Binding JSON-REST API Undertow server on {}:{}", apiHost, apiPort);
server = Undertow.builder().addHttpListener(apiPort, apiHost)
.setHandler(path().addPrefixPath("/", addSecurity(new HttpHandler() {
@@ -156,10 +163,13 @@ private void processRequest(final HttpServerExchange exchange) throws IOExceptio
final long beginningTime = System.currentTimeMillis();
final String body = IOUtils.toString(cis, StandardCharsets.UTF_8);
final AbstractResponse response;
- if (exchange.getRequestHeaders().contains("X-IOTA-API-Version")) {
- response = process(body, exchange.getSourceAddress());
- } else {
+
+ 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);
}
@@ -188,102 +198,41 @@ private AbstractResponse process(final String requestString, InetSocketAddress s
switch (command) {
case "addNeighbors": {
- if (!request.containsKey("uris")) {
- return ErrorResponse.create("Invalid params");
- }
- final List uris = (List) request.get("uris");
+ List uris = getParameterAsList(request,"uris",0);
log.debug("Invoking 'addNeighbors' with {}", uris);
-
return addNeighborsStatement(uris);
}
case "attachToTangle": {
- if (!request.containsKey("trunkTransaction") ||
- !request.containsKey("branchTransaction") ||
- !request.containsKey("minWeightMagnitude") ||
- !request.containsKey("trytes")) {
- return ErrorResponse.create("Invalid params");
- }
- if (!validTrytes((String)request.get("trunkTransaction"), HASH_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid trunkTransaction hash");
- }
- if (!validTrytes((String)request.get("branchTransaction"), HASH_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid branchTransaction hash");
- }
- final Hash trunkTransaction = new Hash((String) request.get("trunkTransaction"));
- final Hash branchTransaction = new Hash((String) request.get("branchTransaction"));
- final int minWeightMagnitude;
- try {
- minWeightMagnitude = ((Double) request.get("minWeightMagnitude")).intValue();
- } catch (ClassCastException e) {
- return ErrorResponse.create("Invalid minWeightMagnitude input");
- }
- final List trytes = (List) request.get("trytes");
- for (final String tryt : trytes) {
- if (!validTrytes(tryt, TRYTES_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid trytes input");
- }
- }
+ final Hash trunkTransaction = new Hash(getParameterAsStringAndValidate(request,"trunkTransaction", HASH_SIZE));
+ final Hash branchTransaction = new Hash(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": {
- if (!request.containsKey("trytes")) {
- return ErrorResponse.create("Invalid params");
- }
- final List trytes = (List) request.get("trytes");
- for (final String tryt : trytes) {
- if (!validTrytes(tryt, TRYTES_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid trytes input");
- }
- }
- broadcastTransactionStatement(trytes);
+ broadcastTransactionStatement(getParameterAsList(request,"trytes", TRYTES_SIZE));
return AbstractResponse.createEmptyResponse();
}
case "findTransactions": {
- if (!request.containsKey("bundles") &&
- !request.containsKey("addresses") &&
- !request.containsKey("tags") &&
- !request.containsKey("approvees")) {
- return ErrorResponse.create("Invalid params");
- }
return findTransactionStatement(request);
}
case "getBalances": {
- if (!request.containsKey("addresses") || !request.containsKey("threshold")) {
- return ErrorResponse.create("Invalid params");
- }
- final List addresses = (List) request.get("addresses");
- for (final String address : addresses) {
- if (!validTrytes(address, HASH_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid addresses input");
- }
- }
- final int threshold = ((Double) request.get("threshold")).intValue();
+ final List addresses = getParameterAsList(request,"addresses", HASH_SIZE);
+ final int threshold = getParameterAsInt(request, "threshold");
return getBalancesStatement(addresses, threshold);
}
case "getInclusionStates": {
- if (!request.containsKey("transactions") || !request.containsKey("tips")) {
- return ErrorResponse.create("Invalid params");
- }
- final List trans = (List) request.get("transactions");
- final List tps = (List) request.get("tips");
-
- for (final String tx : trans) {
- if (!validTrytes(tx, HASH_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid transactions input");
- }
- }
- for (final String ti : tps) {
- if (!validTrytes(ti, HASH_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid tips input");
- }
- }
-
if (invalidSubtangleStatus()) {
return ErrorResponse
.create("This operations cannot be executed: The subtangle has not been updated yet.");
}
- return getNewInclusionStateStatement(trans, tps);
+ final List transactions = getParameterAsList(request,"transactions", HASH_SIZE);
+ final List tips = getParameterAsList(request,"tips", HASH_SIZE);
+
+ return getNewInclusionStateStatement(transactions, tips);
}
case "getNeighbors": {
return getNeighborsStatement();
@@ -302,23 +251,18 @@ private AbstractResponse process(final String requestString, InetSocketAddress s
return getTipsStatement();
}
case "getTransactionsToApprove": {
- final int depth;
- try {
- depth = ((Double) request.get("depth")).intValue();
- } catch (ClassCastException e) {
- return ErrorResponse.create("Invalid depth input");
- }
- final Object referenceObj = request.get("reference");
- final String reference = referenceObj == null? null: (String) referenceObj;
if (invalidSubtangleStatus()) {
return ErrorResponse
.create("This operations cannot be executed: The subtangle has not been updated yet.");
}
- final Object numWalksObj = request.get("numWalks");
- int numWalks = numWalksObj == null? 1 : ((Double) numWalksObj).intValue();
+
+ final int depth = getParameterAsInt(request, "depth");
+ final String reference = request.containsKey("reference") ? getParameterAsStringAndValidate(request,"reference", HASH_SIZE) : null;
+ int numWalks = request.containsKey("numWalks") ? getParameterAsInt(request,"numWalks") : 1;
if(numWalks < minRandomWalks) {
numWalks = minRandomWalks;
}
+
final Hash[] tips = getTransactionToApproveStatement(depth, reference, numWalks);
if(tips == null) {
return ErrorResponse.create("The subtangle is not solid");
@@ -326,18 +270,7 @@ private AbstractResponse process(final String requestString, InetSocketAddress s
return GetTransactionsToApproveResponse.create(tips[0], tips[1]);
}
case "getTrytes": {
- if (!request.containsKey("hashes")) {
- return ErrorResponse.create("Invalid params");
- }
- final List hashes = (List) request.get("hashes");
- if (hashes == null) {
- return ErrorResponse.create("Wrong arguments");
- }
- for (final String hash : hashes) {
- if (!validTrytes(hash, HASH_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid hash input");
- }
- }
+ final List hashes = getParameterAsList(request,"hashes", HASH_SIZE);
return getTrytesStatement(hashes);
}
@@ -346,23 +279,16 @@ private AbstractResponse process(final String requestString, InetSocketAddress s
return AbstractResponse.createEmptyResponse();
}
case "removeNeighbors": {
- if (!request.containsKey("uris")) {
- return ErrorResponse.create("Invalid params");
- }
- final List uris = (List) request.get("uris");
+ List uris = getParameterAsList(request,"uris",0);
log.debug("Invoking 'removeNeighbors' with {}", uris);
return removeNeighborsStatement(uris);
}
case "storeTransactions": {
- if (!request.containsKey("trytes")) {
- return ErrorResponse.create("Invalid params");
- }
- List trytes = (List) request.get("trytes");
- log.debug("Invoking 'storeTransactions' with {}", trytes);
- if(storeTransactionStatement(trytes)) {
- return AbstractResponse.createEmptyResponse();
- } else {
+ try {
+ storeTransactionStatement(getParameterAsList(request,"trytes", TRYTES_SIZE));
+ } catch (RuntimeException e) {
+ //transaction not valid
return ErrorResponse.create("Invalid trytes input");
}
}
@@ -383,22 +309,73 @@ private AbstractResponse process(final String requestString, InetSocketAddress s
}
}
+ } catch (final ValidationException e) {
+ log.info("API Validation failed: " + e.getLocalizedMessage());
+ return ErrorResponse.create(e.getLocalizedMessage());
} catch (final Exception e) {
log.error("API Exception: ", e);
return ExceptionResponse.create(e.getLocalizedMessage());
}
}
+ private int getParameterAsInt(Map request, String paramName) throws ValidationException {
+ validateParamExists(request, paramName);
+ final int result;
+ try {
+ result = ((Double) request.get(paramName)).intValue();
+ } catch (ClassCastException e) {
+ throw new ValidationException("Invalid " + paramName + " input");
+ }
+ return result;
+ }
+
+ private String getParameterAsStringAndValidate(Map request, String paramName, int size) throws ValidationException {
+ validateParamExists(request, paramName);
+ String result = (String) request.get(paramName);
+ validateTrytes(paramName, size, result);
+ return result;
+ }
+
+ private void validateTrytes(String paramName, int size, String result) throws ValidationException {
+ if (!validTrytes(result,size,ZERO_LENGTH_NOT_ALLOWED)) {
+ throw new ValidationException("Invalid " + paramName + " input");
+ }
+ }
+
+ private void validateParamExists(Map request, String paramName) throws ValidationException {
+ if (!request.containsKey(paramName)) {
+ throw new ValidationException(invalidParams);
+ }
+ }
+
+ private List getParameterAsList(Map request, String paramName, int size) throws ValidationException {
+ validateParamExists(request, paramName);
+ final List paramList = (List) request.get(paramName);
+ if (paramList.size() > maxRequestList) {
+ throw new ValidationException(overMaxErrorMessage);
+ }
+
+ if (size > 0) {
+ //validate
+ for (final String param : paramList) {
+ validateTrytes(paramName, size, param);
+ }
+ }
+
+ return paramList;
+
+ }
+
public boolean invalidSubtangleStatus() {
return (instance.milestone.latestSolidSubtangleMilestoneIndex == Milestone.MILESTONE_START_INDEX);
}
private AbstractResponse removeNeighborsStatement(List uris) throws URISyntaxException {
final AtomicInteger numberOfRemovedNeighbors = new AtomicInteger(0);
-
+
for (final String uriString : uris) {
final URI uri = new URI(uriString);
-
+
if ("udp".equals(uri.getScheme()) || "tcp".equals(uri.getScheme())) {
log.info("Removing neighbor: "+uriString);
if (instance.node.removeNeighbor(uri,true)) {
@@ -421,7 +398,7 @@ private synchronized AbstractResponse getTrytesStatement(List hashes) th
}
}
if (elements.size() > maxGetTrytes){
- return ErrorResponse.create("Could not complete request");
+ return ErrorResponse.create(overMaxErrorMessage);
}
return GetTrytesResponse.create(elements);
}
@@ -433,7 +410,7 @@ public static int getCounter_getTxToApprove() {
public static void incCounter_getTxToApprove() {
counter_getTxToApprove++;
}
-
+
private static long ellapsedTime_getTxToApprove = 0L;
public static long getEllapsedTime_getTxToApprove() {
return ellapsedTime_getTxToApprove;
@@ -476,12 +453,8 @@ private synchronized AbstractResponse getTipsStatement() throws Exception {
return GetTipsResponse.create(instance.tipsViewModel.getTips().stream().map(Hash::toString).collect(Collectors.toList()));
}
- public boolean storeTransactionStatement(final List trys) throws Exception {
+ public void storeTransactionStatement(final List trys) throws Exception {
for (final String trytes : trys) {
-
- if (!validTrytes(trytes, TRYTES_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return false;
- }
final TransactionViewModel transactionViewModel = instance.transactionValidator.validate(Converter.trits(trytes),
instance.transactionValidator.getMinWeightMagnitude());
if(transactionViewModel.store(instance.tangle)) {
@@ -491,7 +464,6 @@ public boolean storeTransactionStatement(final List trys) throws Excepti
transactionViewModel.update(instance.tangle, "sender");
}
}
- return true;
}
private AbstractResponse getNeighborsStatement() {
@@ -594,71 +566,70 @@ private boolean exhaustiveSearchWithinIndex(Queue nonAnalyzedTransactions,
}
private synchronized AbstractResponse findTransactionStatement(final Map request) throws Exception {
- final Set bundlesTransactions = new HashSet<>();
+ final Set foundTransactions = new HashSet<>();
+ boolean containsKey = false;
+ final Set bundlesTransactions = new HashSet<>();
if (request.containsKey("bundles")) {
- for (final String bundle : (List) request.get("bundles")) {
- if (!validTrytes(bundle, HASH_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid bundle hash");
- }
+ final HashSet bundles = getParameterAsSet(request,"bundles",HASH_SIZE);
+ for (final String bundle : bundles) {
bundlesTransactions.addAll(BundleViewModel.load(instance.tangle, new Hash(bundle)).getHashes());
}
+ foundTransactions.addAll(bundlesTransactions);
+ containsKey = true;
}
final Set addressesTransactions = new HashSet<>();
if (request.containsKey("addresses")) {
- final List addresses = (List) request.get("addresses");
- log.debug("Searching: {}", addresses.stream().reduce((a, b) -> a += ',' + b));
-
+ final HashSet addresses = getParameterAsSet(request,"addresses",HASH_SIZE);
for (final String address : addresses) {
- if (!validTrytes(address, HASH_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid address input");
- }
addressesTransactions.addAll(AddressViewModel.load(instance.tangle, new Hash(address)).getHashes());
}
+ foundTransactions.addAll(addressesTransactions);
+ containsKey = true;
}
final Set tagsTransactions = new HashSet<>();
if (request.containsKey("tags")) {
- for (String tag : (List) request.get("tags")) {
- if (!validTrytes(tag,tag.length(), ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid tag input");
- }
- while (tag.length() < Curl.HASH_LENGTH / Converter.NUMBER_OF_TRITS_IN_A_TRYTE) {
- tag += Converter.TRYTE_ALPHABET.charAt(0);
- }
+ final HashSet tags = getParameterAsSet(request,"tags",0);
+ for (String tag : tags) {
+ tag = padTag(tag);
tagsTransactions.addAll(TagViewModel.load(instance.tangle, new Hash(tag)).getHashes());
}
+ foundTransactions.addAll(tagsTransactions);
+ containsKey = true;
}
final Set approveeTransactions = new HashSet<>();
if (request.containsKey("approvees")) {
- for (final String approvee : (List) request.get("approvees")) {
- if (!validTrytes(approvee,HASH_SIZE, ZERO_LENGTH_NOT_ALLOWED)) {
- return ErrorResponse.create("Invalid approvees hash");
- }
+ final HashSet approvees = getParameterAsSet(request,"approvees",HASH_SIZE);
+ for (final String approvee : approvees) {
approveeTransactions.addAll(TransactionViewModel.fromHash(instance.tangle, new Hash(approvee)).getApprovers(instance.tangle).getHashes());
}
+ foundTransactions.addAll(approveeTransactions);
+ containsKey = true;
}
- // need refactoring
- final Set foundTransactions = bundlesTransactions.isEmpty() ? (addressesTransactions.isEmpty()
- ? (tagsTransactions.isEmpty()
- ? (approveeTransactions.isEmpty() ? new HashSet<>() : approveeTransactions) : tagsTransactions)
- : addressesTransactions) : bundlesTransactions;
+ if (!containsKey) {
+ throw new ValidationException(invalidParams);
+ }
- if (!addressesTransactions.isEmpty()) {
+ //Using multiple of these input fields returns the intersection of the values.
+ if (request.containsKey("bundles")) {
+ foundTransactions.retainAll(bundlesTransactions);
+ }
+ if (request.containsKey("addresses")) {
foundTransactions.retainAll(addressesTransactions);
}
- if (!tagsTransactions.isEmpty()) {
+ if (request.containsKey("tags")) {
foundTransactions.retainAll(tagsTransactions);
}
- if (!approveeTransactions.isEmpty()) {
+ if (request.containsKey("approvees")) {
foundTransactions.retainAll(approveeTransactions);
}
if (foundTransactions.size() > maxFindTxs){
- return ErrorResponse.create("Could not complete request");
+ return ErrorResponse.create(overMaxErrorMessage);
}
final List elements = foundTransactions.stream()
@@ -668,6 +639,25 @@ private synchronized AbstractResponse findTransactionStatement(final Map getParameterAsSet(Map request, String paramName, int size) throws ValidationException {
+
+ HashSet result = getParameterAsList(request,paramName,size).stream().collect(Collectors.toCollection(HashSet::new));
+ if (result.contains(Hash.NULL_HASH.toString())) {
+ throw new ValidationException("Invalid " + paramName + " input");
+ }
+ return result;
+ }
+
public void broadcastTransactionStatement(final List trytes2) {
for (final String tryte : trytes2) {
//validate PoW - throws exception if invalid
@@ -702,32 +692,31 @@ private AbstractResponse getBalancesStatement(final List addrss, final i
final int milestoneIndex = instance.milestone.latestSolidSubtangleMilestoneIndex;
- Set analyzedTips = new HashSet<>();
+ Set analyzedTips = new HashSet<>();
- final Queue nonAnalyzedTransactions = new LinkedList<>(Collections.singleton(milestone));
- //Collections.singleton(StorageTransactions.instance().transactionPointer(milestone.value())));
- Hash hash;
- while ((hash = nonAnalyzedTransactions.poll()) != null) {
+ final Queue nonAnalyzedTransactions = new LinkedList<>(Collections.singleton(milestone));
+ Hash hash;
+ while ((hash = nonAnalyzedTransactions.poll()) != null) {
- if (analyzedTips.add(hash)) {
+ if (analyzedTips.add(hash)) {
- final TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(instance.tangle, hash);
+ final TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(instance.tangle, hash);
- if(transactionViewModel.snapshotIndex() == 0 || transactionViewModel.snapshotIndex() > index) {
- if (transactionViewModel.value() != 0) {
+ if(transactionViewModel.snapshotIndex() == 0 || transactionViewModel.snapshotIndex() > index) {
+ if (transactionViewModel.value() != 0) {
- final Hash address = transactionViewModel.getAddressHash();
- final Long balance = balances.get(address);
- if (balance != null) {
+ final Hash address = transactionViewModel.getAddressHash();
+ final Long balance = balances.get(address);
+ if (balance != null) {
- balances.put(address, balance + transactionViewModel.value());
- }
+ balances.put(address, balance + transactionViewModel.value());
}
- nonAnalyzedTransactions.offer(transactionViewModel.getTrunkTransactionHash());
- nonAnalyzedTransactions.offer(transactionViewModel.getBranchTransactionHash());
}
+ nonAnalyzedTransactions.offer(transactionViewModel.getTrunkTransactionHash());
+ nonAnalyzedTransactions.offer(transactionViewModel.getBranchTransactionHash());
}
}
+ }
final List elements = addresses.stream().map(address -> balances.get(address).toString())
.collect(Collectors.toCollection(LinkedList::new));
@@ -820,7 +809,7 @@ private AbstractResponse addNeighborsStatement(final List uris) throws U
int numberOfAddedNeighbors = 0;
for (final String uriString : uris) {
final URI uri = new URI(uriString);
-
+
if ("udp".equals(uri.getScheme()) || "tcp".equals(uri.getScheme())) {
log.info("Adding neighbor: "+uriString);
// 3rd parameter true if tcp, 4th parameter true (configured tethering)
@@ -884,11 +873,11 @@ private void sendResponse(final HttpServerExchange exchange, final AbstractRespo
sinkChannel.resumeWrites();
}
- private boolean validTrytes(String trytes, int minimalLength, char zeroAllowed) {
+ private boolean validTrytes(String trytes, int length, char zeroAllowed) {
if (trytes.length() == 0 && zeroAllowed == ZERO_LENGTH_ALLOWED) {
return true;
}
- if (trytes.length() < minimalLength) {
+ if (trytes.length() != length) {
return false;
}
Matcher matcher = trytesPattern.matcher(trytes);
@@ -900,7 +889,7 @@ private static void setupResponseHeaders(final HttpServerExchange exchange) {
headerMap.add(new HttpString("Access-Control-Allow-Origin"),"*");
headerMap.add(new HttpString("Keep-Alive"), "timeout=500, max=100");
}
-
+
private HttpHandler addSecurity(final HttpHandler toWrap) {
String credentials = instance.configuration.string(DefaultConfSettings.REMOTE_AUTH);
if(credentials == null || credentials.isEmpty()) return toWrap;
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 ec2c06b922..795cb0c874 100644
--- a/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java
+++ b/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java
@@ -197,11 +197,8 @@ public Persistable get(Class> model, Indexable index) throws Exception {
@Override
public boolean mayExist(Class> model, Indexable index) throws Exception {
- /* for version 5.4.5
ColumnFamilyHandle handle = classTreeMap.get().get(model);
return db.keyMayExist(handle, index.bytes(), new StringBuilder());
- */
- return db.keyMayExist(classTreeMap.get().get(model), index.bytes(), new StringBuffer());
}
@Override
diff --git a/src/test/java/com/iota/iri/APIIntegrationTests.java b/src/test/java/com/iota/iri/APIIntegrationTests.java
index 8a6afdc238..3d1777eb89 100644
--- a/src/test/java/com/iota/iri/APIIntegrationTests.java
+++ b/src/test/java/com/iota/iri/APIIntegrationTests.java
@@ -15,7 +15,7 @@ public class APIIntegrationTests {
private static final Gson gson = new GsonBuilder().create();
static {
- RestAssured.port = 14700;
+ RestAssured.port = 14265;
}
/**
@@ -31,7 +31,7 @@ public void shouldTestGetNodeInfo() {
request.put("command", "getNodeInfo");
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -70,7 +70,7 @@ public void shouldTestGetNeighbors() {
request.put("command", "getNeighbors");
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -96,7 +96,7 @@ public void shouldTestAddNeighbors() {
request.put("command", "addNeighbors");
request.put("uris", new String [] {"udp://8.8.8.8:14265", "udp://8.8.8.5:14265"} );
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -119,7 +119,7 @@ public void shouldTestRemoveNeighbors() {
request.put("command", "removeNeighbors");
request.put("uris", new String [] {"udp://8.8.8.8:14265", "udp://8.8.8.5:14265"} );
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -142,7 +142,7 @@ public void shouldTestGetTips() {
request.put("command", "getTips");
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -165,7 +165,7 @@ public void shouldTestFindTransactions() {
request.put("command", "findTransactions");
request.put("addresses", new String [] {"RVORZ9SIIP9RCYMREUIXXVPQIPHVCNPQ9HZWYKFWYWZRE9JQKG9REPKIASHUUECPSQO9JT9XNMVKWYGVAZETAIRPTM"});
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -188,7 +188,7 @@ public void shouldTestGetTrytes() {
request.put("command", "getTrytes");
request.put("hashes", new String [] {"OAATQS9VQLSXCLDJVJJVYUGONXAXOFMJOZNSYWRZSWECMXAQQURHQBJNLD9IOFEPGZEPEMPXCIVRX9999"});
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -212,7 +212,7 @@ public void shouldTestGetInclusionStates() {
request.put("transactions", new String [] {"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"});
request.put("tips", new String [] {"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"});
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -237,7 +237,7 @@ public void shouldTestGetBalances() {
request.put("threshold", 100);
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -261,7 +261,7 @@ public void shouldTestGetTransactionsToApprove() {
request.put("depth", 27);
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -286,7 +286,7 @@ public void shouldTestBroadcastTransactions() {
request.put("trytes", new String[] {"BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999"});
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").
@@ -309,7 +309,7 @@ public void shouldTestStoreTransactions() {
request.put("trytes", new String[] {"BYSWEAUTWXHXZ9YBZISEK9LUHWGMHXCGEVNZHRLUWQFCUSDXZHOFHWHL9MQPVJXXZLIXPXPXF9KYEREFSKCPKYIIKPZVLHUTDFQKKVVBBN9ATTLPCNPJDWDEVIYYLGPZGCWXOBDXMLJC9VO9QXTTBLAXTTBFUAROYEGQIVB9MJWJKXJMCUPTWAUGFZBTZCSJVRBGMYXTVBDDS9MYUJCPZ9YDWWQNIPUAIJXXSNLKUBSCOIJPCLEFPOXFJREXQCUVUMKSDOVQGGHRNILCO9GNCLWFM9APMNMWYASHXQAYBEXF9QRIHIBHYEJOYHRQJAOKAQ9AJJFQ9WEIWIJOTZATIBOXQLBMIJU9PCGBLVDDVFP9CFFSXTDUXMEGOOFXWRTLFGV9XXMYWEMGQEEEDBTIJ9OJOXFAPFQXCDAXOUDMLVYRMRLUDBETOLRJQAEDDLNVIRQJUBZBO9CCFDHIX9MSQCWYAXJVWHCUPTRSXJDESISQPRKZAFKFRULCGVRSBLVFOPEYLEE99JD9SEBALQINPDAZHFAB9RNBH9AZWIJOTLBZVIEJIAYGMC9AZGNFWGRSWAXTYSXVROVNKCOQQIWGPNQZKHUNODGYADPYLZZZUQRTJRTODOUKAOITNOMWNGHJBBA99QUMBHRENGBHTH9KHUAOXBVIVDVYYZMSEYSJWIOGGXZVRGN999EEGQMCOYVJQRIRROMPCQBLDYIGQO9AMORPYFSSUGACOJXGAQSPDY9YWRRPESNXXBDQ9OZOXVIOMLGTSWAMKMTDRSPGJKGBXQIVNRJRFRYEZ9VJDLHIKPSKMYC9YEGHFDS9SGVDHRIXBEMLFIINOHVPXIFAZCJKBHVMQZEVWCOSNWQRDYWVAIBLSCBGESJUIBWZECPUCAYAWMTQKRMCHONIPKJYYTEGZCJYCT9ABRWTJLRQXKMWY9GWZMHYZNWPXULNZAPVQLPMYQZCYNEPOCGOHBJUZLZDPIXVHLDMQYJUUBEDXXPXFLNRGIPWBRNQQZJSGSJTTYHIGGFAWJVXWL9THTPWOOHTNQWCNYOYZXALHAZXVMIZE9WMQUDCHDJMIBWKTYH9AC9AFOT9DPCADCV9ZWUTE9QNOMSZPTZDJLJZCJGHXUNBJFUBJWQUEZDMHXGBPTNSPZBR9TGSKVOHMOQSWPGFLSWNESFKSAZY9HHERAXALZCABFYPOVLAHMIHVDBGKUMDXC9WHHTIRYHZVWNXSVQUWCR9M9RAGMFEZZKZ9XEOQGOSLFQCHHOKLDSA9QCMDGCGMRYJZLBVIFOLBIJPROKMHOYTBTJIWUZWJMCTKCJKKTR9LCVYPVJI9AHGI9JOWMIWZAGMLDFJA9WU9QAMEFGABIBEZNNAL9OXSBFLOEHKDGHWFQSHMPLYFCNXAAZYJLMQDEYRGL9QKCEUEJ9LLVUOINVSZZQHCIKPAGMT9CAYIIMTTBCPKWTYHOJIIY9GYNPAJNUJ9BKYYXSV9JSPEXYMCFAIKTGNRSQGUNIYZCRT9FOWENSZQPD9ALUPYYAVICHVYELYFPUYDTWUSWNIYFXPX9MICCCOOZIWRNJIDALWGWRATGLJXNAYTNIZWQ9YTVDBOFZRKO9CFWRPAQQRXTPACOWCPRLYRYSJARRKSQPR9TCFXDVIXLP9XVL99ERRDSOHBFJDJQQGGGCZNDQ9NYCTQJWVZIAELCRBJJFDMCNZU9FIZRPGNURTXOCDSQGXTQHKHUECGWFUUYS9J9NYQ9U9P9UUP9YMZHWWWCIASCFLCMSKTELZWUGCDE9YOKVOVKTAYPHDF9ZCCQAYPJIJNGSHUIHHCOSSOOBUDOKE9CJZGYSSGNCQJVBEFTZFJ9SQUHOASKRRGBSHWKBCBWBTJHOGQ9WOMQFHWJVEG9NYX9KWBTCAIXNXHEBDIOFO9ALYMFGRICLCKKLG9FOBOX9PDWNQRGHBKHGKKRLWTBEQMCWQRLHAVYYZDIIPKVQTHYTWQMTOACXZOQCDTJTBAAUWXSGJF9PNQIJ9AJRUMUVCPWYVYVARKR9RKGOUHHNKNVGGPDDLGKPQNOYHNKAVVKCXWXOQPZNSLATUJT9AUWRMPPSWHSTTYDFAQDXOCYTZHOYYGAIM9CELMZ9AZPWB9MJXGHOKDNNSZVUDAGXTJJSSZCPZVPZBYNNTUQABSXQWZCHDQSLGK9UOHCFKBIBNETK999999999999999999999999999999999999999999999999999999999999999999999999999999999NOXDXXKUDWLOFJLIPQIBRBMGDYCPGDNLQOLQS99EQYKBIU9VHCJVIPFUYCQDNY9APGEVYLCENJIOBLWNB999999999XKBRHUD99C99999999NKZKEKWLDKMJCI9N9XQOLWEPAYWSH9999999999999999999999999KDDTGZLIPBNZKMLTOLOXQVNGLASESDQVPTXALEKRMIOHQLUHD9ELQDBQETS9QFGTYOYWLNTSKKMVJAUXSIROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999IROUICDOXKSYZTDPEDKOQENTJOWJONDEWROCEJIEWFWLUAACVSJFTMCHHXJBJRKAAPUDXXVXFWP9X9999"});
given().
- contentType("application/json").
+ contentType("application/json").header("X-IOTA-API-Version",1).
body(gson.toJson(request)).
when().
post("/").