diff --git a/CHANGELOG.md b/CHANGELOG.md index 92b4a025b..2868bf7b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. +## [0.5.5] - 13.11.2020 + +### Changed + + - traverse the cone of a solid milestone to reapply former milestones if they were missing in the database + +### Fixed + + - update iota.go to fix finalizing of bundles + ## [0.5.4] - 13.11.2020 ### Changed diff --git a/go.mod b/go.mod index a29a67987..270559a3a 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/hashicorp/go-version v1.2.1 // indirect github.com/iotaledger/hive.go v0.0.0-20201016154508-2514b782563a - github.com/iotaledger/iota.go v1.0.0-beta.15.0.20201021142308-f12fd6c4e069 + github.com/iotaledger/iota.go v1.0.0-beta.15.0.20201113171647-14f7a0d87712 github.com/kr/pretty v0.2.1 // indirect github.com/labstack/echo/v4 v4.1.17 github.com/labstack/gommon v0.3.0 diff --git a/go.sum b/go.sum index 69a0fb890..681170a27 100644 --- a/go.sum +++ b/go.sum @@ -291,8 +291,8 @@ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod github.com/iotaledger/hive.go v0.0.0-20201016154508-2514b782563a h1:qIx5/33BqXKQFaLhfq8h3zqBFJMI+49Y/48SAmr5PNk= github.com/iotaledger/hive.go v0.0.0-20201016154508-2514b782563a/go.mod h1:h56kcBj+CYn3UEoCG6RJI5lVdYVJmhmeuIHOUgFCfEg= github.com/iotaledger/iota.go v1.0.0-beta.15/go.mod h1:Rn6v5hLAn8YBaJlRu1ZQdPAgKlshJR1PTeLQaft2778= -github.com/iotaledger/iota.go v1.0.0-beta.15.0.20201021142308-f12fd6c4e069 h1:yZwdtzAKntWsQDcFnwXb8vWDmBWEceSRKTyDVyK/Or8= -github.com/iotaledger/iota.go v1.0.0-beta.15.0.20201021142308-f12fd6c4e069/go.mod h1:Rn6v5hLAn8YBaJlRu1ZQdPAgKlshJR1PTeLQaft2778= +github.com/iotaledger/iota.go v1.0.0-beta.15.0.20201113171647-14f7a0d87712 h1:p6ZjZoXLpv7NlCv6r9uMCg8EZM1ugbaLe8z4m1wPh4s= +github.com/iotaledger/iota.go v1.0.0-beta.15.0.20201113171647-14f7a0d87712/go.mod h1:Rn6v5hLAn8YBaJlRu1ZQdPAgKlshJR1PTeLQaft2778= github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 h1:FUwcHNlEqkqLjLBdCp5PRlCFijNjvcYANOZXzCfXwCM= github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= diff --git a/plugins/cli/plugin.go b/plugins/cli/plugin.go index c07dcd1e6..ac9f42935 100644 --- a/plugins/cli/plugin.go +++ b/plugins/cli/plugin.go @@ -20,7 +20,7 @@ import ( var ( // AppVersion version number - AppVersion = "0.5.4" + AppVersion = "0.5.5" LatestGithubVersion = AppVersion // AppName app code name diff --git a/plugins/tangle/solidifier.go b/plugins/tangle/solidifier.go index 9818f854d..fcec30c94 100644 --- a/plugins/tangle/solidifier.go +++ b/plugins/tangle/solidifier.go @@ -43,8 +43,9 @@ var ( // Index of the first milestone that was sync after node start firstSyncedMilestone = milestone.Index(0) - ErrMilestoneNotFound = errors.New("milestone not found") - ErrDivisionByZero = errors.New("division by zero") + ErrMilestoneNotFound = errors.New("milestone not found") + ErrDivisionByZero = errors.New("division by zero") + ErrMissingMilestoneFound = errors.New("missing milestone found") ) type ConfirmedMilestoneMetric struct { @@ -409,8 +410,19 @@ func solidifyMilestone(newMilestoneIndex milestone.Index, force bool) { // Milestone is stable, but some Milestones are missing in between // => check if they were found, or search for them in the solidified cone cachedClosestNextMs := tangle.FindClosestNextMilestoneOrNil(currentSolidIndex) // bundle +1 - if cachedClosestNextMs.GetBundle().GetMilestoneIndex() == milestoneIndexToSolidify { - log.Panicf("Milestones missing between (%d) and (%d).", currentSolidIndex, cachedClosestNextMs.GetBundle().GetMilestoneIndex()) + cachedClosestNextMsIndex := cachedClosestNextMs.GetBundle().GetMilestoneIndex() + + if cachedClosestNextMsIndex == milestoneIndexToSolidify { + log.Infof("Milestones missing between (%d) and (%d). Search for missing milestones...", currentSolidIndex, cachedClosestNextMsIndex) + + // no Milestones found in between => search an older milestone in the solid cone + if found, err := searchMissingMilestone(currentSolidIndex, cachedClosestNextMsIndex, cachedMsToSolidify.GetBundle().GetTailMetadata(), signalChanMilestoneStopSolidification); !found { // meta pass +1 + if err != nil { + // no milestones found => this should not happen! + log.Panicf("Milestones missing between (%d) and (%d).", currentSolidIndex, cachedClosestNextMsIndex) + } + log.Infof("Aborted search for missing milestones between (%d) and (%d).", currentSolidIndex, cachedClosestNextMsIndex) + } } cachedClosestNextMs.Release() // bundle -1 @@ -478,6 +490,85 @@ func solidifyMilestone(newMilestoneIndex milestone.Index, force bool) { milestoneSolidifierWorkerPool.TrySubmit(milestone.Index(0), false) } +func searchMissingMilestone(solidMilestoneIndex milestone.Index, startMilestoneIndex milestone.Index, cachedMsTailTxMeta *tangle.CachedMetadata, abortSignal chan struct{}) (found bool, err error) { + + defer cachedMsTailTxMeta.Release() // meta -1 + + var milestoneFound bool + + ts := time.Now() + + // search milestones in the cone that are not persisted in the DB yet by traversing the tangle + if err := dag.TraverseApprovees(cachedMsTailTxMeta.GetMetadata().GetTxHash(), + // traversal stops if no more transactions pass the given condition + // Caution: condition func is not in DFS order + func(cachedTxMeta *tangle.CachedMetadata) (bool, error) { // meta +1 + defer cachedTxMeta.Release(true) // meta -1 + + // if the tx is confirmed by an older milestone, there is no need to traverse its approvees + if confirmed, at := cachedTxMeta.GetMetadata().GetConfirmed(); confirmed && (at <= solidMilestoneIndex) { + return false, nil + } + + return true, nil + }, + // consumer + func(cachedTxMeta *tangle.CachedMetadata) error { // meta +1 + defer cachedTxMeta.Release(true) // meta -1 + + cachedTx := tangle.GetCachedTransactionOrNil(cachedTxMeta.GetMetadata().GetTxHash()) // tx +1 + if cachedTx == nil { + return fmt.Errorf("%w hash: %s", tangle.ErrTransactionNotFound, cachedTxMeta.GetMetadata().GetTxHash().Trytes()) + } + defer cachedTx.Release(true) // tx -1 + + if tangle.IsMaybeMilestone(cachedTx.Retain()) { // tx pass +1 + // this tx could belong to a milestone + // => load bundle, and start the milestone check + cachedBndl := tangle.GetCachedBundleOrNil(cachedTxMeta.GetMetadata().GetTxHash()) // bundle +1 + if cachedBndl == nil { + // bundle must be created here + return fmt.Errorf("searchMissingMilestone: bundle not found: %s, TxHash: %s", cachedTx.GetTransaction().Tx.Bundle, cachedTxMeta.GetMetadata().GetTxHash().Trytes()) + } + defer cachedBndl.Release(true) // bundle -1 + + isMilestone, err := tangle.CheckIfMilestone(cachedBndl.GetBundle()) + if err != nil { + log.Infof("searchMissingMilestone: Milestone check failed: %s", err) + } + + if isMilestone { + msIndex := cachedBndl.GetBundle().GetMilestoneIndex() + if (msIndex > solidMilestoneIndex) && (msIndex < startMilestoneIndex) { + // milestone found! + processValidMilestone(cachedBndl.Retain()) // bundle pass +1 + return ErrMissingMilestoneFound // we return this as an error to stop the traverser + } + } + } + + return nil + }, + // called on missing approvees + // return error on missing approvees + nil, + // called on solid entry points + // Ignore solid entry points (snapshot milestone included) + nil, + false, true, abortSignal); err != nil { + if err == tangle.ErrOperationAborted { + return false, nil + } else if err == ErrMissingMilestoneFound { + milestoneFound = true + } else { + return false, err + } + } + + log.Infof("searchMissingMilestone finished, found: %v, total: %v", milestoneFound, time.Since(ts)) + return milestoneFound, nil +} + func getConfirmedMilestoneMetric(cachedMsTailTx *tangle.CachedTransaction, milestoneIndexToSolidify milestone.Index) (*ConfirmedMilestoneMetric, error) { newMilestoneTimestamp := time.Unix(cachedMsTailTx.GetTransaction().GetTimestamp(), 0)