88import com .iota .iri .utils .Converter ;
99
1010import java .util .*;
11+ import com .google .common .annotations .VisibleForTesting ;
1112
1213/**
1314 * Validates bundles.
@@ -109,9 +110,11 @@ public enum Validity {
109110 * we lose the last trit in the process</li>
110111 * </ol>
111112 *
112- * @param tangle used to fetch the bundle's transactions from the persistence layer
113- * @param initialSnapshot the initial snapshot that defines the genesis for our ledger state
114- * @param tailHash the hash of the last transaction in a bundle.
113+ * @param tangle used to fetch the bundle's transactions from the persistence layer
114+ * @param enforceExtraRules true if enforce {@link #validateBundleTailApproval(Tangle, List)} and
115+ * {@link #validateBundleTransactionsApproval(List)}
116+ * @param initialSnapshot the initial snapshot that defines the genesis for our ledger state
117+ * @param tailHash the hash of the last transaction in a bundle.
115118 * @return A list of transactions of the bundle contained in another list. If the bundle is valid then the tail
116119 * transaction's {@link TransactionViewModel#getValidity()} will return 1, else {@link
117120 * TransactionViewModel#getValidity()} will return -1. If the bundle is invalid then an empty list will be
@@ -121,9 +124,32 @@ public enum Validity {
121124 * validate it again.
122125 * </p>
123126 */
124- public List <TransactionViewModel > validate (Tangle tangle , Snapshot initialSnapshot , Hash tailHash ) throws Exception {
127+ public List <TransactionViewModel > validate (Tangle tangle , boolean enforceExtraRules , Snapshot initialSnapshot ,
128+ Hash tailHash ) throws Exception {
129+ int mode = getMode (enforceExtraRules );
130+ return validate (tangle , initialSnapshot , tailHash , mode );
131+ }
132+
133+ /**
134+ * Does {@link #validate(Tangle, boolean, Snapshot, Hash)} but with an option of skipping some checks according to
135+ * the give {@code mode}
136+ *
137+ * @param tangle used to fetch the bundle's transactions from the persistence layer
138+ * @param initialSnapshot the initial snapshot that defines the genesis for our ledger state
139+ * @param tailHash the hash of the last transaction in a bundle.
140+ * @param mode flags that specify which validation checks to perform
141+ * @return A list of transactions of the bundle contained in another list. If the bundle is valid then the tail
142+ * transaction's {@link TransactionViewModel#getValidity()} will return 1, else
143+ * {@link TransactionViewModel#getValidity()} will return -1. If the bundle is invalid then an empty list
144+ * will be returned.
145+ * @throws Exception if a persistence error occurred
146+ * @implNote if {@code tailHash} was already invalidated/validated by a previous call to this method then we don't
147+ * validate it again.
148+ * @see #validate(Tangle, boolean, Snapshot, Hash)
149+ */
150+ private List <TransactionViewModel > validate (Tangle tangle , Snapshot initialSnapshot , Hash tailHash , int mode ) throws Exception {
125151 List <TransactionViewModel > bundleTxs = new LinkedList <>();
126- switch (validate (tangle , tailHash , MODE_VALIDATE_ALL , bundleTxs )) {
152+ switch (validate (tangle , tailHash , mode , bundleTxs )) {
127153 case VALID :
128154 if (bundleTxs .get (0 ).getValidity () != 1 ) {
129155 bundleTxs .get (0 ).setValidity (tangle , initialSnapshot , 1 );
@@ -139,7 +165,14 @@ public List<TransactionViewModel> validate(Tangle tangle, Snapshot initialSnapsh
139165 }
140166 }
141167
142- private static boolean hasMode (int mode , int has ) {
168+ private static int getMode (boolean enforceExtraRules ) {
169+ if (enforceExtraRules ) {
170+ return MODE_VALIDATE_ALL ;
171+ }
172+ return MODE_VALIDATE_SIGNATURES | MODE_VALIDATE_BUNDLE_HASH | MODE_VALIDATE_SEMANTICS ;
173+ }
174+
175+ private static boolean hasMode (int mode , int has ) {
143176 return (mode & has ) == has ;
144177 }
145178
@@ -160,7 +193,9 @@ private static boolean hasMode(int mode, int has) {
160193 * @return whether the validation criteria were passed or not
161194 * @throws Exception if an error occurred in the persistence layer
162195 */
163- public static Validity validate (Tangle tangle , Hash startTxHash , int validationMode , List <TransactionViewModel > bundleTxs ) throws Exception {
196+ @ VisibleForTesting
197+ Validity validate (Tangle tangle , Hash startTxHash , int validationMode , List <TransactionViewModel > bundleTxs )
198+ throws Exception {
164199 TransactionViewModel startTx = TransactionViewModel .fromHash (tangle , startTxHash );
165200 if (startTx == null || (!hasMode (validationMode , MODE_SKIP_TAIL_TX_EXISTENCE ) &&
166201 (startTx .getCurrentIndex () != 0 || startTx .getValidity () == -1 ))) {
@@ -173,7 +208,8 @@ public static Validity validate(Tangle tangle, Hash startTxHash, int validationM
173208 !hasMode (validationMode , MODE_VALIDATE_SEMANTICS ));
174209
175210 // check the semantics of the bundle: total sum, semantics per tx (current/last index), missing txs, supply
176- Validity bundleSemanticsValidity = validateBundleSemantics (startTx , bundleTxsMapping , bundleTxs , validationMode );
211+ Validity bundleSemanticsValidity = validateBundleSemantics (startTx , bundleTxsMapping , bundleTxs ,
212+ validationMode );
177213 if (hasMode (validationMode , MODE_VALIDATE_SEMANTICS ) && bundleSemanticsValidity != Validity .VALID ) {
178214 return bundleSemanticsValidity ;
179215 }
@@ -191,15 +227,19 @@ public static Validity validate(Tangle tangle, Hash startTxHash, int validationM
191227 }
192228
193229 //verify that the bundle only approves tail txs
194- Validity bundleTailApprovalValidity = validateBundleTailApproval (tangle , bundleTxs );
195- if (hasMode (validationMode , MODE_VALIDATE_TAIL_APPROVAL ) && bundleTailApprovalValidity != Validity .VALID ){
196- return bundleTailApprovalValidity ;
230+ if (hasMode (validationMode , MODE_VALIDATE_TAIL_APPROVAL )) {
231+ Validity bundleTailApprovalValidity = validateBundleTailApproval (tangle , bundleTxs );
232+ if (bundleTailApprovalValidity != Validity .VALID ) {
233+ return bundleTailApprovalValidity ;
234+ }
197235 }
198236
199237 //verify all transactions within the bundle approve via their branch the trunk transaction of the head transaction
200- Validity bundleTransactionsApprovalValidity = validateBundleTransactionsApproval (bundleTxs );
201- if (hasMode (validationMode , MODE_VALIDATE_BUNDLE_TX_APPROVAL ) && bundleTransactionsApprovalValidity != Validity .VALID ){
202- return bundleTransactionsApprovalValidity ;
238+ if (hasMode (validationMode , MODE_VALIDATE_BUNDLE_TX_APPROVAL )) {
239+ Validity bundleTransactionsApprovalValidity = validateBundleTransactionsApproval (bundleTxs );
240+ if (bundleTransactionsApprovalValidity != Validity .VALID ) {
241+ return bundleTransactionsApprovalValidity ;
242+ }
203243 }
204244
205245 // verify the signatures of input transactions
@@ -222,7 +262,8 @@ public static Validity validate(Tangle tangle, Hash startTxHash, int validationM
222262 * @param bundleTxs an empty list which gets filled with the transactions in order of trunk ordering
223263 * @return whether the bundle is semantically valid
224264 */
225- public static Validity validateBundleSemantics (TransactionViewModel startTx , Map <Hash , TransactionViewModel > bundleTxsMapping , List <TransactionViewModel > bundleTxs ) {
265+ public Validity validateBundleSemantics (TransactionViewModel startTx ,
266+ Map <Hash , TransactionViewModel > bundleTxsMapping , List <TransactionViewModel > bundleTxs ) {
226267 return validateBundleSemantics (startTx , bundleTxsMapping , bundleTxs , MODE_VALIDATE_SEMANTICS );
227268 }
228269
@@ -245,7 +286,9 @@ public static Validity validateBundleSemantics(TransactionViewModel startTx, Map
245286 * @param validationMode the used validation mode
246287 * @return whether the bundle is semantically valid
247288 */
248- private static Validity validateBundleSemantics (TransactionViewModel startTx , Map <Hash , TransactionViewModel > bundleTxsMapping , List <TransactionViewModel > bundleTxs , int validationMode ) {
289+ private Validity validateBundleSemantics (TransactionViewModel startTx ,
290+ Map <Hash , TransactionViewModel > bundleTxsMapping , List <TransactionViewModel > bundleTxs ,
291+ int validationMode ) {
249292 TransactionViewModel tvm = startTx ;
250293 final long lastIndex = tvm .lastIndex ();
251294 long bundleValue = 0 ;
@@ -410,7 +453,8 @@ public static boolean isInconsistent(Collection<TransactionViewModel> transactio
410453 * @param bundleTxs list of transactions that are in a bundle.
411454 * @return Whether the bundle tx chain is valid.
412455 */
413- public static Validity validateBundleTransactionsApproval (List <TransactionViewModel > bundleTxs ){
456+ @ VisibleForTesting
457+ Validity validateBundleTransactionsApproval (List <TransactionViewModel > bundleTxs ) {
414458 Hash headTrunkTransactionHash = bundleTxs .get (bundleTxs .size () - 1 ).getTrunkTransactionHash ();
415459 for (int i = 0 ; i < bundleTxs .size () - 1 ; i ++){
416460 if (!bundleTxs .get (i ).getBranchTransactionHash ().equals (headTrunkTransactionHash )){
@@ -426,7 +470,8 @@ public static Validity validateBundleTransactionsApproval(List<TransactionViewMo
426470 * @param bundleTxs The txs in the bundle.
427471 * @return Whether the bundle approves only tails.
428472 */
429- public static Validity validateBundleTailApproval (Tangle tangle , List <TransactionViewModel > bundleTxs ) throws Exception {
473+ @ VisibleForTesting
474+ Validity validateBundleTailApproval (Tangle tangle , List <TransactionViewModel > bundleTxs ) throws Exception {
430475 TransactionViewModel headTx = bundleTxs .get (bundleTxs .size () - 1 );
431476 TransactionViewModel bundleTrunkTvm = headTx .getTrunkTransaction (tangle );
432477 TransactionViewModel bundleBranchTvm = headTx .getBranchTransaction (tangle );
0 commit comments