diff --git a/changelog.txt b/changelog.txt
index 02023040ed..55532edbf1 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,10 @@
+1.8.5
+
+Protocol-Change:
+ Added bundle validity rules - check that bundles confirm only tails and 2 bundles at most (#1786)
+ Check validity rules on tip-sel and check-consistency only (#1802)
+
+
1.8.4
Hotfix: Ensure proper creation of solid entrypoints (#1702)
diff --git a/pom.xml b/pom.xml
index 33578a5696..859f2a8e5e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.iota
iri
- 1.8.4
+ 1.8.5
IRI
IOTA Reference Implementation
diff --git a/python-regression/tests/features/steps/api_test_steps.py b/python-regression/tests/features/steps/api_test_steps.py
index 94f9bff418..9afdc7a8d5 100644
--- a/python-regression/tests/features/steps/api_test_steps.py
+++ b/python-regression/tests/features/steps/api_test_steps.py
@@ -1,14 +1,13 @@
from aloe import *
from util import static_vals
+from util import logger as log
from util.test_logic import api_test_logic as api_utils
from util.threading_logic import pool_logic as pool
from util.neighbor_logic import neighbor_logic as neighbors
from util.response_logic import response_handling as responses
from time import sleep, time
-import logging
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
+logger = log.getLogger(__name__)
testAddress = static_vals.TEST_ADDRESS
diff --git a/python-regression/tests/features/steps/local_snapshots_steps.py b/python-regression/tests/features/steps/local_snapshots_steps.py
index 831b177c5e..1026598605 100644
--- a/python-regression/tests/features/steps/local_snapshots_steps.py
+++ b/python-regression/tests/features/steps/local_snapshots_steps.py
@@ -1,9 +1,8 @@
from aloe import step
from util.test_logic import api_test_logic as api_utils
+from util import logger as log
-import logging
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
+logger = log.getLogger(__name__)
@step(r'A local snapshot was taken on "([^"]+)" at index (\d+)')
diff --git a/python-regression/tests/features/steps/response_handling_steps.py b/python-regression/tests/features/steps/response_handling_steps.py
index 7e1817a7ea..b663009f1a 100644
--- a/python-regression/tests/features/steps/response_handling_steps.py
+++ b/python-regression/tests/features/steps/response_handling_steps.py
@@ -1,11 +1,10 @@
-import logging
from aloe import world, step
from util.response_logic import response_handling as response_handling
from util.test_logic import api_test_logic as api_utils
from util.test_logic import value_fetch_logic
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
+from util import logger as log
+logger = log.getLogger(__name__)
world.test_vars = {}
diff --git a/python-regression/tests/features/steps/transaction_steps.py b/python-regression/tests/features/steps/transaction_steps.py
index bb485b5668..a1e758e66f 100644
--- a/python-regression/tests/features/steps/transaction_steps.py
+++ b/python-regression/tests/features/steps/transaction_steps.py
@@ -1,15 +1,13 @@
from aloe import world, step
from iota import Transaction
from util import static_vals as static
+from util import logger as log
from util.test_logic import api_test_logic as api_utils
from util.transaction_bundle_logic import transaction_logic as transactions
from util.milestone_logic import milestones
from time import sleep
-import logging
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
+logger = log.getLogger(__name__)
@step(r'a transaction is generated and attached on "([^"]+)" with:')
def generate_transaction_and_attach(step, node):
@@ -204,7 +202,7 @@ def wait_for_update(index, api):
if node_info['latestSolidSubtangleMilestoneIndex'] == index:
updated = True
break
- i += 1;
+ i += 1
sleep(1)
assert updated is True, "The node was unable to update to index {}".format(index)
diff --git a/python-regression/util/logger.py b/python-regression/util/logger.py
new file mode 100644
index 0000000000..d9e758795b
--- /dev/null
+++ b/python-regression/util/logger.py
@@ -0,0 +1,5 @@
+import logging
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s: %(message)s')
+
+def getLogger(name):
+ return logging.getLogger(name)
\ No newline at end of file
diff --git a/python-regression/util/milestone_logic/milestones.py b/python-regression/util/milestone_logic/milestones.py
index 4eb197ef52..760cd0438b 100644
--- a/python-regression/util/milestone_logic/milestones.py
+++ b/python-regression/util/milestone_logic/milestones.py
@@ -1,11 +1,9 @@
from iota import ProposedTransaction, ProposedBundle, Tag, Address, Transaction
from util import conversion as converter
+from util import logger as log
from util.transaction_bundle_logic import bundle_logic
-import logging
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
+logger = log.getLogger(__name__)
def issue_milestone(address, api, index, *reference_transaction):
txn1 = ProposedTransaction(
diff --git a/python-regression/util/neighbor_logic/neighbor_logic.py b/python-regression/util/neighbor_logic/neighbor_logic.py
index c8ba9c5759..34c5ebcf1d 100644
--- a/python-regression/util/neighbor_logic/neighbor_logic.py
+++ b/python-regression/util/neighbor_logic/neighbor_logic.py
@@ -1,6 +1,5 @@
-import logging
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
+from util import logger as log
+logger = log.getLogger(__name__)
def check_if_neighbors(api, neighbors, expected_neighbor):
diff --git a/python-regression/util/response_logic/response_handling.py b/python-regression/util/response_logic/response_handling.py
index ada6a98334..b485f33c83 100644
--- a/python-regression/util/response_logic/response_handling.py
+++ b/python-regression/util/response_logic/response_handling.py
@@ -1,8 +1,7 @@
-import logging
from util.threading_logic import pool_logic as pool
+from util import logger as log
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
+logger = log.getLogger(__name__)
def find_in_response(key, response):
diff --git a/python-regression/util/test_logic/api_test_logic.py b/python-regression/util/test_logic/api_test_logic.py
index 95019c053b..f6fe3f858d 100644
--- a/python-regression/util/test_logic/api_test_logic.py
+++ b/python-regression/util/test_logic/api_test_logic.py
@@ -1,13 +1,12 @@
import json
-import logging
import urllib3
from aloe import world
from iota import Iota, Address, Tag, TryteString
from . import value_fetch_logic as value_fetch
+from util import logger as log
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
+logger = log.getLogger(__name__)
def prepare_api_call(node_name, **kwargs):
diff --git a/python-regression/util/threading_logic/pool_logic.py b/python-regression/util/threading_logic/pool_logic.py
index 97a6171597..6722f12be2 100644
--- a/python-regression/util/threading_logic/pool_logic.py
+++ b/python-regression/util/threading_logic/pool_logic.py
@@ -1,7 +1,6 @@
from multiprocessing.dummy import Pool
-import logging
-logging.basicConfig(level=logging.DEBUG)
-logger = logging.getLogger(__name__)
+from util import logger as log
+logger = log.getLogger(__name__)
def start_pool(function, iterations, args):
diff --git a/python-regression/util/transaction_bundle_logic/transaction_logic.py b/python-regression/util/transaction_bundle_logic/transaction_logic.py
index e82b49e533..7b100aac11 100644
--- a/python-regression/util/transaction_bundle_logic/transaction_logic.py
+++ b/python-regression/util/transaction_bundle_logic/transaction_logic.py
@@ -2,10 +2,9 @@
from util import static_vals as static
from util.test_logic import api_test_logic as api_utils
from util.test_logic import value_fetch_logic as value_fetch
-import logging
+from util import logger as log
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
+logger = log.getLogger(__name__)
def create_transaction_bundle(address, tag, value):
diff --git a/src/main/java/com/iota/iri/BundleValidator.java b/src/main/java/com/iota/iri/BundleValidator.java
index 4242ada537..6327c44d3a 100644
--- a/src/main/java/com/iota/iri/BundleValidator.java
+++ b/src/main/java/com/iota/iri/BundleValidator.java
@@ -8,6 +8,7 @@
import com.iota.iri.utils.Converter;
import java.util.*;
+import com.google.common.annotations.VisibleForTesting;
/**
* Validates bundles.
@@ -55,22 +56,33 @@ public enum Validity {
*/
public static final int MODE_VALIDATE_SEMANTICS = 1 << 2;
+ /**
+ * Instructs the validation code to validate all transactions within the bundle approve via their branch the trunk
+ * transaction of the head transaction
+ */
+ public static final int MODE_VALIDATE_BUNDLE_TX_APPROVAL = 1 << 3;
+
+ /**
+ * Instructs the validation code to validate that the bundle only approves tail txs.
+ */
+ public static final int MODE_VALIDATE_TAIL_APPROVAL = 1 << 4;
+
/**
* Instructs the validation code to fully validate the semantics, bundle hash and signatures of the given bundle.
*/
- public static final int MODE_VALIDATE_ALL = MODE_VALIDATE_SIGNATURES | MODE_VALIDATE_BUNDLE_HASH | MODE_VALIDATE_SEMANTICS;
+ public static final int MODE_VALIDATE_ALL = MODE_VALIDATE_SIGNATURES | MODE_VALIDATE_BUNDLE_HASH | MODE_VALIDATE_SEMANTICS | MODE_VALIDATE_TAIL_APPROVAL | MODE_VALIDATE_BUNDLE_TX_APPROVAL;
/**
* Instructs the validation code to skip checking the bundle's already computed validity and instead to proceed to
* validate the bundle further.
*/
- public static final int MODE_SKIP_CACHED_VALIDITY = 1 << 3;
+ public static final int MODE_SKIP_CACHED_VALIDITY = 1 << 5;
/**
* Instructs the validation code to skip checking whether the tail transaction is present or a tail transaction was
* given as the start transaction.
*/
- public static final int MODE_SKIP_TAIL_TX_EXISTENCE = 1 << 4;
+ public static final int MODE_SKIP_TAIL_TX_EXISTENCE = 1 << 6;
/**
* Fetches a bundle of transactions identified by the {@code tailHash} and validates the transactions. Bundle is a
@@ -85,6 +97,8 @@ public enum Validity {
*
Total bundle value is 0 (inputs and outputs are balanced)
* Recalculate the bundle hash by absorbing and squeezing the transactions' essence
* Validate the signature on input transactions
+ * The bundle must only approve tail transactions
+ * All transactions within the bundle approve via their branch the trunk transaction of the head transaction.
*
*
* As well as the following syntactic checks:
@@ -96,9 +110,11 @@ public enum Validity {
* we lose the last trit in the process
*
*
- * @param tangle used to fetch the bundle's transactions from the persistence layer
- * @param initialSnapshot the initial snapshot that defines the genesis for our ledger state
- * @param tailHash the hash of the last transaction in a bundle.
+ * @param tangle used to fetch the bundle's transactions from the persistence layer
+ * @param enforceExtraRules true if enforce {@link #validateBundleTailApproval(Tangle, List)} and
+ * {@link #validateBundleTransactionsApproval(List)}
+ * @param initialSnapshot the initial snapshot that defines the genesis for our ledger state
+ * @param tailHash the hash of the last transaction in a bundle.
* @return A list of transactions of the bundle contained in another list. If the bundle is valid then the tail
* transaction's {@link TransactionViewModel#getValidity()} will return 1, else {@link
* TransactionViewModel#getValidity()} will return -1. If the bundle is invalid then an empty list will be
@@ -108,9 +124,32 @@ public enum Validity {
* validate it again.
*
*/
- public List validate(Tangle tangle, Snapshot initialSnapshot, Hash tailHash) throws Exception {
+ public List validate(Tangle tangle, boolean enforceExtraRules, Snapshot initialSnapshot,
+ Hash tailHash) throws Exception {
+ int mode = getMode(enforceExtraRules);
+ return validate(tangle, initialSnapshot, tailHash, mode);
+ }
+
+ /**
+ * Does {@link #validate(Tangle, boolean, Snapshot, Hash)} but with an option of skipping some checks according to
+ * the give {@code mode}
+ *
+ * @param tangle used to fetch the bundle's transactions from the persistence layer
+ * @param initialSnapshot the initial snapshot that defines the genesis for our ledger state
+ * @param tailHash the hash of the last transaction in a bundle.
+ * @param mode flags that specify which validation checks to perform
+ * @return A list of transactions of the bundle contained in another list. If the bundle is valid then the tail
+ * transaction's {@link TransactionViewModel#getValidity()} will return 1, else
+ * {@link TransactionViewModel#getValidity()} will return -1. If the bundle is invalid then an empty list
+ * will be returned.
+ * @throws Exception if a persistence error occurred
+ * @implNote if {@code tailHash} was already invalidated/validated by a previous call to this method then we don't
+ * validate it again.
+ * @see #validate(Tangle, boolean, Snapshot, Hash)
+ */
+ private List validate(Tangle tangle, Snapshot initialSnapshot, Hash tailHash, int mode) throws Exception {
List bundleTxs = new LinkedList<>();
- switch (validate(tangle, tailHash, MODE_VALIDATE_ALL, bundleTxs)) {
+ switch (validate(tangle, tailHash, mode, bundleTxs)) {
case VALID:
if (bundleTxs.get(0).getValidity() != 1) {
bundleTxs.get(0).setValidity(tangle, initialSnapshot, 1);
@@ -126,7 +165,14 @@ public List validate(Tangle tangle, Snapshot initialSnapsh
}
}
- private static boolean hasMode(int mode, int has) {
+ private static int getMode(boolean enforceExtraRules) {
+ if (enforceExtraRules) {
+ return MODE_VALIDATE_ALL;
+ }
+ return MODE_VALIDATE_SIGNATURES | MODE_VALIDATE_BUNDLE_HASH | MODE_VALIDATE_SEMANTICS;
+ }
+
+ private static boolean hasMode(int mode, int has) {
return (mode & has) == has;
}
@@ -147,7 +193,9 @@ private static boolean hasMode(int mode, int has) {
* @return whether the validation criteria were passed or not
* @throws Exception if an error occurred in the persistence layer
*/
- public static Validity validate(Tangle tangle, Hash startTxHash, int validationMode, List bundleTxs) throws Exception {
+ @VisibleForTesting
+ Validity validate(Tangle tangle, Hash startTxHash, int validationMode, List bundleTxs)
+ throws Exception {
TransactionViewModel startTx = TransactionViewModel.fromHash(tangle, startTxHash);
if (startTx == null || (!hasMode(validationMode, MODE_SKIP_TAIL_TX_EXISTENCE) &&
(startTx.getCurrentIndex() != 0 || startTx.getValidity() == -1))) {
@@ -160,7 +208,8 @@ public static Validity validate(Tangle tangle, Hash startTxHash, int validationM
!hasMode(validationMode, MODE_VALIDATE_SEMANTICS));
// check the semantics of the bundle: total sum, semantics per tx (current/last index), missing txs, supply
- Validity bundleSemanticsValidity = validateBundleSemantics(startTx, bundleTxsMapping, bundleTxs, validationMode);
+ Validity bundleSemanticsValidity = validateBundleSemantics(startTx, bundleTxsMapping, bundleTxs,
+ validationMode);
if (hasMode(validationMode, MODE_VALIDATE_SEMANTICS) && bundleSemanticsValidity != Validity.VALID) {
return bundleSemanticsValidity;
}
@@ -177,6 +226,21 @@ public static Validity validate(Tangle tangle, Hash startTxHash, int validationM
return bundleHashValidity;
}
+ //verify that the bundle only approves tail txs
+ if (hasMode(validationMode, MODE_VALIDATE_TAIL_APPROVAL)) {
+ Validity bundleTailApprovalValidity = validateBundleTailApproval(tangle, bundleTxs);
+ if (bundleTailApprovalValidity != Validity.VALID) {
+ return bundleTailApprovalValidity;
+ }
+ }
+
+ //verify all transactions within the bundle approve via their branch the trunk transaction of the head transaction
+ if (hasMode(validationMode, MODE_VALIDATE_BUNDLE_TX_APPROVAL)) {
+ Validity bundleTransactionsApprovalValidity = validateBundleTransactionsApproval(bundleTxs);
+ if (bundleTransactionsApprovalValidity != Validity.VALID) {
+ return bundleTransactionsApprovalValidity;
+ }
+ }
// verify the signatures of input transactions
if (hasMode(validationMode, MODE_VALIDATE_SIGNATURES)) {
@@ -198,7 +262,8 @@ public static Validity validate(Tangle tangle, Hash startTxHash, int validationM
* @param bundleTxs an empty list which gets filled with the transactions in order of trunk ordering
* @return whether the bundle is semantically valid
*/
- public static Validity validateBundleSemantics(TransactionViewModel startTx, Map bundleTxsMapping, List bundleTxs) {
+ public Validity validateBundleSemantics(TransactionViewModel startTx,
+ Map bundleTxsMapping, List bundleTxs) {
return validateBundleSemantics(startTx, bundleTxsMapping, bundleTxs, MODE_VALIDATE_SEMANTICS);
}
@@ -221,7 +286,9 @@ public static Validity validateBundleSemantics(TransactionViewModel startTx, Map
* @param validationMode the used validation mode
* @return whether the bundle is semantically valid
*/
- private static Validity validateBundleSemantics(TransactionViewModel startTx, Map bundleTxsMapping, List bundleTxs, int validationMode) {
+ private Validity validateBundleSemantics(TransactionViewModel startTx,
+ Map bundleTxsMapping, List bundleTxs,
+ int validationMode) {
TransactionViewModel tvm = startTx;
final long lastIndex = tvm.lastIndex();
long bundleValue = 0;
@@ -379,6 +446,39 @@ public static boolean isInconsistent(Collection transactio
return (sum != 0 || transactionViewModels.isEmpty());
}
+ /**
+ * A bundle is invalid if The branch transaction hash of the non head transactions within a bundle, is not the same
+ * as the trunk transaction hash of the head transaction.
+ *
+ * @param bundleTxs list of transactions that are in a bundle.
+ * @return Whether the bundle tx chain is valid.
+ */
+ @VisibleForTesting
+ Validity validateBundleTransactionsApproval(List bundleTxs) {
+ Hash headTrunkTransactionHash = bundleTxs.get(bundleTxs.size() - 1).getTrunkTransactionHash();
+ for(int i = 0; i < bundleTxs.size() - 1; i++){
+ if(!bundleTxs.get(i).getBranchTransactionHash().equals(headTrunkTransactionHash)){
+ return Validity.INVALID;
+ }
+ }
+ return Validity.VALID;
+ }
+
+ /**
+ * A bundle is invalid if the trunk and branch transactions approved by the bundle are non tails.
+ *
+ * @param bundleTxs The txs in the bundle.
+ * @return Whether the bundle approves only tails.
+ */
+ @VisibleForTesting
+ Validity validateBundleTailApproval(Tangle tangle, List bundleTxs) throws Exception {
+ TransactionViewModel headTx = bundleTxs.get(bundleTxs.size() - 1);
+ TransactionViewModel bundleTrunkTvm = headTx.getTrunkTransaction(tangle);
+ TransactionViewModel bundleBranchTvm = headTx.getBranchTransaction(tangle);
+ return bundleTrunkTvm != null && bundleBranchTvm != null && bundleBranchTvm.getCurrentIndex() == 0
+ && bundleTrunkTvm.getCurrentIndex() == 0 ? Validity.VALID : Validity.INVALID;
+ }
+
/**
* Traverses down the given {@code tail} trunk until all transactions that belong to the same bundle (identified by
* the bundle hash) are found and loaded.
diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java
index c901fe8fff..f714603842 100644
--- a/src/main/java/com/iota/iri/Iota.java
+++ b/src/main/java/com/iota/iri/Iota.java
@@ -22,6 +22,7 @@
import com.iota.iri.service.transactionpruning.DepthPruningCondition;
import com.iota.iri.service.transactionpruning.SizePruningCondition;
import com.iota.iri.service.transactionpruning.TransactionPruner;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.storage.*;
import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider;
import com.iota.iri.utils.Pair;
diff --git a/src/main/java/com/iota/iri/MainInjectionConfiguration.java b/src/main/java/com/iota/iri/MainInjectionConfiguration.java
index e8347fe6f1..f3963bc391 100644
--- a/src/main/java/com/iota/iri/MainInjectionConfiguration.java
+++ b/src/main/java/com/iota/iri/MainInjectionConfiguration.java
@@ -27,6 +27,7 @@
import com.iota.iri.service.tipselection.impl.*;
import com.iota.iri.service.transactionpruning.TransactionPruner;
import com.iota.iri.service.transactionpruning.async.AsyncTransactionPruner;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.storage.LocalSnapshotsPersistenceProvider;
import com.iota.iri.storage.Tangle;
import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider;
diff --git a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java
index 89e78407ed..207d7cb72d 100644
--- a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java
+++ b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java
@@ -5,6 +5,7 @@
import com.iota.iri.model.*;
import com.iota.iri.model.persistables.*;
import com.iota.iri.service.snapshot.Snapshot;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.storage.Indexable;
import com.iota.iri.storage.Persistable;
import com.iota.iri.storage.Tangle;
@@ -804,7 +805,7 @@ public static void updateSolidTransactions(Tangle tangle, Snapshot initialSnapsh
/**
* Updates the {@link Transaction#solid} value of the referenced {@link Transaction} object.
*
- * Used by the {@link com.iota.iri.TransactionValidator} to quickly set the solidity of a {@link Transaction} set.
+ * Used by the {@link TransactionValidator} to quickly set the solidity of a {@link Transaction} set.
*
* @param solid The solidity of the transaction in the database
* @return True if the {@link Transaction#solid} has been updated, False if not.
diff --git a/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java b/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java
index 032715736d..d393ff606b 100644
--- a/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java
+++ b/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java
@@ -3,7 +3,8 @@
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
-import com.iota.iri.TransactionValidator;
+import com.iota.iri.BundleValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.conf.IotaConfig;
import com.iota.iri.controllers.TipsViewModel;
import com.iota.iri.network.impl.TipsRequesterImpl;
@@ -44,11 +45,11 @@ TipsRequester provideTipsRequester(NeighborRouter neighborRouter, Tangle tangle,
@Singleton
@Provides
TransactionProcessingPipeline provideTransactionProcessingPipeline(NeighborRouter neighborRouter,
- TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider,
- TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker,
- TransactionRequester transactionRequester) {
+ TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider,
+ TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker,
+ TransactionRequester transactionRequester, BundleValidator bundleValidator) {
return new TransactionProcessingPipelineImpl(neighborRouter, configuration, txValidator, tangle,
- snapshotProvider, tipsViewModel, latestMilestoneTracker, transactionRequester);
+ snapshotProvider, tipsViewModel, latestMilestoneTracker, transactionRequester, bundleValidator);
}
@Singleton
diff --git a/src/main/java/com/iota/iri/network/pipeline/QuickBundleValidationPayload.java b/src/main/java/com/iota/iri/network/pipeline/QuickBundleValidationPayload.java
new file mode 100644
index 0000000000..438ddc31d3
--- /dev/null
+++ b/src/main/java/com/iota/iri/network/pipeline/QuickBundleValidationPayload.java
@@ -0,0 +1,45 @@
+package com.iota.iri.network.pipeline;
+
+import com.iota.iri.controllers.TransactionViewModel;
+import com.iota.iri.network.neighbor.Neighbor;
+
+/**
+ * Defines the payload which gets submitted to the {@link QuickBundleValidationStage}
+ */
+public class QuickBundleValidationPayload extends Payload {
+
+ private Neighbor neighbor;
+ private TransactionViewModel tvm;
+
+ /**
+ * Creates a new {@link QuickBundleValidationPayload}
+ *
+ * @param neighbor The {@link Neighbor} form which the transaction originated from.
+ * @param tvm The transaction
+ */
+ public QuickBundleValidationPayload(Neighbor neighbor, TransactionViewModel tvm) {
+ this.neighbor = neighbor;
+ this.tvm = tvm;
+ }
+
+ @Override
+ public Neighbor getOriginNeighbor() {
+ return neighbor;
+ }
+
+ /**
+ * Gets the transaction
+ *
+ * @return The transaction
+ */
+ public TransactionViewModel getTransactionViewModel() {
+ return tvm;
+ }
+
+ @Override
+ public String toString() {
+ return "QuickBundleValidationPayload{" + "neighbor=" + neighbor.getHostAddressAndPort() + ", tvm="
+ + tvm.getHash() + '}';
+ }
+
+}
diff --git a/src/main/java/com/iota/iri/network/pipeline/QuickBundleValidationStage.java b/src/main/java/com/iota/iri/network/pipeline/QuickBundleValidationStage.java
new file mode 100644
index 0000000000..a0d678cd2d
--- /dev/null
+++ b/src/main/java/com/iota/iri/network/pipeline/QuickBundleValidationStage.java
@@ -0,0 +1,62 @@
+package com.iota.iri.network.pipeline;
+
+import com.iota.iri.BundleValidator;
+import com.iota.iri.controllers.TransactionViewModel;
+import com.iota.iri.service.snapshot.SnapshotProvider;
+import com.iota.iri.service.validation.TransactionValidator;
+import com.iota.iri.storage.Tangle;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link QuickBundleValidationStage} validates the transaction bundle and propagates it if it's valid.
+ */
+public class QuickBundleValidationStage implements Stage {
+
+ private static final Logger log = LoggerFactory.getLogger(QuickBundleValidationStage.class);
+
+ BundleValidator bundleValidator;
+ TransactionValidator transactionValidator;
+ Tangle tangle;
+ SnapshotProvider snapshotProvider;
+
+ /**
+ * Creates a new {@link QuickBundleValidationStage}
+ *
+ * @param tangle Tangle
+ * @param snapshotProvider SnapshotProvider
+ * @param bundleValidator BundleValidator
+ * @param transactionValidator TransactionValidator
+ */
+ public QuickBundleValidationStage(Tangle tangle, SnapshotProvider snapshotProvider, BundleValidator bundleValidator,
+ TransactionValidator transactionValidator) {
+ this.tangle = tangle;
+ this.snapshotProvider = snapshotProvider;
+ this.bundleValidator = bundleValidator;
+ this.transactionValidator = transactionValidator;
+ }
+
+ @Override
+ public ProcessingContext process(ProcessingContext ctx) {
+ QuickBundleValidationPayload payload = (QuickBundleValidationPayload) ctx.getPayload();
+ TransactionViewModel tvm = payload.getTransactionViewModel();
+
+ try {
+ if (tvm.isSolid() && tvm.getCurrentIndex() == 0) {
+ List bundleTransactions = bundleValidator.validate(tangle, true,
+ snapshotProvider.getInitialSnapshot(), tvm.getHash());
+ if (!bundleTransactions.isEmpty()) {
+ transactionValidator.addSolidTransaction(tvm.getHash());
+ }
+ }
+ } catch (Exception e) {
+ log.error("error validating bundle", e);
+ }
+ ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST);
+ ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), tvm));
+ return ctx;
+ }
+}
diff --git a/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java b/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java
index e8c5e99711..f91cb6ff46 100644
--- a/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java
+++ b/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java
@@ -1,6 +1,6 @@
package com.iota.iri.network.pipeline;
-import com.iota.iri.TransactionValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.network.TransactionRequester;
import com.iota.iri.network.neighbor.Neighbor;
@@ -90,9 +90,9 @@ public ProcessingContext process(ProcessingContext ctx) {
transactionRequester.removeRecentlyRequestedTransaction(tvm.getHash());
}
- // broadcast the newly saved tx to the other neighbors
- ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST);
- ctx.setPayload(new BroadcastPayload(originNeighbor, tvm));
+ // validate the bundle
+ ctx.setNextStage(TransactionProcessingPipeline.Stage.QUICK_BUNDLE_VALIDATION);
+ ctx.setPayload(new QuickBundleValidationPayload(originNeighbor, tvm));
return ctx;
}
}
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 1f19e4248c..5a553fdc6f 100644
--- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java
+++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java
@@ -14,7 +14,7 @@ public interface TransactionProcessingPipeline {
* Defines the different stages of the {@link TransactionProcessingPipelineImpl}.
*/
enum Stage {
- PRE_PROCESS, HASHING, VALIDATION, REPLY, RECEIVED, BROADCAST, MULTIPLE, ABORT, FINISH,
+ PRE_PROCESS, HASHING, VALIDATION, REPLY, RECEIVED, QUICK_BUNDLE_VALIDATION, BROADCAST, MULTIPLE, ABORT, FINISH,
}
/**
@@ -111,4 +111,11 @@ enum Stage {
* @param hashingStage the {@link HashingStage} to use
*/
void setHashingStage(HashingStage hashingStage);
+
+ /**
+ * Sets the quick bundle validation stage.
+ *
+ * @param quickBundleValidationStage The {@link QuickBundleValidationStage} to use.
+ */
+ void setQuickBundleValidationStage(QuickBundleValidationStage quickBundleValidationStage);
}
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 0af71b8420..480d8c389c 100644
--- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java
+++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java
@@ -1,6 +1,7 @@
package com.iota.iri.network.pipeline;
-import com.iota.iri.TransactionValidator;
+import com.iota.iri.BundleValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.conf.NodeConfig;
import com.iota.iri.controllers.TipsViewModel;
import com.iota.iri.crypto.batched.BatchedHasher;
@@ -69,12 +70,14 @@ public class TransactionProcessingPipelineImpl implements TransactionProcessingP
private BroadcastStage broadcastStage;
private BatchedHasher batchedHasher;
private HashingStage hashingStage;
+ private QuickBundleValidationStage quickBundleValidationStage;
private BlockingQueue preProcessStageQueue = new ArrayBlockingQueue<>(100);
private BlockingQueue validationStageQueue = new ArrayBlockingQueue<>(100);
private BlockingQueue receivedStageQueue = new ArrayBlockingQueue<>(100);
private BlockingQueue broadcastStageQueue = new ArrayBlockingQueue<>(100);
private BlockingQueue replyStageQueue = new ArrayBlockingQueue<>(100);
+ private BlockingQueue quickBundleValidationStageQueue = new ArrayBlockingQueue<>(100);
/**
* Creates a {@link TransactionProcessingPipeline}.
@@ -89,9 +92,9 @@ public class TransactionProcessingPipelineImpl implements TransactionProcessingP
* reply stage
*/
public TransactionProcessingPipelineImpl(NeighborRouter neighborRouter, NodeConfig config,
- TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider,
- TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker,
- TransactionRequester transactionRequester) {
+ TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider,
+ TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker,
+ TransactionRequester transactionRequester, BundleValidator bundleValidator) {
FIFOCache recentlySeenBytesCache = new FIFOCache<>(config.getCacheSizeBytes());
this.preProcessStage = new PreProcessStage(recentlySeenBytesCache);
this.replyStage = new ReplyStage(neighborRouter, config, tangle, tipsViewModel, latestMilestoneTracker,
@@ -101,6 +104,7 @@ public TransactionProcessingPipelineImpl(NeighborRouter neighborRouter, NodeConf
this.receivedStage = new ReceivedStage(tangle, txValidator, snapshotProvider, transactionRequester);
this.batchedHasher = BatchedHasherFactory.create(BatchedHasherFactory.Type.BCTCURL81, 20);
this.hashingStage = new HashingStage(batchedHasher);
+ this.quickBundleValidationStage = new QuickBundleValidationStage(tangle,snapshotProvider,bundleValidator,txValidator);
}
@Override
@@ -111,6 +115,7 @@ public void start() {
addStage("reply", replyStageQueue, replyStage);
addStage("received", receivedStageQueue, receivedStage);
addStage("broadcast", broadcastStageQueue, broadcastStage);
+ addStage("quickBundleValidation", quickBundleValidationStageQueue, quickBundleValidationStage);
}
/**
@@ -136,6 +141,9 @@ private void addStage(String name, BlockingQueue queue,
case RECEIVED:
receivedStageQueue.put(ctx);
break;
+ case QUICK_BUNDLE_VALIDATION:
+ quickBundleValidationStageQueue.put(ctx);
+ break;
case MULTIPLE:
MultiStagePayload payload = (MultiStagePayload) ctx.getPayload();
replyStageQueue.put(payload.getLeft());
@@ -255,4 +263,9 @@ public void setBroadcastStage(BroadcastStage broadcastStage) {
public void setHashingStage(HashingStage hashingStage) {
this.hashingStage = hashingStage;
}
+
+ @Override
+ public void setQuickBundleValidationStage(QuickBundleValidationStage quickBundleValidationStage) {
+ this.quickBundleValidationStage = quickBundleValidationStage;
+ }
}
diff --git a/src/main/java/com/iota/iri/network/pipeline/ValidationStage.java b/src/main/java/com/iota/iri/network/pipeline/ValidationStage.java
index a139210eb4..47c41fa3a9 100644
--- a/src/main/java/com/iota/iri/network/pipeline/ValidationStage.java
+++ b/src/main/java/com/iota/iri/network/pipeline/ValidationStage.java
@@ -1,6 +1,6 @@
package com.iota.iri.network.pipeline;
-import com.iota.iri.TransactionValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.model.Hash;
import com.iota.iri.model.HashFactory;
diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java
index e3828e9d41..7d902bf611 100644
--- a/src/main/java/com/iota/iri/service/API.java
+++ b/src/main/java/com/iota/iri/service/API.java
@@ -6,7 +6,7 @@
import com.iota.iri.BundleValidator;
import com.iota.iri.IRI;
import com.iota.iri.IXI;
-import com.iota.iri.TransactionValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.conf.APIConfig;
import com.iota.iri.conf.IotaConfig;
import com.iota.iri.controllers.*;
@@ -389,10 +389,14 @@ private AbstractResponse checkConsistencyStatement(List transactionsList
state = false;
info = "tails are not solid (missing a referenced tx): " + transaction;
break;
- } else if (bundleValidator.validate(tangle, snapshotProvider.getInitialSnapshot(), txVM.getHash()).isEmpty()) {
- state = false;
- info = "tails are not consistent (bundle is invalid): " + transaction;
- break;
+ } else {
+ if (bundleValidator
+ .validate(tangle, true, snapshotProvider.getInitialSnapshot(), txVM.getHash())
+ .isEmpty()) {
+ state = false;
+ info = "tails are not consistent (bundle is invalid): " + transaction;
+ break;
+ }
}
}
diff --git a/src/main/java/com/iota/iri/service/ledger/LedgerService.java b/src/main/java/com/iota/iri/service/ledger/LedgerService.java
index b785cdafc9..c2ac3d9cd0 100644
--- a/src/main/java/com/iota/iri/service/ledger/LedgerService.java
+++ b/src/main/java/com/iota/iri/service/ledger/LedgerService.java
@@ -2,6 +2,7 @@
import com.iota.iri.controllers.MilestoneViewModel;
import com.iota.iri.model.Hash;
+import com.iota.iri.storage.Tangle;
import java.util.List;
import java.util.Map;
@@ -99,12 +100,16 @@ public interface LedgerService {
*
*
* @param visitedTransactions a set of transaction hashes that shall be considered to be visited already
- * @param startTransaction the transaction that marks the start of the dag traversal and that has its approvees
- * examined
+ * @param startTransaction the transaction that marks the start of the dag traversal and that has its approvees
+ * examined
+ * @param enforceExtraRules enforce {@link com.iota.iri.BundleValidator#validateBundleTransactionsApproval(List)}
+ * and {@link com.iota.iri.BundleValidator#validateBundleTailApproval(Tangle, List)}.
+ * Enforcing them may break backwards compatibility.
* @return a map of the balance changes (addresses associated to their balance) or {@code null} if the balance could
* not be generated due to inconsistencies
* @throws LedgerException if anything unexpected happens while generating the balance changes
*/
- Map generateBalanceDiff(Set visitedTransactions, Hash startTransaction, int milestoneIndex)
+ Map generateBalanceDiff(Set visitedTransactions, Hash startTransaction, int milestoneIndex,
+ boolean enforceExtraRules)
throws LedgerException;
}
diff --git a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java
index da0224e62d..d340236551 100644
--- a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java
+++ b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java
@@ -133,7 +133,7 @@ public boolean isBalanceDiffConsistent(Set approvedHashes, Map
}
Set visitedHashes = new HashSet<>(approvedHashes);
Map currentState = generateBalanceDiff(visitedHashes, tip,
- snapshotProvider.getLatestSnapshot().getIndex());
+ snapshotProvider.getLatestSnapshot().getIndex(), true);
if (currentState == null) {
return false;
}
@@ -152,7 +152,8 @@ public boolean isBalanceDiffConsistent(Set approvedHashes, Map
}
@Override
- public Map generateBalanceDiff(Set visitedTransactions, Hash startTransaction, int milestoneIndex)
+ public Map generateBalanceDiff(Set visitedTransactions, Hash startTransaction, int milestoneIndex,
+ boolean enforceExtraRules)
throws LedgerException {
Map state = new HashMap<>();
@@ -176,7 +177,8 @@ public Map generateBalanceDiff(Set visitedTransactions, Hash s
if (!milestoneService.isTransactionConfirmed(transactionViewModel, milestoneIndex)) {
final List bundleTransactions = bundleValidator.validate(tangle,
- snapshotProvider.getInitialSnapshot(), transactionViewModel.getHash());
+ enforceExtraRules, snapshotProvider.getInitialSnapshot(),
+ transactionViewModel.getHash());
if (bundleTransactions.isEmpty()) {
return null;
@@ -257,7 +259,7 @@ private boolean generateStateDiff(MilestoneViewModel milestone) throws LedgerExc
try {
Hash tail = transactionViewModel.getHash();
Map balanceChanges = generateBalanceDiff(new HashSet<>(), tail,
- snapshotProvider.getLatestSnapshot().getIndex());
+ snapshotProvider.getLatestSnapshot().getIndex(), false);
successfullyProcessed = balanceChanges != null;
if (successfullyProcessed) {
successfullyProcessed = snapshotProvider.getLatestSnapshot().patchedState(
diff --git a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java
index a6146a8062..6d3188f10b 100644
--- a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java
+++ b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java
@@ -162,7 +162,7 @@ public MilestoneValidity validateMilestone(TransactionViewModel transactionViewM
return existingMilestone.getHash().equals(transactionViewModel.getHash()) ? VALID : INVALID;
}
- final List bundleTransactions = bundleValidator.validate(tangle,
+ final List bundleTransactions = bundleValidator.validate(tangle, true,
snapshotProvider.getInitialSnapshot(), transactionViewModel.getHash());
if (bundleTransactions.isEmpty()) {
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 9706f1f9df..4b2079fd07 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.TransactionValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.model.Hash;
import com.iota.iri.service.milestone.MilestoneSolidifier;
import com.iota.iri.service.snapshot.SnapshotProvider;
diff --git a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java
index 9c2cf55e22..4766caab11 100644
--- a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java
+++ b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java
@@ -125,7 +125,7 @@ private boolean wasTransactionSpentFrom(TransactionViewModel tx) throws Exceptio
private boolean isBundleValid(Hash tailHash) throws Exception {
List validation =
- bundleValidator.validate(tangle, snapshotProvider.getInitialSnapshot(), tailHash);
+ bundleValidator.validate(tangle, false, snapshotProvider.getInitialSnapshot(), tailHash);
return (CollectionUtils.isNotEmpty(validation) && validation.get(0).getValidity() == 1);
}
diff --git a/src/main/java/com/iota/iri/TransactionValidator.java b/src/main/java/com/iota/iri/service/validation/TransactionValidator.java
similarity index 98%
rename from src/main/java/com/iota/iri/TransactionValidator.java
rename to src/main/java/com/iota/iri/service/validation/TransactionValidator.java
index ef51b5c203..b2c4995c70 100644
--- a/src/main/java/com/iota/iri/TransactionValidator.java
+++ b/src/main/java/com/iota/iri/service/validation/TransactionValidator.java
@@ -1,4 +1,4 @@
-package com.iota.iri;
+package com.iota.iri.service.validation;
import com.google.common.annotations.VisibleForTesting;
import com.iota.iri.conf.ProtocolConfig;
@@ -65,7 +65,7 @@ public class TransactionValidator {
* minimum weight magnitude: the minimal number of 9s that ought to appear at the end of the
* transaction hash
*/
- TransactionValidator(Tangle tangle, SnapshotProvider snapshotProvider, TipsViewModel tipsViewModel, TransactionRequester transactionRequester, ProtocolConfig protocolConfig) {
+ public TransactionValidator(Tangle tangle, SnapshotProvider snapshotProvider, TipsViewModel tipsViewModel, TransactionRequester transactionRequester, ProtocolConfig protocolConfig) {
this.tangle = tangle;
this.snapshotProvider = snapshotProvider;
this.tipsViewModel = tipsViewModel;
diff --git a/src/test/java/com/iota/iri/BundleValidatorTest.java b/src/test/java/com/iota/iri/BundleValidatorTest.java
index a9cd40622c..97e453627b 100644
--- a/src/test/java/com/iota/iri/BundleValidatorTest.java
+++ b/src/test/java/com/iota/iri/BundleValidatorTest.java
@@ -4,6 +4,7 @@
import com.iota.iri.crypto.ISS;
import com.iota.iri.crypto.SpongeFactory;
import com.iota.iri.model.Hash;
+import com.iota.iri.model.HashFactory;
import com.iota.iri.model.TransactionHash;
import com.iota.iri.service.snapshot.Snapshot;
import com.iota.iri.service.snapshot.impl.SnapshotMockUtils;
@@ -13,6 +14,10 @@
import com.iota.iri.controllers.TransactionViewModel;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.*;
import java.util.stream.Collectors;
@@ -20,9 +25,15 @@
import static org.junit.Assert.*;
public class BundleValidatorTest {
+
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
private static Tangle tangle = new Tangle();
private static Snapshot snapshot;
- private static BundleValidator bundleValidator = new BundleValidator();
+
+ @Spy
+ private BundleValidator bundleValidator = new BundleValidator();
@Before
public void setUp() throws Exception {
@@ -79,6 +90,80 @@ public void checkInconsistencyOfConsistentBundle() {
assertFalse("should be false because bundle is consistent", BundleValidator.isInconsistent(transactions));
}
+ @Test
+ public void checkBundleTransactionsApproval() {
+ //tx0 and tx1 both approve via branch, the trunk of tx2
+ String[] trytes = {
+ "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999GNXKWQPHMFCEZZCKUCCD9PROQVPCTBVVAPHCLVOPYYQLQRMPGTQXBVZLKGGT9NXHUAASFIAVEOZTBSM9DA99999999999999999999999999RENGLE9BEAT9999999999999999FDKW9AD99999999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9DKZYSMJAQRLXZHCNPTJPJLOONNDZHGNTBIINGYKZQAAWF9LWQTHUQSAXMLQN9DKCQWHIJXNGLGQVA9999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999TANGLE9BEAT9999999999999999SVIDBWNOF999999999K99999999POWSRVIO9VMIJ99UGNETMNPNNNE",
+ "YU9WINMHSBIJ9HRBKDEHZYOCDGOZ9HZSSVXAYYOD9AMAFBCROMLSFDFIAQKEXAVFDLJYVABRSONSTTXLCQJTSDRFDEXZIORLRSQDZQODNCHAAFUPGBCEVYNRSJQGUZRAQIIEZPFQGWPQIBMDVNZGQAKCU9VLHGYSNDPHCMOCTRYQOYHFPRQSGHSDEF9MSHGXYGUTHSBSF9SOSJQFOZXMQGCUXAYWLBJHXOHTOM9ITZPISEXLAYAZBHTZDDCOIHKDBL9AAXZEDFHDYCPXLDGR9JTVXDXBC9DRDVREEEVGVPWVEZHJBYQYTGDEWQHVEDTAVLWW9WBXOIGDGUAAAVKFMPVKSYBVOIWPYGCMACEAAHGUIQVFLFMSCPKTYQWJDGMDNLMDIUUYI9SZOFQPLKKACESSWIXPFYZWDHETYUGEVASPHBGQPJPWVESSUUEVUYDYNQUQURSMCEPKIHYXNFHZSHIIDTMEZQ9IPSHTBDK9UHRSIIEJRILBCMVUMGAGBMHKEMUUKQDCYAVIAN9MBWJCAUSSJRSAUEUBGEFFCFSOBVGLXBAIDOH9NXWQUSIFUINEVZUWELLVIOSSQRVHMTDBCATBWFSDJJDLURAUKEQEIRICSCXAOJNPWHQKYVTRLCRKHLFHHHPYSMUEAPMQPMPS9CHCIYXEYRNIOOGRWKVHUGIWJZIGPWGZGBGYTVC9UVEJ9JBIYMDHYXLWIDXHZVFCCPLYXDPQPJGMKNZMMONGNXSNNPCGEGRJTMCPTJONGPAEDMSHMGAPGBDWVCSCTJAECSHTEGBMVCDZMQYZ9IHERWGCKAIMFOJRAEDRDSWJASV9XYS9CTOEETPQHRKJZ9FPWTVCGLEJQJXVVDTYZ9DNSHQAINKROQQIPTFGTABEELYPBQHGNNB9NCL9BVTG9MH99BPIRNNGC9MQQRH9YUDYSUJIDSZKCIGEKFSJ9PXUYKVPUZYFIREUAMUZE9EIMJK9YGAZXZBPRXIRDCEVDHWOCFIJTVRCRZRRJGFLILKVNMJAX9MUMTXCWTUTP9NXBZUMAYYY9DNVZTCROBCMMHVKSQRXMGROCMMCDENWQWBV9LEIJONWTQXOUQZOTGEJHJELYZOPICLDWPHVCMBFSYHPIJYZQDEGYITVPYNQMXPRTPKADLUKW9AEMSMGIWCUMOQMUDN9HZGYKTNVVNLOQAFVETEDJMJVKYCE9RE9AJP9RMYFKGDEC9GIGAFXEXIYXPFBOMRPULVJMYTQEPDMMIABDHJGRNNCKADZAUFV9WCWFYFHXLCED9E9SKKYHVKDTGNCHEKFJL9YJHTSRUVKEPHPCQSFNBXKOJNGBSEOZLVVGNUQTRACVGEKDSORUHVSDSQNUZASWXRADGAUDYDPODDEDNDDT9OAEARSBLPVSYAOEYYZJIZIZDENQOKMXATCTD9NEFVQWNCET9N9IXWCCWHYUBDTDJUZFXWMATRYSGJUQAFKAPIQIIXVFFEFNLGDPAIGKONUWLUTQTKZJYDEGPASTOMM9QTGOMYHFUKMNEVWPCMTLPDSOEEWDBRHZPZJIFMBUTY9QPRLPSVURIRMCWLH9YIZBOTJQMMF9ZP9VCCEGWVKSSQNANP9ZXVCAFGAVBXDJHUMKTKUBZRNKUGJTVUPDZMUK9KZI9EKY9DQDUNA9ZVPCROKYYPISSTDOJZJXMXZWSGZBEPKAUYPBPUTDVQKUYXWYTZXLYTCLLKLJUUJGFFCMZFKDTJET9KEDRMWZSAEFCSXLMRHRLG9EKLRZJ9NZQEONGFQSKSQMWRZXWVEGHCF9ZKGQITDLGFTZLCQMXVGVYIPKSUPPAOIKVWARJHAEJUZOOJFCFJTGQZWROSCEQSITQWZTQDFKECATBWSIRDFVWH9TXXUVEBRRKOTFSGBMGCMZDK9BJRVEUYOVNSLZNNSTWOB9CFNDJHJENDWSTIMMSWD9HSA9LDGTJDJFRGQCWOHYJNMFFLLDMVQEMCMNVQWMOGCEZUTK9MPULTDMKTIVFNVWXDNTRHNREHEDWIZCCRFVM9TXCTLBLYEKTRVVWHKZDOSYOBNLEKPNXMDZGLSVBBNBJIBZYBXZCENKVIXMNGUJUYFGFYFBKELSBZROQWKMESZTWDEEUCSXOWFSNJUGGJANMJFCBJV9GMDDJCAJHWKYQFJLFWZESTSIRBZQWYHGQDFUUWF9F9HXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMDZ99999999999999999999999999999999999999999999999999999FDKW9AD99A99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH99XJLIVAZYJGPKEHANESX9DORLEIOIFL9WVQBBNUDRZDGJMVSRTSFKOJPIMEXPBCPGKUUEKRGAXOB99999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999999999999999999999999999999RSIDBWNOF999999999K99999999POWSRVIO9VMIJ99FKKNKGPNNNNP",
+ "COSNPEACSPESTKTUS9CSMOCWVUBLERQJQGLLA9AYRRXMOVOBULQZZZJPLNELEX9EVZDAFOHBB9FBAYOTDB9YGJAVTDWNPJVVGEPFJ9QBYLXAYZEETY9GMYQRIRAIPWMJUFMFRSQRCEIOWISMBJKGCIGIIYNMXAYLXZIBNKANT9DSQXPBUPWGUJTOMD9JKSNFZJJFBZYBWRWQIZFOZXQWNJTRZTUPHVEGTAOLMBEPXOB9TCEBNIWXQUHNQJZHAOWGVOOOTSCTEUEMXJUODZP9CDLCMEXAW9YJGVNREENETEYXOAKZVRJVUSCRUSRCNZUGUOLCNHQITWYPJVJGPVULASTONDZRDVCNGJSN9DMMUORCOIBTITSPBXSAVTOHSAXHMGH99APRTQIVCEI9NWYKDOZCCWBNZGGCUCISSMGJHJ9OVSFBZVFMBODKRKSLIDESGFDQITCYFARWYTLFKSPLNVQ9LTYFODCHBAJKCWVZNBLOJH9ABFGDFOXHDNHZILINMYMVZYWTFJJOLADPTZRPJEMGPFQNYTFDGOKMDTDSKPTHIDWLMRSSACZMTHUCWVZHCYSPJRATABZ9JHWEBGTTQORGFOYDWUIYFYGOGGTYGYYI9RPKPMCTYNYKHHNWQKJTDQDHTRUYQPMCDDLVUUHUBPWGPMNQYPKHDHGIUSXJGNUXIYGZRGVYPTTV9KXJDLGVVXRIWIGKZJVCQ9DUWAYNDRKYCGKZDEOKZQCXELSQXLGLAPBDIWRTKGSIRNAZ9XFEGMWOWLPUA9TSGYRDOU9GKY9JJDKCJKKGJCRARGSNECJ9SEVAGPPJMBTMGNWSLNSFBOEYMIQBKOVSCTDIWCOQTHBVTZOFNNIYKKJLAROKIRFFPCLSOWO9BVNNJSXEYLJASPUEFKCEI9SGWADNGS9MZAKFTCAYNRVKGDIGYDTPGJXWFAEFGG9BX9XCNBZGQULH9IPPIQ9VFVAXGELUMCTE9TOFOGCTFYBYEBFOKKAESIIHEWLYLQKWDHPYNELJKMZHDSDSK9ZWFP99FQFLUXGTSVDMGHIFZFUCYTYPVEFEDM9TOHCQVIBXARYWZEKTWEOVSBYZTJZRUFZJCLRBTOWPTKY99CYQTGPMAKFYALIWPEQLUB9RGANTKDDMSDSJOTNPFZCXKJUGTJWWPK9FAJGPJOEBRHL9ZU9XRBDJBVMFIZIZFGKETH9RJUSDIOWDZIXFOJLT9DSIAWSCEYBNOGOAZQPZKVZTBXGACCBVQGFNGXNQFTVPUGUOHNBKTUERLSTDXGRKZSATIRHDBSXARYATBHOLUHCQGPRMQGSJUAXUCWKDXAUGORHFVVVDZQEYYGSV9IUM9VTMHJCIKBQD9TQLLYTZHAKDHPFGYMRGJOEDJKQTBBNFCKDEPYTPWUJEOPRJWHAILMVW9AXS9HAT9GCGRXZEHWNDOVGKGPWFXFC9IKLVZJCTGZUJFRHGL9FZWZYP9TPOWGOUTQ9INYBVTD9N9OV9AM9GYRJ99MRWRWLOAKJXMDTFNS9NZQAYGVZRJ9WVSIDRRQLGKH9VPUVKFWZMSRVVLKXUGJAZESC9LEYDNNDWS9WTWNFPCR9YKPOMIJAVHCRCJVBDFWSTDN9BPJRDMGZFXWTY9GUX9WEPJQMXSECFPREXAJ9DU9OLMNQFTF99QYZOGDUTMBHMNMCNUQTU9FTNEUNBT9NQO9YADGY9C9LQR9DYVJZE9GMMYAOUSWUIVJ9EAR9EEFCO9SZTQ9JQHPIJGXIOQZHOSUSVMHBOLTYSEONVLRISNAN9KJHHFFISTYIHYVAMXLPNALDELETXVSWGUWOIVKNAKZKNNRVNJKE9WMNQBXFGXSYZQQSIODAKINA9CPBOGAYKJ9V9GS9HERXSVYPEILYXC9GURBREBCXRZJVXHUOPGYFLPIFBCOTMHFTAAUQUZVEKOWYFFGSCXDFDBTSGZRMPQR9EYSZYGQETCWFDRXVOXXCUBOQTBQGVLAONSTSLURFYSDPAVNCXNWVPQOUORMVSLYISQUQKXFYYMJSGHQP9VAAZABEAGJMCPIEBZQSYCPS9CNKFBDRDTDA9C9MVKGUISRX9ODVUJNPDWMZ9ESYXJ99ETDGSTJZMZYJEGRUWTORMCLNHLDQ9YIJWPMDYGGJQHMFPJ9VKINJUESTCBJQXFTCJFNUXPZONVKXJRAYGKIWHXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMD999999999999999999999999999999999999999999999999999999FDKW9AD99B99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999FTXZAEQIKGFJABOGCCQGOMKNASGWCQWMNIMRDTHKPJMTKDGMEQNZOAYARIYEWFZYJALRKFODOITTZ9999999999999999999999999999999LLHDBWNOF999999999K99999999POWSRVIO9VMIJ99UMGMTTTPNNNV",
+ };
+ List transactions = persistAndMapTxs(trytes);
+ assertEquals("should be valid", BundleValidator.Validity.VALID,
+ bundleValidator.validateBundleTransactionsApproval(transactions));
+ }
+
+ @Test
+ public void checkBundleTransactionsApprovalFailure(){
+ //tx1 approves via branch, the branch of tx2
+ String[] trytes = {
+ "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999GNXKWQPHMFCEZZCKUCCD9PROQVPCTBVVAPHCLVOPYYQLQRMPGTQXBVZLKGGT9NXHUAASFIAVEOZTBSM9DA99999999999999999999999999RENGLE9BEAT9999999999999999FDKW9AD99999999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9DKZYSMJAQRLXZHCNPTJPJLOONNDZHGNTBIINGYKZQAAWF9LWQTHUQSAXMLQN9DKCQWHIJXNGLGQVA9999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999TANGLE9BEAT9999999999999999SVIDBWNOF999999999K99999999POWSRVIO9VMIJ99UGNETMNPNNNE",
+ "YU9WINMHSBIJ9HRBKDEHZYOCDGOZ9HZSSVXAYYOD9AMAFBCROMLSFDFIAQKEXAVFDLJYVABRSONSTTXLCQJTSDRFDEXZIORLRSQDZQODNCHAAFUPGBCEVYNRSJQGUZRAQIIEZPFQGWPQIBMDVNZGQAKCU9VLHGYSNDPHCMOCTRYQOYHFPRQSGHSDEF9MSHGXYGUTHSBSF9SOSJQFOZXMQGCUXAYWLBJHXOHTOM9ITZPISEXLAYAZBHTZDDCOIHKDBL9AAXZEDFHDYCPXLDGR9JTVXDXBC9DRDVREEEVGVPWVEZHJBYQYTGDEWQHVEDTAVLWW9WBXOIGDGUAAAVKFMPVKSYBVOIWPYGCMACEAAHGUIQVFLFMSCPKTYQWJDGMDNLMDIUUYI9SZOFQPLKKACESSWIXPFYZWDHETYUGEVASPHBGQPJPWVESSUUEVUYDYNQUQURSMCEPKIHYXNFHZSHIIDTMEZQ9IPSHTBDK9UHRSIIEJRILBCMVUMGAGBMHKEMUUKQDCYAVIAN9MBWJCAUSSJRSAUEUBGEFFCFSOBVGLXBAIDOH9NXWQUSIFUINEVZUWELLVIOSSQRVHMTDBCATBWFSDJJDLURAUKEQEIRICSCXAOJNPWHQKYVTRLCRKHLFHHHPYSMUEAPMQPMPS9CHCIYXEYRNIOOGRWKVHUGIWJZIGPWGZGBGYTVC9UVEJ9JBIYMDHYXLWIDXHZVFCCPLYXDPQPJGMKNZMMONGNXSNNPCGEGRJTMCPTJONGPAEDMSHMGAPGBDWVCSCTJAECSHTEGBMVCDZMQYZ9IHERWGCKAIMFOJRAEDRDSWJASV9XYS9CTOEETPQHRKJZ9FPWTVCGLEJQJXVVDTYZ9DNSHQAINKROQQIPTFGTABEELYPBQHGNNB9NCL9BVTG9MH99BPIRNNGC9MQQRH9YUDYSUJIDSZKCIGEKFSJ9PXUYKVPUZYFIREUAMUZE9EIMJK9YGAZXZBPRXIRDCEVDHWOCFIJTVRCRZRRJGFLILKVNMJAX9MUMTXCWTUTP9NXBZUMAYYY9DNVZTCROBCMMHVKSQRXMGROCMMCDENWQWBV9LEIJONWTQXOUQZOTGEJHJELYZOPICLDWPHVCMBFSYHPIJYZQDEGYITVPYNQMXPRTPKADLUKW9AEMSMGIWCUMOQMUDN9HZGYKTNVVNLOQAFVETEDJMJVKYCE9RE9AJP9RMYFKGDEC9GIGAFXEXIYXPFBOMRPULVJMYTQEPDMMIABDHJGRNNCKADZAUFV9WCWFYFHXLCED9E9SKKYHVKDTGNCHEKFJL9YJHTSRUVKEPHPCQSFNBXKOJNGBSEOZLVVGNUQTRACVGEKDSORUHVSDSQNUZASWXRADGAUDYDPODDEDNDDT9OAEARSBLPVSYAOEYYZJIZIZDENQOKMXATCTD9NEFVQWNCET9N9IXWCCWHYUBDTDJUZFXWMATRYSGJUQAFKAPIQIIXVFFEFNLGDPAIGKONUWLUTQTKZJYDEGPASTOMM9QTGOMYHFUKMNEVWPCMTLPDSOEEWDBRHZPZJIFMBUTY9QPRLPSVURIRMCWLH9YIZBOTJQMMF9ZP9VCCEGWVKSSQNANP9ZXVCAFGAVBXDJHUMKTKUBZRNKUGJTVUPDZMUK9KZI9EKY9DQDUNA9ZVPCROKYYPISSTDOJZJXMXZWSGZBEPKAUYPBPUTDVQKUYXWYTZXLYTCLLKLJUUJGFFCMZFKDTJET9KEDRMWZSAEFCSXLMRHRLG9EKLRZJ9NZQEONGFQSKSQMWRZXWVEGHCF9ZKGQITDLGFTZLCQMXVGVYIPKSUPPAOIKVWARJHAEJUZOOJFCFJTGQZWROSCEQSITQWZTQDFKECATBWSIRDFVWH9TXXUVEBRRKOTFSGBMGCMZDK9BJRVEUYOVNSLZNNSTWOB9CFNDJHJENDWSTIMMSWD9HSA9LDGTJDJFRGQCWOHYJNMFFLLDMVQEMCMNVQWMOGCEZUTK9MPULTDMKTIVFNVWXDNTRHNREHEDWIZCCRFVM9TXCTLBLYEKTRVVWHKZDOSYOBNLEKPNXMDZGLSVBBNBJIBZYBXZCENKVIXMNGUJUYFGFYFBKELSBZROQWKMESZTWDEEUCSXOWFSNJUGGJANMJFCBJV9GMDDJCAJHWKYQFJLFWZESTSIRBZQWYHGQDFUUWF9F9HXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMDZ99999999999999999999999999999999999999999999999999999FDKW9AD99A99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH99XJLIVAZYJGPKEHANESX9DORLEIOIFL9WVQBBNUDRZDGJMVSRTSFKOJPIMEXPBCPGKUUEKRGAXOB99999FTXZAEQIKGFJABOGCCQGOMKNASGWCQWMNIMRDTHKPJMTKDGMEQNZOAYARIYEWFZYJALRKFODOITTZ9999999999999999999999999999999RSIDBWNOF999999999K99999999POWSRVIO9VMIJ99FKKNKGPNNNNP",
+ "COSNPEACSPESTKTUS9CSMOCWVUBLERQJQGLLA9AYRRXMOVOBULQZZZJPLNELEX9EVZDAFOHBB9FBAYOTDB9YGJAVTDWNPJVVGEPFJ9QBYLXAYZEETY9GMYQRIRAIPWMJUFMFRSQRCEIOWISMBJKGCIGIIYNMXAYLXZIBNKANT9DSQXPBUPWGUJTOMD9JKSNFZJJFBZYBWRWQIZFOZXQWNJTRZTUPHVEGTAOLMBEPXOB9TCEBNIWXQUHNQJZHAOWGVOOOTSCTEUEMXJUODZP9CDLCMEXAW9YJGVNREENETEYXOAKZVRJVUSCRUSRCNZUGUOLCNHQITWYPJVJGPVULASTONDZRDVCNGJSN9DMMUORCOIBTITSPBXSAVTOHSAXHMGH99APRTQIVCEI9NWYKDOZCCWBNZGGCUCISSMGJHJ9OVSFBZVFMBODKRKSLIDESGFDQITCYFARWYTLFKSPLNVQ9LTYFODCHBAJKCWVZNBLOJH9ABFGDFOXHDNHZILINMYMVZYWTFJJOLADPTZRPJEMGPFQNYTFDGOKMDTDSKPTHIDWLMRSSACZMTHUCWVZHCYSPJRATABZ9JHWEBGTTQORGFOYDWUIYFYGOGGTYGYYI9RPKPMCTYNYKHHNWQKJTDQDHTRUYQPMCDDLVUUHUBPWGPMNQYPKHDHGIUSXJGNUXIYGZRGVYPTTV9KXJDLGVVXRIWIGKZJVCQ9DUWAYNDRKYCGKZDEOKZQCXELSQXLGLAPBDIWRTKGSIRNAZ9XFEGMWOWLPUA9TSGYRDOU9GKY9JJDKCJKKGJCRARGSNECJ9SEVAGPPJMBTMGNWSLNSFBOEYMIQBKOVSCTDIWCOQTHBVTZOFNNIYKKJLAROKIRFFPCLSOWO9BVNNJSXEYLJASPUEFKCEI9SGWADNGS9MZAKFTCAYNRVKGDIGYDTPGJXWFAEFGG9BX9XCNBZGQULH9IPPIQ9VFVAXGELUMCTE9TOFOGCTFYBYEBFOKKAESIIHEWLYLQKWDHPYNELJKMZHDSDSK9ZWFP99FQFLUXGTSVDMGHIFZFUCYTYPVEFEDM9TOHCQVIBXARYWZEKTWEOVSBYZTJZRUFZJCLRBTOWPTKY99CYQTGPMAKFYALIWPEQLUB9RGANTKDDMSDSJOTNPFZCXKJUGTJWWPK9FAJGPJOEBRHL9ZU9XRBDJBVMFIZIZFGKETH9RJUSDIOWDZIXFOJLT9DSIAWSCEYBNOGOAZQPZKVZTBXGACCBVQGFNGXNQFTVPUGUOHNBKTUERLSTDXGRKZSATIRHDBSXARYATBHOLUHCQGPRMQGSJUAXUCWKDXAUGORHFVVVDZQEYYGSV9IUM9VTMHJCIKBQD9TQLLYTZHAKDHPFGYMRGJOEDJKQTBBNFCKDEPYTPWUJEOPRJWHAILMVW9AXS9HAT9GCGRXZEHWNDOVGKGPWFXFC9IKLVZJCTGZUJFRHGL9FZWZYP9TPOWGOUTQ9INYBVTD9N9OV9AM9GYRJ99MRWRWLOAKJXMDTFNS9NZQAYGVZRJ9WVSIDRRQLGKH9VPUVKFWZMSRVVLKXUGJAZESC9LEYDNNDWS9WTWNFPCR9YKPOMIJAVHCRCJVBDFWSTDN9BPJRDMGZFXWTY9GUX9WEPJQMXSECFPREXAJ9DU9OLMNQFTF99QYZOGDUTMBHMNMCNUQTU9FTNEUNBT9NQO9YADGY9C9LQR9DYVJZE9GMMYAOUSWUIVJ9EAR9EEFCO9SZTQ9JQHPIJGXIOQZHOSUSVMHBOLTYSEONVLRISNAN9KJHHFFISTYIHYVAMXLPNALDELETXVSWGUWOIVKNAKZKNNRVNJKE9WMNQBXFGXSYZQQSIODAKINA9CPBOGAYKJ9V9GS9HERXSVYPEILYXC9GURBREBCXRZJVXHUOPGYFLPIFBCOTMHFTAAUQUZVEKOWYFFGSCXDFDBTSGZRMPQR9EYSZYGQETCWFDRXVOXXCUBOQTBQGVLAONSTSLURFYSDPAVNCXNWVPQOUORMVSLYISQUQKXFYYMJSGHQP9VAAZABEAGJMCPIEBZQSYCPS9CNKFBDRDTDA9C9MVKGUISRX9ODVUJNPDWMZ9ESYXJ99ETDGSTJZMZYJEGRUWTORMCLNHLDQ9YIJWPMDYGGJQHMFPJ9VKINJUESTCBJQXFTCJFNUXPZONVKXJRAYGKIWHXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMD999999999999999999999999999999999999999999999999999999FDKW9AD99B99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999FTXZAEQIKGFJABOGCCQGOMKNASGWCQWMNIMRDTHKPJMTKDGMEQNZOAYARIYEWFZYJALRKFODOITTZ9999999999999999999999999999999LLHDBWNOF999999999K99999999POWSRVIO9VMIJ99UMGMTTTPNNNV",
+ };
+ List transactions = persistAndMapTxs(trytes);
+ assertEquals("should be invalid", BundleValidator.Validity.INVALID,
+ bundleValidator.validateBundleTransactionsApproval(transactions));
+ }
+
+ @Test
+ public void checkBundleTailApproval() throws Exception {
+ //tx2 approves via branch and trunk, tail txs
+ String[] trytes = {
+ "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999GNXKWQPHMFCEZZCKUCCD9PROQVPCTBVVAPHCLVOPYYQLQRMPGTQXBVZLKGGT9NXHUAASFIAVEOZTBSM9DA99999999999999999999999999RENGLE9BEAT9999999999999999FDKW9AD99999999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9DKZYSMJAQRLXZHCNPTJPJLOONNDZHGNTBIINGYKZQAAWF9LWQTHUQSAXMLQN9DKCQWHIJXNGLGQVA9999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999TANGLE9BEAT9999999999999999SVIDBWNOF999999999K99999999POWSRVIO9VMIJ99UGNETMNPNNNE",
+ "YU9WINMHSBIJ9HRBKDEHZYOCDGOZ9HZSSVXAYYOD9AMAFBCROMLSFDFIAQKEXAVFDLJYVABRSONSTTXLCQJTSDRFDEXZIORLRSQDZQODNCHAAFUPGBCEVYNRSJQGUZRAQIIEZPFQGWPQIBMDVNZGQAKCU9VLHGYSNDPHCMOCTRYQOYHFPRQSGHSDEF9MSHGXYGUTHSBSF9SOSJQFOZXMQGCUXAYWLBJHXOHTOM9ITZPISEXLAYAZBHTZDDCOIHKDBL9AAXZEDFHDYCPXLDGR9JTVXDXBC9DRDVREEEVGVPWVEZHJBYQYTGDEWQHVEDTAVLWW9WBXOIGDGUAAAVKFMPVKSYBVOIWPYGCMACEAAHGUIQVFLFMSCPKTYQWJDGMDNLMDIUUYI9SZOFQPLKKACESSWIXPFYZWDHETYUGEVASPHBGQPJPWVESSUUEVUYDYNQUQURSMCEPKIHYXNFHZSHIIDTMEZQ9IPSHTBDK9UHRSIIEJRILBCMVUMGAGBMHKEMUUKQDCYAVIAN9MBWJCAUSSJRSAUEUBGEFFCFSOBVGLXBAIDOH9NXWQUSIFUINEVZUWELLVIOSSQRVHMTDBCATBWFSDJJDLURAUKEQEIRICSCXAOJNPWHQKYVTRLCRKHLFHHHPYSMUEAPMQPMPS9CHCIYXEYRNIOOGRWKVHUGIWJZIGPWGZGBGYTVC9UVEJ9JBIYMDHYXLWIDXHZVFCCPLYXDPQPJGMKNZMMONGNXSNNPCGEGRJTMCPTJONGPAEDMSHMGAPGBDWVCSCTJAECSHTEGBMVCDZMQYZ9IHERWGCKAIMFOJRAEDRDSWJASV9XYS9CTOEETPQHRKJZ9FPWTVCGLEJQJXVVDTYZ9DNSHQAINKROQQIPTFGTABEELYPBQHGNNB9NCL9BVTG9MH99BPIRNNGC9MQQRH9YUDYSUJIDSZKCIGEKFSJ9PXUYKVPUZYFIREUAMUZE9EIMJK9YGAZXZBPRXIRDCEVDHWOCFIJTVRCRZRRJGFLILKVNMJAX9MUMTXCWTUTP9NXBZUMAYYY9DNVZTCROBCMMHVKSQRXMGROCMMCDENWQWBV9LEIJONWTQXOUQZOTGEJHJELYZOPICLDWPHVCMBFSYHPIJYZQDEGYITVPYNQMXPRTPKADLUKW9AEMSMGIWCUMOQMUDN9HZGYKTNVVNLOQAFVETEDJMJVKYCE9RE9AJP9RMYFKGDEC9GIGAFXEXIYXPFBOMRPULVJMYTQEPDMMIABDHJGRNNCKADZAUFV9WCWFYFHXLCED9E9SKKYHVKDTGNCHEKFJL9YJHTSRUVKEPHPCQSFNBXKOJNGBSEOZLVVGNUQTRACVGEKDSORUHVSDSQNUZASWXRADGAUDYDPODDEDNDDT9OAEARSBLPVSYAOEYYZJIZIZDENQOKMXATCTD9NEFVQWNCET9N9IXWCCWHYUBDTDJUZFXWMATRYSGJUQAFKAPIQIIXVFFEFNLGDPAIGKONUWLUTQTKZJYDEGPASTOMM9QTGOMYHFUKMNEVWPCMTLPDSOEEWDBRHZPZJIFMBUTY9QPRLPSVURIRMCWLH9YIZBOTJQMMF9ZP9VCCEGWVKSSQNANP9ZXVCAFGAVBXDJHUMKTKUBZRNKUGJTVUPDZMUK9KZI9EKY9DQDUNA9ZVPCROKYYPISSTDOJZJXMXZWSGZBEPKAUYPBPUTDVQKUYXWYTZXLYTCLLKLJUUJGFFCMZFKDTJET9KEDRMWZSAEFCSXLMRHRLG9EKLRZJ9NZQEONGFQSKSQMWRZXWVEGHCF9ZKGQITDLGFTZLCQMXVGVYIPKSUPPAOIKVWARJHAEJUZOOJFCFJTGQZWROSCEQSITQWZTQDFKECATBWSIRDFVWH9TXXUVEBRRKOTFSGBMGCMZDK9BJRVEUYOVNSLZNNSTWOB9CFNDJHJENDWSTIMMSWD9HSA9LDGTJDJFRGQCWOHYJNMFFLLDMVQEMCMNVQWMOGCEZUTK9MPULTDMKTIVFNVWXDNTRHNREHEDWIZCCRFVM9TXCTLBLYEKTRVVWHKZDOSYOBNLEKPNXMDZGLSVBBNBJIBZYBXZCENKVIXMNGUJUYFGFYFBKELSBZROQWKMESZTWDEEUCSXOWFSNJUGGJANMJFCBJV9GMDDJCAJHWKYQFJLFWZESTSIRBZQWYHGQDFUUWF9F9HXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMDZ99999999999999999999999999999999999999999999999999999FDKW9AD99A99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH99XJLIVAZYJGPKEHANESX9DORLEIOIFL9WVQBBNUDRZDGJMVSRTSFKOJPIMEXPBCPGKUUEKRGAXOB99999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999999999999999999999999999999RSIDBWNOF999999999K99999999POWSRVIO9VMIJ99FKKNKGPNNNNP",
+ "COSNPEACSPESTKTUS9CSMOCWVUBLERQJQGLLA9AYRRXMOVOBULQZZZJPLNELEX9EVZDAFOHBB9FBAYOTDB9YGJAVTDWNPJVVGEPFJ9QBYLXAYZEETY9GMYQRIRAIPWMJUFMFRSQRCEIOWISMBJKGCIGIIYNMXAYLXZIBNKANT9DSQXPBUPWGUJTOMD9JKSNFZJJFBZYBWRWQIZFOZXQWNJTRZTUPHVEGTAOLMBEPXOB9TCEBNIWXQUHNQJZHAOWGVOOOTSCTEUEMXJUODZP9CDLCMEXAW9YJGVNREENETEYXOAKZVRJVUSCRUSRCNZUGUOLCNHQITWYPJVJGPVULASTONDZRDVCNGJSN9DMMUORCOIBTITSPBXSAVTOHSAXHMGH99APRTQIVCEI9NWYKDOZCCWBNZGGCUCISSMGJHJ9OVSFBZVFMBODKRKSLIDESGFDQITCYFARWYTLFKSPLNVQ9LTYFODCHBAJKCWVZNBLOJH9ABFGDFOXHDNHZILINMYMVZYWTFJJOLADPTZRPJEMGPFQNYTFDGOKMDTDSKPTHIDWLMRSSACZMTHUCWVZHCYSPJRATABZ9JHWEBGTTQORGFOYDWUIYFYGOGGTYGYYI9RPKPMCTYNYKHHNWQKJTDQDHTRUYQPMCDDLVUUHUBPWGPMNQYPKHDHGIUSXJGNUXIYGZRGVYPTTV9KXJDLGVVXRIWIGKZJVCQ9DUWAYNDRKYCGKZDEOKZQCXELSQXLGLAPBDIWRTKGSIRNAZ9XFEGMWOWLPUA9TSGYRDOU9GKY9JJDKCJKKGJCRARGSNECJ9SEVAGPPJMBTMGNWSLNSFBOEYMIQBKOVSCTDIWCOQTHBVTZOFNNIYKKJLAROKIRFFPCLSOWO9BVNNJSXEYLJASPUEFKCEI9SGWADNGS9MZAKFTCAYNRVKGDIGYDTPGJXWFAEFGG9BX9XCNBZGQULH9IPPIQ9VFVAXGELUMCTE9TOFOGCTFYBYEBFOKKAESIIHEWLYLQKWDHPYNELJKMZHDSDSK9ZWFP99FQFLUXGTSVDMGHIFZFUCYTYPVEFEDM9TOHCQVIBXARYWZEKTWEOVSBYZTJZRUFZJCLRBTOWPTKY99CYQTGPMAKFYALIWPEQLUB9RGANTKDDMSDSJOTNPFZCXKJUGTJWWPK9FAJGPJOEBRHL9ZU9XRBDJBVMFIZIZFGKETH9RJUSDIOWDZIXFOJLT9DSIAWSCEYBNOGOAZQPZKVZTBXGACCBVQGFNGXNQFTVPUGUOHNBKTUERLSTDXGRKZSATIRHDBSXARYATBHOLUHCQGPRMQGSJUAXUCWKDXAUGORHFVVVDZQEYYGSV9IUM9VTMHJCIKBQD9TQLLYTZHAKDHPFGYMRGJOEDJKQTBBNFCKDEPYTPWUJEOPRJWHAILMVW9AXS9HAT9GCGRXZEHWNDOVGKGPWFXFC9IKLVZJCTGZUJFRHGL9FZWZYP9TPOWGOUTQ9INYBVTD9N9OV9AM9GYRJ99MRWRWLOAKJXMDTFNS9NZQAYGVZRJ9WVSIDRRQLGKH9VPUVKFWZMSRVVLKXUGJAZESC9LEYDNNDWS9WTWNFPCR9YKPOMIJAVHCRCJVBDFWSTDN9BPJRDMGZFXWTY9GUX9WEPJQMXSECFPREXAJ9DU9OLMNQFTF99QYZOGDUTMBHMNMCNUQTU9FTNEUNBT9NQO9YADGY9C9LQR9DYVJZE9GMMYAOUSWUIVJ9EAR9EEFCO9SZTQ9JQHPIJGXIOQZHOSUSVMHBOLTYSEONVLRISNAN9KJHHFFISTYIHYVAMXLPNALDELETXVSWGUWOIVKNAKZKNNRVNJKE9WMNQBXFGXSYZQQSIODAKINA9CPBOGAYKJ9V9GS9HERXSVYPEILYXC9GURBREBCXRZJVXHUOPGYFLPIFBCOTMHFTAAUQUZVEKOWYFFGSCXDFDBTSGZRMPQR9EYSZYGQETCWFDRXVOXXCUBOQTBQGVLAONSTSLURFYSDPAVNCXNWVPQOUORMVSLYISQUQKXFYYMJSGHQP9VAAZABEAGJMCPIEBZQSYCPS9CNKFBDRDTDA9C9MVKGUISRX9ODVUJNPDWMZ9ESYXJ99ETDGSTJZMZYJEGRUWTORMCLNHLDQ9YIJWPMDYGGJQHMFPJ9VKINJUESTCBJQXFTCJFNUXPZONVKXJRAYGKIWHXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMD999999999999999999999999999999999999999999999999999999FDKW9AD99B99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999FTXZAEQIKGFJABOGCCQGOMKNASGWCQWMNIMRDTHKPJMTKDGMEQNZOAYARIYEWFZYJALRKFODOITTZ9999999999999999999999999999999LLHDBWNOF999999999K99999999POWSRVIO9VMIJ99UMGMTTTPNNNV",
+ };
+ List transactions = persistAndMapTxs(trytes);
+ assertEquals("should be valid because bundle approves tails", BundleValidator.Validity.VALID,
+ bundleValidator.validateBundleTailApproval(tangle, transactions));
+ }
+
+ @Test
+ public void checkBundleNonTailApproval() throws Exception {
+ //tx with currentIndex = 1
+ String[] nonTailTrytes = {"YU9WINMHSBIJ9HRBKDEHZYOCDGOZ9HZSSVXAYYOD9AMAFBCROMLSFDFIAQKEXAVFDLJYVABRSONSTTXLCQJTSDRFDEXZIORLRSQDZQODNCHAAFUPGBCEVYNRSJQGUZRAQIIEZPFQGWPQIBMDVNZGQAKCU9VLHGYSNDPHCMOCTRYQOYHFPRQSGHSDEF9MSHGXYGUTHSBSF9SOSJQFOZXMQGCUXAYWLBJHXOHTOM9ITZPISEXLAYAZBHTZDDCOIHKDBL9AAXZEDFHDYCPXLDGR9JTVXDXBC9DRDVREEEVGVPWVEZHJBYQYTGDEWQHVEDTAVLWW9WBXOIGDGUAAAVKFMPVKSYBVOIWPYGCMACEAAHGUIQVFLFMSCPKTYQWJDGMDNLMDIUUYI9SZOFQPLKKACESSWIXPFYZWDHETYUGEVASPHBGQPJPWVESSUUEVUYDYNQUQURSMCEPKIHYXNFHZSHIIDTMEZQ9IPSHTBDK9UHRSIIEJRILBCMVUMGAGBMHKEMUUKQDCYAVIAN9MBWJCAUSSJRSAUEUBGEFFCFSOBVGLXBAIDOH9NXWQUSIFUINEVZUWELLVIOSSQRVHMTDBCATBWFSDJJDLURAUKEQEIRICSCXAOJNPWHQKYVTRLCRKHLFHHHPYSMUEAPMQPMPS9CHCIYXEYRNIOOGRWKVHUGIWJZIGPWGZGBGYTVC9UVEJ9JBIYMDHYXLWIDXHZVFCCPLYXDPQPJGMKNZMMONGNXSNNPCGEGRJTMCPTJONGPAEDMSHMGAPGBDWVCSCTJAECSHTEGBMVCDZMQYZ9IHERWGCKAIMFOJRAEDRDSWJASV9XYS9CTOEETPQHRKJZ9FPWTVCGLEJQJXVVDTYZ9DNSHQAINKROQQIPTFGTABEELYPBQHGNNB9NCL9BVTG9MH99BPIRNNGC9MQQRH9YUDYSUJIDSZKCIGEKFSJ9PXUYKVPUZYFIREUAMUZE9EIMJK9YGAZXZBPRXIRDCEVDHWOCFIJTVRCRZRRJGFLILKVNMJAX9MUMTXCWTUTP9NXBZUMAYYY9DNVZTCROBCMMHVKSQRXMGROCMMCDENWQWBV9LEIJONWTQXOUQZOTGEJHJELYZOPICLDWPHVCMBFSYHPIJYZQDEGYITVPYNQMXPRTPKADLUKW9AEMSMGIWCUMOQMUDN9HZGYKTNVVNLOQAFVETEDJMJVKYCE9RE9AJP9RMYFKGDEC9GIGAFXEXIYXPFBOMRPULVJMYTQEPDMMIABDHJGRNNCKADZAUFV9WCWFYFHXLCED9E9SKKYHVKDTGNCHEKFJL9YJHTSRUVKEPHPCQSFNBXKOJNGBSEOZLVVGNUQTRACVGEKDSORUHVSDSQNUZASWXRADGAUDYDPODDEDNDDT9OAEARSBLPVSYAOEYYZJIZIZDENQOKMXATCTD9NEFVQWNCET9N9IXWCCWHYUBDTDJUZFXWMATRYSGJUQAFKAPIQIIXVFFEFNLGDPAIGKONUWLUTQTKZJYDEGPASTOMM9QTGOMYHFUKMNEVWPCMTLPDSOEEWDBRHZPZJIFMBUTY9QPRLPSVURIRMCWLH9YIZBOTJQMMF9ZP9VCCEGWVKSSQNANP9ZXVCAFGAVBXDJHUMKTKUBZRNKUGJTVUPDZMUK9KZI9EKY9DQDUNA9ZVPCROKYYPISSTDOJZJXMXZWSGZBEPKAUYPBPUTDVQKUYXWYTZXLYTCLLKLJUUJGFFCMZFKDTJET9KEDRMWZSAEFCSXLMRHRLG9EKLRZJ9NZQEONGFQSKSQMWRZXWVEGHCF9ZKGQITDLGFTZLCQMXVGVYIPKSUPPAOIKVWARJHAEJUZOOJFCFJTGQZWROSCEQSITQWZTQDFKECATBWSIRDFVWH9TXXUVEBRRKOTFSGBMGCMZDK9BJRVEUYOVNSLZNNSTWOB9CFNDJHJENDWSTIMMSWD9HSA9LDGTJDJFRGQCWOHYJNMFFLLDMVQEMCMNVQWMOGCEZUTK9MPULTDMKTIVFNVWXDNTRHNREHEDWIZCCRFVM9TXCTLBLYEKTRVVWHKZDOSYOBNLEKPNXMDZGLSVBBNBJIBZYBXZCENKVIXMNGUJUYFGFYFBKELSBZROQWKMESZTWDEEUCSXOWFSNJUGGJANMJFCBJV9GMDDJCAJHWKYQFJLFWZESTSIRBZQWYHGQDFUUWF9F9HXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMDZ99999999999999999999999999999999999999999999999999999FDKW9AD99A99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH99XJLIVAZYJGPKEHANESX9DORLEIOIFL9WVQBBNUDRZDGJMVSRTSFKOJPIMEXPBCPGKUUEKRGAXOB99999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999999999999999999999999999999RSIDBWNOF999999999K99999999POWSRVIO9VMIJ99FKKNKGPNNNNP"};
+
+ //approves via head branch, non-tail tx
+ String[] trytes = {
+ "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999GNXKWQPHMFCEZZCKUCCD9PROQVPCTBVVAPHCLVOPYYQLQRMPGTQXBVZLKGGT9NXHUAASFIAVEOZTBSM9DA99999999999999999999999999RENGLE9BEAT9999999999999999FDKW9AD99999999999B99999999ABCDGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9DKZYSMJAQRLXZHCNPTJPJLOONNDZHGNTBIINGYKZQAAWF9LWQTHUQSAXMLQN9DKCQWHIJXNGLGQVA9999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999TANGLE9BEAT9999999999999999SVIDBWNOF999999999K99999999POWSRVIO9VMIJ99UGNETMNPNNNE",
+ "YU9WINMHSBIJ9HRBKDEHZYOCDGOZ9HZSSVXAYYOD9AMAFBCROMLSFDFIAQKEXAVFDLJYVABRSONSTTXLCQJTSDRFDEXZIORLRSQDZQODNCHAAFUPGBCEVYNRSJQGUZRAQIIEZPFQGWPQIBMDVNZGQAKCU9VLHGYSNDPHCMOCTRYQOYHFPRQSGHSDEF9MSHGXYGUTHSBSF9SOSJQFOZXMQGCUXAYWLBJHXOHTOM9ITZPISEXLAYAZBHTZDDCOIHKDBL9AAXZEDFHDYCPXLDGR9JTVXDXBC9DRDVREEEVGVPWVEZHJBYQYTGDEWQHVEDTAVLWW9WBXOIGDGUAAAVKFMPVKSYBVOIWPYGCMACEAAHGUIQVFLFMSCPKTYQWJDGMDNLMDIUUYI9SZOFQPLKKACESSWIXPFYZWDHETYUGEVASPHBGQPJPWVESSUUEVUYDYNQUQURSMCEPKIHYXNFHZSHIIDTMEZQ9IPSHTBDK9UHRSIIEJRILBCMVUMGAGBMHKEMUUKQDCYAVIAN9MBWJCAUSSJRSAUEUBGEFFCFSOBVGLXBAIDOH9NXWQUSIFUINEVZUWELLVIOSSQRVHMTDBCATBWFSDJJDLURAUKEQEIRICSCXAOJNPWHQKYVTRLCRKHLFHHHPYSMUEAPMQPMPS9CHCIYXEYRNIOOGRWKVHUGIWJZIGPWGZGBGYTVC9UVEJ9JBIYMDHYXLWIDXHZVFCCPLYXDPQPJGMKNZMMONGNXSNNPCGEGRJTMCPTJONGPAEDMSHMGAPGBDWVCSCTJAECSHTEGBMVCDZMQYZ9IHERWGCKAIMFOJRAEDRDSWJASV9XYS9CTOEETPQHRKJZ9FPWTVCGLEJQJXVVDTYZ9DNSHQAINKROQQIPTFGTABEELYPBQHGNNB9NCL9BVTG9MH99BPIRNNGC9MQQRH9YUDYSUJIDSZKCIGEKFSJ9PXUYKVPUZYFIREUAMUZE9EIMJK9YGAZXZBPRXIRDCEVDHWOCFIJTVRCRZRRJGFLILKVNMJAX9MUMTXCWTUTP9NXBZUMAYYY9DNVZTCROBCMMHVKSQRXMGROCMMCDENWQWBV9LEIJONWTQXOUQZOTGEJHJELYZOPICLDWPHVCMBFSYHPIJYZQDEGYITVPYNQMXPRTPKADLUKW9AEMSMGIWCUMOQMUDN9HZGYKTNVVNLOQAFVETEDJMJVKYCE9RE9AJP9RMYFKGDEC9GIGAFXEXIYXPFBOMRPULVJMYTQEPDMMIABDHJGRNNCKADZAUFV9WCWFYFHXLCED9E9SKKYHVKDTGNCHEKFJL9YJHTSRUVKEPHPCQSFNBXKOJNGBSEOZLVVGNUQTRACVGEKDSORUHVSDSQNUZASWXRADGAUDYDPODDEDNDDT9OAEARSBLPVSYAOEYYZJIZIZDENQOKMXATCTD9NEFVQWNCET9N9IXWCCWHYUBDTDJUZFXWMATRYSGJUQAFKAPIQIIXVFFEFNLGDPAIGKONUWLUTQTKZJYDEGPASTOMM9QTGOMYHFUKMNEVWPCMTLPDSOEEWDBRHZPZJIFMBUTY9QPRLPSVURIRMCWLH9YIZBOTJQMMF9ZP9VCCEGWVKSSQNANP9ZXVCAFGAVBXDJHUMKTKUBZRNKUGJTVUPDZMUK9KZI9EKY9DQDUNA9ZVPCROKYYPISSTDOJZJXMXZWSGZBEPKAUYPBPUTDVQKUYXWYTZXLYTCLLKLJUUJGFFCMZFKDTJET9KEDRMWZSAEFCSXLMRHRLG9EKLRZJ9NZQEONGFQSKSQMWRZXWVEGHCF9ZKGQITDLGFTZLCQMXVGVYIPKSUPPAOIKVWARJHAEJUZOOJFCFJTGQZWROSCEQSITQWZTQDFKECATBWSIRDFVWH9TXXUVEBRRKOTFSGBMGCMZDK9BJRVEUYOVNSLZNNSTWOB9CFNDJHJENDWSTIMMSWD9HSA9LDGTJDJFRGQCWOHYJNMFFLLDMVQEMCMNVQWMOGCEZUTK9MPULTDMKTIVFNVWXDNTRHNREHEDWIZCCRFVM9TXCTLBLYEKTRVVWHKZDOSYOBNLEKPNXMDZGLSVBBNBJIBZYBXZCENKVIXMNGUJUYFGFYFBKELSBZROQWKMESZTWDEEUCSXOWFSNJUGGJANMJFCBJV9GMDDJCAJHWKYQFJLFWZESTSIRBZQWYHGQDFUUWF9F9HXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMDZ99999999999999999999999999999999999999999999999999999FDKW9AD99A99999999B99999999ABCDGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH99XJLIVAZYJGPKEHANESX9DORLEIOIFL9WVQBBNUDRZDGJMVSRTSFKOJPIMEXPBCPGKUUEKRGAXOB99999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999999999999999999999999999999RSIDBWNOF999999999K99999999POWSRVIO9VMIJ99FKKNKGPNNNNP",
+ "COSNPEACSPESTKTUS9CSMOCWVUBLERQJQGLLA9AYRRXMOVOBULQZZZJPLNELEX9EVZDAFOHBB9FBAYOTDB9YGJAVTDWNPJVVGEPFJ9QBYLXAYZEETY9GMYQRIRAIPWMJUFMFRSQRCEIOWISMBJKGCIGIIYNMXAYLXZIBNKANT9DSQXPBUPWGUJTOMD9JKSNFZJJFBZYBWRWQIZFOZXQWNJTRZTUPHVEGTAOLMBEPXOB9TCEBNIWXQUHNQJZHAOWGVOOOTSCTEUEMXJUODZP9CDLCMEXAW9YJGVNREENETEYXOAKZVRJVUSCRUSRCNZUGUOLCNHQITWYPJVJGPVULASTONDZRDVCNGJSN9DMMUORCOIBTITSPBXSAVTOHSAXHMGH99APRTQIVCEI9NWYKDOZCCWBNZGGCUCISSMGJHJ9OVSFBZVFMBODKRKSLIDESGFDQITCYFARWYTLFKSPLNVQ9LTYFODCHBAJKCWVZNBLOJH9ABFGDFOXHDNHZILINMYMVZYWTFJJOLADPTZRPJEMGPFQNYTFDGOKMDTDSKPTHIDWLMRSSACZMTHUCWVZHCYSPJRATABZ9JHWEBGTTQORGFOYDWUIYFYGOGGTYGYYI9RPKPMCTYNYKHHNWQKJTDQDHTRUYQPMCDDLVUUHUBPWGPMNQYPKHDHGIUSXJGNUXIYGZRGVYPTTV9KXJDLGVVXRIWIGKZJVCQ9DUWAYNDRKYCGKZDEOKZQCXELSQXLGLAPBDIWRTKGSIRNAZ9XFEGMWOWLPUA9TSGYRDOU9GKY9JJDKCJKKGJCRARGSNECJ9SEVAGPPJMBTMGNWSLNSFBOEYMIQBKOVSCTDIWCOQTHBVTZOFNNIYKKJLAROKIRFFPCLSOWO9BVNNJSXEYLJASPUEFKCEI9SGWADNGS9MZAKFTCAYNRVKGDIGYDTPGJXWFAEFGG9BX9XCNBZGQULH9IPPIQ9VFVAXGELUMCTE9TOFOGCTFYBYEBFOKKAESIIHEWLYLQKWDHPYNELJKMZHDSDSK9ZWFP99FQFLUXGTSVDMGHIFZFUCYTYPVEFEDM9TOHCQVIBXARYWZEKTWEOVSBYZTJZRUFZJCLRBTOWPTKY99CYQTGPMAKFYALIWPEQLUB9RGANTKDDMSDSJOTNPFZCXKJUGTJWWPK9FAJGPJOEBRHL9ZU9XRBDJBVMFIZIZFGKETH9RJUSDIOWDZIXFOJLT9DSIAWSCEYBNOGOAZQPZKVZTBXGACCBVQGFNGXNQFTVPUGUOHNBKTUERLSTDXGRKZSATIRHDBSXARYATBHOLUHCQGPRMQGSJUAXUCWKDXAUGORHFVVVDZQEYYGSV9IUM9VTMHJCIKBQD9TQLLYTZHAKDHPFGYMRGJOEDJKQTBBNFCKDEPYTPWUJEOPRJWHAILMVW9AXS9HAT9GCGRXZEHWNDOVGKGPWFXFC9IKLVZJCTGZUJFRHGL9FZWZYP9TPOWGOUTQ9INYBVTD9N9OV9AM9GYRJ99MRWRWLOAKJXMDTFNS9NZQAYGVZRJ9WVSIDRRQLGKH9VPUVKFWZMSRVVLKXUGJAZESC9LEYDNNDWS9WTWNFPCR9YKPOMIJAVHCRCJVBDFWSTDN9BPJRDMGZFXWTY9GUX9WEPJQMXSECFPREXAJ9DU9OLMNQFTF99QYZOGDUTMBHMNMCNUQTU9FTNEUNBT9NQO9YADGY9C9LQR9DYVJZE9GMMYAOUSWUIVJ9EAR9EEFCO9SZTQ9JQHPIJGXIOQZHOSUSVMHBOLTYSEONVLRISNAN9KJHHFFISTYIHYVAMXLPNALDELETXVSWGUWOIVKNAKZKNNRVNJKE9WMNQBXFGXSYZQQSIODAKINA9CPBOGAYKJ9V9GS9HERXSVYPEILYXC9GURBREBCXRZJVXHUOPGYFLPIFBCOTMHFTAAUQUZVEKOWYFFGSCXDFDBTSGZRMPQR9EYSZYGQETCWFDRXVOXXCUBOQTBQGVLAONSTSLURFYSDPAVNCXNWVPQOUORMVSLYISQUQKXFYYMJSGHQP9VAAZABEAGJMCPIEBZQSYCPS9CNKFBDRDTDA9C9MVKGUISRX9ODVUJNPDWMZ9ESYXJ99ETDGSTJZMZYJEGRUWTORMCLNHLDQ9YIJWPMDYGGJQHMFPJ9VKINJUESTCBJQXFTCJFNUXPZONVKXJRAYGKIWHXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMD999999999999999999999999999999999999999999999999999999FDKW9AD99B99999999B99999999ABCDGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999DKZYSMJAQRLXZHCNPTJPJLOONNDZHGNTBIINGYKZQAAWF9LWQTHUQSAXMLQN9DKCQWHIJXNGLGQVA9999999999999999999999999999999LLHDBWNOF999999999K99999999POWSRVIO9VMIJ99UMGMTTTPNNNV",
+ };
+ persistAndMapTxs(nonTailTrytes);
+ List transactions = persistAndMapTxs(trytes);
+ assertEquals("should be invalid because bundle approves non-tails", BundleValidator.Validity.INVALID,
+ bundleValidator.validateBundleTailApproval(tangle, transactions));
+ }
+
+ @Test
+ public void validateBundleNonTailApprovalFailure() throws Exception {
+ // tx with currentIndex = 1
+ TransactionViewModel nonTail = TransactionTestUtils.createBundleHead(1, HashFactory.TRANSACTION
+ .create("XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999"));
+
+ // approves via head branch, non-tail tx
+ String[] trytes = {
+ "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999GNXKWQPHMFCEZZCKUCCD9PROQVPCTBVVAPHCLVOPYYQLQRMPGTQXBVZLKGGT9NXHUAASFIAVEOZTBSM9DA99999999999999999999999999RENGLE9BEAT9999999999999999FDKW9AD99999999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9DKZYSMJAQRLXZHCNPTJPJLOONNDZHGNTBIINGYKZQAAWF9LWQTHUQSAXMLQN9DKCQWHIJXNGLGQVA9999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999TANGLE9BEAT9999999999999999SVIDBWNOF999999999K99999999POWSRVIO9VMIJ99UGNETMNPNNNE",
+ "YU9WINMHSBIJ9HRBKDEHZYOCDGOZ9HZSSVXAYYOD9AMAFBCROMLSFDFIAQKEXAVFDLJYVABRSONSTTXLCQJTSDRFDEXZIORLRSQDZQODNCHAAFUPGBCEVYNRSJQGUZRAQIIEZPFQGWPQIBMDVNZGQAKCU9VLHGYSNDPHCMOCTRYQOYHFPRQSGHSDEF9MSHGXYGUTHSBSF9SOSJQFOZXMQGCUXAYWLBJHXOHTOM9ITZPISEXLAYAZBHTZDDCOIHKDBL9AAXZEDFHDYCPXLDGR9JTVXDXBC9DRDVREEEVGVPWVEZHJBYQYTGDEWQHVEDTAVLWW9WBXOIGDGUAAAVKFMPVKSYBVOIWPYGCMACEAAHGUIQVFLFMSCPKTYQWJDGMDNLMDIUUYI9SZOFQPLKKACESSWIXPFYZWDHETYUGEVASPHBGQPJPWVESSUUEVUYDYNQUQURSMCEPKIHYXNFHZSHIIDTMEZQ9IPSHTBDK9UHRSIIEJRILBCMVUMGAGBMHKEMUUKQDCYAVIAN9MBWJCAUSSJRSAUEUBGEFFCFSOBVGLXBAIDOH9NXWQUSIFUINEVZUWELLVIOSSQRVHMTDBCATBWFSDJJDLURAUKEQEIRICSCXAOJNPWHQKYVTRLCRKHLFHHHPYSMUEAPMQPMPS9CHCIYXEYRNIOOGRWKVHUGIWJZIGPWGZGBGYTVC9UVEJ9JBIYMDHYXLWIDXHZVFCCPLYXDPQPJGMKNZMMONGNXSNNPCGEGRJTMCPTJONGPAEDMSHMGAPGBDWVCSCTJAECSHTEGBMVCDZMQYZ9IHERWGCKAIMFOJRAEDRDSWJASV9XYS9CTOEETPQHRKJZ9FPWTVCGLEJQJXVVDTYZ9DNSHQAINKROQQIPTFGTABEELYPBQHGNNB9NCL9BVTG9MH99BPIRNNGC9MQQRH9YUDYSUJIDSZKCIGEKFSJ9PXUYKVPUZYFIREUAMUZE9EIMJK9YGAZXZBPRXIRDCEVDHWOCFIJTVRCRZRRJGFLILKVNMJAX9MUMTXCWTUTP9NXBZUMAYYY9DNVZTCROBCMMHVKSQRXMGROCMMCDENWQWBV9LEIJONWTQXOUQZOTGEJHJELYZOPICLDWPHVCMBFSYHPIJYZQDEGYITVPYNQMXPRTPKADLUKW9AEMSMGIWCUMOQMUDN9HZGYKTNVVNLOQAFVETEDJMJVKYCE9RE9AJP9RMYFKGDEC9GIGAFXEXIYXPFBOMRPULVJMYTQEPDMMIABDHJGRNNCKADZAUFV9WCWFYFHXLCED9E9SKKYHVKDTGNCHEKFJL9YJHTSRUVKEPHPCQSFNBXKOJNGBSEOZLVVGNUQTRACVGEKDSORUHVSDSQNUZASWXRADGAUDYDPODDEDNDDT9OAEARSBLPVSYAOEYYZJIZIZDENQOKMXATCTD9NEFVQWNCET9N9IXWCCWHYUBDTDJUZFXWMATRYSGJUQAFKAPIQIIXVFFEFNLGDPAIGKONUWLUTQTKZJYDEGPASTOMM9QTGOMYHFUKMNEVWPCMTLPDSOEEWDBRHZPZJIFMBUTY9QPRLPSVURIRMCWLH9YIZBOTJQMMF9ZP9VCCEGWVKSSQNANP9ZXVCAFGAVBXDJHUMKTKUBZRNKUGJTVUPDZMUK9KZI9EKY9DQDUNA9ZVPCROKYYPISSTDOJZJXMXZWSGZBEPKAUYPBPUTDVQKUYXWYTZXLYTCLLKLJUUJGFFCMZFKDTJET9KEDRMWZSAEFCSXLMRHRLG9EKLRZJ9NZQEONGFQSKSQMWRZXWVEGHCF9ZKGQITDLGFTZLCQMXVGVYIPKSUPPAOIKVWARJHAEJUZOOJFCFJTGQZWROSCEQSITQWZTQDFKECATBWSIRDFVWH9TXXUVEBRRKOTFSGBMGCMZDK9BJRVEUYOVNSLZNNSTWOB9CFNDJHJENDWSTIMMSWD9HSA9LDGTJDJFRGQCWOHYJNMFFLLDMVQEMCMNVQWMOGCEZUTK9MPULTDMKTIVFNVWXDNTRHNREHEDWIZCCRFVM9TXCTLBLYEKTRVVWHKZDOSYOBNLEKPNXMDZGLSVBBNBJIBZYBXZCENKVIXMNGUJUYFGFYFBKELSBZROQWKMESZTWDEEUCSXOWFSNJUGGJANMJFCBJV9GMDDJCAJHWKYQFJLFWZESTSIRBZQWYHGQDFUUWF9F9HXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMDZ99999999999999999999999999999999999999999999999999999FDKW9AD99A99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH99XJLIVAZYJGPKEHANESX9DORLEIOIFL9WVQBBNUDRZDGJMVSRTSFKOJPIMEXPBCPGKUUEKRGAXOB99999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999999999999999999999999999999RSIDBWNOF999999999K99999999POWSRVIO9VMIJ99FKKNKGPNNNNP",
+ "COSNPEACSPESTKTUS9CSMOCWVUBLERQJQGLLA9AYRRXMOVOBULQZZZJPLNELEX9EVZDAFOHBB9FBAYOTDB9YGJAVTDWNPJVVGEPFJ9QBYLXAYZEETY9GMYQRIRAIPWMJUFMFRSQRCEIOWISMBJKGCIGIIYNMXAYLXZIBNKANT9DSQXPBUPWGUJTOMD9JKSNFZJJFBZYBWRWQIZFOZXQWNJTRZTUPHVEGTAOLMBEPXOB9TCEBNIWXQUHNQJZHAOWGVOOOTSCTEUEMXJUODZP9CDLCMEXAW9YJGVNREENETEYXOAKZVRJVUSCRUSRCNZUGUOLCNHQITWYPJVJGPVULASTONDZRDVCNGJSN9DMMUORCOIBTITSPBXSAVTOHSAXHMGH99APRTQIVCEI9NWYKDOZCCWBNZGGCUCISSMGJHJ9OVSFBZVFMBODKRKSLIDESGFDQITCYFARWYTLFKSPLNVQ9LTYFODCHBAJKCWVZNBLOJH9ABFGDFOXHDNHZILINMYMVZYWTFJJOLADPTZRPJEMGPFQNYTFDGOKMDTDSKPTHIDWLMRSSACZMTHUCWVZHCYSPJRATABZ9JHWEBGTTQORGFOYDWUIYFYGOGGTYGYYI9RPKPMCTYNYKHHNWQKJTDQDHTRUYQPMCDDLVUUHUBPWGPMNQYPKHDHGIUSXJGNUXIYGZRGVYPTTV9KXJDLGVVXRIWIGKZJVCQ9DUWAYNDRKYCGKZDEOKZQCXELSQXLGLAPBDIWRTKGSIRNAZ9XFEGMWOWLPUA9TSGYRDOU9GKY9JJDKCJKKGJCRARGSNECJ9SEVAGPPJMBTMGNWSLNSFBOEYMIQBKOVSCTDIWCOQTHBVTZOFNNIYKKJLAROKIRFFPCLSOWO9BVNNJSXEYLJASPUEFKCEI9SGWADNGS9MZAKFTCAYNRVKGDIGYDTPGJXWFAEFGG9BX9XCNBZGQULH9IPPIQ9VFVAXGELUMCTE9TOFOGCTFYBYEBFOKKAESIIHEWLYLQKWDHPYNELJKMZHDSDSK9ZWFP99FQFLUXGTSVDMGHIFZFUCYTYPVEFEDM9TOHCQVIBXARYWZEKTWEOVSBYZTJZRUFZJCLRBTOWPTKY99CYQTGPMAKFYALIWPEQLUB9RGANTKDDMSDSJOTNPFZCXKJUGTJWWPK9FAJGPJOEBRHL9ZU9XRBDJBVMFIZIZFGKETH9RJUSDIOWDZIXFOJLT9DSIAWSCEYBNOGOAZQPZKVZTBXGACCBVQGFNGXNQFTVPUGUOHNBKTUERLSTDXGRKZSATIRHDBSXARYATBHOLUHCQGPRMQGSJUAXUCWKDXAUGORHFVVVDZQEYYGSV9IUM9VTMHJCIKBQD9TQLLYTZHAKDHPFGYMRGJOEDJKQTBBNFCKDEPYTPWUJEOPRJWHAILMVW9AXS9HAT9GCGRXZEHWNDOVGKGPWFXFC9IKLVZJCTGZUJFRHGL9FZWZYP9TPOWGOUTQ9INYBVTD9N9OV9AM9GYRJ99MRWRWLOAKJXMDTFNS9NZQAYGVZRJ9WVSIDRRQLGKH9VPUVKFWZMSRVVLKXUGJAZESC9LEYDNNDWS9WTWNFPCR9YKPOMIJAVHCRCJVBDFWSTDN9BPJRDMGZFXWTY9GUX9WEPJQMXSECFPREXAJ9DU9OLMNQFTF99QYZOGDUTMBHMNMCNUQTU9FTNEUNBT9NQO9YADGY9C9LQR9DYVJZE9GMMYAOUSWUIVJ9EAR9EEFCO9SZTQ9JQHPIJGXIOQZHOSUSVMHBOLTYSEONVLRISNAN9KJHHFFISTYIHYVAMXLPNALDELETXVSWGUWOIVKNAKZKNNRVNJKE9WMNQBXFGXSYZQQSIODAKINA9CPBOGAYKJ9V9GS9HERXSVYPEILYXC9GURBREBCXRZJVXHUOPGYFLPIFBCOTMHFTAAUQUZVEKOWYFFGSCXDFDBTSGZRMPQR9EYSZYGQETCWFDRXVOXXCUBOQTBQGVLAONSTSLURFYSDPAVNCXNWVPQOUORMVSLYISQUQKXFYYMJSGHQP9VAAZABEAGJMCPIEBZQSYCPS9CNKFBDRDTDA9C9MVKGUISRX9ODVUJNPDWMZ9ESYXJ99ETDGSTJZMZYJEGRUWTORMCLNHLDQ9YIJWPMDYGGJQHMFPJ9VKINJUESTCBJQXFTCJFNUXPZONVKXJRAYGKIWHXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMD999999999999999999999999999999999999999999999999999999FDKW9AD99B99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999FTXZAEQIKGFJABOGCCQGOMKNASGWCQWMNIMRDTHKPJMTKDGMEQNZOAYARIYEWFZYJALRKFODOITTZ9999999999999999999999999999999LLHDBWNOF999999999K99999999POWSRVIO9VMIJ99UMGMTTTPNNNV", };
+ nonTail.store(tangle, snapshot);
+ List transactions = persistAndMapTxs(trytes);
+ List bundleTxs = bundleValidator.validate(tangle, true, snapshot,
+ transactions.get(0).getHash());
+ assertTrue("no txs from the invalid bundle should be returned", bundleTxs.isEmpty());
+ }
+
@Test
public void validateValidBundle() throws Exception {
String[] trytes = {
@@ -87,7 +172,7 @@ public void validateValidBundle() throws Exception {
"COSNPEACSPESTKTUS9CSMOCWVUBLERQJQGLLA9AYRRXMOVOBULQZZZJPLNELEX9EVZDAFOHBB9FBAYOTDB9YGJAVTDWNPJVVGEPFJ9QBYLXAYZEETY9GMYQRIRAIPWMJUFMFRSQRCEIOWISMBJKGCIGIIYNMXAYLXZIBNKANT9DSQXPBUPWGUJTOMD9JKSNFZJJFBZYBWRWQIZFOZXQWNJTRZTUPHVEGTAOLMBEPXOB9TCEBNIWXQUHNQJZHAOWGVOOOTSCTEUEMXJUODZP9CDLCMEXAW9YJGVNREENETEYXOAKZVRJVUSCRUSRCNZUGUOLCNHQITWYPJVJGPVULASTONDZRDVCNGJSN9DMMUORCOIBTITSPBXSAVTOHSAXHMGH99APRTQIVCEI9NWYKDOZCCWBNZGGCUCISSMGJHJ9OVSFBZVFMBODKRKSLIDESGFDQITCYFARWYTLFKSPLNVQ9LTYFODCHBAJKCWVZNBLOJH9ABFGDFOXHDNHZILINMYMVZYWTFJJOLADPTZRPJEMGPFQNYTFDGOKMDTDSKPTHIDWLMRSSACZMTHUCWVZHCYSPJRATABZ9JHWEBGTTQORGFOYDWUIYFYGOGGTYGYYI9RPKPMCTYNYKHHNWQKJTDQDHTRUYQPMCDDLVUUHUBPWGPMNQYPKHDHGIUSXJGNUXIYGZRGVYPTTV9KXJDLGVVXRIWIGKZJVCQ9DUWAYNDRKYCGKZDEOKZQCXELSQXLGLAPBDIWRTKGSIRNAZ9XFEGMWOWLPUA9TSGYRDOU9GKY9JJDKCJKKGJCRARGSNECJ9SEVAGPPJMBTMGNWSLNSFBOEYMIQBKOVSCTDIWCOQTHBVTZOFNNIYKKJLAROKIRFFPCLSOWO9BVNNJSXEYLJASPUEFKCEI9SGWADNGS9MZAKFTCAYNRVKGDIGYDTPGJXWFAEFGG9BX9XCNBZGQULH9IPPIQ9VFVAXGELUMCTE9TOFOGCTFYBYEBFOKKAESIIHEWLYLQKWDHPYNELJKMZHDSDSK9ZWFP99FQFLUXGTSVDMGHIFZFUCYTYPVEFEDM9TOHCQVIBXARYWZEKTWEOVSBYZTJZRUFZJCLRBTOWPTKY99CYQTGPMAKFYALIWPEQLUB9RGANTKDDMSDSJOTNPFZCXKJUGTJWWPK9FAJGPJOEBRHL9ZU9XRBDJBVMFIZIZFGKETH9RJUSDIOWDZIXFOJLT9DSIAWSCEYBNOGOAZQPZKVZTBXGACCBVQGFNGXNQFTVPUGUOHNBKTUERLSTDXGRKZSATIRHDBSXARYATBHOLUHCQGPRMQGSJUAXUCWKDXAUGORHFVVVDZQEYYGSV9IUM9VTMHJCIKBQD9TQLLYTZHAKDHPFGYMRGJOEDJKQTBBNFCKDEPYTPWUJEOPRJWHAILMVW9AXS9HAT9GCGRXZEHWNDOVGKGPWFXFC9IKLVZJCTGZUJFRHGL9FZWZYP9TPOWGOUTQ9INYBVTD9N9OV9AM9GYRJ99MRWRWLOAKJXMDTFNS9NZQAYGVZRJ9WVSIDRRQLGKH9VPUVKFWZMSRVVLKXUGJAZESC9LEYDNNDWS9WTWNFPCR9YKPOMIJAVHCRCJVBDFWSTDN9BPJRDMGZFXWTY9GUX9WEPJQMXSECFPREXAJ9DU9OLMNQFTF99QYZOGDUTMBHMNMCNUQTU9FTNEUNBT9NQO9YADGY9C9LQR9DYVJZE9GMMYAOUSWUIVJ9EAR9EEFCO9SZTQ9JQHPIJGXIOQZHOSUSVMHBOLTYSEONVLRISNAN9KJHHFFISTYIHYVAMXLPNALDELETXVSWGUWOIVKNAKZKNNRVNJKE9WMNQBXFGXSYZQQSIODAKINA9CPBOGAYKJ9V9GS9HERXSVYPEILYXC9GURBREBCXRZJVXHUOPGYFLPIFBCOTMHFTAAUQUZVEKOWYFFGSCXDFDBTSGZRMPQR9EYSZYGQETCWFDRXVOXXCUBOQTBQGVLAONSTSLURFYSDPAVNCXNWVPQOUORMVSLYISQUQKXFYYMJSGHQP9VAAZABEAGJMCPIEBZQSYCPS9CNKFBDRDTDA9C9MVKGUISRX9ODVUJNPDWMZ9ESYXJ99ETDGSTJZMZYJEGRUWTORMCLNHLDQ9YIJWPMDYGGJQHMFPJ9VKINJUESTCBJQXFTCJFNUXPZONVKXJRAYGKIWHXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMD999999999999999999999999999999999999999999999999999999FDKW9AD99B99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999FTXZAEQIKGFJABOGCCQGOMKNASGWCQWMNIMRDTHKPJMTKDGMEQNZOAYARIYEWFZYJALRKFODOITTZ9999999999999999999999999999999LLHDBWNOF999999999K99999999POWSRVIO9VMIJ99UMGMTTTPNNNV",
};
List transactions = persistAndMapTxs(trytes);
- List bundleTxs = bundleValidator.validate(tangle, snapshot,
+ List bundleTxs = bundleValidator.validate(tangle, true, snapshot,
transactions.get(0).getHash());
assertFalse("all txs from the valid bundle should be returned", bundleTxs.isEmpty());
assertEquals("should have changed the validity of the tail tx to valid", 1,
@@ -108,7 +193,7 @@ public void validateBiggerValidBundle() throws Exception {
"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999G9EQ9IARCEMSKSMKYWKHUCGSXMGCT9LAAIHBWIWNIYWFTSGJRHSLXGXQNXKHJOXNMWQHPYMKOKSRL9TDDOJCQ9QNBOC999999999999999999999999999999999999999999999EULM9D99H99999999H99999999BJVQHAHPDFGNEELCFLIEXWWKZXLPUZQMDXFGKKOGPDEGJDALESJZFVKIIRJSGKXDNWJOQBRIUEBTGACG9ABQONKFWTKFCSKMUPVGPRNYIZTWETHIUNG9MFYKXLZRJHDUVIDBAL9QVDU9B9DHTYPMMDBCEFTLRA9999RNEQYL9XYIWHGTEQHZAPXZPWRRXOGXPJXHCV9OSTYLL9AVTMEXKG9HPIPKITPXQGEWIJIXVIEUNS99999999999999999999999999999999GBMYRLVNF999999999MMMMMMMMMAYSMPUWFG9OTFD9YMETBNVGMLRA",
};
List transactions = persistAndMapTxs(trytes);
- List bundleTxs = bundleValidator.validate(tangle, snapshot,
+ List bundleTxs = bundleValidator.validate(tangle, true, snapshot,
transactions.get(0).getHash());
assertFalse("all txs from the valid bundle should be returned", bundleTxs.isEmpty());
assertEquals("should have changed the validity of the tail tx to valid", 1,
@@ -125,7 +210,7 @@ public void validateBundleWithInvalidSignature() throws Exception {
"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999MUN9LXIQBWCSJDSWFARRJMIQGJWZMJQ9AERHUZGLTH9OAMCJBIJHPAOQIECPTNYHUWZLCSIYNKMNFHSWCNSA999999999999999999999999999999999999999999999999999VKUIAAD99C99999999C99999999DIODRCKI9IUWGBTDTILEUVUPLHEMQZAYKWIULYHGPKVYSGCI9ANDOOBQTXAXIHGSVOLLXOKTRCHZFDHIZNRBM9KWMOYBKSGKVDTVOXQSDJUTIIPVIAOMOYWFXLOFVP9OP9TBCDTJXZNYZQBALCPJCDKWKQQARZ9999FJHO9TPBCDABULPM9ERDYUZMQMNRPEFSYBDYWILOXQFRGAASGBQBBZMGICNJEPTFAC9ENSINGMURA9999999999999999999999999999999LKQRCWPOF999999999K99999999ID99999999RUTA9999999999999",
};
List transactions = persistAndMapTxs(trytes);
- List bundleTxs = bundleValidator.validate(tangle, snapshot,
+ List bundleTxs = bundleValidator.validate(tangle, true, snapshot,
transactions.get(0).getHash());
assertTrue("no transactions should be returned", bundleTxs.isEmpty());
assertEquals("should have changed the validity of the tail tx to invalid", -1,
@@ -139,7 +224,7 @@ public void validateBundleWithMissingNonTailTransaction() throws Exception {
"YU9WINMHSBIJ9HRBKDEHZYOCDGOZ9HZSSVXAYYOD9AMAFBCROMLSFDFIAQKEXAVFDLJYVABRSONSTTXLCQJTSDRFDEXZIORLRSQDZQODNCHAAFUPGBCEVYNRSJQGUZRAQIIEZPFQGWPQIBMDVNZGQAKCU9VLHGYSNDPHCMOCTRYQOYHFPRQSGHSDEF9MSHGXYGUTHSBSF9SOSJQFOZXMQGCUXAYWLBJHXOHTOM9ITZPISEXLAYAZBHTZDDCOIHKDBL9AAXZEDFHDYCPXLDGR9JTVXDXBC9DRDVREEEVGVPWVEZHJBYQYTGDEWQHVEDTAVLWW9WBXOIGDGUAAAVKFMPVKSYBVOIWPYGCMACEAAHGUIQVFLFMSCPKTYQWJDGMDNLMDIUUYI9SZOFQPLKKACESSWIXPFYZWDHETYUGEVASPHBGQPJPWVESSUUEVUYDYNQUQURSMCEPKIHYXNFHZSHIIDTMEZQ9IPSHTBDK9UHRSIIEJRILBCMVUMGAGBMHKEMUUKQDCYAVIAN9MBWJCAUSSJRSAUEUBGEFFCFSOBVGLXBAIDOH9NXWQUSIFUINEVZUWELLVIOSSQRVHMTDBCATBWFSDJJDLURAUKEQEIRICSCXAOJNPWHQKYVTRLCRKHLFHHHPYSMUEAPMQPMPS9CHCIYXEYRNIOOGRWKVHUGIWJZIGPWGZGBGYTVC9UVEJ9JBIYMDHYXLWIDXHZVFCCPLYXDPQPJGMKNZMMONGNXSNNPCGEGRJTMCPTJONGPAEDMSHMGAPGBDWVCSCTJAECSHTEGBMVCDZMQYZ9IHERWGCKAIMFOJRAEDRDSWJASV9XYS9CTOEETPQHRKJZ9FPWTVCGLEJQJXVVDTYZ9DNSHQAINKROQQIPTFGTABEELYPBQHGNNB9NCL9BVTG9MH99BPIRNNGC9MQQRH9YUDYSUJIDSZKCIGEKFSJ9PXUYKVPUZYFIREUAMUZE9EIMJK9YGAZXZBPRXIRDCEVDHWOCFIJTVRCRZRRJGFLILKVNMJAX9MUMTXCWTUTP9NXBZUMAYYY9DNVZTCROBCMMHVKSQRXMGROCMMCDENWQWBV9LEIJONWTQXOUQZOTGEJHJELYZOPICLDWPHVCMBFSYHPIJYZQDEGYITVPYNQMXPRTPKADLUKW9AEMSMGIWCUMOQMUDN9HZGYKTNVVNLOQAFVETEDJMJVKYCE9RE9AJP9RMYFKGDEC9GIGAFXEXIYXPFBOMRPULVJMYTQEPDMMIABDHJGRNNCKADZAUFV9WCWFYFHXLCED9E9SKKYHVKDTGNCHEKFJL9YJHTSRUVKEPHPCQSFNBXKOJNGBSEOZLVVGNUQTRACVGEKDSORUHVSDSQNUZASWXRADGAUDYDPODDEDNDDT9OAEARSBLPVSYAOEYYZJIZIZDENQOKMXATCTD9NEFVQWNCET9N9IXWCCWHYUBDTDJUZFXWMATRYSGJUQAFKAPIQIIXVFFEFNLGDPAIGKONUWLUTQTKZJYDEGPASTOMM9QTGOMYHFUKMNEVWPCMTLPDSOEEWDBRHZPZJIFMBUTY9QPRLPSVURIRMCWLH9YIZBOTJQMMF9ZP9VCCEGWVKSSQNANP9ZXVCAFGAVBXDJHUMKTKUBZRNKUGJTVUPDZMUK9KZI9EKY9DQDUNA9ZVPCROKYYPISSTDOJZJXMXZWSGZBEPKAUYPBPUTDVQKUYXWYTZXLYTCLLKLJUUJGFFCMZFKDTJET9KEDRMWZSAEFCSXLMRHRLG9EKLRZJ9NZQEONGFQSKSQMWRZXWVEGHCF9ZKGQITDLGFTZLCQMXVGVYIPKSUPPAOIKVWARJHAEJUZOOJFCFJTGQZWROSCEQSITQWZTQDFKECATBWSIRDFVWH9TXXUVEBRRKOTFSGBMGCMZDK9BJRVEUYOVNSLZNNSTWOB9CFNDJHJENDWSTIMMSWD9HSA9LDGTJDJFRGQCWOHYJNMFFLLDMVQEMCMNVQWMOGCEZUTK9MPULTDMKTIVFNVWXDNTRHNREHEDWIZCCRFVM9TXCTLBLYEKTRVVWHKZDOSYOBNLEKPNXMDZGLSVBBNBJIBZYBXZCENKVIXMNGUJUYFGFYFBKELSBZROQWKMESZTWDEEUCSXOWFSNJUGGJANMJFCBJV9GMDDJCAJHWKYQFJLFWZESTSIRBZQWYHGQDFUUWF9F9HXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMDZ99999999999999999999999999999999999999999999999999999FDKW9AD99A99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH99XJLIVAZYJGPKEHANESX9DORLEIOIFL9WVQBBNUDRZDGJMVSRTSFKOJPIMEXPBCPGKUUEKRGAXOB99999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999999999999999999999999999999RSIDBWNOF999999999K99999999POWSRVIO9VMIJ99FKKNKGPNNNNP",
};
List transactions = persistAndMapTxs(trytes);
- List bundleTxs = bundleValidator.validate(tangle, snapshot,
+ List bundleTxs = bundleValidator.validate(tangle, true, snapshot,
transactions.get(0).getHash());
assertTrue("no transactions should be returned", bundleTxs.isEmpty());
assertEquals("should not have changed the validity of the tail tx since the bundle is incomplete", 0,
@@ -153,7 +238,7 @@ public void validateBundleWithMissingTailTransaction() throws Exception {
"COSNPEACSPESTKTUS9CSMOCWVUBLERQJQGLLA9AYRRXMOVOBULQZZZJPLNELEX9EVZDAFOHBB9FBAYOTDB9YGJAVTDWNPJVVGEPFJ9QBYLXAYZEETY9GMYQRIRAIPWMJUFMFRSQRCEIOWISMBJKGCIGIIYNMXAYLXZIBNKANT9DSQXPBUPWGUJTOMD9JKSNFZJJFBZYBWRWQIZFOZXQWNJTRZTUPHVEGTAOLMBEPXOB9TCEBNIWXQUHNQJZHAOWGVOOOTSCTEUEMXJUODZP9CDLCMEXAW9YJGVNREENETEYXOAKZVRJVUSCRUSRCNZUGUOLCNHQITWYPJVJGPVULASTONDZRDVCNGJSN9DMMUORCOIBTITSPBXSAVTOHSAXHMGH99APRTQIVCEI9NWYKDOZCCWBNZGGCUCISSMGJHJ9OVSFBZVFMBODKRKSLIDESGFDQITCYFARWYTLFKSPLNVQ9LTYFODCHBAJKCWVZNBLOJH9ABFGDFOXHDNHZILINMYMVZYWTFJJOLADPTZRPJEMGPFQNYTFDGOKMDTDSKPTHIDWLMRSSACZMTHUCWVZHCYSPJRATABZ9JHWEBGTTQORGFOYDWUIYFYGOGGTYGYYI9RPKPMCTYNYKHHNWQKJTDQDHTRUYQPMCDDLVUUHUBPWGPMNQYPKHDHGIUSXJGNUXIYGZRGVYPTTV9KXJDLGVVXRIWIGKZJVCQ9DUWAYNDRKYCGKZDEOKZQCXELSQXLGLAPBDIWRTKGSIRNAZ9XFEGMWOWLPUA9TSGYRDOU9GKY9JJDKCJKKGJCRARGSNECJ9SEVAGPPJMBTMGNWSLNSFBOEYMIQBKOVSCTDIWCOQTHBVTZOFNNIYKKJLAROKIRFFPCLSOWO9BVNNJSXEYLJASPUEFKCEI9SGWADNGS9MZAKFTCAYNRVKGDIGYDTPGJXWFAEFGG9BX9XCNBZGQULH9IPPIQ9VFVAXGELUMCTE9TOFOGCTFYBYEBFOKKAESIIHEWLYLQKWDHPYNELJKMZHDSDSK9ZWFP99FQFLUXGTSVDMGHIFZFUCYTYPVEFEDM9TOHCQVIBXARYWZEKTWEOVSBYZTJZRUFZJCLRBTOWPTKY99CYQTGPMAKFYALIWPEQLUB9RGANTKDDMSDSJOTNPFZCXKJUGTJWWPK9FAJGPJOEBRHL9ZU9XRBDJBVMFIZIZFGKETH9RJUSDIOWDZIXFOJLT9DSIAWSCEYBNOGOAZQPZKVZTBXGACCBVQGFNGXNQFTVPUGUOHNBKTUERLSTDXGRKZSATIRHDBSXARYATBHOLUHCQGPRMQGSJUAXUCWKDXAUGORHFVVVDZQEYYGSV9IUM9VTMHJCIKBQD9TQLLYTZHAKDHPFGYMRGJOEDJKQTBBNFCKDEPYTPWUJEOPRJWHAILMVW9AXS9HAT9GCGRXZEHWNDOVGKGPWFXFC9IKLVZJCTGZUJFRHGL9FZWZYP9TPOWGOUTQ9INYBVTD9N9OV9AM9GYRJ99MRWRWLOAKJXMDTFNS9NZQAYGVZRJ9WVSIDRRQLGKH9VPUVKFWZMSRVVLKXUGJAZESC9LEYDNNDWS9WTWNFPCR9YKPOMIJAVHCRCJVBDFWSTDN9BPJRDMGZFXWTY9GUX9WEPJQMXSECFPREXAJ9DU9OLMNQFTF99QYZOGDUTMBHMNMCNUQTU9FTNEUNBT9NQO9YADGY9C9LQR9DYVJZE9GMMYAOUSWUIVJ9EAR9EEFCO9SZTQ9JQHPIJGXIOQZHOSUSVMHBOLTYSEONVLRISNAN9KJHHFFISTYIHYVAMXLPNALDELETXVSWGUWOIVKNAKZKNNRVNJKE9WMNQBXFGXSYZQQSIODAKINA9CPBOGAYKJ9V9GS9HERXSVYPEILYXC9GURBREBCXRZJVXHUOPGYFLPIFBCOTMHFTAAUQUZVEKOWYFFGSCXDFDBTSGZRMPQR9EYSZYGQETCWFDRXVOXXCUBOQTBQGVLAONSTSLURFYSDPAVNCXNWVPQOUORMVSLYISQUQKXFYYMJSGHQP9VAAZABEAGJMCPIEBZQSYCPS9CNKFBDRDTDA9C9MVKGUISRX9ODVUJNPDWMZ9ESYXJ99ETDGSTJZMZYJEGRUWTORMCLNHLDQ9YIJWPMDYGGJQHMFPJ9VKINJUESTCBJQXFTCJFNUXPZONVKXJRAYGKIWHXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMD999999999999999999999999999999999999999999999999999999FDKW9AD99B99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999FTXZAEQIKGFJABOGCCQGOMKNASGWCQWMNIMRDTHKPJMTKDGMEQNZOAYARIYEWFZYJALRKFODOITTZ9999999999999999999999999999999LLHDBWNOF999999999K99999999POWSRVIO9VMIJ99UMGMTTTPNNNV",
};
List transactions = persistAndMapTxs(trytes);
- List bundleTxs = bundleValidator.validate(tangle, snapshot,
+ List bundleTxs = bundleValidator.validate(tangle, true, snapshot,
transactions.get(0).getHash());
assertTrue("no transactions should be returned", bundleTxs.isEmpty());
// special case, since there's no tail, we don't actually update the validity in the database
@@ -171,7 +256,8 @@ public void validateBundleWithInvalidIndices() throws Exception {
"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999NVPJQUUVRZYJATRWDEXX9UDHZGBPHPMDXYGQSZEYMZA9NOVVNLN9HCDKXRXCWXGCXSZVQVCMPEZKJFJZWNSA999999999999999999999999999999999999999999999999999TXVIAAD99C99999999C99999999YD9SJSKIJDSDTUQDYJOAHDEJBFKXNETCGPFQBF9TGKCTGJINK9OWSATFAFLJWOGWFHZRPHOJVFCAYGPJCWBRFOY9ULDPMHFZYSUKD9OTYKWJUKCXPOIZVACZNPPNTVFPNYUZNQJIYOMRMSULQPVWFLLIVODQDZ9999RM9DMI9FIPZMUBPLGYMXPLQGIXLMUGRQNECIYTVDFTR9TYFJTULEHVECMALIZMJVN9UKZNSKORO999999999999999999999999999999999SOLHCWPOF999999999K99999999SGC9999999FVC99999999999999",
};
List transactions = persistAndMapTxs(trytes);
- List bundleTxs = bundleValidator.validate(tangle, snapshot,
+ List bundleTxs = bundleValidator.validate(tangle, true,
+ snapshot,
transactions.get(0).getHash());
assertTrue("no transactions should be returned", bundleTxs.isEmpty());
assertEquals("should have changed the validity of the tail tx to invalid", -1,
@@ -188,7 +274,7 @@ public void validateBundleWithInvalidBundleHashOnTailTransaction() throws Except
"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999YIDFNIWFHPVPDKBAFQULFV9DFAOCXEPX9JZDIOWVHEUHOQORBPKOONGSYUJVKTYFJZBZCWKTHMWTCY99WNSA999999999999999999999999999999999999999999999999999EUWIAAD99C99999999C99999999EJJWPHCLV9KKDOBRKCBKWP9SQSOQYCLSHBLWNL9BEPOLGDXNISVPZGYSZRQXBHJKAIAZAFJS9K9TGJWTWMVTOHDRMCONUSSESCTMMIFDNGCLFFVBTUWV9AWUI9NSSLVZFKFXBHJMETHLXWGMGVXOKABSVOSFFZ9999MVTOHDRMCONUSSESCTMMIFDNGCLFFVBTUWV9AWUI9NSSLVZFKFXBHJMETHLXWGMGVXOKABSVOSFFZ9999999999999999999999999999999DSYOEWPOF999999999K99999999M999999999IJJC9999999999999",
};
List transactions = persistAndMapTxs(trytes);
- List bundleTxs = bundleValidator.validate(tangle, snapshot,
+ List bundleTxs = bundleValidator.validate(tangle, true, snapshot,
transactions.get(0).getHash());
assertTrue("no transactions should be returned", bundleTxs.isEmpty());
assertEquals("should not have changed the validity of the tail tx since the bundle is incomplete " +
@@ -207,7 +293,7 @@ public void validateBundleWithInvalidBundleHashOnNonTailTransaction() throws Exc
"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999SV9SCHGZVNZDJCFXFFJHENC9KONVG9JNXMIHLVFKNRFWUFFJRT9ADFIMHAXZTYAITABMURFFNWCGHIDUYNSA999999999999999999999999999999999999999999999999999DDWIAAD99C99999999C99999999MLKHIDVEN9KBPWKTGUY9QOQDRLJVSBAGXOBFHWIQRCQYIHVQIQJQJAUNWGHQBINDBAXBDBBJAZEXXGFEWUASDRXHKLY9PRNTZSNOZLLJTMITWQQCJAQLQOPWQIZXXHOPBGLLMIWMETVXAXDLSAWTRQDBDVDTQ99999NTNMNQLYLR9MNUDQITVBEZTWPMFWDGMQNOAJYMSFLMZKV9HBAQGTFPXDGNZUXOCNDDBYEHHUTHOD99999999999999999999999999999999OXQBEWPOF999999999K99999999KKA9999999LJJ99999999999999",
};
List transactions = persistAndMapTxs(trytes);
- List bundleTxs = bundleValidator.validate(tangle, snapshot,
+ List bundleTxs = bundleValidator.validate(tangle, true, snapshot,
transactions.get(0).getHash());
assertTrue("no transactions should be returned", bundleTxs.isEmpty());
assertEquals("should not have changed the validity of the tail tx since the bundle is incomplete " +
@@ -230,7 +316,7 @@ public void validateSemanticsOfValidBundle() {
bundleTxsMapping.put(tvm.getHash(), tvm);
}
List bundleTxs = new LinkedList<>();
- BundleValidator.Validity validity = BundleValidator.validateBundleSemantics(transactions.get(0),
+ BundleValidator.Validity validity = bundleValidator.validateBundleSemantics(transactions.get(0),
bundleTxsMapping, bundleTxs);
assertEquals("the bundle semantics should be valid", BundleValidator.Validity.VALID, validity);
assertEquals("built up bundle txs list should be the same", transactions, bundleTxs);
@@ -248,7 +334,8 @@ public void validateSemanticsOfBundleWithMissingTransaction() {
bundleTxsMapping.put(tvm.getHash(), tvm);
}
List bundleTxs = new LinkedList<>();
- BundleValidator.Validity validity = BundleValidator.validateBundleSemantics(transactions.get(0), bundleTxsMapping,
+ BundleValidator.Validity validity = bundleValidator.validateBundleSemantics(transactions.get(0),
+ bundleTxsMapping,
bundleTxs);
assertEquals("the bundle semantic's validity should be unknown since were are missing a tx",
BundleValidator.Validity.UNKNOWN, validity);
@@ -269,7 +356,8 @@ public void validateSemanticsOfBundleWithInvalidIndices() {
bundleTxsMapping.put(tvm.getHash(), tvm);
}
List bundleTxs = new LinkedList<>();
- BundleValidator.Validity validity = BundleValidator.validateBundleSemantics(transactions.get(0), bundleTxsMapping, bundleTxs);
+ BundleValidator.Validity validity = bundleValidator.validateBundleSemantics(transactions.get(0),
+ bundleTxsMapping, bundleTxs);
assertEquals("the bundle semantics should be invalid", BundleValidator.Validity.INVALID, validity);
}
@@ -290,7 +378,8 @@ public void validateSemanticsOfBundleWhichSpendsMoreThanMaxSupply() {
bundleTxsMapping.put(tvm.getHash(), tvm);
}
List bundleTxs = new LinkedList<>();
- BundleValidator.Validity validity = BundleValidator.validateBundleSemantics(transactions.get(0), bundleTxsMapping, bundleTxs);
+ BundleValidator.Validity validity = bundleValidator.validateBundleSemantics(transactions.get(0),
+ bundleTxsMapping, bundleTxs);
assertEquals("the bundle semantics should be invalid", BundleValidator.Validity.INVALID, validity);
}
@@ -360,7 +449,7 @@ public void validationModeAll() {
};
List transactions = persistAndMapTxs(trytes);
try {
- BundleValidator.Validity validity = BundleValidator.validate(tangle, transactions.get(0).getHash(),
+ BundleValidator.Validity validity = bundleValidator.validate(tangle, transactions.get(0).getHash(),
BundleValidator.MODE_VALIDATE_ALL, new LinkedList<>());
assertEquals("the bundle should be valid", BundleValidator.Validity.VALID, validity);
} catch (Exception e) {
@@ -458,7 +547,7 @@ public void validationModeAllWithInvalidBundle() {
};
List transactions = persistAndMapTxs(trytes);
try {
- BundleValidator.Validity validity = BundleValidator.validate(tangle, transactions.get(0).getHash(),
+ BundleValidator.Validity validity = bundleValidator.validate(tangle, transactions.get(0).getHash(),
BundleValidator.MODE_VALIDATE_ALL, new LinkedList<>());
assertEquals("the bundle should be invalid", BundleValidator.Validity.INVALID, validity);
} catch (Exception e) {
@@ -477,7 +566,8 @@ public void validationModeBundleHash() {
};
List transactions = persistAndMapTxs(trytes);
try {
- BundleValidator.Validity validity = BundleValidator.validate(tangle, transactions.get(0).getHash(),
+ BundleValidator.Validity validity = bundleValidator.validate(
+ tangle, transactions.get(0).getHash(),
BundleValidator.MODE_VALIDATE_BUNDLE_HASH |
BundleValidator.MODE_SKIP_TAIL_TX_EXISTENCE |
BundleValidator.MODE_SKIP_CACHED_VALIDITY, new LinkedList<>());
@@ -498,7 +588,8 @@ public void validationModeSignaturesOnBundleWithInvalidSemantics() {
};
List transactions = persistAndMapTxs(trytes);
try {
- BundleValidator.Validity validity = BundleValidator.validate(tangle, transactions.get(0).getHash(),
+ BundleValidator.Validity validity = bundleValidator.validate(
+ tangle, transactions.get(0).getHash(),
BundleValidator.MODE_VALIDATE_SIGNATURES |
BundleValidator.MODE_SKIP_TAIL_TX_EXISTENCE |
BundleValidator.MODE_SKIP_CACHED_VALIDITY, new LinkedList<>());
@@ -521,7 +612,8 @@ public void validationModeSignaturesOnBundleWithEmptyBundleHash() {
};
List transactions = persistAndMapTxs(trytes);
try {
- BundleValidator.Validity validity = BundleValidator.validate(tangle, transactions.get(0).getHash(),
+ BundleValidator.Validity validity = bundleValidator.validate(
+ tangle, transactions.get(0).getHash(),
BundleValidator.MODE_VALIDATE_SIGNATURES |
BundleValidator.MODE_SKIP_TAIL_TX_EXISTENCE |
BundleValidator.MODE_SKIP_CACHED_VALIDITY, new LinkedList<>());
@@ -542,7 +634,8 @@ public void validationModeSignaturesOnBundleWithInvalidBundleHashAndSemantics()
};
List transactions = persistAndMapTxs(trytes);
try {
- BundleValidator.Validity validity = BundleValidator.validate(tangle, transactions.get(0).getHash(),
+ BundleValidator.Validity validity = bundleValidator.validate(
+ tangle, transactions.get(0).getHash(),
BundleValidator.MODE_VALIDATE_SIGNATURES |
BundleValidator.MODE_SKIP_TAIL_TX_EXISTENCE |
BundleValidator.MODE_SKIP_CACHED_VALIDITY, new LinkedList<>());
@@ -563,7 +656,8 @@ public void validationModeSignaturesWithInvalidSignatures() {
};
List transactions = persistAndMapTxs(trytes);
try {
- BundleValidator.Validity validity = BundleValidator.validate(tangle, transactions.get(0).getHash(),
+ BundleValidator.Validity validity = bundleValidator.validate(
+ tangle, transactions.get(0).getHash(),
BundleValidator.MODE_VALIDATE_SIGNATURES |
BundleValidator.MODE_SKIP_TAIL_TX_EXISTENCE |
BundleValidator.MODE_SKIP_CACHED_VALIDITY, new LinkedList<>());
@@ -572,4 +666,24 @@ public void validationModeSignaturesWithInvalidSignatures() {
e.printStackTrace();
}
}
+
+ @Test
+ public void verifyValidateSkippingChecks() {
+ // valid bundle trytes
+ String[] trytes = {
+ "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999GNXKWQPHMFCEZZCKUCCD9PROQVPCTBVVAPHCLVOPYYQLQRMPGTQXBVZLKGGT9NXHUAASFIAVEOZTBSM9DA99999999999999999999999999RENGLE9BEAT9999999999999999FDKW9AD99999999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9DKZYSMJAQRLXZHCNPTJPJLOONNDZHGNTBIINGYKZQAAWF9LWQTHUQSAXMLQN9DKCQWHIJXNGLGQVA9999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999TANGLE9BEAT9999999999999999SVIDBWNOF999999999K99999999POWSRVIO9VMIJ99UGNETMNPNNNE",
+ "YU9WINMHSBIJ9HRBKDEHZYOCDGOZ9HZSSVXAYYOD9AMAFBCROMLSFDFIAQKEXAVFDLJYVABRSONSTTXLCQJTSDRFDEXZIORLRSQDZQODNCHAAFUPGBCEVYNRSJQGUZRAQIIEZPFQGWPQIBMDVNZGQAKCU9VLHGYSNDPHCMOCTRYQOYHFPRQSGHSDEF9MSHGXYGUTHSBSF9SOSJQFOZXMQGCUXAYWLBJHXOHTOM9ITZPISEXLAYAZBHTZDDCOIHKDBL9AAXZEDFHDYCPXLDGR9JTVXDXBC9DRDVREEEVGVPWVEZHJBYQYTGDEWQHVEDTAVLWW9WBXOIGDGUAAAVKFMPVKSYBVOIWPYGCMACEAAHGUIQVFLFMSCPKTYQWJDGMDNLMDIUUYI9SZOFQPLKKACESSWIXPFYZWDHETYUGEVASPHBGQPJPWVESSUUEVUYDYNQUQURSMCEPKIHYXNFHZSHIIDTMEZQ9IPSHTBDK9UHRSIIEJRILBCMVUMGAGBMHKEMUUKQDCYAVIAN9MBWJCAUSSJRSAUEUBGEFFCFSOBVGLXBAIDOH9NXWQUSIFUINEVZUWELLVIOSSQRVHMTDBCATBWFSDJJDLURAUKEQEIRICSCXAOJNPWHQKYVTRLCRKHLFHHHPYSMUEAPMQPMPS9CHCIYXEYRNIOOGRWKVHUGIWJZIGPWGZGBGYTVC9UVEJ9JBIYMDHYXLWIDXHZVFCCPLYXDPQPJGMKNZMMONGNXSNNPCGEGRJTMCPTJONGPAEDMSHMGAPGBDWVCSCTJAECSHTEGBMVCDZMQYZ9IHERWGCKAIMFOJRAEDRDSWJASV9XYS9CTOEETPQHRKJZ9FPWTVCGLEJQJXVVDTYZ9DNSHQAINKROQQIPTFGTABEELYPBQHGNNB9NCL9BVTG9MH99BPIRNNGC9MQQRH9YUDYSUJIDSZKCIGEKFSJ9PXUYKVPUZYFIREUAMUZE9EIMJK9YGAZXZBPRXIRDCEVDHWOCFIJTVRCRZRRJGFLILKVNMJAX9MUMTXCWTUTP9NXBZUMAYYY9DNVZTCROBCMMHVKSQRXMGROCMMCDENWQWBV9LEIJONWTQXOUQZOTGEJHJELYZOPICLDWPHVCMBFSYHPIJYZQDEGYITVPYNQMXPRTPKADLUKW9AEMSMGIWCUMOQMUDN9HZGYKTNVVNLOQAFVETEDJMJVKYCE9RE9AJP9RMYFKGDEC9GIGAFXEXIYXPFBOMRPULVJMYTQEPDMMIABDHJGRNNCKADZAUFV9WCWFYFHXLCED9E9SKKYHVKDTGNCHEKFJL9YJHTSRUVKEPHPCQSFNBXKOJNGBSEOZLVVGNUQTRACVGEKDSORUHVSDSQNUZASWXRADGAUDYDPODDEDNDDT9OAEARSBLPVSYAOEYYZJIZIZDENQOKMXATCTD9NEFVQWNCET9N9IXWCCWHYUBDTDJUZFXWMATRYSGJUQAFKAPIQIIXVFFEFNLGDPAIGKONUWLUTQTKZJYDEGPASTOMM9QTGOMYHFUKMNEVWPCMTLPDSOEEWDBRHZPZJIFMBUTY9QPRLPSVURIRMCWLH9YIZBOTJQMMF9ZP9VCCEGWVKSSQNANP9ZXVCAFGAVBXDJHUMKTKUBZRNKUGJTVUPDZMUK9KZI9EKY9DQDUNA9ZVPCROKYYPISSTDOJZJXMXZWSGZBEPKAUYPBPUTDVQKUYXWYTZXLYTCLLKLJUUJGFFCMZFKDTJET9KEDRMWZSAEFCSXLMRHRLG9EKLRZJ9NZQEONGFQSKSQMWRZXWVEGHCF9ZKGQITDLGFTZLCQMXVGVYIPKSUPPAOIKVWARJHAEJUZOOJFCFJTGQZWROSCEQSITQWZTQDFKECATBWSIRDFVWH9TXXUVEBRRKOTFSGBMGCMZDK9BJRVEUYOVNSLZNNSTWOB9CFNDJHJENDWSTIMMSWD9HSA9LDGTJDJFRGQCWOHYJNMFFLLDMVQEMCMNVQWMOGCEZUTK9MPULTDMKTIVFNVWXDNTRHNREHEDWIZCCRFVM9TXCTLBLYEKTRVVWHKZDOSYOBNLEKPNXMDZGLSVBBNBJIBZYBXZCENKVIXMNGUJUYFGFYFBKELSBZROQWKMESZTWDEEUCSXOWFSNJUGGJANMJFCBJV9GMDDJCAJHWKYQFJLFWZESTSIRBZQWYHGQDFUUWF9F9HXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMDZ99999999999999999999999999999999999999999999999999999FDKW9AD99A99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH99XJLIVAZYJGPKEHANESX9DORLEIOIFL9WVQBBNUDRZDGJMVSRTSFKOJPIMEXPBCPGKUUEKRGAXOB99999XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999999999999999999999999999999RSIDBWNOF999999999K99999999POWSRVIO9VMIJ99FKKNKGPNNNNP",
+ "COSNPEACSPESTKTUS9CSMOCWVUBLERQJQGLLA9AYRRXMOVOBULQZZZJPLNELEX9EVZDAFOHBB9FBAYOTDB9YGJAVTDWNPJVVGEPFJ9QBYLXAYZEETY9GMYQRIRAIPWMJUFMFRSQRCEIOWISMBJKGCIGIIYNMXAYLXZIBNKANT9DSQXPBUPWGUJTOMD9JKSNFZJJFBZYBWRWQIZFOZXQWNJTRZTUPHVEGTAOLMBEPXOB9TCEBNIWXQUHNQJZHAOWGVOOOTSCTEUEMXJUODZP9CDLCMEXAW9YJGVNREENETEYXOAKZVRJVUSCRUSRCNZUGUOLCNHQITWYPJVJGPVULASTONDZRDVCNGJSN9DMMUORCOIBTITSPBXSAVTOHSAXHMGH99APRTQIVCEI9NWYKDOZCCWBNZGGCUCISSMGJHJ9OVSFBZVFMBODKRKSLIDESGFDQITCYFARWYTLFKSPLNVQ9LTYFODCHBAJKCWVZNBLOJH9ABFGDFOXHDNHZILINMYMVZYWTFJJOLADPTZRPJEMGPFQNYTFDGOKMDTDSKPTHIDWLMRSSACZMTHUCWVZHCYSPJRATABZ9JHWEBGTTQORGFOYDWUIYFYGOGGTYGYYI9RPKPMCTYNYKHHNWQKJTDQDHTRUYQPMCDDLVUUHUBPWGPMNQYPKHDHGIUSXJGNUXIYGZRGVYPTTV9KXJDLGVVXRIWIGKZJVCQ9DUWAYNDRKYCGKZDEOKZQCXELSQXLGLAPBDIWRTKGSIRNAZ9XFEGMWOWLPUA9TSGYRDOU9GKY9JJDKCJKKGJCRARGSNECJ9SEVAGPPJMBTMGNWSLNSFBOEYMIQBKOVSCTDIWCOQTHBVTZOFNNIYKKJLAROKIRFFPCLSOWO9BVNNJSXEYLJASPUEFKCEI9SGWADNGS9MZAKFTCAYNRVKGDIGYDTPGJXWFAEFGG9BX9XCNBZGQULH9IPPIQ9VFVAXGELUMCTE9TOFOGCTFYBYEBFOKKAESIIHEWLYLQKWDHPYNELJKMZHDSDSK9ZWFP99FQFLUXGTSVDMGHIFZFUCYTYPVEFEDM9TOHCQVIBXARYWZEKTWEOVSBYZTJZRUFZJCLRBTOWPTKY99CYQTGPMAKFYALIWPEQLUB9RGANTKDDMSDSJOTNPFZCXKJUGTJWWPK9FAJGPJOEBRHL9ZU9XRBDJBVMFIZIZFGKETH9RJUSDIOWDZIXFOJLT9DSIAWSCEYBNOGOAZQPZKVZTBXGACCBVQGFNGXNQFTVPUGUOHNBKTUERLSTDXGRKZSATIRHDBSXARYATBHOLUHCQGPRMQGSJUAXUCWKDXAUGORHFVVVDZQEYYGSV9IUM9VTMHJCIKBQD9TQLLYTZHAKDHPFGYMRGJOEDJKQTBBNFCKDEPYTPWUJEOPRJWHAILMVW9AXS9HAT9GCGRXZEHWNDOVGKGPWFXFC9IKLVZJCTGZUJFRHGL9FZWZYP9TPOWGOUTQ9INYBVTD9N9OV9AM9GYRJ99MRWRWLOAKJXMDTFNS9NZQAYGVZRJ9WVSIDRRQLGKH9VPUVKFWZMSRVVLKXUGJAZESC9LEYDNNDWS9WTWNFPCR9YKPOMIJAVHCRCJVBDFWSTDN9BPJRDMGZFXWTY9GUX9WEPJQMXSECFPREXAJ9DU9OLMNQFTF99QYZOGDUTMBHMNMCNUQTU9FTNEUNBT9NQO9YADGY9C9LQR9DYVJZE9GMMYAOUSWUIVJ9EAR9EEFCO9SZTQ9JQHPIJGXIOQZHOSUSVMHBOLTYSEONVLRISNAN9KJHHFFISTYIHYVAMXLPNALDELETXVSWGUWOIVKNAKZKNNRVNJKE9WMNQBXFGXSYZQQSIODAKINA9CPBOGAYKJ9V9GS9HERXSVYPEILYXC9GURBREBCXRZJVXHUOPGYFLPIFBCOTMHFTAAUQUZVEKOWYFFGSCXDFDBTSGZRMPQR9EYSZYGQETCWFDRXVOXXCUBOQTBQGVLAONSTSLURFYSDPAVNCXNWVPQOUORMVSLYISQUQKXFYYMJSGHQP9VAAZABEAGJMCPIEBZQSYCPS9CNKFBDRDTDA9C9MVKGUISRX9ODVUJNPDWMZ9ESYXJ99ETDGSTJZMZYJEGRUWTORMCLNHLDQ9YIJWPMDYGGJQHMFPJ9VKINJUESTCBJQXFTCJFNUXPZONVKXJRAYGKIWHXSBZRKBDLRWXSMKW99KPWJOQYPMHB9KWALSVLFNANYSMNWSJLUOCBKAWAJHNWMLPLXHVEPTKBPWLWHMD999999999999999999999999999999999999999999999999999999FDKW9AD99B99999999B99999999RJPQGE9AXNBBLPQ9IFJXFJTXJAIGDDXAWXGAVU9PJQNYFADJDIDBTJKSAEBBX9NWJGZVAVXYWXPCEFFH9XIFVJWCABR9XQKOWCXJAZDZWMWWBVHROBOPISAWDIJTSFPVAUZNJUMNUCOFIBIAAVIOWDTCNIPRVA9999FTXZAEQIKGFJABOGCCQGOMKNASGWCQWMNIMRDTHKPJMTKDGMEQNZOAYARIYEWFZYJALRKFODOITTZ9999999999999999999999999999999LLHDBWNOF999999999K99999999POWSRVIO9VMIJ99UMGMTTTPNNNV", };
+ List transactions = persistAndMapTxs(trytes);
+ try {
+ bundleValidator.validate(tangle, false, snapshot, transactions.get(0).getHash());
+
+ Mockito.verify(bundleValidator, Mockito.never()).validateBundleTailApproval(Mockito.any(),
+ Mockito.anyList());
+ Mockito.verify(bundleValidator, Mockito.never()).validateBundleTransactionsApproval(Mockito.anyList());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
}
diff --git a/src/test/java/com/iota/iri/MainInjectionConfigurationTest.java b/src/test/java/com/iota/iri/MainInjectionConfigurationTest.java
index 3695f6cdba..be16d5df2f 100644
--- a/src/test/java/com/iota/iri/MainInjectionConfigurationTest.java
+++ b/src/test/java/com/iota/iri/MainInjectionConfigurationTest.java
@@ -23,6 +23,7 @@
import com.iota.iri.service.spentaddresses.SpentAddressesProvider;
import com.iota.iri.service.spentaddresses.SpentAddressesService;
import com.iota.iri.service.transactionpruning.TransactionPruner;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.storage.LocalSnapshotsPersistenceProvider;
import com.iota.iri.storage.Tangle;
import org.junit.Test;
diff --git a/src/test/java/com/iota/iri/TangleMockUtils.java b/src/test/java/com/iota/iri/TangleMockUtils.java
index 300afb4497..d5a35613c1 100644
--- a/src/test/java/com/iota/iri/TangleMockUtils.java
+++ b/src/test/java/com/iota/iri/TangleMockUtils.java
@@ -103,7 +103,7 @@ public static List mockValidBundle(Tangle tangle,
}
Collections.reverse(bundle);
- Mockito.when(bundleValidator.validate(Mockito.eq(tangle), Mockito.any(),
+ Mockito.when(bundleValidator.validate(Mockito.eq(tangle), Mockito.eq(true), Mockito.any(),
Mockito.eq(bundle.iterator().next().getHash())))
.thenReturn(bundle);
diff --git a/src/test/java/com/iota/iri/TransactionTestUtils.java b/src/test/java/com/iota/iri/TransactionTestUtils.java
index 3cba89c1ba..648f134c24 100644
--- a/src/test/java/com/iota/iri/TransactionTestUtils.java
+++ b/src/test/java/com/iota/iri/TransactionTestUtils.java
@@ -98,6 +98,20 @@ public static TransactionViewModel createBundleHead(int index) {
return tx;
}
+ /**
+ * Generates a transaction with a hash. Transaction last and current index are set to the index provided.
+ *
+ * @param index The index to set the transaction to
+ * @return A transaction which is located on the end of its (nonexistent) bundle
+ */
+ public static TransactionViewModel createBundleHead(int index, Hash hash) {
+
+ TransactionViewModel tx = new TransactionViewModel(getTransactionTrits(), hash);
+ setLastIndex(tx, index);
+ setCurrentIndex(tx, index);
+ return tx;
+ }
+
/**
* Generates a transaction with the specified trunk and branch, and bundle hash from trunk.
* This transaction indices are updated to match the trunk index.
diff --git a/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java b/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java
index c02749a893..6cb4f26b8e 100644
--- a/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java
+++ b/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java
@@ -3,7 +3,7 @@
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
-import com.iota.iri.TransactionValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.conf.BaseIotaConfig;
import com.iota.iri.conf.IotaConfig;
import com.iota.iri.network.pipeline.TransactionProcessingPipeline;
diff --git a/src/test/java/com/iota/iri/network/pipeline/QuickBundleValidationStageTest.java b/src/test/java/com/iota/iri/network/pipeline/QuickBundleValidationStageTest.java
new file mode 100644
index 0000000000..fd8574d72f
--- /dev/null
+++ b/src/test/java/com/iota/iri/network/pipeline/QuickBundleValidationStageTest.java
@@ -0,0 +1,98 @@
+package com.iota.iri.network.pipeline;
+
+import com.iota.iri.BundleValidator;
+import com.iota.iri.controllers.TransactionViewModel;
+import com.iota.iri.network.neighbor.Neighbor;
+import com.iota.iri.service.snapshot.SnapshotProvider;
+import com.iota.iri.service.validation.TransactionValidator;
+import com.iota.iri.storage.Tangle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+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;
+
+public class QuickBundleValidationStageTest {
+
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ BundleValidator bundleValidator;
+ @Mock
+ TransactionValidator transactionValidator;
+ @Mock
+ Tangle tangle;
+ @Mock
+ SnapshotProvider snapshotProvider;
+ @Mock
+ private Neighbor neighbor;
+ @Mock
+ TransactionViewModel tvm;
+
+ @Test
+ public void propagatesSolidTailAndValidBundle() throws Exception {
+ List bundleTransactions = new ArrayList() {
+
+ {
+ add(tvm);
+ }
+ };
+ Mockito.when(tvm.isSolid()).thenReturn(true);
+ Mockito.when(tvm.getCurrentIndex()).thenReturn(0L);
+ Mockito.when(bundleValidator.validate(tangle, true, snapshotProvider.getInitialSnapshot(), tvm.getHash()))
+ .thenReturn(bundleTransactions);
+
+ QuickBundleValidationStage stage = new QuickBundleValidationStage(tangle, snapshotProvider, bundleValidator,
+ transactionValidator);
+ QuickBundleValidationPayload payload = new QuickBundleValidationPayload(neighbor, tvm);
+ ProcessingContext ctx = new ProcessingContext(null, payload);
+ stage.process(ctx);
+
+ Mockito.verify(transactionValidator).addSolidTransaction(tvm.getHash());
+ }
+
+ @Test
+ public void shouldNotPropagateNonSolidTail() throws Exception {
+ Mockito.when(tvm.isSolid()).thenReturn(false);
+ Mockito.when(tvm.getCurrentIndex()).thenReturn(0L);
+ QuickBundleValidationStage stage = new QuickBundleValidationStage(tangle, snapshotProvider, bundleValidator,
+ transactionValidator);
+ QuickBundleValidationPayload payload = new QuickBundleValidationPayload(neighbor, tvm);
+ ProcessingContext ctx = new ProcessingContext(null, payload);
+ stage.process(ctx);
+ Mockito.verify(transactionValidator, Mockito.never()).addSolidTransaction(tvm.getHash());
+ }
+
+ @Test
+ public void shouldNotPropagateNonTail() throws Exception {
+ Mockito.when(tvm.isSolid()).thenReturn(false);
+ Mockito.when(tvm.getCurrentIndex()).thenReturn(1L);
+ QuickBundleValidationStage stage = new QuickBundleValidationStage(tangle, snapshotProvider, bundleValidator,
+ transactionValidator);
+ QuickBundleValidationPayload payload = new QuickBundleValidationPayload(neighbor, tvm);
+ ProcessingContext ctx = new ProcessingContext(null, payload);
+ stage.process(ctx);
+ Mockito.verify(transactionValidator, Mockito.never()).addSolidTransaction(tvm.getHash());
+ }
+
+ @Test
+ public void shouldNotPropagateInvalidBundle() throws Exception {
+ Mockito.when(tvm.isSolid()).thenReturn(false);
+ Mockito.when(tvm.getCurrentIndex()).thenReturn(1L);
+ Mockito.when(bundleValidator.validate(tangle, true, snapshotProvider.getInitialSnapshot(), tvm.getHash()))
+ .thenReturn(Collections.emptyList());
+ QuickBundleValidationStage stage = new QuickBundleValidationStage(tangle, snapshotProvider, bundleValidator,
+ transactionValidator);
+ QuickBundleValidationPayload payload = new QuickBundleValidationPayload(neighbor, tvm);
+ ProcessingContext ctx = new ProcessingContext(null, payload);
+ stage.process(ctx);
+ Mockito.verify(transactionValidator, Mockito.never()).addSolidTransaction(tvm.getHash());
+ }
+}
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 772295c4e5..8fa51f6b58 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.TransactionValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.network.TransactionRequester;
import com.iota.iri.network.neighbor.Neighbor;
@@ -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.QUICK_BUNDLE_VALIDATION,
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());
+ QuickBundleValidationPayload quickBundleValidationPayload = (QuickBundleValidationPayload) ctx.getPayload();
+ assertEquals("neighbor is still the same", neighbor, quickBundleValidationPayload.getOriginNeighbor());
+ assertEquals("tvm is still the same", tvm, quickBundleValidationPayload.getTransactionViewModel());
}
@Test
@@ -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.QUICK_BUNDLE_VALIDATION,
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());
+ QuickBundleValidationPayload quickBundleValidationPayload = (QuickBundleValidationPayload) ctx.getPayload();
+ assertEquals("neighbor should still be the same", neighbor, quickBundleValidationPayload.getOriginNeighbor());
+ assertEquals("tvm should still be the same", tvm, quickBundleValidationPayload.getTransactionViewModel());
}
}
\ 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 b9b8c4ab8f..ed0a25b579 100644
--- a/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java
+++ b/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java
@@ -1,6 +1,7 @@
package com.iota.iri.network.pipeline;
-import com.iota.iri.TransactionValidator;
+import com.iota.iri.BundleValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.conf.NodeConfig;
import com.iota.iri.controllers.TipsViewModel;
import com.iota.iri.network.NeighborRouter;
@@ -35,6 +36,9 @@ public class TransactionProcessingPipelineTest {
@Mock
private TransactionValidator transactionValidator;
+ @Mock
+ private BundleValidator bundleValidator;
+
@Mock
private SnapshotProvider snapshotProvider;
@@ -65,6 +69,9 @@ public class TransactionProcessingPipelineTest {
@Mock
private BroadcastStage broadcastStage;
+ @Mock
+ private QuickBundleValidationStage quickBundleValidationStage;
+
@Mock
private HashingStage hashingStage;
@@ -86,6 +93,9 @@ public class TransactionProcessingPipelineTest {
@Mock
private ProcessingContext receivedCtx;
+ @Mock
+ private ProcessingContext quickBundleValidationCtx;
+
@Mock
private ProcessingContext broadcastCtx;
@@ -107,6 +117,7 @@ private void injectMockedStagesIntoPipeline(TransactionProcessingPipeline pipeli
pipeline.setHashingStage(hashingStage);
pipeline.setReplyStage(replyStage);
pipeline.setValidationStage(validationStage);
+ pipeline.setQuickBundleValidationStage(quickBundleValidationStage);
}
@Test
@@ -114,7 +125,7 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws
TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig,
transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker,
- transactionRequester);
+ transactionRequester, bundleValidator);
// inject mocks
injectMockedStagesIntoPipeline(pipeline);
@@ -134,9 +145,15 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws
.thenReturn(TransactionProcessingPipeline.Stage.MULTIPLE);
Mockito.when(divergeToReplyAndReceivedCtx.getPayload()).thenReturn(divergePayload);
- // mock received
+ //mock quick bundle validation
+ Mockito.when(quickBundleValidationCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.QUICK_BUNDLE_VALIDATION);
+ Mockito.when(quickBundleValidationStage.process(quickBundleValidationCtx)).thenReturn(broadcastCtx);
+
+ //mock received
+ Mockito.when(receivedStage.process(receivedCtx)).thenReturn(quickBundleValidationCtx);
+
+ // mock broadcast
Mockito.when(broadcastCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.BROADCAST);
- Mockito.when(receivedStage.process(receivedCtx)).thenReturn(broadcastCtx);
pipeline.start();
@@ -151,6 +168,7 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws
Mockito.verify(hashingStage).process(Mockito.any());
Mockito.verify(validationStage).process(Mockito.any());
Mockito.verify(receivedStage).process(Mockito.any());
+ Mockito.verify(quickBundleValidationStage).process(Mockito.any());
Mockito.verify(replyStage).process(Mockito.any());
Mockito.verify(broadcastStage).process(Mockito.any());
}
@@ -159,7 +177,7 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws
public void processingAKnownTransactionOnlyFlowsToTheReplyStage() throws InterruptedException {
TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig,
transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker,
- transactionRequester);
+ transactionRequester, bundleValidator);
// inject mocks
pipeline.setPreProcessStage(preProcessStage);
@@ -193,7 +211,7 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug
throws InterruptedException {
TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig,
transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker,
- transactionRequester);
+ transactionRequester, bundleValidator);
// inject mocks
injectMockedStagesIntoPipeline(pipeline);
@@ -210,9 +228,15 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug
Mockito.when(validationStage.process(validationCtx)).thenReturn(receivedCtx);
Mockito.when(receivedCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.RECEIVED);
- // mock received
+ //mock quick bundle validation
+ Mockito.when(quickBundleValidationCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.QUICK_BUNDLE_VALIDATION);
+ Mockito.when(quickBundleValidationStage.process(quickBundleValidationCtx)).thenReturn(broadcastCtx);
+
+ //mock received
+ Mockito.when(receivedStage.process(receivedCtx)).thenReturn(quickBundleValidationCtx);
+
+ // mock broadcast
Mockito.when(broadcastCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.BROADCAST);
- Mockito.when(receivedStage.process(receivedCtx)).thenReturn(broadcastCtx);
pipeline.start();
@@ -231,6 +255,7 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug
Mockito.verify(hashingStage).process(Mockito.any());
Mockito.verify(validationStage).process(Mockito.any());
Mockito.verify(receivedStage).process(Mockito.any());
+ Mockito.verify(quickBundleValidationStage).process(Mockito.any());
Mockito.verify(broadcastStage).process(Mockito.any());
}
@@ -238,7 +263,7 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug
public void anInvalidNewTransactionStopsBeingProcessedAfterTheValidationStage() throws InterruptedException {
TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig,
transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker,
- transactionRequester);
+ transactionRequester, bundleValidator);
// inject mocks
injectMockedStagesIntoPipeline(pipeline);
@@ -269,6 +294,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(quickBundleValidationStage, Mockito.never()).process(Mockito.any());
// should have called
Mockito.verify(hashingStage).process(Mockito.any());
diff --git a/src/test/java/com/iota/iri/network/pipeline/ValidationStageTest.java b/src/test/java/com/iota/iri/network/pipeline/ValidationStageTest.java
index 5bd0eecdff..c02d57d3b4 100644
--- a/src/test/java/com/iota/iri/network/pipeline/ValidationStageTest.java
+++ b/src/test/java/com/iota/iri/network/pipeline/ValidationStageTest.java
@@ -1,6 +1,6 @@
package com.iota.iri.network.pipeline;
-import com.iota.iri.TransactionValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.model.Hash;
import com.iota.iri.network.FIFOCache;
diff --git a/src/test/java/com/iota/iri/service/APITest.java b/src/test/java/com/iota/iri/service/APITest.java
index f5639156fb..9643959f54 100644
--- a/src/test/java/com/iota/iri/service/APITest.java
+++ b/src/test/java/com/iota/iri/service/APITest.java
@@ -1,6 +1,6 @@
package com.iota.iri.service;
-import com.iota.iri.TransactionValidator;
+import com.iota.iri.service.validation.TransactionValidator;
import com.iota.iri.conf.IotaConfig;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.service.snapshot.SnapshotProvider;
diff --git a/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java b/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java
index 72dbb53539..59dd13e500 100644
--- a/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java
+++ b/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java
@@ -71,7 +71,7 @@ public void generateBalanceDiffWithPersistsSpentAddresses() throws Exception {
int milestoneIndex = 1;
when(milestoneService.isTransactionConfirmed(tailTx, milestoneIndex)).thenReturn(false);
when(snapshotProvider.getInitialSnapshot().getSolidEntryPoints()).thenReturn(Collections.emptyMap());
- ledgerService.generateBalanceDiff(new HashSet<>(), tailTx.getHash(), milestoneIndex);
+ ledgerService.generateBalanceDiff(new HashSet<>(), tailTx.getHash(), milestoneIndex, true);
verify(spentAddressesService, times(1)).persistValidatedSpentAddressesAsync(eq(bundle));
}
}
\ No newline at end of file
diff --git a/src/test/java/com/iota/iri/TransactionValidatorTest.java b/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java
similarity index 98%
rename from src/test/java/com/iota/iri/TransactionValidatorTest.java
rename to src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java
index ad35624978..99a39ea952 100644
--- a/src/test/java/com/iota/iri/TransactionValidatorTest.java
+++ b/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java
@@ -1,5 +1,6 @@
-package com.iota.iri;
+package com.iota.iri.service.validation;
+import com.iota.iri.TransactionTestUtils;
import com.iota.iri.conf.MainnetConfig;
import com.iota.iri.conf.ProtocolConfig;
@@ -11,6 +12,7 @@
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;