Skip to content

Commit

Permalink
Merge pull request #919 from iotaledger/debug/tip-selection-genesis-p…
Browse files Browse the repository at this point in the history
…arent

Fix: TipSelection (select correct amount of weak and likedInstead parents)
  • Loading branch information
jonastheis authored Apr 19, 2024
2 parents e45acab + f4ab09b commit d167244
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 57 deletions.
2 changes: 1 addition & 1 deletion components/inx/server_issuance.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

func (s *Server) RequestTips(_ context.Context, req *inx.TipsRequest) (*inx.TipsResponse, error) {
references := deps.Protocol.Engines.Main.Get().TipSelection.SelectTips(int(req.GetCount()))
references := deps.Protocol.Engines.Main.Get().TipSelection.SelectTips(int(req.GetCount()), int(req.GetCount()), int(req.GetCount()))

return &inx.TipsResponse{
StrongTips: inx.NewBlockIds(references[iotago.StrongParentType]),
Expand Down
6 changes: 3 additions & 3 deletions pkg/protocol/engine/blockdag/inmemoryblockdag/blockdag.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,9 @@ func (b *BlockDAG) shouldAppend(modelBlock *model.Block) (shouldAppend bool, err
// canAppendToParents determines if the Block references parents in a non-pruned slot. If a Block is found to violate
// this condition but exists as a missing entry, we mark it as invalid.
func (b *BlockDAG) canAppendToParents(modelBlock *model.Block) (parentsValid bool, err error) {
for _, parentID := range modelBlock.ProtocolBlock().Parents() {
if isBelowRange, isInRange := b.evictionState.BelowOrInActiveRootBlockRange(parentID); isBelowRange || isInRange && !b.evictionState.IsActiveRootBlock(parentID) {
return false, ierrors.Errorf("parent %s of block %s is too old", parentID, modelBlock.ID())
for _, parent := range modelBlock.ProtocolBlock().ParentsWithType() {
if isBelowRange, isInRange := b.evictionState.BelowOrInActiveRootBlockRange(parent.ID); isBelowRange || isInRange && !b.evictionState.IsActiveRootBlock(parent.ID) {
return false, ierrors.Errorf("parent %s with type %s of block %s is too old", parent.ID, parent.Type, modelBlock.ID())
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/protocol/engine/tipselection/tipselection.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
// TipSelection is a component that is used to abstract away the tip selection strategy, used to issuing new blocks.
type TipSelection interface {
// SelectTips selects the tips that should be used as references for a new block.
SelectTips(count int) (references model.ParentReferences)
SelectTips(maxStrongParents int, maxLikedInsteadParents int, maxWeakParents int) (references model.ParentReferences)

// SetAcceptanceTime updates the acceptance time of the TipSelection.
SetAcceptanceTime(acceptanceTime time.Time) (previousTime time.Time)
Expand Down
69 changes: 18 additions & 51 deletions pkg/protocol/engine/tipselection/v1/tip_selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,6 @@ type TipSelection struct {
// acceptanceTime holds the current acceptance time.
acceptanceTime reactive.Variable[time.Time]

// optMaxStrongParents contains the maximum number of strong parents that are allowed.
optMaxStrongParents int

// optMaxLikedInsteadReferences contains the maximum number of liked instead references that are allowed.
optMaxLikedInsteadReferences int

// optMaxLikedInsteadReferencesPerParent contains the maximum number of liked instead references that are allowed
// per parent.
optMaxLikedInsteadReferencesPerParent int

// optMaxWeakReferences contains the maximum number of weak references that are allowed.
optMaxWeakReferences int

// livenessThresholdQueueMutex is used to synchronize access to the liveness threshold queue.
livenessThresholdQueueMutex syncutils.RWMutex

Expand All @@ -70,13 +57,9 @@ type TipSelection struct {
// New is the constructor for the TipSelection.
func New(subModule module.Module, opts ...options.Option[TipSelection]) *TipSelection {
return options.Apply(&TipSelection{
Module: subModule,
livenessThresholdQueue: timed.NewPriorityQueue[tipmanager.TipMetadata](true),
acceptanceTime: reactive.NewVariable[time.Time](monotonicallyIncreasing),
optMaxStrongParents: 8,
optMaxLikedInsteadReferences: 8,
optMaxLikedInsteadReferencesPerParent: 4,
optMaxWeakReferences: 8,
Module: subModule,
livenessThresholdQueue: timed.NewPriorityQueue[tipmanager.TipMetadata](true),
acceptanceTime: reactive.NewVariable[time.Time](monotonicallyIncreasing),
}, opts, func(t *TipSelection) {
t.ShutdownEvent().OnTrigger(func() {
t.StoppedEvent().Trigger()
Expand Down Expand Up @@ -108,35 +91,41 @@ func (t *TipSelection) Construct(tipManager tipmanager.TipManager, spendDAG spen
}

// SelectTips selects the tips that should be used as references for a new block.
func (t *TipSelection) SelectTips(amount int) (references model.ParentReferences) {
func (t *TipSelection) SelectTips(maxStrongParents int, maxLikedInsteadParents int, maxWeakParents int) (references model.ParentReferences) {
references = make(model.ParentReferences)
strongParents := ds.NewSet[iotago.BlockID]()
shallowLikesParents := ds.NewSet[iotago.BlockID]()
maxLikedInsteadReferencesPerParent := maxLikedInsteadParents / 2

_ = t.spendDAG.ReadConsistent(func(_ spenddag.ReadLockedSpendDAG[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank]) error {
previousLikedInsteadConflicts := ds.NewSet[iotago.TransactionID]()

if t.collectReferences(func(tip tipmanager.TipMetadata) {
addedLikedInsteadReferences, updatedLikedInsteadConflicts, err := t.likedInsteadReferences(previousLikedInsteadConflicts, tip)
if err != nil {
switch addedLikedInsteadReferences, updatedLikedInsteadConflicts, err := t.likedInsteadReferences(maxLikedInsteadReferencesPerParent, previousLikedInsteadConflicts, tip); {
case err != nil:
tip.TipPool().Set(tipmanager.WeakTipPool)
} else if len(addedLikedInsteadReferences) <= t.optMaxLikedInsteadReferences-len(references[iotago.ShallowLikeParentType]) {
case len(addedLikedInsteadReferences) <= maxLikedInsteadParents-len(references[iotago.ShallowLikeParentType]):
references[iotago.StrongParentType] = append(references[iotago.StrongParentType], tip.ID())
references[iotago.ShallowLikeParentType] = append(references[iotago.ShallowLikeParentType], addedLikedInsteadReferences...)

shallowLikesParents.AddAll(ds.NewSet(addedLikedInsteadReferences...))
strongParents.Add(tip.ID())

previousLikedInsteadConflicts = updatedLikedInsteadConflicts
default:
t.LogTrace("could not add liked instead references to tip", "tip", tip.ID(), "addedLikedInsteadReferences", addedLikedInsteadReferences)
}
}, func() int {
return len(references[iotago.StrongParentType])
},
// We select one validation tip as a strong parent. This is a security step to ensure that the tangle maintains
// acceptance by stitching together validation blocks.
types.NewTuple[func(optAmount ...int) []tipmanager.TipMetadata, int](t.tipManager.ValidationTips, 2),
types.NewTuple[func(optAmount ...int) []tipmanager.TipMetadata, int](t.tipManager.StrongTips, amount-2),
types.NewTuple[func(optAmount ...int) []tipmanager.TipMetadata, int](t.tipManager.StrongTips, maxStrongParents-2),
); len(references[iotago.StrongParentType]) == 0 {
references[iotago.StrongParentType] = iotago.BlockIDs{t.rootBlock()}
rootBlock := t.rootBlock()
t.LogDebug("no strong parents found, using root block as strong parent", "rootBlock", rootBlock)
references[iotago.StrongParentType] = iotago.BlockIDs{rootBlock}
}

t.collectReferences(func(tip tipmanager.TipMetadata) {
Expand All @@ -147,7 +136,7 @@ func (t *TipSelection) SelectTips(amount int) (references model.ParentReferences
}
}, func() int {
return len(references[iotago.WeakParentType])
}, types.NewTuple[func(optAmount ...int) []tipmanager.TipMetadata, int](t.tipManager.WeakTips, t.optMaxWeakReferences))
}, types.NewTuple[func(optAmount ...int) []tipmanager.TipMetadata, int](t.tipManager.WeakTips, maxWeakParents))

return nil
})
Expand Down Expand Up @@ -189,7 +178,7 @@ func (t *TipSelection) classifyTip(tipMetadata tipmanager.TipMetadata) {
}

// likedInsteadReferences returns the liked instead references that are required to be able to reference the given tip.
func (t *TipSelection) likedInsteadReferences(likedConflicts ds.Set[iotago.TransactionID], tipMetadata tipmanager.TipMetadata) (references []iotago.BlockID, updatedLikedConflicts ds.Set[iotago.TransactionID], err error) {
func (t *TipSelection) likedInsteadReferences(maxLikedInsteadReferencesPerParent int, likedConflicts ds.Set[iotago.TransactionID], tipMetadata tipmanager.TipMetadata) (references []iotago.BlockID, updatedLikedConflicts ds.Set[iotago.TransactionID], err error) {
necessaryReferences := make(map[iotago.TransactionID]iotago.BlockID)
if err = t.spendDAG.LikedInstead(tipMetadata.Block().SpenderIDs()).ForEach(func(likedSpenderID iotago.TransactionID) error {
transactionMetadata, exists := t.transactionMetadata(likedSpenderID)
Expand All @@ -211,7 +200,7 @@ func (t *TipSelection) likedInsteadReferences(likedConflicts ds.Set[iotago.Trans
}
}

if len(references) > t.optMaxLikedInsteadReferencesPerParent {
if len(references) > maxLikedInsteadReferencesPerParent {
return nil, nil, ierrors.Errorf("too many liked instead references (%d) for block %s", len(references), tipMetadata.ID())
}

Expand Down Expand Up @@ -309,28 +298,6 @@ func (t *TipSelection) resetAcceptanceTime() {
})
}

// WithMaxStrongParents is an option for the TipSelection that allows to configure the maximum number of strong parents.
func WithMaxStrongParents(maxStrongParents int) options.Option[TipSelection] {
return func(tipManager *TipSelection) {
tipManager.optMaxStrongParents = maxStrongParents
}
}

// WithMaxLikedInsteadReferences is an option for the TipSelection that allows to configure the maximum number of liked
// instead references.
func WithMaxLikedInsteadReferences(maxLikedInsteadReferences int) options.Option[TipSelection] {
return func(tipManager *TipSelection) {
tipManager.optMaxLikedInsteadReferences = maxLikedInsteadReferences
}
}

// WithMaxWeakReferences is an option for the TipSelection that allows to configure the maximum number of weak references.
func WithMaxWeakReferences(maxWeakReferences int) options.Option[TipSelection] {
return func(tipManager *TipSelection) {
tipManager.optMaxWeakReferences = maxWeakReferences
}
}

// monotonicallyIncreasing returns the maximum of the two given times which is used as a transformation function to make
// the acceptance time of the TipSelection monotonically increasing.
func monotonicallyIncreasing(currentTime time.Time, newTime time.Time) time.Time {
Expand Down
2 changes: 1 addition & 1 deletion pkg/requesthandler/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (r *RequestHandler) BlockWithMetadataFromBlockID(blockID iotago.BlockID) (*
}

func (r *RequestHandler) BlockIssuance() (*api.IssuanceBlockHeaderResponse, error) {
references := r.protocol.Engines.Main.Get().TipSelection.SelectTips(iotago.BasicBlockMaxParents)
references := r.protocol.Engines.Main.Get().TipSelection.SelectTips(iotago.BasicBlockMaxParents, iotago.BasicBlockMaxParents, iotago.BasicBlockMaxParents)
if len(references[iotago.StrongParentType]) == 0 {
return nil, ierrors.WithMessage(echo.ErrServiceUnavailable, "no strong parents available")
}
Expand Down
3 changes: 3 additions & 0 deletions tools/genesis-snapshot/presets/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,22 @@ var (
// use defaults from iota.go.
ProtocolParamsBase = iotago.NewV3SnapshotProtocolParameters(
iotago.WithNetworkOptions("default", iotago.PrefixTestnet),
iotago.WithChainSwitchingThreshold(10),
)

// use defaults from iota.go.
ProtocolParamsDocker = iotago.NewV3SnapshotProtocolParameters(
iotago.WithNetworkOptions(fmt.Sprintf("docker-%d", time.Now().Unix()), iotago.PrefixTestnet),
iotago.WithTimeProviderOptions(5, time.Now().Unix(), 10, 13),
iotago.WithLivenessOptions(10, 15, 3, 6, 8),
iotago.WithChainSwitchingThreshold(10),
)

// use defaults from iota.go.
ProtocolParamsFeature = iotago.NewV3SnapshotProtocolParameters(
iotago.WithNetworkOptions(fmt.Sprintf("feature-%d", time.Now().Unix()), iotago.PrefixTestnet),
iotago.WithTimeProviderOptions(666666, time.Now().Unix()-100_000, 10, 13),
iotago.WithChainSwitchingThreshold(10),
)
)

Expand Down

0 comments on commit d167244

Please sign in to comment.