diff --git a/changelog.txt b/changelog.txt index 032b9a0f75..02023040ed 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,34 @@ +1.8.4 + +Hotfix: Ensure proper creation of solid entrypoints (#1702) + +1.8.3 + +There is an edge case where IRI didn't account for a transaction that was shared between two distinct bundles. +Once it marked it as "counted" in one bundle, it was ignored for the next bundle. This lead to a corrupt ledger state. + +Hotfix: Gyros - take transaction reattachments into account (#1699) + +1.8.2 + +Change: The alpha value is now 0 by default, streamlining the tip selection process which will reduce memory and cpu load. This is done by skipping cumulative weight calculations. (Issue #1567) + +Fix: correct data setting into Hash object (#1589) +Fix: Persistables merge and load functionality +Documentation: Fix broken link of online documentation (#1623) +Documentation: Update README.md (#1617) +Fix: always set the domain field of a neighbor (#1604) +Change: Use mocked SnapshotProvider (#1531) +Fix: Fixes regression introduced through the bundle validator refactor (#1588) +Documentation: Document Persistable and Indexable (#1169) +Feature: Adds progress bar and estimated time until the node is synced (#1575) +Change: Curl improvement +Change: Bundle validator refactor +Change: Use maven assembly plugin to create one jar with all dependencies (#1573) +Documentation: Document Persistence Provider (#1157) +Documentation: Updated links to official documentation (#1560) + + 1.8.1 - Feature: Improved CW Calculation (#1451) diff --git a/pom.xml b/pom.xml index 78401e9f56..bd427b0bfc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.iota iri - 1.8.3 + 1.8.4 IRI IOTA Reference Implementation diff --git a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java index 332c23546e..cbf91fc878 100644 --- a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java +++ b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java @@ -510,7 +510,7 @@ private void persistLocalSnapshot(SnapshotProvider snapshotProvider, Snapshot ne /** *

- * This method determines if a transaction is orphaned. + * This method determines if a transaction is orphaned when none of its approvers is confirmed by a milestone. *

*

* Since there is no hard definition for when a transaction can be considered to be orphaned, we define orphaned in @@ -522,14 +522,14 @@ private void persistLocalSnapshot(SnapshotProvider snapshotProvider, Snapshot ne * a relatively safe way to determine if a subtangle "above" a transaction got orphaned. *

* - * @param tangle Tangle object which acts as a database interface - * @param transaction transaction that shall be checked - * @param referenceTransaction transaction that acts as a judge to the other transaction + * @param tangle Tangle object which acts as a database interface + * @param transaction transaction that shall be checked + * @param referenceTransaction transaction that acts as a judge to the other transaction * @param processedTransactions transactions that were visited already while trying to determine the orphaned status * @return true if the transaction got orphaned and false otherwise * @throws SnapshotException if anything goes wrong while determining the orphaned status */ - private boolean isOrphaned(Tangle tangle, TransactionViewModel transaction, + private boolean isProbablyOrphaned(Tangle tangle, TransactionViewModel transaction, TransactionViewModel referenceTransaction, Set processedTransactions) throws SnapshotException { AtomicBoolean nonOrphanedTransactionFound = new AtomicBoolean(false); @@ -553,29 +553,27 @@ private boolean isOrphaned(Tangle tangle, TransactionViewModel transaction, /** *

- * This method checks if a transaction is a solid entry point for the targetMilestone. - *

- *

- * A transaction is considered a solid entry point if it has non-orphaned approvers. + * We determine whether future milestones will approve {@param transactionHash}. This should aid in determining + * solid entry points. *

*

* To check if the transaction has non-orphaned approvers we first check if any of its approvers got confirmed by a * future milestone, since this is very cheap. If none of them got confirmed by another milestone we do the more - * expensive check from {@link #isOrphaned(Tangle, TransactionViewModel, TransactionViewModel, Set)}. + * expensive check from {@link #isProbablyOrphaned(Tangle, TransactionViewModel, TransactionViewModel, Set)}. *

*

* Since solid entry points have a limited life time and to prevent potential problems due to temporary errors in - * the database, we assume that the checked transaction is a solid entry point if any error occurs while determining - * its status. This is a storage <=> reliability trade off, since the only bad effect of having too many solid entry - * points) is a bigger snapshot file. + * the database, we assume that the checked transaction is not orphaned if any error occurs while determining its + * status, thus adding solid entry points. This is a storage <=> reliability trade off, since the only bad effect of + * having too many solid entry points) is a bigger snapshot file. *

* - * @param tangle Tangle object which acts as a database interface + * @param tangle Tangle object which acts as a database interface * @param transactionHash hash of the transaction that shall be checked * @param targetMilestone milestone that is used as an anchor for our checks * @return true if the transaction is a solid entry point and false otherwise */ - private boolean isSolidEntryPoint(Tangle tangle, Hash transactionHash, MilestoneViewModel targetMilestone) { + private boolean isNotOrphaned(Tangle tangle, Hash transactionHash, MilestoneViewModel targetMilestone) { Set unconfirmedApprovers = new HashSet<>(); try { @@ -592,7 +590,7 @@ private boolean isSolidEntryPoint(Tangle tangle, Hash transactionHash, Milestone Set processedTransactions = new HashSet<>(); TransactionViewModel milestoneTransaction = TransactionViewModel.fromHash(tangle, targetMilestone.getHash()); for (TransactionViewModel unconfirmedApprover : unconfirmedApprovers) { - if (!isOrphaned(tangle, unconfirmedApprover, milestoneTransaction, processedTransactions)) { + if (!isProbablyOrphaned(tangle, unconfirmedApprover, milestoneTransaction, processedTransactions)) { return true; } } @@ -610,34 +608,44 @@ private boolean isSolidEntryPoint(Tangle tangle, Hash transactionHash, Milestone * This method analyzes the old solid entry points and determines if they are still not orphaned. *

*

- * It simply iterates through the old solid entry points and checks them one by one. If an old solid entry point - * is found to still be relevant it is added to the passed in map. + * It simply iterates through the old solid entry points and checks them one by one. If an old solid entry point is + * found to still be relevant it is added to the passed in map. *

- * - * @param tangle Tangle object which acts as a database interface + * + * @see #processNewSolidEntryPoints to understand the definition for solid entry points + * @param tangle Tangle object which acts as a database interface * @param snapshotProvider data provider for the {@link Snapshot}s that are relevant for the node - * @param targetMilestone milestone that is used to generate the solid entry points + * @param targetMilestone milestone that is used to generate the solid entry points * @param solidEntryPoints map that is used to collect the solid entry points */ private void processOldSolidEntryPoints(Tangle tangle, SnapshotProvider snapshotProvider, - MilestoneViewModel targetMilestone, Map solidEntryPoints) { + MilestoneViewModel targetMilestone, Map solidEntryPoints) throws SnapshotException { ProgressLogger progressLogger = new IntervalProgressLogger( "Taking local snapshot [analyzing old solid entry points]", log) .start(snapshotProvider.getInitialSnapshot().getSolidEntryPoints().size()); + try { + Snapshot initialSnapshot = snapshotProvider.getInitialSnapshot(); + Map orgSolidEntryPoints = initialSnapshot.getSolidEntryPoints(); + for (Map.Entry solidPoint : orgSolidEntryPoints.entrySet()) { + Hash hash = solidPoint.getKey(); + int milestoneIndex = solidPoint.getValue(); + if (!Hash.NULL_HASH.equals(hash) + && targetMilestone.index() - milestoneIndex <= SOLID_ENTRY_POINT_LIFETIME + && isNotOrphaned(tangle, hash, targetMilestone)) { + TransactionViewModel tvm = TransactionViewModel.fromHash(tangle, hash); + addTailsToSolidEntryPoints(milestoneIndex, solidEntryPoints, tvm); + solidEntryPoints.put(hash, milestoneIndex); + } - Snapshot initialSnapshot = snapshotProvider.getInitialSnapshot(); - initialSnapshot.getSolidEntryPoints().forEach((hash, milestoneIndex) -> { - if (!Hash.NULL_HASH.equals(hash) && targetMilestone.index() - milestoneIndex <= SOLID_ENTRY_POINT_LIFETIME - && isSolidEntryPoint(tangle, hash, targetMilestone)) { - - solidEntryPoints.put(hash, milestoneIndex); + progressLogger.progress(); } - - progressLogger.progress(); - }); - - progressLogger.finish(); + } catch (Exception e) { + throw new SnapshotException( + "Couldn't process old solid entry point for target milestone " + targetMilestone.index(), e); + } finally { + progressLogger.finish(); + } } /** @@ -645,16 +653,21 @@ && isSolidEntryPoint(tangle, hash, targetMilestone)) { * This method retrieves the new solid entry points of the snapshot reference given by the target milestone. *

*

+ * A transaction is considered a solid entry point if it is a bundle tail that can be traversed down from a + * non-orphaned transaction that was approved by a milestone that is above the last local snapshot. Or if it is a + * bundle tail of a non-orphaned transaction that was approved by a milestone that is above the last local snapshot. + * * It iterates over all unprocessed milestones and analyzes their directly and indirectly approved transactions. - * Every transaction is checked for being a solid entry point and added to the passed in map (if it was found to be - * one). + * Every transaction is checked for being not orphaned and the appropriate SEP is added to {@param SolidEntryPoints} *

- * - * @param tangle Tangle object which acts as a database interface + * + * + * @param tangle Tangle object which acts as a database interface * @param snapshotProvider data provider for the {@link Snapshot}s that are relevant for the node - * @param targetMilestone milestone that is used to generate the solid entry points + * @param targetMilestone milestone that is used to generate the solid entry points * @param solidEntryPoints map that is used to collect the solid entry points * @throws SnapshotException if anything goes wrong while determining the solid entry points + * @see #isNotOrphaned(Tangle, Hash, MilestoneViewModel) */ private void processNewSolidEntryPoints(Tangle tangle, SnapshotProvider snapshotProvider, MilestoneViewModel targetMilestone, Map solidEntryPoints) throws SnapshotException { @@ -675,8 +688,9 @@ private void processNewSolidEntryPoints(Tangle tangle, SnapshotProvider snapshot currentMilestone.getHash(), currentTransaction -> currentTransaction.snapshotIndex() >= currentMilestone.index(), currentTransaction -> { - if (isSolidEntryPoint(tangle, currentTransaction.getHash(), targetMilestone)) { - solidEntryPoints.put(currentTransaction.getHash(), targetMilestone.index()); + if (isNotOrphaned(tangle, currentTransaction.getHash(), targetMilestone)) { + addTailsToSolidEntryPoints(targetMilestone.index(), solidEntryPoints, + currentTransaction); } } ); @@ -696,4 +710,15 @@ private void processNewSolidEntryPoints(Tangle tangle, SnapshotProvider snapshot throw new SnapshotException("could not generate the solid entry points for " + targetMilestone, e); } } + + private void addTailsToSolidEntryPoints(int milestoneIndex, Map solidEntryPoints, + TransactionViewModel currentTransaction) throws TraversalException { + // if tail + if (currentTransaction.getCurrentIndex() == 0) { + solidEntryPoints.put(currentTransaction.getHash(), milestoneIndex); + } else { + Set tails = DAGHelper.get(tangle).findTails(currentTransaction); + tails.forEach(tail -> solidEntryPoints.put(tail, milestoneIndex)); + } + } }