trytes) {
return elements;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneSolidifierImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneSolidifierImpl.java
index 4b2079fd07..61b3fa1de2 100644
--- a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneSolidifierImpl.java
+++ b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneSolidifierImpl.java
@@ -1,6 +1,6 @@
package com.iota.iri.service.milestone.impl;
-import com.iota.iri.service.validation.TransactionValidator;
+import com.iota.iri.service.validation.TransactionSolidifier;
import com.iota.iri.model.Hash;
import com.iota.iri.service.milestone.MilestoneSolidifier;
import com.iota.iri.service.snapshot.SnapshotProvider;
@@ -35,7 +35,7 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier {
/**
* Defines the interval in which solidity checks are issued (in milliseconds).
*/
- private static final int SOLIDIFICATION_INTERVAL = 500;
+ private static final int SOLIDIFICATION_INTERVAL = 100;
/**
*
@@ -44,7 +44,7 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier {
*
*
* Note: We want to find the next previous milestone and not get stuck somewhere at the end of the tangle with a
- * long running {@link TransactionValidator#checkSolidity(Hash)} call.
+ * long running {@link TransactionSolidifier#checkSolidity(Hash)} call.
*
*/
private static final int SOLIDIFICATION_TRANSACTIONS_LIMIT = 50000;
@@ -60,9 +60,9 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier {
private final SnapshotProvider snapshotProvider;
/**
- * Holds a reference to the TransactionValidator which allows us to issue solidity checks.
+ * Holds a reference to the transactionSolidifier which allows us to issue solidity checks.
*/
- private final TransactionValidator transactionValidator;
+ private final TransactionSolidifier transactionSolidifier;
/**
* Holds a reference to the manager of the background worker.
@@ -105,16 +105,16 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier {
/**
* @param snapshotProvider snapshot provider which gives us access to the relevant snapshots
- * @param transactionValidator TransactionValidator instance that is used by the node
+ * @param transactionSolidifier transactionSolidifier instance that is used by the node
*/
- public MilestoneSolidifierImpl(SnapshotProvider snapshotProvider, TransactionValidator transactionValidator) {
+ public MilestoneSolidifierImpl(SnapshotProvider snapshotProvider, TransactionSolidifier transactionSolidifier) {
this.snapshotProvider = snapshotProvider;
- this.transactionValidator = transactionValidator;
+ this.transactionSolidifier = transactionSolidifier;
}
/**
* {@inheritDoc}
- *
+ *
*
* Since this method might be called from a performance critical context, we simply add the milestone to a temporary
* pool, that gets examined later by the background process. This doesn't just speed up the addition of new jobs but
@@ -206,7 +206,7 @@ private void milestoneSolidificationThread() {
*/
private void processNewlyAddedMilestones() {
for (Iterator> iterator = newlyAddedMilestones.entrySet().iterator();
- !Thread.currentThread().isInterrupted() && iterator.hasNext();) {
+ !Thread.currentThread().isInterrupted() && iterator.hasNext();) {
Map.Entry currentEntry = iterator.next();
@@ -232,7 +232,7 @@ private void processNewlyAddedMilestones() {
*/
private void processSolidificationQueue() {
for (Iterator> iterator = milestonesToSolidify.entrySet().iterator();
- !Thread.currentThread().isInterrupted() && iterator.hasNext();) {
+ !Thread.currentThread().isInterrupted() && iterator.hasNext();) {
Map.Entry currentEntry = iterator.next();
@@ -321,7 +321,7 @@ private Map.Entry getNextSolidificationCandidate() {
*
*
* It first dumps a log message to keep the node operator informed about the progress of solidification, and then
- * issues the {@link TransactionValidator#checkSolidity(Hash, int)} call that starts the solidification
+ * issues the {@link TransactionSolidifier#checkSolidity(Hash, int)} call that starts the solidification
* process.
*
*
@@ -341,11 +341,11 @@ private boolean isSolid(Map.Entry currentEntry) {
}
try {
- return transactionValidator.checkSolidity(currentEntry.getKey(), SOLIDIFICATION_TRANSACTIONS_LIMIT);
+ return transactionSolidifier.addMilestoneToSolidificationQueue(currentEntry.getKey(), SOLIDIFICATION_TRANSACTIONS_LIMIT);
} catch (Exception e) {
log.error("Error while solidifying milestone #" + currentEntry.getValue(), e);
return false;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java
new file mode 100644
index 0000000000..e79098318c
--- /dev/null
+++ b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java
@@ -0,0 +1,126 @@
+package com.iota.iri.service.validation;
+
+import com.iota.iri.controllers.TipsViewModel;
+import com.iota.iri.controllers.TransactionViewModel;
+import com.iota.iri.model.Hash;
+import com.iota.iri.service.validation.impl.TransactionSolidifierImpl;
+import com.iota.iri.network.TransactionRequester;
+
+import java.util.Set;
+
+/**
+ * Solidification tool. Transactions placed into the solidification queue will be checked for solidity. Any missing
+ * reference transactions will be placed into the {@link TransactionRequester}. If a transaction is found to be solid
+ * it is updated as such and placed into the BroadcastQueue to be sent off to the node's neighbours.
+ */
+public interface TransactionSolidifier {
+
+ /**
+ * Initialize the executor service. Start processing transactions to solidify.
+ */
+ void start();
+
+ /**
+ * Interrupt thread processes and shut down the executor service.
+ */
+ void shutdown();
+
+ /**
+ * Add a hash to the solidification queue, and runs an initial {@link #checkSolidity} call.
+ *
+ * @param hash Hash of the transaction to solidify
+ */
+ void addToSolidificationQueue(Hash hash);
+
+ /**
+ * Checks if milestone transaction is solid. Returns true if it is, and if it is not, it adds the hash to the
+ * solidification queue and returns false.
+ *
+ * @param hash Hash of the transaction to solidify
+ * @param maxToProcess Maximum number of transactions to analyze
+ * @return True if solid, false if not
+ */
+ boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess);
+ /**
+ * Fetch a copy of the current transactionsToBroadcast set.
+ * @return A set of {@link TransactionViewModel} objects to be broadcast.
+ */
+ Set getBroadcastQueue();
+
+ /**
+ * Remove any broadcasted transactions from the transactionsToBroadcast set
+ * @param transactionsBroadcasted A set of {@link TransactionViewModel} objects to remove from the set.
+ */
+ void clearFromBroadcastQueue(Set transactionsBroadcasted);
+
+ /**
+ * This method does the same as {@link #checkSolidity(Hash, int)} but defaults to an unlimited amount
+ * of transactions that are allowed to be traversed.
+ *
+ * @param hash hash of the transactions that shall get checked
+ * @return true if the transaction is solid and false otherwise
+ * @throws Exception if anything goes wrong while trying to solidify the transaction
+ */
+ boolean checkSolidity(Hash hash) throws Exception;
+
+ /**
+ * This method checks transactions for solidity and marks them accordingly if they are found to be solid.
+ *
+ * It iterates through all approved transactions until it finds one that is missing in the database or until it
+ * reached solid transactions on all traversed subtangles. In case of a missing transactions it issues a transaction
+ * request and returns false. If no missing transaction is found, it marks the processed transactions as solid in
+ * the database and returns true.
+ *
+ * Since this operation can potentially take a long time to terminate if it would have to traverse big parts of the
+ * tangle, it is possible to limit the amount of transactions that are allowed to be processed, while looking for
+ * unsolid / missing approvees. This can be useful when trying to "interrupt" the solidification of one transaction
+ * (if it takes too many steps) to give another one the chance to be solidified instead (i.e. prevent blocks in the
+ * solidification threads).
+ *
+ * @param hash hash of the transactions that shall get checked
+ * @param maxProcessedTransactions the maximum amount of transactions that are allowed to be traversed
+ * @return true if the transaction is solid and false otherwise
+ * @throws Exception if anything goes wrong while trying to solidify the transaction
+ */
+ boolean checkSolidity(Hash hash, int maxProcessedTransactions) throws Exception;
+
+ /**
+ * Updates a transaction after it was stored in the tangle. Tells the node to not request the transaction anymore,
+ * to update the live tips accordingly, and attempts to quickly solidify the transaction.
+ *
+ *
+ * Performs the following operations:
+ *
+ *
+ * - Removes {@code transactionViewModel}'s hash from the the request queue since we already found it.
+ * - If {@code transactionViewModel} has no children (approvers), we add it to the node's active tip list.
+ * - Removes {@code transactionViewModel}'s parents (branch & trunk) from the node's tip list
+ * (if they're present there).
+ * - Attempts to quickly solidify {@code transactionViewModel} by checking whether its direct parents
+ * are solid. If solid we add it to the queue transaction solidification thread to help it propagate the
+ * solidification to the approving child transactions.
+ * - Requests missing direct parent (trunk & branch) transactions that are needed to solidify
+ * {@code transactionViewModel}.
+ *
+ * @param transactionViewModel received transaction that is being updated
+ * @throws Exception if an error occurred while trying to solidify
+ * @see TipsViewModel
+ */
+ void updateStatus(TransactionViewModel transactionViewModel) throws Exception;
+
+ /**
+ * Tries to solidify the transactions quickly by performing {@link TransactionSolidifierImpl#checkApproovee} on
+ * both parents (trunk and branch). If the parents are solid, mark the transactions as solid.
+ * @param transactionViewModel transaction to solidify
+ * @return true if we made the transaction solid, else false.
+ * @throws Exception
+ */
+ boolean quickSetSolid(TransactionViewModel transactionViewModel) throws Exception;
+
+ /**
+ * Add to the propagation queue where it will be processed to help solidify approving transactions faster
+ * @param hash The transaction hash to be removed
+ * @throws Exception
+ */
+ void addToPropagationQueue(Hash hash) throws Exception;
+}
\ No newline at end of file
diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java
new file mode 100644
index 0000000000..19ef32f7c8
--- /dev/null
+++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java
@@ -0,0 +1,383 @@
+package com.iota.iri.service.validation.impl;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.iota.iri.controllers.TipsViewModel;
+import com.iota.iri.controllers.TransactionViewModel;
+import com.iota.iri.model.Hash;
+import com.iota.iri.network.pipeline.TransactionProcessingPipeline;
+import com.iota.iri.network.TransactionRequester;
+import com.iota.iri.service.snapshot.SnapshotProvider;
+import com.iota.iri.service.validation.TransactionSolidifier;
+import com.iota.iri.storage.Tangle;
+import com.iota.iri.utils.log.interval.IntervalLogger;
+import com.iota.iri.utils.thread.DedicatedScheduledExecutorService;
+import com.iota.iri.utils.thread.SilentScheduledExecutorService;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import static com.iota.iri.controllers.TransactionViewModel.PREFILLED_SLOT;
+import static com.iota.iri.controllers.TransactionViewModel.fromHash;
+
+/**
+ * A solidifier class for processing transactions. Transactions are checked for solidity, and missing transactions are
+ * subsequently requested. Once a transaction is solidified correctly it is placed into a broadcasting set to be sent to
+ * neighboring nodes.
+ */
+public class TransactionSolidifierImpl implements TransactionSolidifier {
+
+ private Tangle tangle;
+ private SnapshotProvider snapshotProvider;
+ private TransactionRequester transactionRequester;
+
+ /**
+ * Max size for all queues.
+ */
+ private static final int MAX_SIZE= 10000;
+
+ private static final int SOLIDIFICATION_INTERVAL = 100;
+
+ private static final IntervalLogger log = new IntervalLogger(TransactionSolidifier.class);
+
+ /**
+ * Executor service for running the {@link #processTransactionsToSolidify()}.
+ */
+ private SilentScheduledExecutorService executorService = new DedicatedScheduledExecutorService(
+ "Transaction Solidifier", log.delegate());
+
+ /**
+ * A queue for processing transactions with the {@link #checkSolidity(Hash)} call. Once a transaction has been
+ * marked solid it will be placed into the {@link #transactionsToBroadcast} queue.
+ */
+ private BlockingQueue transactionsToSolidify = new ArrayBlockingQueue<>(MAX_SIZE);
+
+ /**
+ * A queue for processing transactions with the {@link #propagateSolidTransactions()} call. This will check
+ * approving transactions with {@link #quickSetSolid(TransactionViewModel)}.
+ */
+ private BlockingQueue solidTransactions = new ArrayBlockingQueue<>(MAX_SIZE);
+
+ /**
+ * A set of transactions that will be called by the {@link TransactionProcessingPipeline} to be broadcast to
+ * neighboring nodes.
+ */
+ private BlockingQueue transactionsToBroadcast = new ArrayBlockingQueue<>(MAX_SIZE);
+
+ private TipsViewModel tipsViewModel;
+
+ /**
+ * Constructor for the solidifier.
+ * @param tangle The DB reference
+ * @param snapshotProvider For fetching entry points for solidity checks
+ * @param transactionRequester A requester for missing transactions
+ */
+ public TransactionSolidifierImpl(Tangle tangle, SnapshotProvider snapshotProvider, TransactionRequester transactionRequester,
+ TipsViewModel tipsViewModel){
+ this.tangle = tangle;
+ this.snapshotProvider = snapshotProvider;
+ this.transactionRequester = transactionRequester;
+ this.tipsViewModel = tipsViewModel;
+ }
+
+ /**
+ *{@inheritDoc}
+ */
+ @Override
+ public void start(){
+ executorService.silentScheduleWithFixedDelay(this::processTransactionsToSolidify, 0,
+ SOLIDIFICATION_INTERVAL, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ *{@inheritDoc}
+ */
+ @Override
+ public void shutdown() {
+ executorService.shutdownNow();
+ }
+
+ /**
+ *{@inheritDoc}
+ */
+ @Override
+ public void addToSolidificationQueue(Hash hash){
+ try{
+ if(!transactionsToSolidify.contains(hash)) {
+ if (transactionsToSolidify.size() >= MAX_SIZE - 1) {
+ transactionsToSolidify.remove();
+ }
+
+ transactionsToSolidify.put(hash);
+ }
+ } catch(Exception e){
+ log.error("Error placing transaction into solidification queue",e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess){
+ try{
+ TransactionViewModel tx = fromHash(tangle, hash);
+ if(tx.isSolid()){
+ addToPropagationQueue(hash);
+ return true;
+ }
+ addToSolidificationQueue(hash);
+ return false;
+ }catch(Exception e){
+ log.error("Error adding milestone to solidification queue", e);
+ return false;
+ }
+ }
+
+ /**
+ *{@inheritDoc}
+ */
+ @Override
+ public Set getBroadcastQueue(){
+ return new LinkedHashSet<>(transactionsToBroadcast);
+ }
+
+ /**
+ *{@inheritDoc}
+ */
+ @Override
+ public void clearFromBroadcastQueue(Set transactionsBroadcasted){
+ for (TransactionViewModel tvm : transactionsBroadcasted) {
+ transactionsToBroadcast.remove(tvm);
+ }
+ }
+
+
+ /**
+ * Iterate through the {@link #transactionsToSolidify} queue and call {@link #checkSolidity(Hash)} on each hash.
+ * Solid transactions are then processed into the {@link #transactionsToBroadcast} queue.
+ */
+ private void processTransactionsToSolidify(){
+ Hash hash;
+ if((hash = transactionsToSolidify.poll()) != null) {
+ try {
+ checkSolidity(hash);
+ } catch (Exception e) {
+ log.info(e.getMessage());
+ }
+ }
+ propagateSolidTransactions();
+ }
+
+ /**
+ *{@inheritDoc}
+ */
+ @Override
+ public boolean checkSolidity(Hash hash) throws Exception {
+ return checkSolidity(hash, 50000);
+ }
+
+ /**
+ *{@inheritDoc}
+ */
+ @Override
+ public boolean checkSolidity(Hash hash, int maxProcessedTransactions) throws Exception {
+ if(fromHash(tangle, hash).isSolid()) {
+ return true;
+ }
+ LinkedHashSet analyzedHashes = new LinkedHashSet<>(snapshotProvider.getInitialSnapshot().getSolidEntryPoints().keySet());
+ if(maxProcessedTransactions != Integer.MAX_VALUE) {
+ maxProcessedTransactions += analyzedHashes.size();
+ }
+ boolean solid = true;
+ final Queue nonAnalyzedTransactions = new LinkedList<>(Collections.singleton(hash));
+ Hash hashPointer;
+ while ((hashPointer = nonAnalyzedTransactions.poll()) != null) {
+ if (!analyzedHashes.add(hashPointer)) {
+ continue;
+ }
+
+ if (analyzedHashes.size() >= maxProcessedTransactions) {
+ return false;
+ }
+
+ TransactionViewModel transaction = fromHash(tangle, hashPointer);
+ if (isUnsolidWithoutEntryPoint(transaction, hashPointer)) {
+ if (transaction.getType() == PREFILLED_SLOT) {
+ solid = false;
+ checkRequester(hashPointer);
+ } else {
+ nonAnalyzedTransactions.offer(transaction.getTrunkTransactionHash());
+ nonAnalyzedTransactions.offer(transaction.getBranchTransactionHash());
+ }
+ }
+ }
+ if (solid) {
+ updateTransactions(analyzedHashes);
+ }
+ analyzedHashes.clear();
+ return solid;
+ }
+
+
+ /**
+ * Check if a transaction is present in the {@link #transactionRequester}, if not, it is added.
+ * @param hashPointer The hash of the transaction to request
+ */
+ private void checkRequester(Hash hashPointer){
+ if (!transactionRequester.isTransactionRequested(hashPointer)) {
+ transactionRequester.requestTransaction(hashPointer);
+ }
+ }
+
+ /**
+ * Iterate through analyzed hashes and place them in the {@link #transactionsToBroadcast} queue
+ * @param hashes Analyzed hashes from the {@link #checkSolidity(Hash)} call
+ */
+ private void updateTransactions(Set hashes) {
+ hashes.forEach(hash -> {
+ try {
+ TransactionViewModel tvm = fromHash(tangle, hash);
+ tvm.updateHeights(tangle, snapshotProvider.getInitialSnapshot());
+
+ if(!tvm.isSolid()){
+ tvm.updateSolid(true);
+ tvm.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height");
+ }
+ addToBroadcastQueue(tvm);
+ addToPropagationQueue(tvm.getHash());
+ } catch (Exception e) {
+ log.info(e.getMessage());
+ }
+ });
+ }
+
+ /**
+ * Returns true if transaction is not solid and there are no solid entry points from the initial snapshot.
+ */
+ private boolean isUnsolidWithoutEntryPoint(TransactionViewModel transaction, Hash hashPointer) throws Exception{
+ if(!transaction.isSolid() && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(hashPointer)){
+ return true;
+ }
+ addToPropagationQueue(hashPointer);
+ return false;
+ }
+
+
+ private void addToBroadcastQueue(TransactionViewModel tvm) {
+ try {
+ if (transactionsToBroadcast.size() >= MAX_SIZE) {
+ transactionsToBroadcast.remove();
+ }
+
+ transactionsToBroadcast.put(tvm);
+ } catch(Exception e){
+ log.info("Error placing transaction into broadcast queue: " + e.getMessage());
+ }
+ }
+
+ @VisibleForTesting
+ Set getSolidificationQueue(){
+ return new LinkedHashSet<>(transactionsToSolidify);
+ }
+
+
+ @Override
+ public void updateStatus(TransactionViewModel transactionViewModel) throws Exception {
+ transactionRequester.clearTransactionRequest(transactionViewModel.getHash());
+ if(transactionViewModel.getApprovers(tangle).size() == 0) {
+ tipsViewModel.addTipHash(transactionViewModel.getHash());
+ }
+ tipsViewModel.removeTipHash(transactionViewModel.getTrunkTransactionHash());
+ tipsViewModel.removeTipHash(transactionViewModel.getBranchTransactionHash());
+
+ if(quickSetSolid(transactionViewModel)) {
+ transactionViewModel.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height");
+ tipsViewModel.setSolid(transactionViewModel.getHash());
+ addToPropagationQueue(transactionViewModel.getHash());
+ }
+ }
+
+ @Override
+ public void addToPropagationQueue(Hash hash) throws Exception{
+ if(!solidTransactions.contains(hash)) {
+ if (solidTransactions.size() >= MAX_SIZE) {
+ solidTransactions.poll();
+ }
+ solidTransactions.put(hash);
+ }
+ }
+
+ @Override
+ public boolean quickSetSolid(final TransactionViewModel transactionViewModel) throws Exception {
+ if(!transactionViewModel.isSolid()) {
+ boolean solid = true;
+ if (!checkApproovee(transactionViewModel.getTrunkTransaction(tangle))) {
+ solid = false;
+ }
+ if (!checkApproovee(transactionViewModel.getBranchTransaction(tangle))) {
+ solid = false;
+ }
+ if(solid) {
+ transactionViewModel.updateSolid(true);
+ transactionViewModel.updateHeights(tangle, snapshotProvider.getInitialSnapshot());
+ addToPropagationQueue(transactionViewModel.getHash());
+ addToBroadcastQueue(transactionViewModel);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * If the the {@code approvee} is missing, request it from a neighbor.
+ * @param approovee transaction we check.
+ * @return true if {@code approvee} is solid.
+ * @throws Exception if we encounter an error while requesting a transaction
+ */
+ private boolean checkApproovee(TransactionViewModel approovee) throws Exception {
+ if(snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(approovee.getHash())) {
+ return true;
+ }
+ if(approovee.getType() == PREFILLED_SLOT) {
+ // don't solidify from the bottom until cuckoo filters can identify where we deleted -> otherwise we will
+ // continue requesting old transactions forever
+ //transactionRequester.requestTransaction(approovee.getHash(), false);
+ return false;
+ }
+ return approovee.isSolid();
+ }
+
+ @VisibleForTesting
+ void propagateSolidTransactions() {
+ while(!Thread.currentThread().isInterrupted() && solidTransactions.peek() != null) {
+ try {
+ Hash hash = solidTransactions.poll();
+ TransactionViewModel transaction = fromHash(tangle, hash);
+ Set approvers = transaction.getApprovers(tangle).getHashes();
+ for(Hash h: approvers) {
+ TransactionViewModel tx = fromHash(tangle, h);
+ if (quietQuickSetSolid(tx)) {
+ tx.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height");
+ tipsViewModel.setSolid(h);
+ }
+ }
+ } catch (Exception e) {
+ log.error("Error while propagating solidity upwards", e);
+ }
+ }
+ }
+
+ /**
+ * Perform a {@link #quickSetSolid} while capturing and logging errors
+ * @param transactionViewModel transaction we try to solidify.
+ * @return true if we managed to solidify, else false.
+ */
+ private boolean quietQuickSetSolid(TransactionViewModel transactionViewModel) {
+ try {
+ return quickSetSolid(transactionViewModel);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ return false;
+ }
+ }
+}
\ No newline at end of file
From c72efc3e694b49d9f0cf2983b6fa8b64ad255542 Mon Sep 17 00:00:00 2001
From: DyrellC
Date: Thu, 19 Mar 2020 13:24:29 -0600
Subject: [PATCH 03/18] Update Transaction Validator
---
.../validation/TransactionValidator.java | 337 ++----------------
1 file changed, 21 insertions(+), 316 deletions(-)
diff --git a/src/main/java/com/iota/iri/service/validation/TransactionValidator.java b/src/main/java/com/iota/iri/service/validation/TransactionValidator.java
index b2c4995c70..cf36e7f94e 100644
--- a/src/main/java/com/iota/iri/service/validation/TransactionValidator.java
+++ b/src/main/java/com/iota/iri/service/validation/TransactionValidator.java
@@ -2,93 +2,46 @@
import com.google.common.annotations.VisibleForTesting;
import com.iota.iri.conf.ProtocolConfig;
-import com.iota.iri.controllers.TipsViewModel;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.crypto.Curl;
import com.iota.iri.crypto.Sponge;
import com.iota.iri.crypto.SpongeFactory;
-import com.iota.iri.model.Hash;
import com.iota.iri.model.TransactionHash;
import com.iota.iri.network.TransactionRequester;
import com.iota.iri.service.snapshot.SnapshotProvider;
-import com.iota.iri.storage.Tangle;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static com.iota.iri.controllers.TransactionViewModel.*;
+/**
+ * Tool for determining validity of a transaction via a {@link TransactionViewModel}, tryte array or byte array.
+ */
public class TransactionValidator {
- private static final Logger log = LoggerFactory.getLogger(TransactionValidator.class);
private static final int TESTNET_MWM_CAP = 13;
- public static final int SOLID_SLEEP_TIME = 500;
- private final Tangle tangle;
private final SnapshotProvider snapshotProvider;
- private final TipsViewModel tipsViewModel;
private final TransactionRequester transactionRequester;
private int minWeightMagnitude = 81;
private static final long MAX_TIMESTAMP_FUTURE = 2L * 60L * 60L;
private static final long MAX_TIMESTAMP_FUTURE_MS = MAX_TIMESTAMP_FUTURE * 1_000L;
- /////////////////////////////////fields for solidification thread//////////////////////////////////////
-
- private Thread newSolidThread;
-
- /**
- * If true use {@link #newSolidTransactionsOne} while solidifying. Else use {@link #newSolidTransactionsTwo}.
- */
- private final AtomicBoolean useFirst = new AtomicBoolean(true);
- /**
- * Is {@link #newSolidThread} shutting down
- */
- private final AtomicBoolean shuttingDown = new AtomicBoolean(false);
- /**
- * mutex for solidification
- */
- private final Object cascadeSync = new Object();
- private final Set newSolidTransactionsOne = new LinkedHashSet<>();
- private final Set newSolidTransactionsTwo = new LinkedHashSet<>();
-
/**
* Constructor for Tangle Validator
*
- * @param tangle relays tangle data to and from the persistence layer
* @param snapshotProvider data provider for the snapshots that are relevant for the node
- * @param tipsViewModel container that gets updated with the latest tips (transactions with no children)
* @param transactionRequester used to request missing transactions from neighbors
* @param protocolConfig used for checking if we are in testnet and mwm. testnet true if we are in testnet
* mode, this caps {@code mwm} to {@value #TESTNET_MWM_CAP} regardless of parameter input.
* minimum weight magnitude: the minimal number of 9s that ought to appear at the end of the
* transaction hash
*/
- public TransactionValidator(Tangle tangle, SnapshotProvider snapshotProvider, TipsViewModel tipsViewModel, TransactionRequester transactionRequester, ProtocolConfig protocolConfig) {
- this.tangle = tangle;
+ public TransactionValidator(SnapshotProvider snapshotProvider, TransactionRequester transactionRequester, ProtocolConfig protocolConfig) {
this.snapshotProvider = snapshotProvider;
- this.tipsViewModel = tipsViewModel;
this.transactionRequester = transactionRequester;
- this.newSolidThread = new Thread(spawnSolidTransactionsPropagation(), "Solid TX cascader");
setMwm(protocolConfig.isTestnet(), protocolConfig.getMwm());
}
/**
- * Does two things:
- *
- * - Sets the minimum weight magnitude (MWM). POW on a transaction is validated by counting a certain
- * number of consecutive 9s in the end of the transaction hash. The number of 9s is the MWM.
- * - Starts the transaction solidification thread.
- *
- *
- *
- * @see #spawnSolidTransactionsPropagation()
+ * Set the Minimum Weight Magnitude for validation checks.
*/
- public void init() {
- newSolidThread.start();
- }
-
@VisibleForTesting
void setMwm(boolean testnet, int mwm) {
minWeightMagnitude = mwm;
@@ -99,16 +52,6 @@ void setMwm(boolean testnet, int mwm) {
}
}
- /**
- * Shutdown roots to tip solidification thread
- * @throws InterruptedException
- * @see #spawnSolidTransactionsPropagation()
- */
- public void shutdown() throws InterruptedException {
- shuttingDown.set(true);
- newSolidThread.join();
- }
-
/**
* @return the minimal number of trailing 9s that have to be present at the end of the transaction hash
* in order to validate that sufficient proof of work has been done
@@ -163,18 +106,14 @@ private boolean hasInvalidTimestamp(TransactionViewModel transactionViewModel) {
public void runValidation(TransactionViewModel transactionViewModel, final int minWeightMagnitude) {
transactionViewModel.setMetadata();
transactionViewModel.setAttachmentData();
- if(hasInvalidTimestamp(transactionViewModel)) {
+ if (hasInvalidTimestamp(transactionViewModel)) {
throw new StaleTimestampException("Invalid transaction timestamp.");
}
- for (int i = VALUE_TRINARY_OFFSET + VALUE_USABLE_TRINARY_SIZE; i < VALUE_TRINARY_OFFSET + VALUE_TRINARY_SIZE; i++) {
- if (transactionViewModel.trits()[i] != 0) {
- throw new IllegalStateException("Invalid transaction value");
- }
- }
+ confirmValidTransactionValues(transactionViewModel);
int weightMagnitude = transactionViewModel.weightMagnitude;
- if(weightMagnitude < minWeightMagnitude) {
- throw new IllegalStateException("Invalid weight magnitude");
+ if (weightMagnitude < minWeightMagnitude) {
+ throw new IllegalStateException("Invalid transaction hash");
}
if (transactionViewModel.value() != 0 && transactionViewModel.getAddressHash().trits()[Curl.HASH_LENGTH - 1] != 0) {
@@ -182,6 +121,15 @@ public void runValidation(TransactionViewModel transactionViewModel, final int m
}
}
+ private void confirmValidTransactionValues(TransactionViewModel transactionViewModel) throws IllegalStateException {
+ for (int i = TransactionViewModel.VALUE_TRINARY_OFFSET + TransactionViewModel.VALUE_USABLE_TRINARY_SIZE;
+ i < TransactionViewModel.VALUE_TRINARY_OFFSET + TransactionViewModel.VALUE_TRINARY_SIZE; i++) {
+ if (transactionViewModel.trits()[i] != 0) {
+ throw new IllegalStateException("Invalid transaction value");
+ }
+ }
+ }
+
/**
* Creates a new transaction from {@code trits} and validates it with {@link #runValidation}.
*
@@ -205,255 +153,12 @@ public TransactionViewModel validateTrits(final byte[] trits, int minWeightMagni
* @throws RuntimeException if validation fails
*/
public TransactionViewModel validateBytes(final byte[] bytes, int minWeightMagnitude, Sponge curl) {
- TransactionViewModel transactionViewModel = new TransactionViewModel(bytes, TransactionHash.calculate(bytes, TRINARY_SIZE, curl));
+ TransactionViewModel transactionViewModel = new TransactionViewModel(bytes, TransactionHash.calculate(bytes,
+ TransactionViewModel.TRINARY_SIZE, curl));
runValidation(transactionViewModel, minWeightMagnitude);
return transactionViewModel;
}
- /**
- * This method does the same as {@link #checkSolidity(Hash, int)} but defaults to an unlimited amount
- * of transactions that are allowed to be traversed.
- *
- * @param hash hash of the transactions that shall get checked
- * @return true if the transaction is solid and false otherwise
- * @throws Exception if anything goes wrong while trying to solidify the transaction
- */
- public boolean checkSolidity(Hash hash) throws Exception {
- return checkSolidity(hash, Integer.MAX_VALUE);
- }
-
- /**
- * This method checks transactions for solidity and marks them accordingly if they are found to be solid.
- *
- * It iterates through all approved transactions until it finds one that is missing in the database or until it
- * reached solid transactions on all traversed subtangles. In case of a missing transactions it issues a transaction
- * request and returns false. If no missing transaction is found, it marks the processed transactions as solid in
- * the database and returns true.
- *
- * Since this operation can potentially take a long time to terminate if it would have to traverse big parts of the
- * tangle, it is possible to limit the amount of transactions that are allowed to be processed, while looking for
- * unsolid / missing approvees. This can be useful when trying to "interrupt" the solidification of one transaction
- * (if it takes too many steps) to give another one the chance to be solidified instead (i.e. prevent blocks in the
- * solidification threads).
- *
- * @param hash hash of the transactions that shall get checked
- * @param maxProcessedTransactions the maximum amount of transactions that are allowed to be traversed
- * @return true if the transaction is solid and false otherwise
- * @throws Exception if anything goes wrong while trying to solidify the transaction
- */
- public boolean checkSolidity(Hash hash, int maxProcessedTransactions) throws Exception {
- if(fromHash(tangle, hash).isSolid()) {
- return true;
- }
- LinkedHashSet analyzedHashes = new LinkedHashSet<>(snapshotProvider.getInitialSnapshot().getSolidEntryPoints().keySet());
- if(maxProcessedTransactions != Integer.MAX_VALUE) {
- maxProcessedTransactions += analyzedHashes.size();
- }
- boolean solid = true;
- final Queue nonAnalyzedTransactions = new LinkedList<>(Collections.singleton(hash));
- Hash hashPointer;
- while ((hashPointer = nonAnalyzedTransactions.poll()) != null) {
- if (!analyzedHashes.add(hashPointer)) {
- continue;
- }
-
- if (analyzedHashes.size() >= maxProcessedTransactions) {
- return false;
- }
-
- TransactionViewModel transaction = fromHash(tangle, hashPointer);
- if (!transaction.isSolid() && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(hashPointer)) {
- if (transaction.getType() == PREFILLED_SLOT) {
- solid = false;
-
- if (!transactionRequester.isTransactionRequested(hashPointer)) {
- transactionRequester.requestTransaction(hashPointer);
- continue;
- }
- } else {
- nonAnalyzedTransactions.offer(transaction.getTrunkTransactionHash());
- nonAnalyzedTransactions.offer(transaction.getBranchTransactionHash());
- }
- }
- }
- if (solid) {
- updateSolidTransactions(tangle, snapshotProvider.getInitialSnapshot(), analyzedHashes);
- }
- analyzedHashes.clear();
- return solid;
- }
-
- public void addSolidTransaction(Hash hash) {
- synchronized (cascadeSync) {
- if (useFirst.get()) {
- newSolidTransactionsOne.add(hash);
- } else {
- newSolidTransactionsTwo.add(hash);
- }
- }
- }
-
- /**
- * Creates a runnable that runs {@link #propagateSolidTransactions()} in a loop every {@value #SOLID_SLEEP_TIME} ms
- * @return runnable that is not started
- */
- private Runnable spawnSolidTransactionsPropagation() {
- return () -> {
- while(!shuttingDown.get()) {
- propagateSolidTransactions();
- try {
- Thread.sleep(SOLID_SLEEP_TIME);
- } catch (InterruptedException e) {
- // Ignoring InterruptedException. Do not use Thread.currentThread().interrupt() here.
- log.error("Thread was interrupted: ", e);
- }
- }
- };
- }
-
- /**
- * Iterates over all currently known solid transactions. For each solid transaction, we find
- * its children (approvers) and try to quickly solidify them with {@link #quietQuickSetSolid}.
- * If we manage to solidify the transactions, we add them to the solidification queue for a traversal by a later run.
- */
- @VisibleForTesting
- void propagateSolidTransactions() {
- Set newSolidHashes = new HashSet<>();
- useFirst.set(!useFirst.get());
- //synchronized to make sure no one is changing the newSolidTransactions collections during addAll
- synchronized (cascadeSync) {
- //We are using a collection that doesn't get updated by other threads
- if (useFirst.get()) {
- newSolidHashes.addAll(newSolidTransactionsTwo);
- newSolidTransactionsTwo.clear();
- } else {
- newSolidHashes.addAll(newSolidTransactionsOne);
- newSolidTransactionsOne.clear();
- }
- }
- Iterator cascadeIterator = newSolidHashes.iterator();
- while(cascadeIterator.hasNext() && !shuttingDown.get()) {
- try {
- Hash hash = cascadeIterator.next();
- TransactionViewModel transaction = fromHash(tangle, hash);
- Set approvers = transaction.getApprovers(tangle).getHashes();
- for(Hash h: approvers) {
- TransactionViewModel tx = fromHash(tangle, h);
- if(quietQuickSetSolid(tx)) {
- tx.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height");
- tipsViewModel.setSolid(h);
- addSolidTransaction(h);
- }
- }
- } catch (Exception e) {
- log.error("Error while propagating solidity upwards", e);
- }
- }
- }
-
-
- /**
- * Updates a transaction after it was stored in the tangle. Tells the node to not request the transaction anymore,
- * to update the live tips accordingly, and attempts to quickly solidify the transaction.
- *
- *
- * Performs the following operations:
- *
- *
- * - Removes {@code transactionViewModel}'s hash from the the request queue since we already found it.
- * - If {@code transactionViewModel} has no children (approvers), we add it to the node's active tip list.
- * - Removes {@code transactionViewModel}'s parents (branch & trunk) from the node's tip list
- * (if they're present there).
- * - Attempts to quickly solidify {@code transactionViewModel} by checking whether its direct parents
- * are solid. If solid we add it to the queue transaction solidification thread to help it propagate the
- * solidification to the approving child transactions.
- * - Requests missing direct parent (trunk & branch) transactions that are needed to solidify
- * {@code transactionViewModel}.
- *
- * @param transactionViewModel received transaction that is being updated
- * @throws Exception if an error occurred while trying to solidify
- * @see TipsViewModel
- */
- //Not part of the validation process. This should be moved to a component in charge of
- //what transaction we gossip.
- public void updateStatus(TransactionViewModel transactionViewModel) throws Exception {
- transactionRequester.clearTransactionRequest(transactionViewModel.getHash());
- if(transactionViewModel.getApprovers(tangle).size() == 0) {
- tipsViewModel.addTipHash(transactionViewModel.getHash());
- }
- tipsViewModel.removeTipHash(transactionViewModel.getTrunkTransactionHash());
- tipsViewModel.removeTipHash(transactionViewModel.getBranchTransactionHash());
-
- if(quickSetSolid(transactionViewModel)) {
- transactionViewModel.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height");
- tipsViewModel.setSolid(transactionViewModel.getHash());
- addSolidTransaction(transactionViewModel.getHash());
- }
- }
-
- /**
- * Perform a {@link #quickSetSolid} while capturing and logging errors
- * @param transactionViewModel transaction we try to solidify.
- * @return true if we managed to solidify, else false.
- */
- private boolean quietQuickSetSolid(TransactionViewModel transactionViewModel) {
- try {
- return quickSetSolid(transactionViewModel);
- } catch (Exception e) {
- log.error(e.getMessage(), e);
- return false;
- }
- }
-
- /**
- * Tries to solidify the transactions quickly by performing {@link #checkApproovee} on both parents (trunk and
- * branch). If the parents are solid, mark the transactions as solid.
- * @param transactionViewModel transaction to solidify
- * @return true if we made the transaction solid, else false.
- * @throws Exception
- */
- private boolean quickSetSolid(final TransactionViewModel transactionViewModel) throws Exception {
- if(!transactionViewModel.isSolid()) {
- boolean solid = true;
- if (!checkApproovee(transactionViewModel.getTrunkTransaction(tangle))) {
- solid = false;
- }
- if (!checkApproovee(transactionViewModel.getBranchTransaction(tangle))) {
- solid = false;
- }
- if(solid) {
- transactionViewModel.updateSolid(true);
- transactionViewModel.updateHeights(tangle, snapshotProvider.getInitialSnapshot());
- return true;
- }
- }
- return false;
- }
-
- /**
- * If the the {@code approvee} is missing, request it from a neighbor.
- * @param approovee transaction we check.
- * @return true if {@code approvee} is solid.
- * @throws Exception if we encounter an error while requesting a transaction
- */
- private boolean checkApproovee(TransactionViewModel approovee) throws Exception {
- if(snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(approovee.getHash())) {
- return true;
- }
- if(approovee.getType() == PREFILLED_SLOT) {
- // don't solidify from the bottom until cuckoo filters can identify where we deleted -> otherwise we will
- // continue requesting old transactions forever
- //transactionRequester.requestTransaction(approovee.getHash(), false);
- return false;
- }
- return approovee.isSolid();
- }
-
- @VisibleForTesting
- boolean isNewSolidTxSetsEmpty () {
- return newSolidTransactionsOne.isEmpty() && newSolidTransactionsTwo.isEmpty();
- }
-
/**
* Thrown if transaction fails {@link #hasInvalidTimestamp} check.
*/
@@ -462,4 +167,4 @@ public static class StaleTimestampException extends RuntimeException {
super(message);
}
}
-}
+}
\ No newline at end of file
From b6a4e4802959069782f48993da715bed698537a3 Mon Sep 17 00:00:00 2001
From: DyrellC
Date: Thu, 19 Mar 2020 13:30:29 -0600
Subject: [PATCH 04/18] Update Unit Tests
---
.../NetworkInjectionConfigurationTest.java | 7 +
.../network/pipeline/ReceivedStageTest.java | 24 +--
.../network/pipeline/SolidifyStageTest.java | 115 +++++++++++++
.../TransactionProcessingPipelineTest.java | 46 ++++-
.../java/com/iota/iri/service/APITest.java | 6 +-
.../com/iota/iri/service/ApiCallTest.java | 2 +-
.../validation/TransactionValidatorTest.java | 154 ++---------------
.../impl/TransactionSolidifierImplTest.java | 160 ++++++++++++++++++
8 files changed, 353 insertions(+), 161 deletions(-)
create mode 100644 src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java
create mode 100644 src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java
diff --git a/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java b/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java
index 6cb4f26b8e..5affbf062e 100644
--- a/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java
+++ b/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java
@@ -3,6 +3,7 @@
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
+import com.iota.iri.service.validation.TransactionSolidifier;
import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.conf.BaseIotaConfig;
import com.iota.iri.conf.IotaConfig;
@@ -37,6 +38,11 @@ public void provideTransactionProcessingPipeline() {
assertNotNull("instance creation did not work", testInjector().getInstance(TransactionProcessingPipeline.class));
}
+ @Test
+ public void provideTransactionSolidifier(){
+ assertNotNull("instance creation did not work", testInjector().getInstance(TransactionSolidifier.class));
+ }
+
private Injector testInjector() {
IotaConfig config = mock(IotaConfig.class);
when(config.getCoordinator()).thenReturn(BaseIotaConfig.Defaults.COORDINATOR);
@@ -50,6 +56,7 @@ protected void configure() {
bind(LatestMilestoneTracker.class).toInstance(mock(LatestMilestoneTracker.class));
bind(SnapshotProvider.class).toInstance(mock(SnapshotProvider.class));
bind(TransactionValidator.class).toInstance(mock(TransactionValidator.class));
+ bind(TransactionSolidifier.class).toInstance(mock(TransactionSolidifier.class));
}
}
diff --git a/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java b/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java
index 01e9746782..6ab434e8b6 100644
--- a/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java
+++ b/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java
@@ -1,6 +1,6 @@
package com.iota.iri.network.pipeline;
-import com.iota.iri.service.validation.TransactionValidator;
+import com.iota.iri.service.validation.TransactionSolidifier;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.network.TransactionRequester;
import com.iota.iri.network.neighbor.Neighbor;
@@ -26,7 +26,7 @@ public class ReceivedStageTest {
private Tangle tangle;
@Mock
- private TransactionValidator transactionValidator;
+ private TransactionSolidifier transactionSolidifier;
@Mock
private SnapshotProvider snapshotProvider;
@@ -49,7 +49,7 @@ public void newlyStoredTransactionUpdatesAlsoArrivalTimeAndSender() throws Excep
Mockito.when(neighbor.getMetrics()).thenReturn(neighborMetrics);
Mockito.when(transactionRequester.removeRecentlyRequestedTransaction(Mockito.any())).thenReturn(true);
- ReceivedStage stage = new ReceivedStage(tangle, transactionValidator, snapshotProvider, transactionRequester);
+ ReceivedStage stage = new ReceivedStage(tangle, transactionSolidifier, snapshotProvider, transactionRequester);
ReceivedPayload receivedPayload = new ReceivedPayload(neighbor, tvm);
ProcessingContext ctx = new ProcessingContext(null, receivedPayload);
stage.process(ctx);
@@ -58,11 +58,11 @@ public void newlyStoredTransactionUpdatesAlsoArrivalTimeAndSender() throws Excep
Mockito.verify(tvm).update(Mockito.any(), Mockito.any(), Mockito.any());
Mockito.verify(transactionRequester).removeRecentlyRequestedTransaction(Mockito.any());
Mockito.verify(transactionRequester).requestTrunkAndBranch(Mockito.any());
- assertEquals("should submit to broadcast stage next", TransactionProcessingPipeline.Stage.BROADCAST,
+ assertEquals("should submit to broadcast stage next", TransactionProcessingPipeline.Stage.SOLIDIFY,
ctx.getNextStage());
- BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload();
- assertEquals("neighbor is still the same", neighbor, broadcastPayload.getOriginNeighbor());
- assertEquals("tvm is still the same", tvm, broadcastPayload.getTransactionViewModel());
+ SolidifyPayload solidifyPayload = (SolidifyPayload) ctx.getPayload();
+ assertEquals("neighbor is still the same", neighbor, solidifyPayload.getOriginNeighbor());
+ assertEquals("tvm is still the same", tvm, solidifyPayload.getTransaction());
}
@Test
@@ -70,7 +70,7 @@ public void alreadyStoredTransactionDoesNoUpdates() throws Exception {
Mockito.when(tvm.store(tangle, snapshotProvider.getInitialSnapshot())).thenReturn(false);
Mockito.when(neighbor.getMetrics()).thenReturn(neighborMetrics);
- ReceivedStage stage = new ReceivedStage(tangle, transactionValidator, snapshotProvider, transactionRequester);
+ ReceivedStage stage = new ReceivedStage(tangle, transactionSolidifier, snapshotProvider, transactionRequester);
ReceivedPayload receivedPayload = new ReceivedPayload(neighbor, tvm);
ProcessingContext ctx = new ProcessingContext(null, receivedPayload);
stage.process(ctx);
@@ -79,11 +79,11 @@ public void alreadyStoredTransactionDoesNoUpdates() throws Exception {
Mockito.verify(tvm, Mockito.never()).update(Mockito.any(), Mockito.any(), Mockito.any());
Mockito.verify(transactionRequester).removeRecentlyRequestedTransaction(Mockito.any());
Mockito.verify(transactionRequester, Mockito.never()).requestTrunkAndBranch(Mockito.any());
- assertEquals("should submit to broadcast stage next", TransactionProcessingPipeline.Stage.BROADCAST,
+ assertEquals("should submit to broadcast stage next", TransactionProcessingPipeline.Stage.SOLIDIFY,
ctx.getNextStage());
- BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload();
- assertEquals("neighbor should still be the same", neighbor, broadcastPayload.getOriginNeighbor());
- assertEquals("tvm should still be the same", tvm, broadcastPayload.getTransactionViewModel());
+ SolidifyPayload solidifyPayload = (SolidifyPayload) ctx.getPayload();
+ assertEquals("neighbor should still be the same", neighbor, solidifyPayload.getOriginNeighbor());
+ assertEquals("tvm should still be the same", tvm, solidifyPayload.getTransaction());
}
}
\ No newline at end of file
diff --git a/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java
new file mode 100644
index 0000000000..22f62411dc
--- /dev/null
+++ b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java
@@ -0,0 +1,115 @@
+package com.iota.iri.network.pipeline;
+
+import com.iota.iri.controllers.TipsViewModel;
+import com.iota.iri.controllers.TransactionViewModel;
+import com.iota.iri.model.Hash;
+import com.iota.iri.model.persistables.Transaction;
+import com.iota.iri.service.validation.TransactionSolidifier;
+import com.iota.iri.storage.Tangle;
+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 static org.junit.Assert.assertEquals;
+
+public class SolidifyStageTest {
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private Tangle tangle;
+
+ @Mock
+ private TipsViewModel tipsViewModel;
+
+ @Mock
+ private TransactionViewModel tvm;
+
+ @Mock
+ private Hash originalHash;
+
+ @Mock
+ private Hash tipHash;
+
+ @Mock
+ private TransactionSolidifier transactionSolidifier;
+
+ @Test
+ public void solidTransactionIsBroadcast() throws Exception{
+ Mockito.when(tvm.isSolid()).thenReturn(true);
+ Mockito.when(tvm.getHash()).thenReturn(originalHash);
+
+ SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle);
+ SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm);
+ ProcessingContext ctx = new ProcessingContext(solidifyPayload);
+
+ solidifyStage.process(ctx);
+ Thread.sleep(100);
+
+ assertEquals("Expected next stage to be broadcast", ctx.getNextStage(),
+ TransactionProcessingPipeline.Stage.BROADCAST);
+ BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload();
+ assertEquals("Expected payload hash to equal the original transaction hash",
+ broadcastPayload.getTransactionViewModel().getHash(), originalHash);
+ }
+
+ @Test
+ public void quickSetSolidTransactionIsBroadcast() throws Exception{
+ Mockito.when(transactionSolidifier.quickSetSolid(tvm)).thenReturn(true);
+ Mockito.when(tvm.getHash()).thenReturn(originalHash);
+
+ SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle);
+ SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm);
+ ProcessingContext ctx = new ProcessingContext(solidifyPayload);
+
+ solidifyStage.process(ctx);
+ Thread.sleep(100);
+
+ assertEquals("Expected next stage to be broadcast", ctx.getNextStage(),
+ TransactionProcessingPipeline.Stage.BROADCAST);
+ BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload();
+ assertEquals("Expected payload hash to equal the original transaction hash",
+ broadcastPayload.getTransactionViewModel().getHash(), originalHash);
+ }
+
+ @Test
+ public void unsolidTransactionBroadcastsRandomSolidTip() throws Exception{
+ Mockito.when(tvm.isSolid()).thenReturn(false);
+ Mockito.when(transactionSolidifier.quickSetSolid(tvm)).thenReturn(false);
+ TransactionViewModel tip = new TransactionViewModel(new Transaction(), tipHash);
+
+ SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle);
+ SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm);
+ ProcessingContext ctx = new ProcessingContext(solidifyPayload);
+
+ solidifyStage.injectTip(tip);
+ solidifyStage.process(ctx);
+ Thread.sleep(100);
+
+ assertEquals("Expected next stage to be broadcast", ctx.getNextStage(),
+ TransactionProcessingPipeline.Stage.BROADCAST);
+ BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload();
+ assertEquals("Expected payload hash to equal random tip hash",
+ broadcastPayload.getTransactionViewModel().getHash(), tipHash);
+ }
+
+ @Test
+ public void unsolidWithNoRandomTipsAborts() throws Exception{
+ Mockito.when(tvm.isSolid()).thenReturn(false);
+ Mockito.when(transactionSolidifier.quickSetSolid(tvm)).thenReturn(false);
+ Mockito.when(tipsViewModel.getRandomSolidTipHash()).thenReturn(null);
+
+ SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle);
+ SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm);
+ ProcessingContext ctx = new ProcessingContext(solidifyPayload);
+
+ solidifyStage.process(ctx);
+ Thread.sleep(100);
+
+ assertEquals("Expected next stage to be broadcast", ctx.getNextStage(),
+ TransactionProcessingPipeline.Stage.FINISH);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java b/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java
index a88daa900e..576f418bc0 100644
--- a/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java
+++ b/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java
@@ -1,5 +1,6 @@
package com.iota.iri.network.pipeline;
+import com.iota.iri.service.validation.TransactionSolidifier;
import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.conf.NodeConfig;
import com.iota.iri.controllers.TipsViewModel;
@@ -18,6 +19,7 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+
public class TransactionProcessingPipelineTest {
@Rule
@@ -74,6 +76,15 @@ public class TransactionProcessingPipelineTest {
@Mock
private HashingPayload hashingPayload;
+ @Mock
+ private BroadcastPayload broadcastPayload;
+
+ @Mock
+ private SolidifyStage solidifyStage;
+
+ @Mock
+ private SolidifyPayload solidifyPayload;
+
@Mock
private ProcessingContext validationCtx;
@@ -89,9 +100,15 @@ public class TransactionProcessingPipelineTest {
@Mock
private ProcessingContext broadcastCtx;
+ @Mock
+ private ProcessingContext solidifyCtx;
+
@Mock
private ProcessingContext abortCtx;
+ @Mock
+ private TransactionSolidifier transactionSolidifier;
+
private void mockHashingStage(TransactionProcessingPipeline pipeline) {
Mockito.when(hashingPayload.getTxTrits()).thenReturn(null);
Mockito.doAnswer(invocation -> {
@@ -107,6 +124,7 @@ private void injectMockedStagesIntoPipeline(TransactionProcessingPipeline pipeli
pipeline.setHashingStage(hashingStage);
pipeline.setReplyStage(replyStage);
pipeline.setValidationStage(validationStage);
+ pipeline.setSolidifyStage(solidifyStage);
}
@Test
@@ -114,7 +132,7 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws
TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig,
transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker,
- transactionRequester);
+ transactionRequester, transactionSolidifier);
// inject mocks
injectMockedStagesIntoPipeline(pipeline);
@@ -136,7 +154,13 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws
// mock received
Mockito.when(broadcastCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.BROADCAST);
- Mockito.when(receivedStage.process(receivedCtx)).thenReturn(broadcastCtx);
+ Mockito.when(broadcastCtx.getPayload()).thenReturn(broadcastPayload);
+ Mockito.when(receivedStage.process(receivedCtx)).thenReturn(solidifyCtx);
+
+ // mock solidify
+ Mockito.when(solidifyCtx.getPayload()).thenReturn(solidifyPayload);
+ Mockito.when(solidifyCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.SOLIDIFY);
+ Mockito.when(solidifyStage.process(solidifyCtx)).thenReturn(broadcastCtx);
pipeline.start();
@@ -152,6 +176,7 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws
Mockito.verify(validationStage).process(Mockito.any());
Mockito.verify(receivedStage).process(Mockito.any());
Mockito.verify(replyStage).process(Mockito.any());
+ Mockito.verify(solidifyStage).process(Mockito.any());
Mockito.verify(broadcastStage).process(Mockito.any());
}
@@ -159,7 +184,7 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws
public void processingAKnownTransactionOnlyFlowsToTheReplyStage() throws InterruptedException {
TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig,
transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker,
- transactionRequester);
+ transactionRequester, transactionSolidifier);
// inject mocks
pipeline.setPreProcessStage(preProcessStage);
@@ -181,6 +206,7 @@ public void processingAKnownTransactionOnlyFlowsToTheReplyStage() throws Interru
Mockito.verify(hashingStage, Mockito.never()).process(Mockito.any());
Mockito.verify(validationStage, Mockito.never()).process(Mockito.any());
Mockito.verify(receivedStage, Mockito.never()).process(Mockito.any());
+ Mockito.verify(solidifyStage, Mockito.never()).process(Mockito.any());
Mockito.verify(broadcastStage, Mockito.never()).process(Mockito.any());
// should have called
@@ -193,7 +219,7 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug
throws InterruptedException {
TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig,
transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker,
- transactionRequester);
+ transactionRequester, transactionSolidifier);
// inject mocks
injectMockedStagesIntoPipeline(pipeline);
@@ -202,7 +228,6 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug
Mockito.when(hashingCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.HASHING);
Mockito.when(hashingCtx.getPayload()).thenReturn(hashingPayload);
- // mock hashing context/stage
// mock hashing context/stage
mockHashingStage(pipeline);
@@ -212,7 +237,12 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug
// mock received
Mockito.when(broadcastCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.BROADCAST);
- Mockito.when(receivedStage.process(receivedCtx)).thenReturn(broadcastCtx);
+ Mockito.when(receivedStage.process(receivedCtx)).thenReturn(solidifyCtx);
+
+ // mock solidify
+ Mockito.when(solidifyCtx.getPayload()).thenReturn(solidifyPayload);
+ Mockito.when(solidifyCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.SOLIDIFY);
+ Mockito.when(solidifyStage.process(solidifyCtx)).thenReturn(broadcastCtx);
pipeline.start();
@@ -231,6 +261,7 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug
Mockito.verify(hashingStage).process(Mockito.any());
Mockito.verify(validationStage).process(Mockito.any());
Mockito.verify(receivedStage).process(Mockito.any());
+ Mockito.verify(solidifyStage).process(Mockito.any());
Mockito.verify(broadcastStage).process(Mockito.any());
}
@@ -238,7 +269,7 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug
public void anInvalidNewTransactionStopsBeingProcessedAfterTheValidationStage() throws InterruptedException {
TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig,
transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker,
- transactionRequester);
+ transactionRequester, transactionSolidifier);
// inject mocks
injectMockedStagesIntoPipeline(pipeline);
@@ -269,6 +300,7 @@ public void anInvalidNewTransactionStopsBeingProcessedAfterTheValidationStage()
Mockito.verify(preProcessStage, Mockito.never()).process(Mockito.any());
Mockito.verify(broadcastStage, Mockito.never()).process(Mockito.any());
Mockito.verify(receivedStage, Mockito.never()).process(Mockito.any());
+ Mockito.verify(solidifyStage, Mockito.never()).process(Mockito.any());
// should have called
Mockito.verify(hashingStage).process(Mockito.any());
diff --git a/src/test/java/com/iota/iri/service/APITest.java b/src/test/java/com/iota/iri/service/APITest.java
index 9643959f54..7169f339ad 100644
--- a/src/test/java/com/iota/iri/service/APITest.java
+++ b/src/test/java/com/iota/iri/service/APITest.java
@@ -1,5 +1,6 @@
package com.iota.iri.service;
+import com.iota.iri.service.validation.TransactionSolidifier;
import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.conf.IotaConfig;
import com.iota.iri.controllers.TransactionViewModel;
@@ -28,6 +29,9 @@ public class APITest {
@Mock(answer = Answers.RETURNS_SMART_NULLS)
private TransactionValidator transactionValidator;
+ @Mock
+ private TransactionSolidifier transactionSolidifier;
+
@Mock
private SnapshotProvider snapshotProvider;
@@ -43,7 +47,7 @@ public void whenStoreTransactionsStatementThenSetArrivalTimeToCurrentMillis() th
API api = new API(config, null, null, null,
null, null,
snapshotProvider, null, null, null, null,
- transactionValidator, null, null);
+ transactionValidator, null, null, transactionSolidifier);
api.storeTransactionsStatement(Collections.singletonList("FOO"));
diff --git a/src/test/java/com/iota/iri/service/ApiCallTest.java b/src/test/java/com/iota/iri/service/ApiCallTest.java
index a6f4f0de39..d2ca7dc9b0 100644
--- a/src/test/java/com/iota/iri/service/ApiCallTest.java
+++ b/src/test/java/com/iota/iri/service/ApiCallTest.java
@@ -14,7 +14,7 @@ public class ApiCallTest {
@Before
public void setUp() {
IotaConfig configuration = Mockito.mock(IotaConfig.class);
- api = new API(configuration, null, null, null, null, null, null, null, null, null, null, null, null, null);
+ api = new API(configuration, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
}
@Test
diff --git a/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java b/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java
index 99a39ea952..c2cb3c99fd 100644
--- a/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java
+++ b/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java
@@ -1,34 +1,27 @@
package com.iota.iri.service.validation;
-import com.iota.iri.TransactionTestUtils;
import com.iota.iri.conf.MainnetConfig;
import com.iota.iri.conf.ProtocolConfig;
-
-import com.iota.iri.controllers.TipsViewModel;
-import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.crypto.SpongeFactory;
-import com.iota.iri.model.TransactionHash;
import com.iota.iri.network.TransactionRequester;
import com.iota.iri.service.snapshot.SnapshotProvider;
-
import com.iota.iri.service.snapshot.impl.SnapshotMockUtils;
-import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.storage.Tangle;
import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider;
import com.iota.iri.utils.Converter;
-import org.junit.*;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.Rule;
+import org.junit.AfterClass;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import static com.iota.iri.TransactionTestUtils.getTransactionHash;
import static com.iota.iri.TransactionTestUtils.getTransactionTrits;
-import static com.iota.iri.TransactionTestUtils.getTransactionTritsWithTrunkAndBranch;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -46,14 +39,17 @@ public class TransactionValidatorTest {
@Mock
private static SnapshotProvider snapshotProvider;
+ @Mock
+ private static TransactionRequester txRequester;
+
@BeforeClass
public static void setUp() throws Exception {
dbFolder.create();
logFolder.create();
tangle = new Tangle();
tangle.addPersistenceProvider(
- new RocksDBPersistenceProvider(
- dbFolder.getRoot().getAbsolutePath(), logFolder.getRoot().getAbsolutePath(),1000, Tangle.COLUMN_FAMILIES, Tangle.METADATA_COLUMN_FAMILY));
+ new RocksDBPersistenceProvider(
+ dbFolder.getRoot().getAbsolutePath(), logFolder.getRoot().getAbsolutePath(),1000, Tangle.COLUMN_FAMILIES, Tangle.METADATA_COLUMN_FAMILY));
tangle.init();
}
@@ -67,9 +63,8 @@ public static void tearDown() throws Exception {
@Before
public void setUpEach() {
when(snapshotProvider.getInitialSnapshot()).thenReturn(SnapshotMockUtils.createSnapshot());
- TipsViewModel tipsViewModel = new TipsViewModel();
- TransactionRequester txRequester = new TransactionRequester(tangle, snapshotProvider);
- txValidator = new TransactionValidator(tangle, snapshotProvider, tipsViewModel, txRequester, new MainnetConfig());
+ txRequester = new TransactionRequester(tangle, snapshotProvider);
+ txValidator = new TransactionValidator(snapshotProvider, txRequester, new MainnetConfig());
txValidator.setMwm(false, MAINNET_MWM);
}
@@ -77,7 +72,7 @@ public void setUpEach() {
public void testMinMwm() {
ProtocolConfig protocolConfig = mock(ProtocolConfig.class);
when(protocolConfig.getMwm()).thenReturn(5);
- TransactionValidator transactionValidator = new TransactionValidator(null, null, null, null, protocolConfig);
+ TransactionValidator transactionValidator = new TransactionValidator(null, null, protocolConfig);
assertEquals("Expected testnet minimum minWeightMagnitude", 13, transactionValidator.getMinWeightMagnitude());
}
@@ -102,125 +97,4 @@ public void validateBytesWithNewCurl() {
Converter.bytes(trits, 0, bytes, 0, trits.length);
txValidator.validateBytes(bytes, txValidator.getMinWeightMagnitude(), SpongeFactory.create(SpongeFactory.Mode.CURLP81));
}
-
- @Test
- public void verifyTxIsSolid() throws Exception {
- TransactionViewModel tx = getTxWithBranchAndTrunk();
- assertTrue(txValidator.checkSolidity(tx.getHash()));
- assertTrue(txValidator.checkSolidity(tx.getHash()));
- }
-
- @Test
- public void verifyTxIsNotSolid() throws Exception {
- TransactionViewModel tx = getTxWithoutBranchAndTrunk();
- assertFalse(txValidator.checkSolidity(tx.getHash()));
- assertFalse(txValidator.checkSolidity(tx.getHash()));
- }
-
- @Test
- public void addSolidTransactionWithoutErrors() {
- byte[] trits = getTransactionTrits();
- Converter.copyTrits(0, trits, 0, trits.length);
- txValidator.addSolidTransaction(TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits));
- }
-
- private TransactionViewModel getTxWithBranchAndTrunk() throws Exception {
- TransactionViewModel tx, trunkTx, branchTx;
- String trytes = "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CFDEZBLZQYA9999999999999999999999999999999999999999999ZZWQHWD99C99999999C99999999CKWWDBWSCLMQULCTAAJGXDEMFJXPMGMAQIHDGHRBGEMUYNNCOK9YPHKEEFLFCZUSPMCJHAKLCIBQSGWAS999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999";
-
- byte[] trits = Converter.allocateTritsForTrytes(trytes.length());
- Converter.trits(trytes, trits, 0);
- trunkTx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits));
- branchTx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits));
-
- byte[] childTx = getTransactionTrits();
- System.arraycopy(trunkTx.getHash().trits(), 0, childTx, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_SIZE);
- System.arraycopy(branchTx.getHash().trits(), 0, childTx, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_SIZE);
- tx = new TransactionViewModel(childTx, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, childTx));
-
- trunkTx.store(tangle, snapshotProvider.getInitialSnapshot());
- branchTx.store(tangle, snapshotProvider.getInitialSnapshot());
- tx.store(tangle, snapshotProvider.getInitialSnapshot());
-
- return tx;
- }
-
- @Test
- public void testTransactionPropagation() throws Exception {
- TransactionViewModel leftChildLeaf = TransactionTestUtils.createTransactionWithTrytes("CHILDTX");
- leftChildLeaf.updateSolid(true);
- leftChildLeaf.store(tangle, snapshotProvider.getInitialSnapshot());
-
- TransactionViewModel rightChildLeaf = TransactionTestUtils.createTransactionWithTrytes("CHILDTWOTX");
- rightChildLeaf.updateSolid(true);
- rightChildLeaf.store(tangle, snapshotProvider.getInitialSnapshot());
-
- TransactionViewModel parent = TransactionTestUtils.createTransactionWithTrunkAndBranch("PARENT",
- leftChildLeaf.getHash(), rightChildLeaf.getHash());
- parent.updateSolid(false);
- parent.store(tangle, snapshotProvider.getInitialSnapshot());
-
- TransactionViewModel parentSibling = TransactionTestUtils.createTransactionWithTrytes("PARENTLEAF");
- parentSibling.updateSolid(true);
- parentSibling.store(tangle, snapshotProvider.getInitialSnapshot());
-
- TransactionViewModel grandParent = TransactionTestUtils.createTransactionWithTrunkAndBranch("GRANDPARENT", parent.getHash(),
- parentSibling.getHash());
- grandParent.updateSolid(false);
- grandParent.store(tangle, snapshotProvider.getInitialSnapshot());
-
- txValidator.addSolidTransaction(leftChildLeaf.getHash());
- while (!txValidator.isNewSolidTxSetsEmpty()) {
- txValidator.propagateSolidTransactions();
- }
-
- parent = TransactionViewModel.fromHash(tangle, parent.getHash());
- assertTrue("Parent tx was expected to be solid", parent.isSolid());
- grandParent = TransactionViewModel.fromHash(tangle, grandParent.getHash());
- assertTrue("Grandparent was expected to be solid", grandParent.isSolid());
- }
-
- @Test
- public void testTransactionPropagationFailure() throws Exception {
- TransactionViewModel leftChildLeaf = new TransactionViewModel(getTransactionTrits(), getTransactionHash());
- leftChildLeaf.updateSolid(true);
- leftChildLeaf.store(tangle, snapshotProvider.getInitialSnapshot());
-
- TransactionViewModel rightChildLeaf = new TransactionViewModel(getTransactionTrits(), getTransactionHash());
- rightChildLeaf.updateSolid(true);
- rightChildLeaf.store(tangle, snapshotProvider.getInitialSnapshot());
-
- TransactionViewModel parent = new TransactionViewModel(getTransactionTritsWithTrunkAndBranch(leftChildLeaf.getHash(),
- rightChildLeaf.getHash()), getTransactionHash());
- parent.updateSolid(false);
- parent.store(tangle, snapshotProvider.getInitialSnapshot());
-
- TransactionViewModel parentSibling = new TransactionViewModel(getTransactionTrits(), getTransactionHash());
- parentSibling.updateSolid(false);
- parentSibling.store(tangle, snapshotProvider.getInitialSnapshot());
-
- TransactionViewModel grandParent = new TransactionViewModel(getTransactionTritsWithTrunkAndBranch(parent.getHash(),
- parentSibling.getHash()), getTransactionHash());
- grandParent.updateSolid(false);
- grandParent.store(tangle, snapshotProvider.getInitialSnapshot());
-
- txValidator.addSolidTransaction(leftChildLeaf.getHash());
- while (!txValidator.isNewSolidTxSetsEmpty()) {
- txValidator.propagateSolidTransactions();
- }
-
- parent = TransactionViewModel.fromHash(tangle, parent.getHash());
- assertTrue("Parent tx was expected to be solid", parent.isSolid());
- grandParent = TransactionViewModel.fromHash(tangle, grandParent.getHash());
- assertFalse("GrandParent tx was expected to be not solid", grandParent.isSolid());
- }
-
- private TransactionViewModel getTxWithoutBranchAndTrunk() throws Exception {
- byte[] trits = getTransactionTrits();
- TransactionViewModel tx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits));
-
- tx.store(tangle, snapshotProvider.getInitialSnapshot());
-
- return tx;
- }
-}
+}
\ No newline at end of file
diff --git a/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java
new file mode 100644
index 0000000000..69fe1eb046
--- /dev/null
+++ b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java
@@ -0,0 +1,160 @@
+package com.iota.iri.service.validation.impl;
+
+import com.iota.iri.controllers.TipsViewModel;
+import com.iota.iri.controllers.TransactionViewModel;
+import com.iota.iri.crypto.SpongeFactory;
+import com.iota.iri.model.TransactionHash;
+import com.iota.iri.network.TransactionRequester;
+import com.iota.iri.service.snapshot.SnapshotProvider;
+import com.iota.iri.service.snapshot.impl.SnapshotMockUtils;
+import com.iota.iri.storage.Tangle;
+import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider;
+import com.iota.iri.utils.Converter;
+import org.junit.Rule;
+import org.junit.BeforeClass;
+import org.junit.Before;
+import org.junit.AfterClass;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import static com.iota.iri.TransactionTestUtils.getTransactionTrits;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+public class TransactionSolidifierImplTest {
+ private static final TemporaryFolder dbFolder = new TemporaryFolder();
+ private static final TemporaryFolder logFolder = new TemporaryFolder();
+ private static Tangle tangle;
+
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private static SnapshotProvider snapshotProvider;
+
+ @Mock
+ private static TransactionSolidifierImpl txSolidifier;
+
+ @Mock
+ private static TipsViewModel tipsViewModel;
+
+ @Mock
+ private static TransactionRequester txRequester;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ dbFolder.create();
+ logFolder.create();
+ tangle = new Tangle();
+ tangle.addPersistenceProvider(
+ new RocksDBPersistenceProvider(
+ dbFolder.getRoot().getAbsolutePath(), logFolder.getRoot().getAbsolutePath(),1000, Tangle.COLUMN_FAMILIES, Tangle.METADATA_COLUMN_FAMILY));
+ tangle.init();
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ tangle.shutdown();
+ dbFolder.delete();
+ logFolder.delete();
+ }
+
+ @Before
+ public void setUpEach() {
+ when(snapshotProvider.getInitialSnapshot()).thenReturn(SnapshotMockUtils.createSnapshot());
+ txRequester = new TransactionRequester(tangle, snapshotProvider);
+ txSolidifier = new TransactionSolidifierImpl(tangle, snapshotProvider, txRequester, tipsViewModel);
+ txSolidifier.start();
+ }
+
+ @After
+ public void tearDownEach(){
+ txSolidifier.shutdown();
+ }
+
+
+ @Test
+ public void verifyTxIsSolid() throws Exception {
+ TransactionViewModel tx = getTxWithBranchAndTrunk();
+ assertTrue("Expected transaction to be solid", txSolidifier.checkSolidity(tx.getHash()));
+ assertTrue("Expected transaction to be solid", txSolidifier.checkSolidity(tx.getHash()));
+ }
+
+ @Test
+ public void verifyTxIsNotSolid() throws Exception {
+ TransactionViewModel tx = getTxWithoutBranchAndTrunk();
+ assertFalse("Expected transaction to fail solidity check", txSolidifier.checkSolidity(tx.getHash()));
+ assertFalse("Expected transaction to fail solidity check", txSolidifier.checkSolidity(tx.getHash()));
+ }
+
+ @Test
+ public void getSolidificationQueue() throws Exception {
+ TransactionViewModel mainTx = getTxWithBranchAndTrunk();
+ for(int i = 0; i < 10; i++) {
+ TransactionViewModel tx = getTxWithBranchAndTrunk();
+ txSolidifier.addToSolidificationQueue(tx.getHash());
+ }
+ txSolidifier.addToSolidificationQueue(mainTx.getHash());
+ assertTrue("Expected transaction to be present in the solidification queue",
+ txSolidifier.getSolidificationQueue().contains(mainTx.getHash()));
+ }
+
+ @Test
+ public void verifyTransactionIsProcessedFully() throws Exception {
+ TransactionViewModel tx = getTxWithBranchAndTrunk();
+ txSolidifier.addToSolidificationQueue(tx.getHash());
+
+ //Time to process through the steps
+ Thread.sleep(1000);
+ assertTrue("Expected transaction to be present in the broadcast queue",
+ txSolidifier.getBroadcastQueue().contains(tx));
+ }
+
+
+ @Test
+ public void verifyInconsistentTransactionIsNotProcessedFully() throws Exception {
+ TransactionViewModel tx = getTxWithoutBranchAndTrunk();
+ txSolidifier.addToSolidificationQueue(tx.getHash());
+
+ //Time to process through the steps
+ Thread.sleep(1000);
+ assertFalse("Expected transaction not to be present in the broadcast queue",
+ txSolidifier.getBroadcastQueue().contains(tx));
+ }
+
+ private TransactionViewModel getTxWithBranchAndTrunk() throws Exception {
+ TransactionViewModel tx, trunkTx, branchTx;
+ String trytes = "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CFDEZBLZQYA9999999999999999999999999999999999999999999ZZWQHWD99C99999999C99999999CKWWDBWSCLMQULCTAAJGXDEMFJXPMGMAQIHDGHRBGEMUYNNCOK9YPHKEEFLFCZUSPMCJHAKLCIBQSGWAS999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999";
+
+ byte[] trits = Converter.allocateTritsForTrytes(trytes.length());
+ Converter.trits(trytes, trits, 0);
+ trunkTx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits));
+ branchTx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits));
+
+ byte[] childTx = getTransactionTrits();
+ System.arraycopy(trunkTx.getHash().trits(), 0, childTx, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_SIZE);
+ System.arraycopy(branchTx.getHash().trits(), 0, childTx, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_SIZE);
+ tx = new TransactionViewModel(childTx, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, childTx));
+
+ trunkTx.store(tangle, snapshotProvider.getInitialSnapshot());
+ branchTx.store(tangle, snapshotProvider.getInitialSnapshot());
+ tx.store(tangle, snapshotProvider.getInitialSnapshot());
+
+ return tx;
+ }
+
+ private TransactionViewModel getTxWithoutBranchAndTrunk() throws Exception {
+ byte[] trits = getTransactionTrits();
+ TransactionViewModel tx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits));
+
+ tx.store(tangle, snapshotProvider.getInitialSnapshot());
+
+ return tx;
+ }
+
+}
\ No newline at end of file
From 7ad63941db1af9d9eb688efb9b0b0b32a9b555a9 Mon Sep 17 00:00:00 2001
From: DyrellC
Date: Thu, 26 Mar 2020 14:48:19 -0600
Subject: [PATCH 05/18] Remove tip field from solidify stage
---
.../iri/network/pipeline/SolidifyStage.java | 22 +++++--------------
.../network/pipeline/SolidifyStageTest.java | 3 +--
2 files changed, 7 insertions(+), 18 deletions(-)
diff --git a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java
index 85d4ee85ac..07bbaf0def 100644
--- a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java
+++ b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java
@@ -25,7 +25,6 @@ public class SolidifyStage implements Stage {
private TransactionSolidifier txSolidifier;
private TipsViewModel tipsViewModel;
private Tangle tangle;
- private TransactionViewModel tip;
/**
* Constructor for the {@link SolidifyStage}.
@@ -71,27 +70,18 @@ public ProcessingContext process(ProcessingContext ctx){
}
private ProcessingContext broadcastTip(ProcessingContext ctx, SolidifyPayload payload) throws Exception{
- if(tip == null) {
- Hash tipHash = tipsViewModel.getRandomSolidTipHash();
+ Hash tipHash = tipsViewModel.getRandomSolidTipHash();
- if (tipHash == null) {
- ctx.setNextStage(TransactionProcessingPipeline.Stage.FINISH);
- return ctx;
- }
-
- tip = fromHash(tangle, tipHash);
+ if (tipHash == null) {
+ ctx.setNextStage(TransactionProcessingPipeline.Stage.FINISH);
+ return ctx;
}
+ TransactionViewModel tip = fromHash(tangle, tipHash);
+
ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST);
ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), tip));
- tip = null;
return ctx;
}
-
- @VisibleForTesting
- void injectTip(TransactionViewModel tvm) throws Exception {
- tip = tvm;
- tip.updateSolid(true);
- }
}
diff --git a/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java
index 22f62411dc..be3582bf54 100644
--- a/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java
+++ b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java
@@ -79,13 +79,12 @@ public void quickSetSolidTransactionIsBroadcast() throws Exception{
public void unsolidTransactionBroadcastsRandomSolidTip() throws Exception{
Mockito.when(tvm.isSolid()).thenReturn(false);
Mockito.when(transactionSolidifier.quickSetSolid(tvm)).thenReturn(false);
- TransactionViewModel tip = new TransactionViewModel(new Transaction(), tipHash);
+ Mockito.when(tipsViewModel.getRandomSolidTipHash()).thenReturn(tipHash);
SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle);
SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm);
ProcessingContext ctx = new ProcessingContext(solidifyPayload);
- solidifyStage.injectTip(tip);
solidifyStage.process(ctx);
Thread.sleep(100);
From bff1915d73bc651053e6703159f168859bf0eec3 Mon Sep 17 00:00:00 2001
From: DyrellC
Date: Thu, 26 Mar 2020 17:06:29 -0600
Subject: [PATCH 06/18] Move broadcast queue retreival to solidify stage
---
.../iri/network/pipeline/SolidifyStage.java | 21 ++++++++++------
.../TransactionProcessingPipeline.java | 6 -----
.../TransactionProcessingPipelineImpl.java | 17 -------------
.../validation/TransactionSolidifier.java | 14 +++++------
.../impl/TransactionSolidifierImpl.java | 10 ++++----
.../impl/TransactionSolidifierImplTest.java | 24 +++++++++++++++++--
6 files changed, 46 insertions(+), 46 deletions(-)
diff --git a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java
index 07bbaf0def..4db01d6a7c 100644
--- a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java
+++ b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java
@@ -1,6 +1,5 @@
package com.iota.iri.network.pipeline;
-import com.google.common.annotations.VisibleForTesting;
import com.iota.iri.controllers.TipsViewModel;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.model.Hash;
@@ -55,6 +54,8 @@ public ProcessingContext process(ProcessingContext ctx){
TransactionViewModel tvm = payload.getTransaction();
if (tvm.isSolid() || txSolidifier.quickSetSolid(tvm)) {
+ // If the transaction is in the solidifier broadcast queue, remove it as it will be broadcast now
+ txSolidifier.clearFromBroadcastQueue(tvm);
ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST);
ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), payload.getTransaction()));
return ctx;
@@ -70,14 +71,20 @@ public ProcessingContext process(ProcessingContext ctx){
}
private ProcessingContext broadcastTip(ProcessingContext ctx, SolidifyPayload payload) throws Exception{
- Hash tipHash = tipsViewModel.getRandomSolidTipHash();
+ // First check if there is a transaction available to broadcast from the broadcast queue
+ TransactionViewModel tip = txSolidifier.getNextTxInBroadcastQueue();
- if (tipHash == null) {
- ctx.setNextStage(TransactionProcessingPipeline.Stage.FINISH);
- return ctx;
- }
+ // If there is not a transaction available from the broadcast queue, instead try to send a solid tip
+ if (tip == null) {
+ Hash tipHash = tipsViewModel.getRandomSolidTipHash();
+
+ if (tipHash == null) {
+ ctx.setNextStage(TransactionProcessingPipeline.Stage.FINISH);
+ return ctx;
+ }
- TransactionViewModel tip = fromHash(tangle, tipHash);
+ tip = fromHash(tangle, tipHash);
+ }
ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST);
ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), tip));
diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java
index 081deb810a..9c471a91c0 100644
--- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java
+++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java
@@ -66,12 +66,6 @@ enum Stage {
*/
void process(byte[] txTrits);
- /**
- * Fetches a set of transactions from the {@link TransactionSolidifier} and submits
- * the object into the {@link BroadcastStage} queue.
- */
- void refillBroadcastQueue();
-
/**
* Shut downs the pipeline by shutting down all stages.
*/
diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java
index 8f8c57d753..a847c1b35e 100644
--- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java
+++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java
@@ -199,7 +199,6 @@ public BlockingQueue getValidationStageQueue() {
public void process(Neighbor neighbor, ByteBuffer data) {
try {
preProcessStageQueue.put(new ProcessingContext(new PreProcessPayload(neighbor, data)));
- refillBroadcastQueue();
} catch (InterruptedException e) {
e.printStackTrace();
}
@@ -214,22 +213,6 @@ public void process(byte[] txTrits) {
hashAndValidate(new ProcessingContext(payload));
}
- @Override
- public void refillBroadcastQueue(){
- try{
- Iterator hashIterator = txSolidifier.getBroadcastQueue().iterator();
- Set toRemove = new LinkedHashSet<>();
- while(!Thread.currentThread().isInterrupted() && hashIterator.hasNext()){
- TransactionViewModel tx = hashIterator.next();
- broadcastStageQueue.put(new ProcessingContext(new BroadcastPayload(null, tx)));
- toRemove.add(tx);
- hashIterator.remove();
- }
- txSolidifier.clearFromBroadcastQueue(toRemove);
- } catch(InterruptedException e){
- log.info(e.getMessage());
- }
- }
/**
* Sets up the given hashing stage {@link ProcessingContext} so that up on success, it will submit further to the
diff --git a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java
index e79098318c..9dd8273cb4 100644
--- a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java
+++ b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java
@@ -6,8 +6,6 @@
import com.iota.iri.service.validation.impl.TransactionSolidifierImpl;
import com.iota.iri.network.TransactionRequester;
-import java.util.Set;
-
/**
* Solidification tool. Transactions placed into the solidification queue will be checked for solidity. Any missing
* reference transactions will be placed into the {@link TransactionRequester}. If a transaction is found to be solid
@@ -42,16 +40,16 @@ public interface TransactionSolidifier {
*/
boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess);
/**
- * Fetch a copy of the current transactionsToBroadcast set.
- * @return A set of {@link TransactionViewModel} objects to be broadcast.
+ * Fetch the next transaction in the transactionsToBroadcast set.
+ * @return A {@link TransactionViewModel} object to be broadcast.
*/
- Set getBroadcastQueue();
+ TransactionViewModel getNextTxInBroadcastQueue();
/**
- * Remove any broadcasted transactions from the transactionsToBroadcast set
- * @param transactionsBroadcasted A set of {@link TransactionViewModel} objects to remove from the set.
+ * Remove a broadcasted transaction from the transactionsToBroadcast set
+ * @param transactionsBroadcasted A {@link TransactionViewModel} object to remove from the set.
*/
- void clearFromBroadcastQueue(Set transactionsBroadcasted);
+ void clearFromBroadcastQueue(TransactionViewModel transactionsBroadcasted);
/**
* This method does the same as {@link #checkSolidity(Hash, int)} but defaults to an unlimited amount
diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java
index 19ef32f7c8..7608f871e9 100644
--- a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java
+++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java
@@ -137,18 +137,16 @@ public boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess){
*{@inheritDoc}
*/
@Override
- public Set getBroadcastQueue(){
- return new LinkedHashSet<>(transactionsToBroadcast);
+ public TransactionViewModel getNextTxInBroadcastQueue(){
+ return transactionsToBroadcast.poll();
}
/**
*{@inheritDoc}
*/
@Override
- public void clearFromBroadcastQueue(Set transactionsBroadcasted){
- for (TransactionViewModel tvm : transactionsBroadcasted) {
- transactionsToBroadcast.remove(tvm);
- }
+ public void clearFromBroadcastQueue(TransactionViewModel transaction){
+ transactionsToBroadcast.remove(transaction);
}
diff --git a/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java
index 69fe1eb046..0220fcaa82 100644
--- a/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java
+++ b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java
@@ -21,6 +21,9 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+import java.util.List;
+
import static com.iota.iri.TransactionTestUtils.getTransactionTrits;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -31,6 +34,9 @@ public class TransactionSolidifierImplTest {
private static final TemporaryFolder logFolder = new TemporaryFolder();
private static Tangle tangle;
+ private List broadcastTransactions = new ArrayList<>();
+ private TransactionViewModel transactionForBroadcast;
+
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@@ -111,8 +117,15 @@ public void verifyTransactionIsProcessedFully() throws Exception {
//Time to process through the steps
Thread.sleep(1000);
+
+ while((transactionForBroadcast = txSolidifier.getNextTxInBroadcastQueue()) != null){
+ broadcastTransactions.add(transactionForBroadcast);
+ }
+
assertTrue("Expected transaction to be present in the broadcast queue",
- txSolidifier.getBroadcastQueue().contains(tx));
+ broadcastTransactions.contains(tx));
+
+ broadcastTransactions.clear();
}
@@ -123,8 +136,15 @@ public void verifyInconsistentTransactionIsNotProcessedFully() throws Exception
//Time to process through the steps
Thread.sleep(1000);
+
+ while((transactionForBroadcast = txSolidifier.getNextTxInBroadcastQueue()) != null){
+ broadcastTransactions.add(transactionForBroadcast);
+ }
+
assertFalse("Expected transaction not to be present in the broadcast queue",
- txSolidifier.getBroadcastQueue().contains(tx));
+ broadcastTransactions.contains(tx));
+
+ broadcastTransactions.clear();
}
private TransactionViewModel getTxWithBranchAndTrunk() throws Exception {
From 0f09a05c0c6253adfb28ef0d7ad6bf4eecdcdec2 Mon Sep 17 00:00:00 2001
From: DyrellC
Date: Thu, 26 Mar 2020 17:27:31 -0600
Subject: [PATCH 07/18] Undo auto-formatting
---
src/main/java/com/iota/iri/Iota.java | 26 +-
src/main/java/com/iota/iri/service/API.java | 324 ++++++++++----------
2 files changed, 175 insertions(+), 175 deletions(-)
diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java
index 8b0f871ff4..420cc51c71 100644
--- a/src/main/java/com/iota/iri/Iota.java
+++ b/src/main/java/com/iota/iri/Iota.java
@@ -121,16 +121,16 @@ public class Iota {
*
*/
public Iota(IotaConfig configuration, SpentAddressesProvider spentAddressesProvider,
- SpentAddressesService spentAddressesService, SnapshotProvider snapshotProvider,
- SnapshotService snapshotService, LocalSnapshotManager localSnapshotManager,
- MilestoneService milestoneService, LatestMilestoneTracker latestMilestoneTracker,
- LatestSolidMilestoneTracker latestSolidMilestoneTracker, SeenMilestonesRetriever seenMilestonesRetriever,
- LedgerService ledgerService, TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier,
- BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator,
- TransactionRequester transactionRequester, NeighborRouter neighborRouter,
- TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester,
- TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb,
- CacheManager cacheManager, TransactionSolidifier transactionSolidifier) {
+ SpentAddressesService spentAddressesService, SnapshotProvider snapshotProvider,
+ SnapshotService snapshotService, LocalSnapshotManager localSnapshotManager,
+ MilestoneService milestoneService, LatestMilestoneTracker latestMilestoneTracker,
+ LatestSolidMilestoneTracker latestSolidMilestoneTracker, SeenMilestonesRetriever seenMilestonesRetriever,
+ LedgerService ledgerService, TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier,
+ BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator,
+ TransactionRequester transactionRequester, NeighborRouter neighborRouter,
+ TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester,
+ TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb,
+ CacheManager cacheManager, TransactionSolidifier transactionSolidifier) {
this.configuration = configuration;
this.ledgerService = ledgerService;
@@ -316,10 +316,10 @@ private void initializeTangle() {
* @return A new Persistance provider
*/
private PersistenceProvider createRocksDbProvider(String path, String log, String configFile, int cacheSize,
- Map> columnFamily,
- Map.Entry> metadata) {
+ Map> columnFamily,
+ Map.Entry> metadata) {
return new RocksDBPersistenceProvider(
path, log, configFile, cacheSize, columnFamily, metadata);
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index 1471cd990d..78a01d960e 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -67,33 +67,33 @@
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.";
-
+ + "The subtangle has not been updated yet.";
+
private static final String OVER_MAX_ERROR_MESSAGE = "Could not complete request";
private static final String INVALID_PARAMS = "Invalid parameters";
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 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;
@@ -111,13 +111,13 @@ public class API {
private final TransactionValidator transactionValidator;
private final TransactionSolidifier transactionSolidifier;
private final LatestMilestoneTracker latestMilestoneTracker;
-
+
private final int maxFindTxs;
private final int maxRequestList;
private final int maxGetTrytes;
private final String[] features;
-
+
//endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////
private final Gson gson = new GsonBuilder().create();
@@ -136,9 +136,9 @@ public class API {
/**
* Starts loading the IOTA API, parameters do not have to be initialized.
- *
+ *
* @param configuration Holds IRI configuration parameters.
- * @param ixi If a command is not in the standard API,
+ * @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 Service where transactions get requested
* @param spentAddressesService Service to check if addresses are spent
@@ -156,14 +156,14 @@ public class API {
*
*/
public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRequester,
- SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator,
- SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter,
- TipSelector tipsSelector, TipsViewModel tipsViewModel, TransactionValidator transactionValidator,
- LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline,
- TransactionSolidifier transactionSolidifier) {
+ SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator,
+ SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter,
+ TipSelector tipsSelector, TipsViewModel tipsViewModel, TransactionValidator transactionValidator,
+ LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline,
+ TransactionSolidifier transactionSolidifier) {
this.configuration = configuration;
this.ixi = ixi;
-
+
this.transactionRequester = transactionRequester;
this.spentAddressesService = spentAddressesService;
this.tangle = tangle;
@@ -177,13 +177,13 @@ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRe
this.transactionValidator = transactionValidator;
this.transactionSolidifier = transactionSolidifier;
this.latestMilestoneTracker = latestMilestoneTracker;
-
+
maxFindTxs = configuration.getMaxFindTransactions();
maxRequestList = configuration.getMaxRequestsList();
maxGetTrytes = configuration.getMaxGetTrytes();
features = Feature.calculateFeatureNames(configuration);
-
+
commandRoute = new HashMap<>();
commandRoute.put(ApiCommand.ADD_NEIGHBORS, addNeighbors());
commandRoute.put(ApiCommand.ATTACH_TO_TANGLE, attachToTangle());
@@ -208,7 +208,7 @@ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRe
/**
* Initializes the API for usage.
* Will initialize and start the supplied {@link RestConnector}
- *
+ *
* @param connector THe connector we use to handle API requests
*/
public void init(RestConnector connector){
@@ -216,7 +216,7 @@ public void init(RestConnector connector){
connector.init(this::process);
connector.start();
}
-
+
/**
* Handles an API request body.
* Its returned {@link AbstractResponse} is created using the following logic
@@ -479,20 +479,20 @@ private AbstractResponse addNeighborsStatement(List uris) {
}
return AddedNeighborsResponse.create(numberOfAddedNeighbors);
}
-
+
/**
- * Temporarily removes a list of neighbors from your node.
- * The added neighbors will be added again after relaunching IRI.
- * Remove the neighbors from your config file or make sure you don't supply them in the -n command line option if you want to keep them removed after restart.
- *
- * The URI (Unique Resource Identification) for removing neighbors is:
- * udp://IPADDRESS:PORT
- *
- * Returns an {@link com.iota.iri.service.dto.ErrorResponse} if the URI scheme is wrong
- *
- * @param uris The URIs of the neighbors we want to remove.
- * @return {@link com.iota.iri.service.dto.RemoveNeighborsResponse}
- **/
+ * Temporarily removes a list of neighbors from your node.
+ * The added neighbors will be added again after relaunching IRI.
+ * Remove the neighbors from your config file or make sure you don't supply them in the -n command line option if you want to keep them removed after restart.
+ *
+ * The URI (Unique Resource Identification) for removing neighbors is:
+ * udp://IPADDRESS:PORT
+ *
+ * Returns an {@link com.iota.iri.service.dto.ErrorResponse} if the URI scheme is wrong
+ *
+ * @param uris The URIs of the neighbors we want to remove.
+ * @return {@link com.iota.iri.service.dto.RemoveNeighborsResponse}
+ **/
@Document(name="removeNeighbors")
private AbstractResponse removeNeighborsStatement(List uris) {
int numberOfRemovedNeighbors = 0;
@@ -521,13 +521,13 @@ private AbstractResponse removeNeighborsStatement(List uris) {
}
/**
- * Returns raw transaction data (trytes) of a specific transaction.
- * These trytes can then be converted into the actual transaction object.
- * See utility and {@link Transaction} functions in an IOTA library for more details.
- *
- * @param hashes The transaction hashes you want to get trytes from.
- * @return {@link com.iota.iri.service.dto.GetTrytesResponse}
- **/
+ * Returns raw transaction data (trytes) of a specific transaction.
+ * These trytes can then be converted into the actual transaction object.
+ * See utility and {@link Transaction} functions in an IOTA library for more details.
+ *
+ * @param hashes The transaction hashes you want to get trytes from.
+ * @return {@link com.iota.iri.service.dto.GetTrytesResponse}
+ **/
@Document(name="getTrytes")
private synchronized AbstractResponse getTrytesStatement(List hashes) throws Exception {
final List elements = new LinkedList<>();
@@ -579,17 +579,17 @@ private static void incEllapsedTimeGetTxToApprove(long ellapsedTime) {
}
/**
- * Tip selection which returns trunkTransaction and branchTransaction.
- * The input value depth determines how many milestones to go back for finding the transactions to approve.
- * The higher your depth value, the more work you have to do as you are confirming more transactions.
- * If the depth is too large (usually above 15, it depends on the node's configuration) an error will be returned.
- * The reference is an optional hash of a transaction you want to approve.
- * If it can't be found at the specified depth then an error will be returned.
- *
- * @param depth Number of bundles to go back to determine the transactions for approval.
- * @param reference Hash of transaction to start random-walk from, used to make sure the tips returned reference a given transaction in their past.
- * @return {@link com.iota.iri.service.dto.GetTransactionsToApproveResponse}
- **/
+ * Tip selection which returns trunkTransaction and branchTransaction.
+ * The input value depth determines how many milestones to go back for finding the transactions to approve.
+ * The higher your depth value, the more work you have to do as you are confirming more transactions.
+ * If the depth is too large (usually above 15, it depends on the node's configuration) an error will be returned.
+ * The reference is an optional hash of a transaction you want to approve.
+ * If it can't be found at the specified depth then an error will be returned.
+ *
+ * @param depth Number of bundles to go back to determine the transactions for approval.
+ * @param reference Hash of transaction to start random-walk from, used to make sure the tips returned reference a given transaction in their past.
+ * @return {@link com.iota.iri.service.dto.GetTransactionsToApproveResponse}
+ **/
@Document(name="getTransactionsToApprove")
private synchronized AbstractResponse getTransactionsToApproveStatement(int depth, Optional reference) {
if (depth < 0 || depth > configuration.getMaxDepth()) {
@@ -641,7 +641,7 @@ List getTransactionToApproveTips(int depth, Optional reference) thro
/**
*
- * Handles statistics on tip selection.
+ * Handles statistics on tip selection.
* Increases the tip selection by one use.
*
*
@@ -662,10 +662,10 @@ private void gatherStatisticsOnTipSelection() {
}
/**
- * Returns all tips currently known by this node.
- *
- * @return {@link com.iota.iri.service.dto.GetTipsResponse}
- **/
+ * Returns all tips currently known by this node.
+ *
+ * @return {@link com.iota.iri.service.dto.GetTipsResponse}
+ **/
@Document(name="getTips")
private synchronized AbstractResponse getTipsStatement() throws Exception {
return GetTipsResponse.create(tipsViewModel.getTips()
@@ -675,14 +675,14 @@ private synchronized AbstractResponse getTipsStatement() throws Exception {
}
/**
- * Stores transactions in the local storage.
- * The trytes to be used for this call should be valid, attached transaction trytes.
- * These trytes are returned by attachToTangle, or by doing proof of work somewhere else.
- *
- * @param trytes Transaction data to be stored.
- * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness}
- * @throws Exception When storing or updating a transaction fails.
- **/
+ * Stores transactions in the local storage.
+ * The trytes to be used for this call should be valid, attached transaction trytes.
+ * These trytes are returned by attachToTangle, or by doing proof of work somewhere else.
+ *
+ * @param trytes Transaction data to be stored.
+ * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness}
+ * @throws Exception When storing or updating a transaction fails.
+ **/
@Document(name="storeTransactions")
public AbstractResponse storeTransactionsStatement(List trytes) throws Exception {
final List elements = convertTrytes(trytes);
@@ -699,10 +699,10 @@ public AbstractResponse storeTransactionsStatement(List trytes) throws E
}
/**
- * Interrupts and completely aborts the attachToTangle process.
- *
- * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness}
- **/
+ * Interrupts and completely aborts the attachToTangle process.
+ *
+ * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness}
+ **/
@Document(name="interruptAttachingToTangle")
private AbstractResponse interruptAttachingToTangleStatement(){
pearlDiver.cancel();
@@ -710,16 +710,16 @@ private AbstractResponse interruptAttachingToTangleStatement(){
}
/**
- * Returns information about this node.
- *
- * @return {@link com.iota.iri.service.dto.GetNodeInfoResponse}
- * @throws Exception When we cant find the first milestone in the database
- **/
+ * Returns information about this node.
+ *
+ * @return {@link com.iota.iri.service.dto.GetNodeInfoResponse}
+ * @throws Exception When we cant find the first milestone in the database
+ **/
@Document(name="getNodeInfo")
private AbstractResponse getNodeInfoStatement() throws Exception{
String name = configuration.isTestnet() ? IRI.TESTNET_NAME : IRI.MAINNET_NAME;
MilestoneViewModel milestone = MilestoneViewModel.first(tangle);
-
+
return GetNodeInfoResponse.create(
name,
IotaUtils.getIriVersion(),
@@ -731,13 +731,13 @@ private AbstractResponse getNodeInfoStatement() throws Exception{
Runtime.getRuntime().totalMemory(),
latestMilestoneTracker.getLatestMilestoneHash(),
latestMilestoneTracker.getLatestMilestoneIndex(),
-
+
snapshotProvider.getLatestSnapshot().getHash(),
snapshotProvider.getLatestSnapshot().getIndex(),
-
+
milestone != null ? milestone.index() : -1,
snapshotProvider.getLatestSnapshot().getInitialIndex(),
-
+
neighborRouter.getConnectedNeighbors().size(),
txPipeline.getBroadcastStageQueue().size(),
System.currentTimeMillis(),
@@ -849,20 +849,20 @@ private boolean exhaustiveSearchWithinIndex(
}
/**
- *
- * Find transactions that contain the given values in their transaction fields.
- * All input values are lists, for which a list of return values (transaction hashes), in the same order, is returned for all individual elements.
- * The input fields can either be bundles, addresses, tags or approvees.
- *
- *
- * Using multiple transaction fields returns transactions hashes at the intersection of those values.
- *
- * @param request The map with input fields
- * Must contain at least one of 'bundles', 'addresses', 'tags' or 'approvees'.
- * @return {@link com.iota.iri.service.dto.FindTransactionsResponse}.
- * @throws Exception If a model cannot be loaded, no valid input fields were supplied
- * or the total transactions to find exceeds {@link APIConfig#getMaxFindTransactions()}.
- **/
+ *
+ * Find transactions that contain the given values in their transaction fields.
+ * All input values are lists, for which a list of return values (transaction hashes), in the same order, is returned for all individual elements.
+ * The input fields can either be bundles, addresses, tags or approvees.
+ *
+ *
+ * Using multiple transaction fields returns transactions hashes at the intersection of those values.
+ *
+ * @param request The map with input fields
+ * Must contain at least one of 'bundles', 'addresses', 'tags' or 'approvees'.
+ * @return {@link com.iota.iri.service.dto.FindTransactionsResponse}.
+ * @throws Exception If a model cannot be loaded, no valid input fields were supplied
+ * or the total transactions to find exceeds {@link APIConfig#getMaxFindTransactions()}.
+ **/
@Document(name="findTransactions")
private synchronized AbstractResponse findTransactionsStatement(final Map request) throws Exception {
@@ -875,7 +875,7 @@ private synchronized AbstractResponse findTransactionsStatement(final Maptag is a {@link Hash#NULL_HASH}.
*/
private String padTag(String tag) throws ValidationException {
@@ -999,13 +999,13 @@ private Set getParameterAsSet(
}
/**
- * Broadcast a list of transactions to all neighbors.
- * The trytes to be used for this call should be valid, attached transaction trytes.
- * These trytes are returned by attachToTangle, or by doing proof of work somewhere else.
- *
- * @param trytes the list of transaction trytes to broadcast
- * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness}
- **/
+ * Broadcast a list of transactions to all neighbors.
+ * The trytes to be used for this call should be valid, attached transaction trytes.
+ * These trytes are returned by attachToTangle, or by doing proof of work somewhere else.
+ *
+ * @param trytes the list of transaction trytes to broadcast
+ * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness}
+ **/
@Document(name="broadcastTransactions")
public AbstractResponse broadcastTransactionsStatement(List trytes) {
for (final String tryte : trytes) {
@@ -1018,23 +1018,23 @@ public AbstractResponse broadcastTransactionsStatement(List trytes) {
/**
- *
- * Calculates the confirmed balance, as viewed by the specified tips.
- * If the tips parameter is missing, the returned balance is correct as of the latest confirmed milestone.
- * In addition to the balances, it also returns the referencing tips (or milestone),
- * as well as the index with which the confirmed balance was determined.
- * The balances are returned as a list in the same order as the addresses were provided as input.
- *
- *
- * @param addresses Address for which to get the balance (do not include the checksum)
- * @param tips The optional tips to find the balance through.
- * @param threshold The confirmation threshold between 0 and 100(inclusive).
- * Should be set to 100 for getting balance by counting only confirmed transactions.
- * @return {@link com.iota.iri.service.dto.GetBalancesResponse}
- * @throws Exception When the database has encountered an error
- **/
+ *
+ * Calculates the confirmed balance, as viewed by the specified tips.
+ * If the tips parameter is missing, the returned balance is correct as of the latest confirmed milestone.
+ * In addition to the balances, it also returns the referencing tips (or milestone),
+ * as well as the index with which the confirmed balance was determined.
+ * The balances are returned as a list in the same order as the addresses were provided as input.
+ *
+ *
+ * @param addresses Address for which to get the balance (do not include the checksum)
+ * @param tips The optional tips to find the balance through.
+ * @param threshold The confirmation threshold between 0 and 100(inclusive).
+ * Should be set to 100 for getting balance by counting only confirmed transactions.
+ * @return {@link com.iota.iri.service.dto.GetBalancesResponse}
+ * @throws Exception When the database has encountered an error
+ **/
@Document(name="getBalances")
- private AbstractResponse getBalancesStatement(List addresses,
+ private AbstractResponse getBalancesStatement(List addresses,
List tips,
int threshold) throws Exception {
@@ -1136,37 +1136,37 @@ public static void incEllapsedTimePoW(long ellapsedTime) {
}
/**
- *
- * Prepares the specified transactions (trytes) for attachment to the Tangle by doing Proof of Work.
- * You need to supply branchTransaction as well as trunkTransaction.
- * These are the tips which you're going to validate and reference with this transaction.
- * These are obtainable by the getTransactionsToApprove API call.
- *
- *
- * The returned value is a different set of tryte values which you can input into
- * broadcastTransactions and storeTransactions.
- * The last 243 trytes of the return value consist of the following:
- *
- * trunkTransaction
- * branchTransaction
- * nonce
- *
- *
- * These are valid trytes which are then accepted by the network.
- * @param trunkTransaction A reference to an external transaction (tip) used as trunk.
- * The transaction with index 0 will have this tip in its trunk.
- * All other transactions reference the previous transaction in the bundle (Their index-1).
- *
- * @param branchTransaction A reference to an external transaction (tip) used as branch.
- * Each Transaction in the bundle will have this tip as their branch, except the last.
- * The last one will have the branch in its trunk.
- * @param minWeightMagnitude The amount of work we should do to confirm this transaction.
- * Each 0-trit on the end of the transaction represents 1 magnitude.
- * A 9-tryte represents 3 magnitudes, since a 9 is represented by 3 0-trits.
- * Transactions with a different minWeightMagnitude are compatible.
- * @param trytes The list of trytes to prepare for network attachment, by doing proof of work.
- * @return The list of transactions in trytes, ready to be broadcast to the network.
- **/
+ *
+ * Prepares the specified transactions (trytes) for attachment to the Tangle by doing Proof of Work.
+ * You need to supply branchTransaction as well as trunkTransaction.
+ * These are the tips which you're going to validate and reference with this transaction.
+ * These are obtainable by the getTransactionsToApprove API call.
+ *
+ *
+ * The returned value is a different set of tryte values which you can input into
+ * broadcastTransactions and storeTransactions.
+ * The last 243 trytes of the return value consist of the following:
+ *
+ * trunkTransaction
+ * branchTransaction
+ * nonce
+ *
+ *
+ * These are valid trytes which are then accepted by the network.
+ * @param trunkTransaction A reference to an external transaction (tip) used as trunk.
+ * The transaction with index 0 will have this tip in its trunk.
+ * All other transactions reference the previous transaction in the bundle (Their index-1).
+ *
+ * @param branchTransaction A reference to an external transaction (tip) used as branch.
+ * Each Transaction in the bundle will have this tip as their branch, except the last.
+ * The last one will have the branch in its trunk.
+ * @param minWeightMagnitude The amount of work we should do to confirm this transaction.
+ * Each 0-trit on the end of the transaction represents 1 magnitude.
+ * A 9-tryte represents 3 magnitudes, since a 9 is represented by 3 0-trits.
+ * Transactions with a different minWeightMagnitude are compatible.
+ * @param trytes The list of trytes to prepare for network attachment, by doing proof of work.
+ * @return The list of transactions in trytes, ready to be broadcast to the network.
+ **/
@Document(name="attachToTangle", returnParam="trytes")
public synchronized List attachToTangleStatement(Hash trunkTransaction, Hash branchTransaction,
int minWeightMagnitude, List trytes) {
@@ -1447,7 +1447,7 @@ private synchronized AbstractResponse storeMessageStatement(String address, Stri
return AbstractResponse.createEmptyResponse();
}
-
+
//
// FUNCTIONAL COMMAND ROUTES
//
@@ -1494,10 +1494,10 @@ private Function