From 33436b4fa06d5a7f476f9b5f39c85d661a5b4661 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 21 Aug 2023 11:48:51 +0200 Subject: [PATCH] Feat: factored out eviction --- pkg/protocol/chainmanagerv1/chain.go | 20 ++++ pkg/protocol/chainmanagerv1/chainmanager.go | 96 +++++-------------- .../chainmanagerv1/chainmanager_test.go | 3 +- .../chainmanagerv1/commitment_metadata.go | 4 - pkg/protocol/chainmanagerv1/eviction.go | 66 +++++++++++++ 5 files changed, 113 insertions(+), 76 deletions(-) create mode 100644 pkg/protocol/chainmanagerv1/eviction.go diff --git a/pkg/protocol/chainmanagerv1/chain.go b/pkg/protocol/chainmanagerv1/chain.go index 061911b6e..cf337dc66 100644 --- a/pkg/protocol/chainmanagerv1/chain.go +++ b/pkg/protocol/chainmanagerv1/chain.go @@ -1,6 +1,8 @@ package chainmanagerv1 import ( + "fmt" + "github.com/iotaledger/hive.go/ds/reactive" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" @@ -34,6 +36,8 @@ type Chain struct { // warpSyncThreshold defines a lower bound for where the warp sync process starts (to not requests slots that we are // about to commit ourselves once we are in sync). warpSyncThreshold reactive.Variable[iotago.SlotIndex] + + cumulativeWeight reactive.Variable[uint64] } func NewChain(forkingPoint *CommitmentMetadata) *Chain { @@ -55,9 +59,25 @@ func NewChain(forkingPoint *CommitmentMetadata) *Chain { return latestCommitmentIndex - WarpSyncOffset }, c.latestCommitmentIndex) + c.cumulativeWeight = reactive.NewDerivedVariable[uint64](func(latestCommitmentIndex iotago.SlotIndex) uint64 { + if latestCommitment, exists := c.commitments.Get(latestCommitmentIndex); exists { + return latestCommitment.CumulativeWeight() + } else { + panic(fmt.Sprintf("latest commitment with index %d does not exist", latestCommitmentIndex)) + } + }, c.latestCommitmentIndex) + return c } +func (c *Chain) ForkingPoint() reactive.Variable[*CommitmentMetadata] { + return c.forkingPoint +} + +func (c *Chain) LatestCommitmentIndex() reactive.Variable[iotago.SlotIndex] { + return c.latestCommitmentIndex +} + func (c *Chain) LatestVerifiedCommitmentIndex() reactive.Variable[iotago.SlotIndex] { return c.latestVerifiedCommitmentIndex } diff --git a/pkg/protocol/chainmanagerv1/chainmanager.go b/pkg/protocol/chainmanagerv1/chainmanager.go index ed440a851..bafa4616e 100644 --- a/pkg/protocol/chainmanagerv1/chainmanager.go +++ b/pkg/protocol/chainmanagerv1/chainmanager.go @@ -23,22 +23,34 @@ type ChainManager struct { commitmentRequester *eventticker.EventTicker[iotago.SlotIndex, iotago.CommitmentID] - slotEvictionEvents *shrinkingmap.ShrinkingMap[iotago.SlotIndex, reactive.Event] - - lastEvictedSlotIndex reactive.Variable[iotago.SlotIndex] + *SlotEviction } func NewChainManager() *ChainManager { return &ChainManager{ - rootCommitment: reactive.NewVariable[*CommitmentMetadata](), - commitmentCreated: event.New1[*CommitmentMetadata](), - cachedCommitments: shrinkingmap.New[iotago.CommitmentID, *promise.Promise[*CommitmentMetadata]](), - commitmentRequester: eventticker.New[iotago.SlotIndex, iotago.CommitmentID](), - slotEvictionEvents: shrinkingmap.New[iotago.SlotIndex, reactive.Event](), - lastEvictedSlotIndex: reactive.NewVariable[iotago.SlotIndex](), + rootCommitment: reactive.NewVariable[*CommitmentMetadata](), + commitmentCreated: event.New1[*CommitmentMetadata](), + cachedCommitments: shrinkingmap.New[iotago.CommitmentID, *promise.Promise[*CommitmentMetadata]](), + commitmentRequester: eventticker.New[iotago.SlotIndex, iotago.CommitmentID](), + + SlotEviction: NewSlotEviction(), } } +func (c *ChainManager) ProcessCommitment(commitment *model.Commitment) (commitmentMetadata *CommitmentMetadata) { + if commitmentRequest, _ := c.requestCommitment(commitment.ID(), commitment.Index(), false, func(resolvedMetadata *CommitmentMetadata) { + commitmentMetadata = resolvedMetadata + }); commitmentRequest != nil { + commitmentRequest.Resolve(NewCommitmentMetadata(commitment)) + } + + return commitmentMetadata +} + +func (c *ChainManager) OnCommitmentCreated(callback func(commitment *CommitmentMetadata)) (unsubscribe func()) { + return c.commitmentCreated.Hook(callback).Unhook +} + func (c *ChainManager) SetRootCommitment(commitment *model.Commitment) (commitmentMetadata *CommitmentMetadata, err error) { c.rootCommitment.Compute(func(currentRoot *CommitmentMetadata) *CommitmentMetadata { if currentRoot != nil { @@ -74,58 +86,6 @@ func (c *ChainManager) SetRootCommitment(commitment *model.Commitment) (commitme return commitmentMetadata, nil } -func (c *ChainManager) ProcessCommitment(commitment *model.Commitment) (commitmentMetadata *CommitmentMetadata) { - if commitmentRequest, _ := c.requestCommitment(commitment.ID(), commitment.Index(), false, func(resolvedMetadata *CommitmentMetadata) { - commitmentMetadata = resolvedMetadata - }); commitmentRequest != nil { - commitmentRequest.Resolve(NewCommitmentMetadata(commitment)) - } - - return commitmentMetadata -} - -func (c *ChainManager) OnCommitmentCreated(callback func(commitment *CommitmentMetadata)) (unsubscribe func()) { - return c.commitmentCreated.Hook(callback).Unhook -} - -func (c *ChainManager) SlotEvictedEvent(index iotago.SlotIndex) reactive.Event { - var slotEvictedEvent reactive.Event - - c.lastEvictedSlotIndex.Compute(func(lastEvictedSlotIndex iotago.SlotIndex) iotago.SlotIndex { - if index > lastEvictedSlotIndex { - slotEvictedEvent, _ = c.slotEvictionEvents.GetOrCreate(index, reactive.NewEvent) - } else { - slotEvictedEvent = defaultTriggeredEvent - } - - return lastEvictedSlotIndex - }) - - return slotEvictedEvent -} - -func (c *ChainManager) Evict(slotIndex iotago.SlotIndex) { - slotEvictedEventsToTrigger := make([]reactive.Event, 0) - - c.lastEvictedSlotIndex.Compute(func(lastEvictedSlotIndex iotago.SlotIndex) iotago.SlotIndex { - if slotIndex <= lastEvictedSlotIndex { - return lastEvictedSlotIndex - } - - for i := lastEvictedSlotIndex + 1; i <= slotIndex; i++ { - if slotEvictedEvent, exists := c.slotEvictionEvents.Get(i); exists { - slotEvictedEventsToTrigger = append(slotEvictedEventsToTrigger, slotEvictedEvent) - } - } - - return slotIndex - }) - - for _, slotEvictedEvent := range slotEvictedEventsToTrigger { - slotEvictedEvent.Trigger() - } -} - func (c *ChainManager) setupCommitment(commitment *CommitmentMetadata, slotEvictedEvent reactive.Event) { c.requestCommitment(commitment.PrevID(), commitment.Index()-1, true, commitment.registerParent) @@ -135,8 +95,8 @@ func (c *ChainManager) setupCommitment(commitment *CommitmentMetadata, slotEvict } func (c *ChainManager) requestCommitment(id iotago.CommitmentID, index iotago.SlotIndex, requestIfMissing bool, optSuccessCallbacks ...func(metadata *CommitmentMetadata)) (commitmentRequest *promise.Promise[*CommitmentMetadata], requestCreated bool) { - slotEvictedEvent := c.SlotEvictedEvent(index) - if slotEvictedEvent.WasTriggered() { + slotEvicted := c.EvictedEvent(index) + if slotEvicted.WasTriggered() { if rootCommitment := c.rootCommitment.Get(); rootCommitment != nil && id == rootCommitment.ID() { for _, successCallback := range optSuccessCallbacks { successCallback(rootCommitment) @@ -158,10 +118,10 @@ func (c *ChainManager) requestCommitment(id iotago.CommitmentID, index iotago.Sl c.commitmentRequester.StopTicker(commitment.ID()) } - c.setupCommitment(commitment, slotEvictedEvent) + c.setupCommitment(commitment, slotEvicted) }) - slotEvictedEvent.OnTrigger(func() { c.cachedCommitments.Delete(id) }) + slotEvicted.OnTrigger(func() { c.cachedCommitments.Delete(id) }) } for _, successCallback := range optSuccessCallbacks { @@ -170,9 +130,3 @@ func (c *ChainManager) requestCommitment(id iotago.CommitmentID, index iotago.Sl return commitmentRequest, requestCreated } - -var defaultTriggeredEvent = reactive.NewEvent() - -func init() { - defaultTriggeredEvent.Trigger() -} diff --git a/pkg/protocol/chainmanagerv1/chainmanager_test.go b/pkg/protocol/chainmanagerv1/chainmanager_test.go index 7faba32a5..df8bcb04a 100644 --- a/pkg/protocol/chainmanagerv1/chainmanager_test.go +++ b/pkg/protocol/chainmanagerv1/chainmanager_test.go @@ -37,7 +37,7 @@ func TestChainManager(t *testing.T) { commitment2.Index()+1, commitment2.ID(), commitment2.RootsID(), - 2, + 3, 2, ), testAPI))) @@ -56,4 +56,5 @@ func TestChainManager(t *testing.T) { require.True(t, commitment3Metadata.BelowSyncThreshold().Get()) require.Equal(t, iotago.SlotIndex(3), commitment3Metadata.Chain().Get().latestCommitmentIndex.Get()) + require.Equal(t, uint64(3), commitment3Metadata.Chain().Get().cumulativeWeight.Get()) } diff --git a/pkg/protocol/chainmanagerv1/commitment_metadata.go b/pkg/protocol/chainmanagerv1/commitment_metadata.go index 5a5ed14f2..af09f9296 100644 --- a/pkg/protocol/chainmanagerv1/commitment_metadata.go +++ b/pkg/protocol/chainmanagerv1/commitment_metadata.go @@ -151,10 +151,6 @@ func (c *CommitmentMetadata) registerChild(newChild *CommitmentMetadata, onSucce c.evicted.OnTrigger(c.chainSuccessor.OnUpdate(onSuccessorUpdated)) } -// inheritChain returns a function that implements the chain inheritance rules. -// -// It must be called whenever the successor of the parent changes as we spawn a new chain for each child that is not the -// direct successor of a parent, and we inherit its chain otherwise. func (c *CommitmentMetadata) inheritChain(parent *CommitmentMetadata) func(*CommitmentMetadata, *CommitmentMetadata) { var spawnedChain *Chain spawnChain := func() { diff --git a/pkg/protocol/chainmanagerv1/eviction.go b/pkg/protocol/chainmanagerv1/eviction.go new file mode 100644 index 000000000..e8a53485d --- /dev/null +++ b/pkg/protocol/chainmanagerv1/eviction.go @@ -0,0 +1,66 @@ +package chainmanagerv1 + +import ( + "github.com/iotaledger/hive.go/ds/reactive" + "github.com/iotaledger/hive.go/ds/shrinkingmap" + iotago "github.com/iotaledger/iota.go/v4" +) + +type SlotEviction struct { + evictionEvents *shrinkingmap.ShrinkingMap[iotago.SlotIndex, reactive.Event] + + lastEvictedSlotIndex reactive.Variable[iotago.SlotIndex] +} + +func NewSlotEviction() *SlotEviction { + return &SlotEviction{ + evictionEvents: shrinkingmap.New[iotago.SlotIndex, reactive.Event](), + lastEvictedSlotIndex: reactive.NewVariable[iotago.SlotIndex](), + } +} + +func (c *SlotEviction) LastEvictedSlotIndex() reactive.Variable[iotago.SlotIndex] { + return c.lastEvictedSlotIndex +} + +func (c *SlotEviction) EvictedEvent(index iotago.SlotIndex) reactive.Event { + slotEvictedEvent := defaultTriggeredEvent + + c.lastEvictedSlotIndex.Compute(func(lastEvictedSlotIndex iotago.SlotIndex) iotago.SlotIndex { + if index > lastEvictedSlotIndex { + slotEvictedEvent, _ = c.evictionEvents.GetOrCreate(index, reactive.NewEvent) + } + + return lastEvictedSlotIndex + }) + + return slotEvictedEvent +} + +func (c *SlotEviction) Evict(slotIndex iotago.SlotIndex) { + slotEvictedEventsToTrigger := make([]reactive.Event, 0) + + c.lastEvictedSlotIndex.Compute(func(lastEvictedSlotIndex iotago.SlotIndex) iotago.SlotIndex { + if slotIndex <= lastEvictedSlotIndex { + return lastEvictedSlotIndex + } + + for i := lastEvictedSlotIndex + 1; i <= slotIndex; i++ { + if slotEvictedEvent, exists := c.evictionEvents.Get(i); exists { + slotEvictedEventsToTrigger = append(slotEvictedEventsToTrigger, slotEvictedEvent) + } + } + + return slotIndex + }) + + for _, slotEvictedEvent := range slotEvictedEventsToTrigger { + slotEvictedEvent.Trigger() + } +} + +var defaultTriggeredEvent = reactive.NewEvent() + +func init() { + defaultTriggeredEvent.Trigger() +}