From fa8e94deaab1c368d725c2a1e499f16b13096a97 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 6 Dec 2023 02:58:31 +0100 Subject: [PATCH 01/28] Feat: Protocol Eviction --- pkg/protocol/protocol.go | 5 +- pkg/protocol/protocol_attestations.go | 114 ++++++++++---------- pkg/protocol/protocol_commitments.go | 24 ++++- pkg/protocol/protocol_warp_sync.go | 77 ++++++------- pkg/tests/protocol_engine_switching_test.go | 3 + 5 files changed, 117 insertions(+), 106 deletions(-) diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index 436ef0dcd..fd74b4b3f 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -172,8 +172,9 @@ func (p *Protocol) initSubcomponents(networkEndpoint network.Endpoint) (shutdown // initEviction initializes the eviction of old data when the engine advances and returns a function that shuts it down. func (p *Protocol) initEviction() (shutdown func()) { return p.Commitments.Root.OnUpdate(func(_ *Commitment, rootCommitment *Commitment) { - // TODO: DECIDE ON DATA AVAILABILITY TIMESPAN / EVICTION STRATEGY - // p.Evict(rootCommitment.Slot() - 1) + if rootSlot := rootCommitment.Slot(); rootSlot > 0 { + p.Evict(rootSlot - 1) + } }) } diff --git a/pkg/protocol/protocol_attestations.go b/pkg/protocol/protocol_attestations.go index 16a7f509c..5c5324fd1 100644 --- a/pkg/protocol/protocol_attestations.go +++ b/pkg/protocol/protocol_attestations.go @@ -11,6 +11,7 @@ import ( "github.com/iotaledger/hive.go/log" "github.com/iotaledger/hive.go/runtime/workerpool" "github.com/iotaledger/iota-core/pkg/model" + "github.com/iotaledger/iota-core/pkg/protocol/engine" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/merklehasher" ) @@ -123,85 +124,78 @@ func (a *AttestationsProtocol) ProcessResponse(commitmentModel *model.Commitment // ProcessRequest processes the given attestation request. func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { a.workerPool.Submit(func() { - commitment, err := a.protocol.Commitments.Get(commitmentID, false) - if err != nil { - if !ierrors.Is(err, ErrorCommitmentNotFound) { - a.LogError("failed to load requested commitment", "commitmentID", commitmentID, "fromPeer", from, "err", err) - } else { - a.LogTrace("failed to load requested commitment", "commitmentID", commitmentID, "fromPeer", from, "err", err) - } - - return + if commitment, err := a.protocol.Commitments.Get(commitmentID, false); err == nil { + a.processRequest(commitment.TargetEngine(), commitmentID, from) + } else if ierrors.Is(err, ErrorCommitmentNotFound) { + a.processRequest(a.protocol.Engines.Main.Get(), commitmentID, from) + } else { + a.LogError("failed to load requested commitment", "commitmentID", commitmentID, "fromPeer", from, "err", err) } + }) +} - chain := commitment.Chain.Get() - if chain == nil { - a.LogTrace("request for unsolid commitment", "commitmentID", commitment.LogName(), "fromPeer", from) - - return - } +func (a *AttestationsProtocol) processRequest(targetEngine *engine.Engine, commitmentID iotago.CommitmentID, from peer.ID) { + if targetEngine == nil { + a.LogTrace("request for commitment without engine", "commitmentID", commitmentID, "fromPeer", from) - targetEngine := commitment.TargetEngine() - if targetEngine == nil { - a.LogTrace("request for chain without engine", "chain", chain.LogName(), "fromPeer", from) + return + } - return - } + if targetEngine.Storage.Settings().LatestCommitment().Slot() < commitmentID.Slot() { + a.LogTrace("requested commitment not verified", "commitmentID", commitmentID, "fromPeer", from) - if targetEngine.Storage.Settings().LatestCommitment().Slot() < commitmentID.Slot() { - a.LogTrace("requested commitment not verified", "commitment", commitment.LogName(), "fromPeer", from) + return + } - return + commitmentModel, err := targetEngine.Storage.Commitments().Load(commitmentID.Slot()) + if err != nil { + if !ierrors.Is(err, kvstore.ErrKeyNotFound) { + a.LogError("failed to load requested commitment from engine", "commitmentID", commitmentID, "fromPeer", from, "err", err) + } else { + a.LogTrace("requested commitment not found in engine", "commitmentID", commitmentID, "fromPeer", from) } - commitmentModel, err := targetEngine.Storage.Commitments().Load(commitmentID.Slot()) - if err != nil { - if !ierrors.Is(err, kvstore.ErrKeyNotFound) { - a.LogError("failed to load requested commitment from engine", "commitment", commitment.LogName(), "fromPeer", from, "err", err) - } else { - a.LogTrace("requested commitment not found in engine", "commitment", commitment.LogName(), "fromPeer", from) - } + return + } - return - } + if commitmentModel.ID() != commitmentID { + a.LogTrace("commitment ID mismatch", "requestedCommitment", commitmentID, "loadedCommitment", commitmentModel.ID(), "fromPeer", from) - if commitmentModel.ID() != commitmentID { - a.LogTrace("commitment ID mismatch", "requestedCommitment", commitment.LogName(), "loadedCommitment", commitmentModel.ID(), "fromPeer", from) + return + } - return - } + attestations, err := targetEngine.Attestations.Get(commitmentID.Slot()) + if err != nil { + a.LogDebug("failed to load requested attestations", "commitmentID", commitmentID, "fromPeer", from) - attestations, err := targetEngine.Attestations.Get(commitmentID.Slot()) - if err != nil { - a.LogError("failed to load requested attestations", "commitment", commitment.LogName(), "fromPeer", from) + return + } - return - } + rootsStorage, err := targetEngine.Storage.Roots(commitmentID.Slot()) + if err != nil { + a.LogDebug("failed to load roots storage for requested attestations", "commitmentID", commitmentID, "fromPeer", from) - rootsStorage, err := targetEngine.Storage.Roots(commitmentID.Slot()) - if err != nil { - a.LogError("failed to load roots storage for requested attestations", "commitment", commitment.LogName(), "fromPeer", from) + return + } - return - } + roots, exists, err := rootsStorage.Load(commitmentID) + if err != nil { + a.LogDebug("failed to load roots for requested attestations", "commitmentID", commitmentID, "err", err, "fromPeer", from) - roots, exists, err := rootsStorage.Load(commitmentID) - if err != nil { - a.LogError("failed to load roots for requested attestations", "commitment", commitment.LogName(), "err", err, "fromPeer", from) + return + } else if !exists { + a.LogDebug("roots not found for requested attestations", "commitmentID", commitmentID, "fromPeer", from) - return - } else if !exists { - a.LogTrace("roots not found for requested attestations", "commitment", commitment.LogName(), "fromPeer", from) + return + } - return - } + if err = a.protocol.Network.SendAttestations(commitmentModel, attestations, roots.AttestationsProof(), from); err != nil { + a.LogError("failed to send attestations", "commitmentID", commitmentID, "fromPeer", from, "err", err) - if err = a.protocol.Network.SendAttestations(commitmentModel, attestations, roots.AttestationsProof(), from); err != nil { - a.LogError("failed to send attestations", "commitment", commitment.LogName(), "fromPeer", from, "err", err) - } else { - a.LogTrace("processed request", "commitment", commitment.LogName(), "fromPeer", from) - } - }) + return + } + + a.LogTrace("processed request", "commitmentID", commitmentID, "fromPeer", from) } // Shutdown shuts down the attestation protocol. diff --git a/pkg/protocol/protocol_commitments.go b/pkg/protocol/protocol_commitments.go index 56516d18f..4aea3dd00 100644 --- a/pkg/protocol/protocol_commitments.go +++ b/pkg/protocol/protocol_commitments.go @@ -94,9 +94,29 @@ func (c *CommitmentsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, f c.workerPool.Submit(func() { commitment, err := c.protocol.Commitments.Get(commitmentID) if err != nil { - logLevel := lo.Cond(ierrors.Is(err, ErrorCommitmentNotFound), log.LevelTrace, log.LevelError) + if !ierrors.Is(err, ErrorCommitmentNotFound) { + c.LogError("failed to load commitment for commitment request", "commitmentID", commitmentID, "fromPeer", from, "error", err) - c.Log("failed to load commitment for commitment request", logLevel, "commitmentID", commitmentID, "fromPeer", from, "error", err) + return + } + + slotAPI, slotAPIErr := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID) + if slotAPIErr != nil { + c.LogDebug("failed to load committed slot for commitment request", "commitmentID", commitmentID, "fromPeer", from, "error", slotAPIErr) + + return + } + + commitmentModel, slotAPIErr := slotAPI.Commitment() + if slotAPIErr != nil { + c.LogDebug("failed to load commitment for commitment request", "commitmentID", commitmentID, "fromPeer", from, "error", slotAPIErr) + + return + } + + c.protocol.Network.SendSlotCommitment(commitmentModel, from) + + c.LogTrace("sent commitment", "commitmentID", commitmentID, "toPeer", from) return } diff --git a/pkg/protocol/protocol_warp_sync.go b/pkg/protocol/protocol_warp_sync.go index b74bdcf33..baa382f7e 100644 --- a/pkg/protocol/protocol_warp_sync.go +++ b/pkg/protocol/protocol_warp_sync.go @@ -292,61 +292,54 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo // ProcessRequest processes the given warp sync request. func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { w.workerPool.Submit(func() { - commitment, err := w.protocol.Commitments.Get(commitmentID) - if err != nil { - if !ierrors.Is(err, ErrorCommitmentNotFound) { - w.LogError("failed to load commitment for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) - } else { - w.LogTrace("failed to load commitment for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) - } - - return + if commitment, err := w.protocol.Commitments.Get(commitmentID); err == nil { + w.processRequest(commitment.TargetEngine(), commitmentID, from) + } else if ierrors.Is(err, ErrorCommitmentNotFound) { + w.processRequest(w.protocol.Engines.Main.Get(), commitmentID, from) + } else { + w.LogDebug("failed to load commitment for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) } + }) +} - chain := commitment.Chain.Get() - if chain == nil { - w.LogTrace("warp-sync request for unsolid commitment", "commitment", commitment.LogName(), "fromPeer", from) - - return - } +func (w *WarpSyncProtocol) processRequest(targetEngine *engine.Engine, commitmentID iotago.CommitmentID, from peer.ID) { + if targetEngine == nil { + w.LogTrace("warp-sync request for chain without engine", "commitmentID", commitmentID, "fromPeer", from) - targetEngine := commitment.TargetEngine() - if targetEngine == nil { - w.LogTrace("warp-sync request for chain without engine", "chain", chain.LogName(), "fromPeer", from) + return + } - return - } + committedSlot, err := targetEngine.CommittedSlot(commitmentID) + if err != nil { + w.LogTrace("warp-sync request for uncommitted slot", "commitmentID", commitmentID, "fromPeer", from) - committedSlot, err := targetEngine.CommittedSlot(commitmentID) - if err != nil { - w.LogTrace("warp-sync request for uncommitted slot", "chain", chain.LogName(), "commitment", commitment.LogName(), "fromPeer", from) + return + } - return - } + blockIDsBySlotCommitment, err := committedSlot.BlocksIDsBySlotCommitmentID() + if err != nil { + w.LogTrace("failed to get block ids for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) - blockIDsBySlotCommitment, err := committedSlot.BlocksIDsBySlotCommitmentID() - if err != nil { - w.LogTrace("failed to get block ids for warp-sync request", "chain", chain.LogName(), "commitment", commitment.LogName(), "fromPeer", from, "err", err) + return + } - return - } + roots, err := committedSlot.Roots() + if err != nil { + w.LogTrace("failed to get roots for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) - roots, err := committedSlot.Roots() - if err != nil { - w.LogTrace("failed to get roots for warp-sync request", "chain", chain.LogName(), "commitment", commitment.LogName(), "fromPeer", from, "err", err) + return + } - return - } + transactionIDs, err := committedSlot.TransactionIDs() + if err != nil { + w.LogTrace("failed to get transaction ids for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) - transactionIDs, err := committedSlot.TransactionIDs() - if err != nil { - w.LogTrace("failed to get transaction ids for warp-sync request", "chain", chain.LogName(), "commitment", commitment.LogName(), "fromPeer", from, "err", err) + return + } - return - } + w.protocol.Network.SendWarpSyncResponse(commitmentID, blockIDsBySlotCommitment, roots.TangleProof(), transactionIDs, roots.MutationProof(), from) - w.SendResponse(commitment, blockIDsBySlotCommitment, roots, transactionIDs, from) - }) + w.LogTrace("sent response", "commitmentID", commitmentID, "toPeer", from) } // Shutdown shuts down the warp sync protocol. diff --git a/pkg/tests/protocol_engine_switching_test.go b/pkg/tests/protocol_engine_switching_test.go index 046ffbd99..3ee0855f4 100644 --- a/pkg/tests/protocol_engine_switching_test.go +++ b/pkg/tests/protocol_engine_switching_test.go @@ -9,6 +9,7 @@ import ( "github.com/iotaledger/hive.go/core/eventticker" "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/log" "github.com/iotaledger/hive.go/runtime/module" "github.com/iotaledger/hive.go/runtime/options" "github.com/iotaledger/iota-core/pkg/core/account" @@ -439,6 +440,8 @@ func TestProtocol_EngineSwitching_CommitteeRotation(t *testing.T) { "node3": nodeOpts, }) + node3.Protocol.SetLogLevel(log.LevelTrace) + // Verify that nodes have the expected states after startup. { genesisCommitment := iotago.NewEmptyCommitment(ts.API) From 748ab526966d8422abda5df37cb50362920c55c4 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:54:41 +0100 Subject: [PATCH 02/28] Feat: refactor utility method --- pkg/protocol/protocol_attestations.go | 127 +++++++++++++------------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/pkg/protocol/protocol_attestations.go b/pkg/protocol/protocol_attestations.go index 5c5324fd1..4d48ff0c4 100644 --- a/pkg/protocol/protocol_attestations.go +++ b/pkg/protocol/protocol_attestations.go @@ -125,29 +125,85 @@ func (a *AttestationsProtocol) ProcessResponse(commitmentModel *model.Commitment func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { a.workerPool.Submit(func() { if commitment, err := a.protocol.Commitments.Get(commitmentID, false); err == nil { - a.processRequest(commitment.TargetEngine(), commitmentID, from) + a.processRequestWithEngine(commitment.TargetEngine(), commitmentID, from) } else if ierrors.Is(err, ErrorCommitmentNotFound) { - a.processRequest(a.protocol.Engines.Main.Get(), commitmentID, from) + a.processRequestWithEngine(a.protocol.Engines.Main.Get(), commitmentID, from) } else { a.LogError("failed to load requested commitment", "commitmentID", commitmentID, "fromPeer", from, "err", err) } }) } -func (a *AttestationsProtocol) processRequest(targetEngine *engine.Engine, commitmentID iotago.CommitmentID, from peer.ID) { - if targetEngine == nil { +// Shutdown shuts down the attestation protocol. +func (a *AttestationsProtocol) Shutdown() { + a.ticker.Shutdown() + a.workerPool.Shutdown().ShutdownComplete.Wait() +} + +// setupCommitmentVerifier sets up the commitment verifier for the given chain. +func (a *AttestationsProtocol) setupCommitmentVerifier(chain *Chain) (shutdown func()) { + forkingPoint := chain.ForkingPoint.Get() + if forkingPoint == nil { + a.LogError("failed to retrieve forking point", "chain", chain.LogName()) + + return nil + } + + if forkingPoint.IsRoot.Get() { + a.LogTrace("skipping commitment verifier setup for main chain", "chain", chain.LogName()) + + return nil + } + + parentOfForkingPoint := forkingPoint.Parent.Get() + if parentOfForkingPoint == nil { + a.LogError("failed to retrieve parent of forking point", "chain", chain.LogName()) + + return nil + } + + a.commitmentVerifiers.GetOrCreate(forkingPoint.ID(), func() (commitmentVerifier *CommitmentVerifier) { + commitmentVerifier, err := newCommitmentVerifier(forkingPoint.Chain.Get().LatestEngine(), parentOfForkingPoint.Commitment) + if err != nil { + a.LogError("failed to create commitment verifier", "chain", chain.LogName(), "error", err) + } + + return commitmentVerifier + }) + + return func() { + a.commitmentVerifiers.Delete(forkingPoint.ID()) + } +} + +// sendRequest sends an attestation request for the given commitment ID. +func (a *AttestationsProtocol) sendRequest(commitmentID iotago.CommitmentID) { + a.workerPool.Submit(func() { + if commitment, err := a.protocol.Commitments.Get(commitmentID, false); err == nil { + a.protocol.Network.RequestAttestations(commitmentID) + + a.LogDebug("request", "commitment", commitment.LogName()) + } else { + a.LogError("failed to load commitment", "commitmentID", commitmentID, "err", err) + } + }) +} + +// processRequestWithEngine processes an attestation request by sourcing the requested commitment from the given engine. +func (a *AttestationsProtocol) processRequestWithEngine(engineInstance *engine.Engine, commitmentID iotago.CommitmentID, from peer.ID) { + if engineInstance == nil { a.LogTrace("request for commitment without engine", "commitmentID", commitmentID, "fromPeer", from) return } - if targetEngine.Storage.Settings().LatestCommitment().Slot() < commitmentID.Slot() { + if engineInstance.Storage.Settings().LatestCommitment().Slot() < commitmentID.Slot() { a.LogTrace("requested commitment not verified", "commitmentID", commitmentID, "fromPeer", from) return } - commitmentModel, err := targetEngine.Storage.Commitments().Load(commitmentID.Slot()) + commitmentModel, err := engineInstance.Storage.Commitments().Load(commitmentID.Slot()) if err != nil { if !ierrors.Is(err, kvstore.ErrKeyNotFound) { a.LogError("failed to load requested commitment from engine", "commitmentID", commitmentID, "fromPeer", from, "err", err) @@ -164,14 +220,14 @@ func (a *AttestationsProtocol) processRequest(targetEngine *engine.Engine, commi return } - attestations, err := targetEngine.Attestations.Get(commitmentID.Slot()) + attestations, err := engineInstance.Attestations.Get(commitmentID.Slot()) if err != nil { a.LogDebug("failed to load requested attestations", "commitmentID", commitmentID, "fromPeer", from) return } - rootsStorage, err := targetEngine.Storage.Roots(commitmentID.Slot()) + rootsStorage, err := engineInstance.Storage.Roots(commitmentID.Slot()) if err != nil { a.LogDebug("failed to load roots storage for requested attestations", "commitmentID", commitmentID, "fromPeer", from) @@ -197,58 +253,3 @@ func (a *AttestationsProtocol) processRequest(targetEngine *engine.Engine, commi a.LogTrace("processed request", "commitmentID", commitmentID, "fromPeer", from) } - -// Shutdown shuts down the attestation protocol. -func (a *AttestationsProtocol) Shutdown() { - a.ticker.Shutdown() - a.workerPool.Shutdown().ShutdownComplete.Wait() -} - -// setupCommitmentVerifier sets up the commitment verifier for the given chain. -func (a *AttestationsProtocol) setupCommitmentVerifier(chain *Chain) (shutdown func()) { - forkingPoint := chain.ForkingPoint.Get() - if forkingPoint == nil { - a.LogError("failed to retrieve forking point", "chain", chain.LogName()) - - return nil - } - - if forkingPoint.IsRoot.Get() { - a.LogTrace("skipping commitment verifier setup for main chain", "chain", chain.LogName()) - - return nil - } - - parentOfForkingPoint := forkingPoint.Parent.Get() - if parentOfForkingPoint == nil { - a.LogError("failed to retrieve parent of forking point", "chain", chain.LogName()) - - return nil - } - - a.commitmentVerifiers.GetOrCreate(forkingPoint.ID(), func() (commitmentVerifier *CommitmentVerifier) { - commitmentVerifier, err := newCommitmentVerifier(forkingPoint.Chain.Get().LatestEngine(), parentOfForkingPoint.Commitment) - if err != nil { - a.LogError("failed to create commitment verifier", "chain", chain.LogName(), "error", err) - } - - return commitmentVerifier - }) - - return func() { - a.commitmentVerifiers.Delete(forkingPoint.ID()) - } -} - -// sendRequest sends an attestation request for the given commitment ID. -func (a *AttestationsProtocol) sendRequest(commitmentID iotago.CommitmentID) { - a.workerPool.Submit(func() { - if commitment, err := a.protocol.Commitments.Get(commitmentID, false); err == nil { - a.protocol.Network.RequestAttestations(commitmentID) - - a.LogDebug("request", "commitment", commitment.LogName()) - } else { - a.LogError("failed to load commitment", "commitmentID", commitmentID, "err", err) - } - }) -} From 27eaca809a7358423bfd470b6f1143ba28913017 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:17:09 +0100 Subject: [PATCH 03/28] Refactor: commented utility methods --- pkg/protocol/protocol_warp_sync.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pkg/protocol/protocol_warp_sync.go b/pkg/protocol/protocol_warp_sync.go index baa382f7e..16bed81cc 100644 --- a/pkg/protocol/protocol_warp_sync.go +++ b/pkg/protocol/protocol_warp_sync.go @@ -293,16 +293,23 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { w.workerPool.Submit(func() { if commitment, err := w.protocol.Commitments.Get(commitmentID); err == nil { - w.processRequest(commitment.TargetEngine(), commitmentID, from) + w.processRequestWithEngine(commitment.TargetEngine(), commitmentID, from) } else if ierrors.Is(err, ErrorCommitmentNotFound) { - w.processRequest(w.protocol.Engines.Main.Get(), commitmentID, from) + w.processRequestWithEngine(w.protocol.Engines.Main.Get(), commitmentID, from) } else { w.LogDebug("failed to load commitment for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) } }) } -func (w *WarpSyncProtocol) processRequest(targetEngine *engine.Engine, commitmentID iotago.CommitmentID, from peer.ID) { +// Shutdown shuts down the warp sync protocol. +func (w *WarpSyncProtocol) Shutdown() { + w.ticker.Shutdown() + w.workerPool.Shutdown().ShutdownComplete.Wait() +} + +// processRequestWithEngine processes a warp-sync request by sourcing the requested commitment from the given engine. +func (w *WarpSyncProtocol) processRequestWithEngine(targetEngine *engine.Engine, commitmentID iotago.CommitmentID, from peer.ID) { if targetEngine == nil { w.LogTrace("warp-sync request for chain without engine", "commitmentID", commitmentID, "fromPeer", from) @@ -341,9 +348,3 @@ func (w *WarpSyncProtocol) processRequest(targetEngine *engine.Engine, commitmen w.LogTrace("sent response", "commitmentID", commitmentID, "toPeer", from) } - -// Shutdown shuts down the warp sync protocol. -func (w *WarpSyncProtocol) Shutdown() { - w.ticker.Shutdown() - w.workerPool.Shutdown().ShutdownComplete.Wait() -} From 3b5a32832fa38fea2a4a6d106a18a34f8a1a026c Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 02:04:17 +0100 Subject: [PATCH 04/28] Feat: started refactoring process response logic --- pkg/protocol/protocol_warp_sync.go | 80 ++++++++++++++---------------- pkg/protocol/utils.go | 16 ++++++ 2 files changed, 52 insertions(+), 44 deletions(-) create mode 100644 pkg/protocol/utils.go diff --git a/pkg/protocol/protocol_warp_sync.go b/pkg/protocol/protocol_warp_sync.go index 16bed81cc..620ebdedf 100644 --- a/pkg/protocol/protocol_warp_sync.go +++ b/pkg/protocol/protocol_warp_sync.go @@ -291,60 +291,52 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo // ProcessRequest processes the given warp sync request. func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - w.workerPool.Submit(func() { - if commitment, err := w.protocol.Commitments.Get(commitmentID); err == nil { - w.processRequestWithEngine(commitment.TargetEngine(), commitmentID, from) - } else if ierrors.Is(err, ErrorCommitmentNotFound) { - w.processRequestWithEngine(w.protocol.Engines.Main.Get(), commitmentID, from) - } else { - w.LogDebug("failed to load commitment for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) + processWithEngine := func(targetEngine *engine.Engine) (err error) { + if targetEngine == nil { + return ierrors.New("no engine found") } - }) -} - -// Shutdown shuts down the warp sync protocol. -func (w *WarpSyncProtocol) Shutdown() { - w.ticker.Shutdown() - w.workerPool.Shutdown().ShutdownComplete.Wait() -} - -// processRequestWithEngine processes a warp-sync request by sourcing the requested commitment from the given engine. -func (w *WarpSyncProtocol) processRequestWithEngine(targetEngine *engine.Engine, commitmentID iotago.CommitmentID, from peer.ID) { - if targetEngine == nil { - w.LogTrace("warp-sync request for chain without engine", "commitmentID", commitmentID, "fromPeer", from) - - return - } - committedSlot, err := targetEngine.CommittedSlot(commitmentID) - if err != nil { - w.LogTrace("warp-sync request for uncommitted slot", "commitmentID", commitmentID, "fromPeer", from) + committedSlot, err := targetEngine.CommittedSlot(commitmentID) + if err != nil { + return ierrors.Wrap(err, "failed to load committed slot") + } - return - } + blockIDsBySlotCommitment, err := committedSlot.BlocksIDsBySlotCommitmentID() + if err != nil { + return ierrors.Wrap(err, "failed to get block ids") + } - blockIDsBySlotCommitment, err := committedSlot.BlocksIDsBySlotCommitmentID() - if err != nil { - w.LogTrace("failed to get block ids for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) + roots, err := committedSlot.Roots() + if err != nil { + return ierrors.Wrap(err, "failed to get roots") + } - return - } + transactionIDs, err := committedSlot.TransactionIDs() + if err != nil { + return ierrors.Wrap(err, "failed to get transaction ids") + } - roots, err := committedSlot.Roots() - if err != nil { - w.LogTrace("failed to get roots for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) + w.protocol.Network.SendWarpSyncResponse(commitmentID, blockIDsBySlotCommitment, roots.TangleProof(), transactionIDs, roots.MutationProof(), from) - return + return nil } - transactionIDs, err := committedSlot.TransactionIDs() - if err != nil { - w.LogTrace("failed to get transaction ids for warp-sync request", "commitmentID", commitmentID, "fromPeer", from, "err", err) + processRequest(w.workerPool, func() error { + commitment, err := w.protocol.Commitments.Get(commitmentID) + if err == nil { + return processWithEngine(commitment.TargetEngine()) + } - return - } + if ierrors.Is(err, ErrorCommitmentNotFound) { + return processWithEngine(w.protocol.Engines.Main.Get()) + } - w.protocol.Network.SendWarpSyncResponse(commitmentID, blockIDsBySlotCommitment, roots.TangleProof(), transactionIDs, roots.MutationProof(), from) + return err + }, w, "commitmentID", commitmentID, "fromPeer", from) +} - w.LogTrace("sent response", "commitmentID", commitmentID, "toPeer", from) +// Shutdown shuts down the warp sync protocol. +func (w *WarpSyncProtocol) Shutdown() { + w.ticker.Shutdown() + w.workerPool.Shutdown().ShutdownComplete.Wait() } diff --git a/pkg/protocol/utils.go b/pkg/protocol/utils.go new file mode 100644 index 000000000..54b9e2150 --- /dev/null +++ b/pkg/protocol/utils.go @@ -0,0 +1,16 @@ +package protocol + +import ( + "github.com/iotaledger/hive.go/log" + "github.com/iotaledger/hive.go/runtime/workerpool" +) + +func processRequest(workerPool *workerpool.WorkerPool, requestFunc func() error, logger log.Logger, loggerArgs ...any) { + workerPool.Submit(func() { + if err := requestFunc(); err != nil { + logger.LogDebug("failed to answer request", append(loggerArgs, "err", err)...) + } else { + logger.LogTrace("answered request", loggerArgs...) + } + }) +} From 0b0327fe5f522194fca7400f40f458335f816e1a Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 03:21:34 +0100 Subject: [PATCH 05/28] Refactor: started refactoring --- pkg/protocol/protocol.go | 31 ++++++++ pkg/protocol/protocol_attestations.go | 104 ++++++++------------------ pkg/protocol/protocol_warp_sync.go | 21 +----- pkg/protocol/utils.go | 16 ---- 4 files changed, 63 insertions(+), 109 deletions(-) delete mode 100644 pkg/protocol/utils.go diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index fd74b4b3f..006b31c55 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -216,3 +216,34 @@ func (p *Protocol) waitInitialized() { waitInitialized.Wait() } + +// processCommitmentRequest is a generic utility function that implements common logic for processing requests from the network. +func (p *Protocol) processCommitmentRequest(workerPool *workerpool.WorkerPool, commitmentID iotago.CommitmentID, handleRequest func(targetEngine *engine.Engine) error, logger log.Logger, loggerArgs ...any) { + submitRequest := func(processRequest func() error) { + workerPool.Submit(func() { + if err := processRequest(); err != nil { + logger.LogDebug("failed to answer request", append(loggerArgs, "commitmentID", commitmentID, "err", err)...) + } else { + logger.LogTrace("answered request", append(loggerArgs, "commitmentID", commitmentID)...) + } + }) + } + + submitRequest(func() error { + var targetEngine *engine.Engine + + if commitment, err := p.Commitments.Get(commitmentID, false); err == nil { + targetEngine = commitment.TargetEngine() + } else if ierrors.Is(err, ErrorCommitmentNotFound) { + targetEngine = p.Engines.Main.Get() + } else { + return err + } + + if targetEngine == nil { + return ierrors.New("no target engine found") + } + + return handleRequest(targetEngine) + }) +} diff --git a/pkg/protocol/protocol_attestations.go b/pkg/protocol/protocol_attestations.go index 4d48ff0c4..f35abf712 100644 --- a/pkg/protocol/protocol_attestations.go +++ b/pkg/protocol/protocol_attestations.go @@ -6,7 +6,6 @@ import ( "github.com/iotaledger/hive.go/core/eventticker" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/log" "github.com/iotaledger/hive.go/runtime/workerpool" @@ -123,15 +122,37 @@ func (a *AttestationsProtocol) ProcessResponse(commitmentModel *model.Commitment // ProcessRequest processes the given attestation request. func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - a.workerPool.Submit(func() { - if commitment, err := a.protocol.Commitments.Get(commitmentID, false); err == nil { - a.processRequestWithEngine(commitment.TargetEngine(), commitmentID, from) - } else if ierrors.Is(err, ErrorCommitmentNotFound) { - a.processRequestWithEngine(a.protocol.Engines.Main.Get(), commitmentID, from) - } else { - a.LogError("failed to load requested commitment", "commitmentID", commitmentID, "fromPeer", from, "err", err) + a.protocol.processCommitmentRequest(a.workerPool, commitmentID, func(engineInstance *engine.Engine) error { + if engineInstance.Storage.Settings().LatestCommitment().Slot() < commitmentID.Slot() { + return ierrors.New("not committed yet") } - }) + + commitmentModel, err := engineInstance.Storage.Commitments().Load(commitmentID.Slot()) + if err != nil { + return ierrors.Wrapf(err, "failed to load commitment") + } else if commitmentModel.ID() != commitmentID { + return ierrors.Errorf("commitment ID mismatch: %s != %s", commitmentModel.ID(), commitmentID) + } + + attestations, err := engineInstance.Attestations.Get(commitmentID.Slot()) + if err != nil { + return ierrors.Wrapf(err, "failed to load attestations") + } + + rootsStorage, err := engineInstance.Storage.Roots(commitmentID.Slot()) + if err != nil { + return ierrors.Wrapf(err, "failed to load roots storage") + } + + roots, exists, err := rootsStorage.Load(commitmentID) + if err != nil { + return ierrors.Wrapf(err, "failed to load roots") + } else if !exists { + return ierrors.New("roots not found") + } + + return a.protocol.Network.SendAttestations(commitmentModel, attestations, roots.AttestationsProof(), from) + }, a, "fromPeer", from) } // Shutdown shuts down the attestation protocol. @@ -188,68 +209,3 @@ func (a *AttestationsProtocol) sendRequest(commitmentID iotago.CommitmentID) { } }) } - -// processRequestWithEngine processes an attestation request by sourcing the requested commitment from the given engine. -func (a *AttestationsProtocol) processRequestWithEngine(engineInstance *engine.Engine, commitmentID iotago.CommitmentID, from peer.ID) { - if engineInstance == nil { - a.LogTrace("request for commitment without engine", "commitmentID", commitmentID, "fromPeer", from) - - return - } - - if engineInstance.Storage.Settings().LatestCommitment().Slot() < commitmentID.Slot() { - a.LogTrace("requested commitment not verified", "commitmentID", commitmentID, "fromPeer", from) - - return - } - - commitmentModel, err := engineInstance.Storage.Commitments().Load(commitmentID.Slot()) - if err != nil { - if !ierrors.Is(err, kvstore.ErrKeyNotFound) { - a.LogError("failed to load requested commitment from engine", "commitmentID", commitmentID, "fromPeer", from, "err", err) - } else { - a.LogTrace("requested commitment not found in engine", "commitmentID", commitmentID, "fromPeer", from) - } - - return - } - - if commitmentModel.ID() != commitmentID { - a.LogTrace("commitment ID mismatch", "requestedCommitment", commitmentID, "loadedCommitment", commitmentModel.ID(), "fromPeer", from) - - return - } - - attestations, err := engineInstance.Attestations.Get(commitmentID.Slot()) - if err != nil { - a.LogDebug("failed to load requested attestations", "commitmentID", commitmentID, "fromPeer", from) - - return - } - - rootsStorage, err := engineInstance.Storage.Roots(commitmentID.Slot()) - if err != nil { - a.LogDebug("failed to load roots storage for requested attestations", "commitmentID", commitmentID, "fromPeer", from) - - return - } - - roots, exists, err := rootsStorage.Load(commitmentID) - if err != nil { - a.LogDebug("failed to load roots for requested attestations", "commitmentID", commitmentID, "err", err, "fromPeer", from) - - return - } else if !exists { - a.LogDebug("roots not found for requested attestations", "commitmentID", commitmentID, "fromPeer", from) - - return - } - - if err = a.protocol.Network.SendAttestations(commitmentModel, attestations, roots.AttestationsProof(), from); err != nil { - a.LogError("failed to send attestations", "commitmentID", commitmentID, "fromPeer", from, "err", err) - - return - } - - a.LogTrace("processed request", "commitmentID", commitmentID, "fromPeer", from) -} diff --git a/pkg/protocol/protocol_warp_sync.go b/pkg/protocol/protocol_warp_sync.go index 620ebdedf..e70245ec0 100644 --- a/pkg/protocol/protocol_warp_sync.go +++ b/pkg/protocol/protocol_warp_sync.go @@ -291,11 +291,7 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo // ProcessRequest processes the given warp sync request. func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - processWithEngine := func(targetEngine *engine.Engine) (err error) { - if targetEngine == nil { - return ierrors.New("no engine found") - } - + w.protocol.processCommitmentRequest(w.workerPool, commitmentID, func(targetEngine *engine.Engine) (err error) { committedSlot, err := targetEngine.CommittedSlot(commitmentID) if err != nil { return ierrors.Wrap(err, "failed to load committed slot") @@ -319,20 +315,7 @@ func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from w.protocol.Network.SendWarpSyncResponse(commitmentID, blockIDsBySlotCommitment, roots.TangleProof(), transactionIDs, roots.MutationProof(), from) return nil - } - - processRequest(w.workerPool, func() error { - commitment, err := w.protocol.Commitments.Get(commitmentID) - if err == nil { - return processWithEngine(commitment.TargetEngine()) - } - - if ierrors.Is(err, ErrorCommitmentNotFound) { - return processWithEngine(w.protocol.Engines.Main.Get()) - } - - return err - }, w, "commitmentID", commitmentID, "fromPeer", from) + }, w, "fromPeer", from) } // Shutdown shuts down the warp sync protocol. diff --git a/pkg/protocol/utils.go b/pkg/protocol/utils.go deleted file mode 100644 index 54b9e2150..000000000 --- a/pkg/protocol/utils.go +++ /dev/null @@ -1,16 +0,0 @@ -package protocol - -import ( - "github.com/iotaledger/hive.go/log" - "github.com/iotaledger/hive.go/runtime/workerpool" -) - -func processRequest(workerPool *workerpool.WorkerPool, requestFunc func() error, logger log.Logger, loggerArgs ...any) { - workerPool.Submit(func() { - if err := requestFunc(); err != nil { - logger.LogDebug("failed to answer request", append(loggerArgs, "err", err)...) - } else { - logger.LogTrace("answered request", loggerArgs...) - } - }) -} From c8a61a37242963457d74e53349712ea8db9772ff Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 03:28:42 +0100 Subject: [PATCH 06/28] Refactor: refactored more code --- pkg/protocol/protocol.go | 26 ++++++++++++++------------ pkg/protocol/protocol_warp_sync.go | 4 ++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index 006b31c55..090a774e3 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -217,19 +217,10 @@ func (p *Protocol) waitInitialized() { waitInitialized.Wait() } -// processCommitmentRequest is a generic utility function that implements common logic for processing requests from the network. +// processCommitmentRequest is a generic utility function that implements common logic for processing commitment +// requests from the network. func (p *Protocol) processCommitmentRequest(workerPool *workerpool.WorkerPool, commitmentID iotago.CommitmentID, handleRequest func(targetEngine *engine.Engine) error, logger log.Logger, loggerArgs ...any) { - submitRequest := func(processRequest func() error) { - workerPool.Submit(func() { - if err := processRequest(); err != nil { - logger.LogDebug("failed to answer request", append(loggerArgs, "commitmentID", commitmentID, "err", err)...) - } else { - logger.LogTrace("answered request", append(loggerArgs, "commitmentID", commitmentID)...) - } - }) - } - - submitRequest(func() error { + p.processRequest(workerPool, func() error { var targetEngine *engine.Engine if commitment, err := p.Commitments.Get(commitmentID, false); err == nil { @@ -245,5 +236,16 @@ func (p *Protocol) processCommitmentRequest(workerPool *workerpool.WorkerPool, c } return handleRequest(targetEngine) + }, logger, append(loggerArgs, "commitmentID", commitmentID)...) +} + +// processRequest is a generic utility function that implements common logic for processing requests from the network. +func (p *Protocol) processRequest(workerPool *workerpool.WorkerPool, processRequest func() error, logger log.Logger, loggerArgs ...any) { + workerPool.Submit(func() { + if err := processRequest(); err != nil { + logger.LogDebug("failed to answer request", append(loggerArgs, "err", err)...) + } else { + logger.LogTrace("answered request", loggerArgs...) + } }) } diff --git a/pkg/protocol/protocol_warp_sync.go b/pkg/protocol/protocol_warp_sync.go index e70245ec0..27416ef30 100644 --- a/pkg/protocol/protocol_warp_sync.go +++ b/pkg/protocol/protocol_warp_sync.go @@ -291,8 +291,8 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo // ProcessRequest processes the given warp sync request. func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - w.protocol.processCommitmentRequest(w.workerPool, commitmentID, func(targetEngine *engine.Engine) (err error) { - committedSlot, err := targetEngine.CommittedSlot(commitmentID) + w.protocol.processCommitmentRequest(w.workerPool, commitmentID, func(engineInstance *engine.Engine) (err error) { + committedSlot, err := engineInstance.CommittedSlot(commitmentID) if err != nil { return ierrors.Wrap(err, "failed to load committed slot") } From dd90a0fc1d921a30bafa6205fd86894af057ba84 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 03:30:56 +0100 Subject: [PATCH 07/28] Refactor: removed logging --- pkg/tests/protocol_engine_switching_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/tests/protocol_engine_switching_test.go b/pkg/tests/protocol_engine_switching_test.go index 3ee0855f4..046ffbd99 100644 --- a/pkg/tests/protocol_engine_switching_test.go +++ b/pkg/tests/protocol_engine_switching_test.go @@ -9,7 +9,6 @@ import ( "github.com/iotaledger/hive.go/core/eventticker" "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/log" "github.com/iotaledger/hive.go/runtime/module" "github.com/iotaledger/hive.go/runtime/options" "github.com/iotaledger/iota-core/pkg/core/account" @@ -440,8 +439,6 @@ func TestProtocol_EngineSwitching_CommitteeRotation(t *testing.T) { "node3": nodeOpts, }) - node3.Protocol.SetLogLevel(log.LevelTrace) - // Verify that nodes have the expected states after startup. { genesisCommitment := iotago.NewEmptyCommitment(ts.API) From 87f59a1c19dcc83f65363257b7ca2202b4a0cfce Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:32:17 +0100 Subject: [PATCH 08/28] Refactor: cleaned up code --- pkg/protocol/commitments.go | 14 ++++++++++++ pkg/protocol/protocol.go | 33 --------------------------- pkg/protocol/protocol_attestations.go | 10 +++++--- pkg/protocol/protocol_warp_sync.go | 9 ++++++-- pkg/protocol/utils.go | 17 ++++++++++++++ 5 files changed, 45 insertions(+), 38 deletions(-) create mode 100644 pkg/protocol/utils.go diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 857b1c00d..0c4084ff0 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -69,6 +69,20 @@ func (c *Commitments) Get(commitmentID iotago.CommitmentID, requestIfMissing ... return cachedRequest.Result(), cachedRequest.Err() } +// TargetEngine returns the engine that manages the data for the given commitment (or nil if no engine was found while +// commitment IDs below the Root are resolved against the main engine). +func (c *Commitments) TargetEngine(commitmentID iotago.CommitmentID) (targetEngine *engine.Engine) { + if commitmentID.Slot() <= c.Root.Get().Slot() { + targetEngine = c.protocol.Engines.Main.Get() + } else if commitment, err := c.Get(commitmentID); err == nil { + targetEngine = commitment.TargetEngine() + } else if !ierrors.Is(err, ErrorCommitmentNotFound) { + c.LogDebug("failed to retrieve commitment", "commitmentID", commitmentID, "err", err) + } + + return targetEngine +} + // initLogger initializes the logger for this component. func (c *Commitments) initLogger() (shutdown func()) { c.Logger, shutdown = c.protocol.NewChildLogger("Commitments") diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index 090a774e3..fd74b4b3f 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -216,36 +216,3 @@ func (p *Protocol) waitInitialized() { waitInitialized.Wait() } - -// processCommitmentRequest is a generic utility function that implements common logic for processing commitment -// requests from the network. -func (p *Protocol) processCommitmentRequest(workerPool *workerpool.WorkerPool, commitmentID iotago.CommitmentID, handleRequest func(targetEngine *engine.Engine) error, logger log.Logger, loggerArgs ...any) { - p.processRequest(workerPool, func() error { - var targetEngine *engine.Engine - - if commitment, err := p.Commitments.Get(commitmentID, false); err == nil { - targetEngine = commitment.TargetEngine() - } else if ierrors.Is(err, ErrorCommitmentNotFound) { - targetEngine = p.Engines.Main.Get() - } else { - return err - } - - if targetEngine == nil { - return ierrors.New("no target engine found") - } - - return handleRequest(targetEngine) - }, logger, append(loggerArgs, "commitmentID", commitmentID)...) -} - -// processRequest is a generic utility function that implements common logic for processing requests from the network. -func (p *Protocol) processRequest(workerPool *workerpool.WorkerPool, processRequest func() error, logger log.Logger, loggerArgs ...any) { - workerPool.Submit(func() { - if err := processRequest(); err != nil { - logger.LogDebug("failed to answer request", append(loggerArgs, "err", err)...) - } else { - logger.LogTrace("answered request", loggerArgs...) - } - }) -} diff --git a/pkg/protocol/protocol_attestations.go b/pkg/protocol/protocol_attestations.go index f35abf712..f6a0aa2b2 100644 --- a/pkg/protocol/protocol_attestations.go +++ b/pkg/protocol/protocol_attestations.go @@ -10,7 +10,6 @@ import ( "github.com/iotaledger/hive.go/log" "github.com/iotaledger/hive.go/runtime/workerpool" "github.com/iotaledger/iota-core/pkg/model" - "github.com/iotaledger/iota-core/pkg/protocol/engine" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/merklehasher" ) @@ -122,7 +121,12 @@ func (a *AttestationsProtocol) ProcessResponse(commitmentModel *model.Commitment // ProcessRequest processes the given attestation request. func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - a.protocol.processCommitmentRequest(a.workerPool, commitmentID, func(engineInstance *engine.Engine) error { + logRequest(a.workerPool, func() error { + engineInstance := a.protocol.Commitments.TargetEngine(commitmentID) + if engineInstance == nil { + return ierrors.New("no engine found") + } + if engineInstance.Storage.Settings().LatestCommitment().Slot() < commitmentID.Slot() { return ierrors.New("not committed yet") } @@ -152,7 +156,7 @@ func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, } return a.protocol.Network.SendAttestations(commitmentModel, attestations, roots.AttestationsProof(), from) - }, a, "fromPeer", from) + }, a, "commitmentID", commitmentID, "fromPeer", from) } // Shutdown shuts down the attestation protocol. diff --git a/pkg/protocol/protocol_warp_sync.go b/pkg/protocol/protocol_warp_sync.go index 27416ef30..57fce4f2a 100644 --- a/pkg/protocol/protocol_warp_sync.go +++ b/pkg/protocol/protocol_warp_sync.go @@ -291,7 +291,12 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo // ProcessRequest processes the given warp sync request. func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - w.protocol.processCommitmentRequest(w.workerPool, commitmentID, func(engineInstance *engine.Engine) (err error) { + logRequest(w.workerPool, func() (err error) { + engineInstance := w.protocol.Commitments.TargetEngine(commitmentID) + if engineInstance == nil { + return ierrors.New("no engine found") + } + committedSlot, err := engineInstance.CommittedSlot(commitmentID) if err != nil { return ierrors.Wrap(err, "failed to load committed slot") @@ -315,7 +320,7 @@ func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from w.protocol.Network.SendWarpSyncResponse(commitmentID, blockIDsBySlotCommitment, roots.TangleProof(), transactionIDs, roots.MutationProof(), from) return nil - }, w, "fromPeer", from) + }, w, "commitmentID", commitmentID, "fromPeer", from) } // Shutdown shuts down the warp sync protocol. diff --git a/pkg/protocol/utils.go b/pkg/protocol/utils.go new file mode 100644 index 000000000..5273470e1 --- /dev/null +++ b/pkg/protocol/utils.go @@ -0,0 +1,17 @@ +package protocol + +import ( + "github.com/iotaledger/hive.go/log" + "github.com/iotaledger/hive.go/runtime/workerpool" +) + +// logRequest is a generic utility function that submits a request to the given worker pool and logs the result. +func logRequest(workerPool *workerpool.WorkerPool, processRequest func() error, logger log.Logger, loggerArgs ...any) { + workerPool.Submit(func() { + if err := processRequest(); err != nil { + logger.LogDebug("failed to answer request", append(loggerArgs, "err", err)...) + } else { + logger.LogTrace("answered request", loggerArgs...) + } + }) +} From bb8b5a0fa43bde5fba48c8b50ba5ba96e23956ed Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:12:54 +0100 Subject: [PATCH 09/28] Feat: more refactor --- pkg/protocol/protocol_commitments.go | 41 ++++++++-------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/pkg/protocol/protocol_commitments.go b/pkg/protocol/protocol_commitments.go index 4aea3dd00..b6af9c68c 100644 --- a/pkg/protocol/protocol_commitments.go +++ b/pkg/protocol/protocol_commitments.go @@ -91,38 +91,21 @@ func (c *CommitmentsProtocol) ProcessResponse(commitmentModel *model.Commitment, // ProcessRequest processes the given commitment request. func (c *CommitmentsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - c.workerPool.Submit(func() { - commitment, err := c.protocol.Commitments.Get(commitmentID) - if err != nil { - if !ierrors.Is(err, ErrorCommitmentNotFound) { - c.LogError("failed to load commitment for commitment request", "commitmentID", commitmentID, "fromPeer", from, "error", err) - - return - } - - slotAPI, slotAPIErr := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID) - if slotAPIErr != nil { - c.LogDebug("failed to load committed slot for commitment request", "commitmentID", commitmentID, "fromPeer", from, "error", slotAPIErr) - - return - } - - commitmentModel, slotAPIErr := slotAPI.Commitment() - if slotAPIErr != nil { - c.LogDebug("failed to load commitment for commitment request", "commitmentID", commitmentID, "fromPeer", from, "error", slotAPIErr) - - return - } - + logRequest(c.workerPool, func() error { + if commitment, err := c.protocol.Commitments.Get(commitmentID); err == nil { + c.protocol.Network.SendSlotCommitment(commitment.Commitment, from) + } else if !ierrors.Is(err, ErrorCommitmentNotFound) { + return ierrors.Wrapf(err, "failed to load commitment") + } else if slotAPI, slotAPIErr := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID); slotAPIErr != nil { + return ierrors.Wrapf(slotAPIErr, "failed to load committed slot API") + } else if commitmentModel, commitmentModelErr := slotAPI.Commitment(); commitmentModelErr != nil { + return ierrors.Wrapf(commitmentModelErr, "failed to load commitment") + } else { c.protocol.Network.SendSlotCommitment(commitmentModel, from) - - c.LogTrace("sent commitment", "commitmentID", commitmentID, "toPeer", from) - - return } - c.SendResponse(commitment, from) - }) + return nil + }, c, "commitmentID", commitmentID, "fromPeer", from) } // Shutdown shuts down the commitment protocol and waits for all pending requests to be processed. From ae914ed1fa17be774f996ffd4de848e31ed6fdf2 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:05:18 +0100 Subject: [PATCH 10/28] Refactor: more structural changes --- pkg/protocol/commitments.go | 33 ++++++++++++++++++----- pkg/protocol/engine/committed_slot_api.go | 21 +++++++++++++++ pkg/protocol/engine/engine.go | 4 +++ pkg/protocol/protocol_attestations.go | 32 +++++----------------- pkg/protocol/protocol_commitments.go | 25 +++++++---------- pkg/protocol/protocol_warp_sync.go | 9 ++----- pkg/protocol/utils.go | 4 +-- 7 files changed, 73 insertions(+), 55 deletions(-) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 0c4084ff0..e70757717 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -69,18 +69,39 @@ func (c *Commitments) Get(commitmentID iotago.CommitmentID, requestIfMissing ... return cachedRequest.Result(), cachedRequest.Err() } +// Model returns the model of the Commitment for the given commitmentID. +func (c *Commitments) Model(commitmentID iotago.CommitmentID) (model *model.Commitment, err error) { + // serve from cache first + commitment, err := c.Get(commitmentID) + if err == nil { + return commitment.Commitment, nil + } else if !ierrors.Is(err, ErrorCommitmentNotFound) { + return nil, ierrors.Wrapf(err, "failed to load commitment") + } + + // otherwise, load from main engine + engineAPI, err := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID) + if err != nil { + return nil, ierrors.Wrapf(err, "failed to load engine API") + } + + return engineAPI.Commitment() +} + // TargetEngine returns the engine that manages the data for the given commitment (or nil if no engine was found while -// commitment IDs below the Root are resolved against the main engine). -func (c *Commitments) TargetEngine(commitmentID iotago.CommitmentID) (targetEngine *engine.Engine) { +// commitment IDs below the Root are always resolved against the main engine). +func (c *Commitments) TargetEngine(commitmentID iotago.CommitmentID) *engine.Engine { if commitmentID.Slot() <= c.Root.Get().Slot() { - targetEngine = c.protocol.Engines.Main.Get() - } else if commitment, err := c.Get(commitmentID); err == nil { - targetEngine = commitment.TargetEngine() + return c.protocol.Engines.Main.Get() + } + + if commitment, err := c.Get(commitmentID); err == nil { + return commitment.TargetEngine() } else if !ierrors.Is(err, ErrorCommitmentNotFound) { c.LogDebug("failed to retrieve commitment", "commitmentID", commitmentID, "err", err) } - return targetEngine + return nil } // initLogger initializes the logger for this component. diff --git a/pkg/protocol/engine/committed_slot_api.go b/pkg/protocol/engine/committed_slot_api.go index 64ed9a2a5..a82a82128 100644 --- a/pkg/protocol/engine/committed_slot_api.go +++ b/pkg/protocol/engine/committed_slot_api.go @@ -5,6 +5,7 @@ import ( "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/iota-core/pkg/model" iotago "github.com/iotaledger/iota.go/v4" + "github.com/iotaledger/iota.go/v4/merklehasher" ) // CommittedSlotAPI is a wrapper for the Engine that provides access to the data of a committed slot. @@ -37,6 +38,26 @@ func (c *CommittedSlotAPI) Commitment() (commitment *model.Commitment, err error return commitment, nil } +func (c *CommittedSlotAPI) Attestations() (attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], err error) { + if attestations, err = c.engine.Attestations.Get(c.CommitmentID.Slot()); err != nil { + return nil, nil, ierrors.Wrapf(err, "failed to load attestations") + } + + rootsStorage, err := c.engine.Storage.Roots(c.CommitmentID.Slot()) + if err != nil { + return nil, nil, ierrors.Wrapf(err, "failed to load roots storage") + } + + roots, exists, err := rootsStorage.Load(c.CommitmentID) + if err != nil { + return nil, nil, ierrors.Wrapf(err, "failed to load roots") + } else if !exists { + return nil, nil, ierrors.New("roots not found") + } + + return attestations, roots.AttestationsProof(), nil +} + // Roots returns the roots of the slot. func (c *CommittedSlotAPI) Roots() (committedRoots *iotago.Roots, err error) { if c.engine.Storage.Settings().LatestCommitment().Slot() < c.CommitmentID.Slot() { diff --git a/pkg/protocol/engine/engine.go b/pkg/protocol/engine/engine.go index 880816d87..b755d290e 100644 --- a/pkg/protocol/engine/engine.go +++ b/pkg/protocol/engine/engine.go @@ -327,6 +327,10 @@ func (e *Engine) LatestAPI() iotago.API { // CommittedSlot returns the committed slot for the given slot index. func (e *Engine) CommittedSlot(commitmentID iotago.CommitmentID) (*CommittedSlotAPI, error) { + if e == nil { + return nil, ierrors.New("engine is nil") + } + if e.Storage.Settings().LatestCommitment().Slot() < commitmentID.Slot() { return nil, ierrors.Errorf("slot %d is not committed yet", commitmentID.Slot()) } diff --git a/pkg/protocol/protocol_attestations.go b/pkg/protocol/protocol_attestations.go index f6a0aa2b2..0bf48b8e2 100644 --- a/pkg/protocol/protocol_attestations.go +++ b/pkg/protocol/protocol_attestations.go @@ -121,41 +121,23 @@ func (a *AttestationsProtocol) ProcessResponse(commitmentModel *model.Commitment // ProcessRequest processes the given attestation request. func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - logRequest(a.workerPool, func() error { - engineInstance := a.protocol.Commitments.TargetEngine(commitmentID) - if engineInstance == nil { - return ierrors.New("no engine found") - } - - if engineInstance.Storage.Settings().LatestCommitment().Slot() < commitmentID.Slot() { - return ierrors.New("not committed yet") + submitLoggedRequest(a.workerPool, func() error { + engineAPI, err := a.protocol.Commitments.TargetEngine(commitmentID).CommittedSlot(commitmentID) + if err != nil { + return ierrors.Wrapf(err, "failed to load committed slot API") } - commitmentModel, err := engineInstance.Storage.Commitments().Load(commitmentID.Slot()) + commitmentModel, err := engineAPI.Commitment() if err != nil { return ierrors.Wrapf(err, "failed to load commitment") - } else if commitmentModel.ID() != commitmentID { - return ierrors.Errorf("commitment ID mismatch: %s != %s", commitmentModel.ID(), commitmentID) } - attestations, err := engineInstance.Attestations.Get(commitmentID.Slot()) + attestations, proof, err := engineAPI.Attestations() if err != nil { return ierrors.Wrapf(err, "failed to load attestations") } - rootsStorage, err := engineInstance.Storage.Roots(commitmentID.Slot()) - if err != nil { - return ierrors.Wrapf(err, "failed to load roots storage") - } - - roots, exists, err := rootsStorage.Load(commitmentID) - if err != nil { - return ierrors.Wrapf(err, "failed to load roots") - } else if !exists { - return ierrors.New("roots not found") - } - - return a.protocol.Network.SendAttestations(commitmentModel, attestations, roots.AttestationsProof(), from) + return a.protocol.Network.SendAttestations(commitmentModel, attestations, proof, from) }, a, "commitmentID", commitmentID, "fromPeer", from) } diff --git a/pkg/protocol/protocol_commitments.go b/pkg/protocol/protocol_commitments.go index b6af9c68c..216f7674c 100644 --- a/pkg/protocol/protocol_commitments.go +++ b/pkg/protocol/protocol_commitments.go @@ -4,7 +4,6 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/iotaledger/hive.go/core/eventticker" - "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/log" "github.com/iotaledger/hive.go/runtime/workerpool" @@ -91,25 +90,21 @@ func (c *CommitmentsProtocol) ProcessResponse(commitmentModel *model.Commitment, // ProcessRequest processes the given commitment request. func (c *CommitmentsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - logRequest(c.workerPool, func() error { - if commitment, err := c.protocol.Commitments.Get(commitmentID); err == nil { - c.protocol.Network.SendSlotCommitment(commitment.Commitment, from) - } else if !ierrors.Is(err, ErrorCommitmentNotFound) { - return ierrors.Wrapf(err, "failed to load commitment") - } else if slotAPI, slotAPIErr := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID); slotAPIErr != nil { - return ierrors.Wrapf(slotAPIErr, "failed to load committed slot API") - } else if commitmentModel, commitmentModelErr := slotAPI.Commitment(); commitmentModelErr != nil { - return ierrors.Wrapf(commitmentModelErr, "failed to load commitment") - } else { - c.protocol.Network.SendSlotCommitment(commitmentModel, from) + c.workerPool.Submit(func() { + commitmentModel, err := c.protocol.Commitments.Model(commitmentID) + if err != nil { + c.LogDebug("failed to answer request", "commitmentID", commitmentID, "fromPeer", from, "err", err) + + return } - return nil - }, c, "commitmentID", commitmentID, "fromPeer", from) + c.protocol.Network.SendSlotCommitment(commitmentModel, from) + + c.LogTrace("answered request", "commitmentID", commitmentID, "fromPeer", from) + }) } // Shutdown shuts down the commitment protocol and waits for all pending requests to be processed. func (c *CommitmentsProtocol) Shutdown() { c.ticker.Shutdown() - c.workerPool.Shutdown().ShutdownComplete.Wait() } diff --git a/pkg/protocol/protocol_warp_sync.go b/pkg/protocol/protocol_warp_sync.go index 57fce4f2a..0ad50e355 100644 --- a/pkg/protocol/protocol_warp_sync.go +++ b/pkg/protocol/protocol_warp_sync.go @@ -291,13 +291,8 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo // ProcessRequest processes the given warp sync request. func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - logRequest(w.workerPool, func() (err error) { - engineInstance := w.protocol.Commitments.TargetEngine(commitmentID) - if engineInstance == nil { - return ierrors.New("no engine found") - } - - committedSlot, err := engineInstance.CommittedSlot(commitmentID) + submitLoggedRequest(w.workerPool, func() (err error) { + committedSlot, err := w.protocol.Commitments.TargetEngine(commitmentID).CommittedSlot(commitmentID) if err != nil { return ierrors.Wrap(err, "failed to load committed slot") } diff --git a/pkg/protocol/utils.go b/pkg/protocol/utils.go index 5273470e1..f31af24c0 100644 --- a/pkg/protocol/utils.go +++ b/pkg/protocol/utils.go @@ -5,8 +5,8 @@ import ( "github.com/iotaledger/hive.go/runtime/workerpool" ) -// logRequest is a generic utility function that submits a request to the given worker pool and logs the result. -func logRequest(workerPool *workerpool.WorkerPool, processRequest func() error, logger log.Logger, loggerArgs ...any) { +// submitLoggedRequest is a generic utility function that submits a request to the given worker pool logging the result. +func submitLoggedRequest(workerPool *workerpool.WorkerPool, processRequest func() error, logger log.Logger, loggerArgs ...any) { workerPool.Submit(func() { if err := processRequest(); err != nil { logger.LogDebug("failed to answer request", append(loggerArgs, "err", err)...) From de21cfb9d151ed9cf388a2442c6ba0a49db3e648 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:13:55 +0100 Subject: [PATCH 11/28] Refactor: rename variables --- pkg/protocol/commitments.go | 7 +++---- pkg/protocol/protocol_commitments.go | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index e70757717..906d40583 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -69,17 +69,16 @@ func (c *Commitments) Get(commitmentID iotago.CommitmentID, requestIfMissing ... return cachedRequest.Result(), cachedRequest.Err() } -// Model returns the model of the Commitment for the given commitmentID. +// Model returns the model of the Commitment for the given commitmentID. It tries to retrieve the Commitment from the +// cache first and falls back to serve it from the main engine if the Commitment is below the Root commitment. func (c *Commitments) Model(commitmentID iotago.CommitmentID) (model *model.Commitment, err error) { - // serve from cache first commitment, err := c.Get(commitmentID) if err == nil { return commitment.Commitment, nil - } else if !ierrors.Is(err, ErrorCommitmentNotFound) { + } else if !ierrors.Is(err, ErrorCommitmentNotFound) || commitmentID.Slot() > c.Root.Get().Slot() { return nil, ierrors.Wrapf(err, "failed to load commitment") } - // otherwise, load from main engine engineAPI, err := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID) if err != nil { return nil, ierrors.Wrapf(err, "failed to load engine API") diff --git a/pkg/protocol/protocol_commitments.go b/pkg/protocol/protocol_commitments.go index 216f7674c..9d65555d3 100644 --- a/pkg/protocol/protocol_commitments.go +++ b/pkg/protocol/protocol_commitments.go @@ -91,14 +91,14 @@ func (c *CommitmentsProtocol) ProcessResponse(commitmentModel *model.Commitment, // ProcessRequest processes the given commitment request. func (c *CommitmentsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { c.workerPool.Submit(func() { - commitmentModel, err := c.protocol.Commitments.Model(commitmentID) + commitment, err := c.protocol.Commitments.Model(commitmentID) if err != nil { c.LogDebug("failed to answer request", "commitmentID", commitmentID, "fromPeer", from, "err", err) return } - c.protocol.Network.SendSlotCommitment(commitmentModel, from) + c.protocol.Network.SendSlotCommitment(commitment, from) c.LogTrace("answered request", "commitmentID", commitmentID, "fromPeer", from) }) From 33fd317b4e96491caa9b7686bb3ecabf44c30e7c Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:17:50 +0100 Subject: [PATCH 12/28] Refactor: removed unused code --- pkg/protocol/protocol_commitments.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pkg/protocol/protocol_commitments.go b/pkg/protocol/protocol_commitments.go index 9d65555d3..ca1193005 100644 --- a/pkg/protocol/protocol_commitments.go +++ b/pkg/protocol/protocol_commitments.go @@ -60,15 +60,6 @@ func (c *CommitmentsProtocol) SendRequest(commitmentID iotago.CommitmentID) { }) } -// SendResponse sends a commitment response for the given commitment to the given peer. -func (c *CommitmentsProtocol) SendResponse(commitment *Commitment, to peer.ID) { - c.workerPool.Submit(func() { - c.protocol.Network.SendSlotCommitment(commitment.Commitment, to) - - c.LogTrace("sent commitment", "commitment", commitment.LogName(), "toPeer", to) - }) -} - // ProcessResponse processes the given commitment response. func (c *CommitmentsProtocol) ProcessResponse(commitmentModel *model.Commitment, from peer.ID) { c.workerPool.Submit(func() { From 49124d759c2581dd4a978f446dc6458078565340 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:16:19 +0100 Subject: [PATCH 13/28] Refactor: started preparing CommitmentMetadata refactor --- pkg/protocol/commitments.go | 92 +++++++++++++-------------- pkg/protocol/protocol_attestations.go | 12 ++-- pkg/protocol/protocol_blocks.go | 6 +- pkg/protocol/protocol_commitments.go | 15 ++--- pkg/protocol/protocol_warp_sync.go | 53 ++++++++------- 5 files changed, 88 insertions(+), 90 deletions(-) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 906d40583..9c87620dc 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -51,56 +51,40 @@ func newCommitments(protocol *Protocol) *Commitments { return c } -// Get returns the Commitment for the given commitmentID. If the Commitment is not available yet, it will return an -// ErrorCommitmentNotFound. It is possible to trigger a request for the Commitment by passing true as the second -// argument. -func (c *Commitments) Get(commitmentID iotago.CommitmentID, requestIfMissing ...bool) (commitment *Commitment, err error) { - cachedRequest, exists := c.cachedRequests.Get(commitmentID) - if !exists && lo.First(requestIfMissing) { - if cachedRequest = c.cachedRequest(commitmentID, true); cachedRequest.WasRejected() { - return nil, ierrors.Wrapf(cachedRequest.Err(), "failed to request commitment %s", commitmentID) - } - } - - if cachedRequest == nil || !cachedRequest.WasCompleted() { - return nil, ErrorCommitmentNotFound - } - - return cachedRequest.Result(), cachedRequest.Err() -} - -// Model returns the model of the Commitment for the given commitmentID. It tries to retrieve the Commitment from the +// Commitment returns the Commitment for the given commitmentID. It tries to retrieve the Commitment from the // cache first and falls back to serve it from the main engine if the Commitment is below the Root commitment. -func (c *Commitments) Model(commitmentID iotago.CommitmentID) (model *model.Commitment, err error) { - commitment, err := c.Get(commitmentID) +func (c *Commitments) Commitment(commitmentID iotago.CommitmentID) (commitment *model.Commitment, err error) { + commitmentMetadata, err := c.Metadata(commitmentID) if err == nil { - return commitment.Commitment, nil + return commitmentMetadata.Commitment, nil } else if !ierrors.Is(err, ErrorCommitmentNotFound) || commitmentID.Slot() > c.Root.Get().Slot() { - return nil, ierrors.Wrapf(err, "failed to load commitment") + return nil, ierrors.Wrapf(err, "failed to load commitment metadata") } - engineAPI, err := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID) + slotAPI, err := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID) if err != nil { return nil, ierrors.Wrapf(err, "failed to load engine API") } - return engineAPI.Commitment() + return slotAPI.Commitment() } -// TargetEngine returns the engine that manages the data for the given commitment (or nil if no engine was found while -// commitment IDs below the Root are always resolved against the main engine). -func (c *Commitments) TargetEngine(commitmentID iotago.CommitmentID) *engine.Engine { - if commitmentID.Slot() <= c.Root.Get().Slot() { - return c.protocol.Engines.Main.Get() +// Metadata returns the protocol Commitment for the given commitmentID. If the Commitment is not available yet, it +// will return an ErrorCommitmentNotFound. It is possible to trigger a request for the Commitment by passing true as the +// second argument. +func (c *Commitments) Metadata(commitmentID iotago.CommitmentID, requestIfMissing ...bool) (commitmentMetadata *Commitment, err error) { + cachedRequest, exists := c.cachedRequests.Get(commitmentID) + if !exists && lo.First(requestIfMissing) { + if cachedRequest = c.cachedRequest(commitmentID, true); cachedRequest.WasRejected() { + return nil, ierrors.Wrapf(cachedRequest.Err(), "failed to request commitment %s", commitmentID) + } } - if commitment, err := c.Get(commitmentID); err == nil { - return commitment.TargetEngine() - } else if !ierrors.Is(err, ErrorCommitmentNotFound) { - c.LogDebug("failed to retrieve commitment", "commitmentID", commitmentID, "err", err) + if cachedRequest == nil || !cachedRequest.WasCompleted() { + return nil, ErrorCommitmentNotFound } - return nil + return cachedRequest.Result(), cachedRequest.Err() } // initLogger initializes the logger for this component. @@ -136,7 +120,7 @@ func (c *Commitments) initEngineCommitmentSynchronization() func() { // publishRootCommitment publishes the root commitment of the main engine. func (c *Commitments) publishRootCommitment(mainChain *Chain, mainEngine *engine.Engine) func() { return mainEngine.RootCommitment.OnUpdate(func(_ *model.Commitment, newRootCommitmentModel *model.Commitment) { - newRootCommitment, published, err := c.publishCommitmentModel(newRootCommitmentModel) + newRootCommitment, published, err := c.publishCommitment(newRootCommitmentModel) if err != nil { c.LogError("failed to publish new root commitment", "id", newRootCommitmentModel.ID(), "error", err) @@ -179,7 +163,7 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi } // publish the model - publishedCommitment, _, err := c.publishCommitmentModel(modelToPublish) + publishedCommitment, _, err := c.publishCommitment(modelToPublish) if err != nil { c.LogError("failed to publish commitment from engine", "engine", engine.LogName(), "commitment", modelToPublish, "err", err) @@ -195,25 +179,25 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi }) } -// publishCommitmentModel publishes the given commitment model as a Commitment instance. If the Commitment was already +// publishCommitment publishes the given commitment as a Commitment instance. If the Commitment was already // published, it will return the existing Commitment instance. Otherwise, it will create a new Commitment instance and // resolve the Promise that was created for it. -func (c *Commitments) publishCommitmentModel(model *model.Commitment) (commitment *Commitment, published bool, err error) { +func (c *Commitments) publishCommitment(commitment *model.Commitment) (commitmentMetadata *Commitment, published bool, err error) { // retrieve promise and abort if it was already rejected - cachedRequest := c.cachedRequest(model.ID()) + cachedRequest := c.cachedRequest(commitment.ID()) if cachedRequest.WasRejected() { - return nil, false, ierrors.Wrapf(cachedRequest.Err(), "failed to request commitment %s", model.ID()) + return nil, false, ierrors.Wrapf(cachedRequest.Err(), "failed to request commitment %s", commitment.ID()) } // otherwise try to provideCommitment it and determine if we were the goroutine that resolved it - commitment = newCommitment(c, model) - cachedRequest.Resolve(commitment).OnSuccess(func(resolvedCommitment *Commitment) { - if published = resolvedCommitment == commitment; !published { - commitment = resolvedCommitment + commitmentMetadata = newCommitment(c, commitment) + cachedRequest.Resolve(commitmentMetadata).OnSuccess(func(resolvedCommitment *Commitment) { + if published = resolvedCommitment == commitmentMetadata; !published { + commitmentMetadata = resolvedCommitment } }) - return commitment, published, nil + return commitmentMetadata, published, nil } // cachedRequest returns a singleton Promise for the given commitmentID. If the Promise does not exist yet, it will be @@ -276,3 +260,19 @@ func (c *Commitments) initCommitment(commitment *Commitment, slotEvicted reactiv commitment.IsEvicted.Trigger() }) } + +// targetEngine returns the engine that manages the data for the given commitment (or nil if no engine was found while +// commitment IDs below the Root are always resolved against the main engine). +func (c *Commitments) targetEngine(commitmentID iotago.CommitmentID) *engine.Engine { + if commitmentID.Slot() <= c.Root.Get().Slot() { + return c.protocol.Engines.Main.Get() + } + + if commitmentMetadata, err := c.Metadata(commitmentID); err == nil { + return commitmentMetadata.TargetEngine() + } else if !ierrors.Is(err, ErrorCommitmentNotFound) { + c.LogDebug("failed to retrieve commitment", "commitmentID", commitmentID, "err", err) + } + + return nil +} diff --git a/pkg/protocol/protocol_attestations.go b/pkg/protocol/protocol_attestations.go index 0bf48b8e2..703aa5819 100644 --- a/pkg/protocol/protocol_attestations.go +++ b/pkg/protocol/protocol_attestations.go @@ -73,7 +73,7 @@ func newAttestationsProtocol(protocol *Protocol) *AttestationsProtocol { // ProcessResponse processes the given attestation response. func (a *AttestationsProtocol) ProcessResponse(commitmentModel *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { a.workerPool.Submit(func() { - commitment, _, err := a.protocol.Commitments.publishCommitmentModel(commitmentModel) + commitment, _, err := a.protocol.Commitments.publishCommitment(commitmentModel) if err != nil { a.LogDebug("failed to publish commitment when processing attestations", "commitmentID", commitmentModel.ID(), "peer", from, "error", err) @@ -122,17 +122,17 @@ func (a *AttestationsProtocol) ProcessResponse(commitmentModel *model.Commitment // ProcessRequest processes the given attestation request. func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { submitLoggedRequest(a.workerPool, func() error { - engineAPI, err := a.protocol.Commitments.TargetEngine(commitmentID).CommittedSlot(commitmentID) + slotAPI, err := a.protocol.Commitments.targetEngine(commitmentID).CommittedSlot(commitmentID) if err != nil { return ierrors.Wrapf(err, "failed to load committed slot API") } - commitmentModel, err := engineAPI.Commitment() + commitmentModel, err := slotAPI.Commitment() if err != nil { return ierrors.Wrapf(err, "failed to load commitment") } - attestations, proof, err := engineAPI.Attestations() + attestations, proof, err := slotAPI.Attestations() if err != nil { return ierrors.Wrapf(err, "failed to load attestations") } @@ -186,10 +186,10 @@ func (a *AttestationsProtocol) setupCommitmentVerifier(chain *Chain) (shutdown f // sendRequest sends an attestation request for the given commitment ID. func (a *AttestationsProtocol) sendRequest(commitmentID iotago.CommitmentID) { a.workerPool.Submit(func() { - if commitment, err := a.protocol.Commitments.Get(commitmentID, false); err == nil { + if commitmentMetadata, err := a.protocol.Commitments.Metadata(commitmentID, false); err == nil { a.protocol.Network.RequestAttestations(commitmentID) - a.LogDebug("request", "commitment", commitment.LogName()) + a.LogDebug("request", "commitment", commitmentMetadata.LogName()) } else { a.LogError("failed to load commitment", "commitmentID", commitmentID, "err", err) } diff --git a/pkg/protocol/protocol_blocks.go b/pkg/protocol/protocol_blocks.go index 83bdc8fc6..22b2e900f 100644 --- a/pkg/protocol/protocol_blocks.go +++ b/pkg/protocol/protocol_blocks.go @@ -87,7 +87,7 @@ func (b *BlocksProtocol) SendResponse(block *model.Block) { func (b *BlocksProtocol) ProcessResponse(block *model.Block, from peer.ID) { b.workerPool.Submit(func() { // abort if the commitment belongs to an evicted slot - commitment, err := b.protocol.Commitments.Get(block.ProtocolBlock().Header.SlotCommitmentID, true) + commitmentMetadata, err := b.protocol.Commitments.Metadata(block.ProtocolBlock().Header.SlotCommitmentID, true) if err != nil && ierrors.Is(ErrorSlotEvicted, err) { b.LogError("dropped block referencing unsolidifiable commitment", "commitmentID", block.ProtocolBlock().Header.SlotCommitmentID, "blockID", block.ID(), "err", err) @@ -95,7 +95,7 @@ func (b *BlocksProtocol) ProcessResponse(block *model.Block, from peer.ID) { } // add the block to the dropped blocks buffer if we could not dispatch it to the chain - if commitment == nil || !commitment.Chain.Get().DispatchBlock(block, from) { + if commitmentMetadata == nil || !commitmentMetadata.Chain.Get().DispatchBlock(block, from) { if !b.droppedBlocksBuffer.Add(block.ProtocolBlock().Header.SlotCommitmentID, types.NewTuple(block, from)) { b.LogError("failed to add dropped block referencing unsolid commitment to dropped blocks buffer", "commitmentID", block.ProtocolBlock().Header.SlotCommitmentID, "blockID", block.ID()) } else { @@ -105,7 +105,7 @@ func (b *BlocksProtocol) ProcessResponse(block *model.Block, from peer.ID) { return } - b.LogTrace("received block", "blockID", block.ID(), "commitment", commitment.LogName()) + b.LogTrace("received block", "blockID", block.ID(), "commitment", commitmentMetadata.LogName()) }) } diff --git a/pkg/protocol/protocol_commitments.go b/pkg/protocol/protocol_commitments.go index ca1193005..d1c7b9193 100644 --- a/pkg/protocol/protocol_commitments.go +++ b/pkg/protocol/protocol_commitments.go @@ -4,6 +4,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/iotaledger/hive.go/core/eventticker" + "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/log" "github.com/iotaledger/hive.go/runtime/workerpool" @@ -71,7 +72,7 @@ func (c *CommitmentsProtocol) ProcessResponse(commitmentModel *model.Commitment, return } - if commitment, published, err := c.protocol.Commitments.publishCommitmentModel(commitmentModel); err != nil { + if commitment, published, err := c.protocol.Commitments.publishCommitment(commitmentModel); err != nil { c.LogError("failed to process commitment", "fromPeer", from, "err", err) } else if published { c.LogTrace("received response", "commitment", commitment.LogName(), "fromPeer", from) @@ -81,18 +82,16 @@ func (c *CommitmentsProtocol) ProcessResponse(commitmentModel *model.Commitment, // ProcessRequest processes the given commitment request. func (c *CommitmentsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - c.workerPool.Submit(func() { - commitment, err := c.protocol.Commitments.Model(commitmentID) + submitLoggedRequest(c.workerPool, func() error { + commitment, err := c.protocol.Commitments.Commitment(commitmentID) if err != nil { - c.LogDebug("failed to answer request", "commitmentID", commitmentID, "fromPeer", from, "err", err) - - return + return ierrors.Wrap(err, "failed to load commitment") } c.protocol.Network.SendSlotCommitment(commitment, from) - c.LogTrace("answered request", "commitmentID", commitmentID, "fromPeer", from) - }) + return nil + }, c, "commitmentID", commitmentID, "fromPeer", from) } // Shutdown shuts down the commitment protocol and waits for all pending requests to be processed. diff --git a/pkg/protocol/protocol_warp_sync.go b/pkg/protocol/protocol_warp_sync.go index 0ad50e355..a2aeea62f 100644 --- a/pkg/protocol/protocol_warp_sync.go +++ b/pkg/protocol/protocol_warp_sync.go @@ -72,10 +72,10 @@ func newWarpSyncProtocol(protocol *Protocol) *WarpSyncProtocol { // SendRequest sends a warp sync request for the given commitment ID to all peers. func (w *WarpSyncProtocol) SendRequest(commitmentID iotago.CommitmentID) { w.workerPool.Submit(func() { - if commitment, err := w.protocol.Commitments.Get(commitmentID, false); err == nil { + if commitmentMetadata, err := w.protocol.Commitments.Metadata(commitmentID, false); err == nil { w.protocol.Network.SendWarpSyncRequest(commitmentID) - w.LogDebug("request", "commitment", commitment.LogName()) + w.LogDebug("request", "commitment", commitmentMetadata.LogName()) } }) } @@ -92,7 +92,7 @@ func (w *WarpSyncProtocol) SendResponse(commitment *Commitment, blockIDsBySlotCo // ProcessResponse processes the given warp sync response. func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsBySlotCommitment map[iotago.CommitmentID]iotago.BlockIDs, proof *merklehasher.Proof[iotago.Identifier], transactionIDs iotago.TransactionIDs, mutationProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { w.workerPool.Submit(func() { - commitment, err := w.protocol.Commitments.Get(commitmentID) + commitmentMetadata, err := w.protocol.Commitments.Metadata(commitmentID) if err != nil { if !ierrors.Is(err, ErrorCommitmentNotFound) { w.LogError("failed to load commitment for response", "commitmentID", commitmentID, "fromPeer", from, "err", err) @@ -103,9 +103,9 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo return } - chain := commitment.Chain.Get() + chain := commitmentMetadata.Chain.Get() if chain == nil { - w.LogTrace("failed to get chain for response", "commitment", commitment.LogName(), "fromPeer", from) + w.LogTrace("failed to get chain for response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) return } @@ -116,16 +116,16 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo return } - targetEngine := commitment.TargetEngine() + targetEngine := commitmentMetadata.TargetEngine() if targetEngine == nil { - w.LogDebug("failed to get target engine for response", "commitment", commitment.LogName()) + w.LogDebug("failed to get target engine for response", "commitment", commitmentMetadata.LogName()) return } - commitment.BlocksToWarpSync.Compute(func(blocksToWarpSync ds.Set[iotago.BlockID]) ds.Set[iotago.BlockID] { - if blocksToWarpSync != nil || !commitment.WarpSyncBlocks.Get() { - w.LogTrace("response for already synced commitment", "commitment", commitment.LogName(), "fromPeer", from) + commitmentMetadata.BlocksToWarpSync.Compute(func(blocksToWarpSync ds.Set[iotago.BlockID]) ds.Set[iotago.BlockID] { + if blocksToWarpSync != nil || !commitmentMetadata.WarpSyncBlocks.Get() { + w.LogTrace("response for already synced commitment", "commitment", commitmentMetadata.LogName(), "fromPeer", from) return blocksToWarpSync } @@ -140,8 +140,8 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo } } - if !iotago.VerifyProof(proof, acceptedBlocks.Root(), commitment.RootsID()) { - w.LogError("failed to verify blocks proof", "commitment", commitment.LogName(), "blockIDs", blockIDsBySlotCommitment, "proof", proof, "fromPeer", from) + if !iotago.VerifyProof(proof, acceptedBlocks.Root(), commitmentMetadata.RootsID()) { + w.LogError("failed to verify blocks proof", "commitment", commitmentMetadata.LogName(), "blockIDs", blockIDsBySlotCommitment, "proof", proof, "fromPeer", from) return blocksToWarpSync } @@ -151,8 +151,8 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo _ = acceptedTransactionIDs.Add(transactionID) // a mapdb can never return an error } - if !iotago.VerifyProof(mutationProof, acceptedTransactionIDs.Root(), commitment.RootsID()) { - w.LogError("failed to verify mutations proof", "commitment", commitment.LogName(), "transactionIDs", transactionIDs, "proof", mutationProof, "fromPeer", from) + if !iotago.VerifyProof(mutationProof, acceptedTransactionIDs.Root(), commitmentMetadata.RootsID()) { + w.LogError("failed to verify mutations proof", "commitment", commitmentMetadata.LogName(), "transactionIDs", transactionIDs, "proof", mutationProof, "fromPeer", from) return blocksToWarpSync } @@ -233,7 +233,7 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo // Once all blocks are fully booked we can mark the commitment that is minCommittableAge older as this // commitment to be committable. - commitment.IsSynced.OnUpdateOnce(func(_ bool, _ bool) { + commitmentMetadata.IsSynced.OnUpdateOnce(func(_ bool, _ bool) { // update the flag in a worker since it can potentially cause a commit w.workerPool.Submit(func() { if committableCommitment, exists := chain.Commitment(commitmentID.Slot() - targetEngine.LatestAPI().ProtocolParameters().MinCommittableAge()); exists { @@ -243,16 +243,16 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo }) // force commit one by one and wait for the parent to be verified before we commit the next one - commitment.Parent.WithNonEmptyValue(func(parent *Commitment) (teardown func()) { + commitmentMetadata.Parent.WithNonEmptyValue(func(parent *Commitment) (teardown func()) { return parent.IsVerified.WithNonEmptyValue(func(_ bool) (teardown func()) { - return commitment.IsCommittable.OnTrigger(commitmentFunc) + return commitmentMetadata.IsCommittable.OnTrigger(commitmentFunc) }) }) if totalBlocks == 0 { // mark empty slots as committable and synced - commitment.IsCommittable.Set(true) - commitment.IsSynced.Set(true) + commitmentMetadata.IsCommittable.Set(true) + commitmentMetadata.IsSynced.Set(true) return blocksToWarpSync } @@ -277,12 +277,12 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo return } - commitment.IsSynced.Set(true) + commitmentMetadata.IsSynced.Set(true) }) } } - w.LogDebug("received response", "commitment", commitment.LogName()) + w.LogDebug("received response", "commitment", commitmentMetadata.LogName()) return blocksToWarpSync }) @@ -292,22 +292,22 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo // ProcessRequest processes the given warp sync request. func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { submitLoggedRequest(w.workerPool, func() (err error) { - committedSlot, err := w.protocol.Commitments.TargetEngine(commitmentID).CommittedSlot(commitmentID) + slotAPI, err := w.protocol.Commitments.targetEngine(commitmentID).CommittedSlot(commitmentID) if err != nil { - return ierrors.Wrap(err, "failed to load committed slot") + return ierrors.Wrap(err, "failed to load slot api") } - blockIDsBySlotCommitment, err := committedSlot.BlocksIDsBySlotCommitmentID() + blockIDsBySlotCommitment, err := slotAPI.BlocksIDsBySlotCommitmentID() if err != nil { return ierrors.Wrap(err, "failed to get block ids") } - roots, err := committedSlot.Roots() + roots, err := slotAPI.Roots() if err != nil { return ierrors.Wrap(err, "failed to get roots") } - transactionIDs, err := committedSlot.TransactionIDs() + transactionIDs, err := slotAPI.TransactionIDs() if err != nil { return ierrors.Wrap(err, "failed to get transaction ids") } @@ -321,5 +321,4 @@ func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from // Shutdown shuts down the warp sync protocol. func (w *WarpSyncProtocol) Shutdown() { w.ticker.Shutdown() - w.workerPool.Shutdown().ShutdownComplete.Wait() } From fe6b6beb2655d558cdf96dfe7f8a38adbca0a3b5 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:30:22 +0100 Subject: [PATCH 14/28] Feat: more refactor --- pkg/protocol/commitments.go | 30 +++++++++++++-------------- pkg/protocol/protocol_attestations.go | 30 +++++++++++++-------------- pkg/protocol/protocol_commitments.go | 13 ++++++------ 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 9c87620dc..4c5e2038e 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -119,23 +119,23 @@ func (c *Commitments) initEngineCommitmentSynchronization() func() { // publishRootCommitment publishes the root commitment of the main engine. func (c *Commitments) publishRootCommitment(mainChain *Chain, mainEngine *engine.Engine) func() { - return mainEngine.RootCommitment.OnUpdate(func(_ *model.Commitment, newRootCommitmentModel *model.Commitment) { - newRootCommitment, published, err := c.publishCommitment(newRootCommitmentModel) + return mainEngine.RootCommitment.OnUpdate(func(_ *model.Commitment, rootCommitment *model.Commitment) { + commitmentMetadata, published, err := c.publishCommitment(rootCommitment) if err != nil { - c.LogError("failed to publish new root commitment", "id", newRootCommitmentModel.ID(), "error", err) + c.LogError("failed to publish new root commitment", "id", rootCommitment.ID(), "error", err) return } - newRootCommitment.IsRoot.Set(true) + commitmentMetadata.IsRoot.Set(true) if published { - newRootCommitment.Chain.Set(mainChain) + commitmentMetadata.Chain.Set(mainChain) } // TODO: USE SET HERE (debug eviction issues) - mainChain.ForkingPoint.DefaultTo(newRootCommitment) + mainChain.ForkingPoint.DefaultTo(commitmentMetadata) - c.Root.Set(newRootCommitment) + c.Root.Set(commitmentMetadata) }) } @@ -144,7 +144,7 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi latestPublishedSlot := chain.LastCommonSlot() return engine.LatestCommitment.OnUpdate(func(_ *model.Commitment, latestCommitment *model.Commitment) { - loadModel := func(slot iotago.SlotIndex) (*model.Commitment, error) { + loadCommitment := func(slot iotago.SlotIndex) (*model.Commitment, error) { // prevent disk access if possible if slot == latestCommitment.Slot() { return latestCommitment, nil @@ -154,8 +154,8 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi } for ; latestPublishedSlot < latestCommitment.Slot(); latestPublishedSlot++ { - // retrieve the model to publish - modelToPublish, err := loadModel(latestPublishedSlot + 1) + // retrieve the commitment to publish + commitment, err := loadCommitment(latestPublishedSlot + 1) if err != nil { c.LogError("failed to load commitment to publish from engine", "slot", latestPublishedSlot+1, "err", err) @@ -163,18 +163,18 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi } // publish the model - publishedCommitment, _, err := c.publishCommitment(modelToPublish) + commitmentMetadata, _, err := c.publishCommitment(commitment) if err != nil { - c.LogError("failed to publish commitment from engine", "engine", engine.LogName(), "commitment", modelToPublish, "err", err) + c.LogError("failed to publish commitment from engine", "engine", engine.LogName(), "commitment", commitment, "err", err) return } // mark it as produced by ourselves and force it to be on the right chain (in case our chain produced a // different commitment than the one we erroneously expected it to be - we always trust our engine most). - publishedCommitment.AttestedWeight.Set(publishedCommitment.Weight.Get()) - publishedCommitment.IsVerified.Set(true) - publishedCommitment.forceChain(chain) + commitmentMetadata.AttestedWeight.Set(commitmentMetadata.Weight.Get()) + commitmentMetadata.IsVerified.Set(true) + commitmentMetadata.forceChain(chain) } }) } diff --git a/pkg/protocol/protocol_attestations.go b/pkg/protocol/protocol_attestations.go index 703aa5819..7a24a4edd 100644 --- a/pkg/protocol/protocol_attestations.go +++ b/pkg/protocol/protocol_attestations.go @@ -71,50 +71,50 @@ func newAttestationsProtocol(protocol *Protocol) *AttestationsProtocol { } // ProcessResponse processes the given attestation response. -func (a *AttestationsProtocol) ProcessResponse(commitmentModel *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { +func (a *AttestationsProtocol) ProcessResponse(commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { a.workerPool.Submit(func() { - commitment, _, err := a.protocol.Commitments.publishCommitment(commitmentModel) + commitmentMetadata, _, err := a.protocol.Commitments.publishCommitment(commitment) if err != nil { - a.LogDebug("failed to publish commitment when processing attestations", "commitmentID", commitmentModel.ID(), "peer", from, "error", err) + a.LogDebug("failed to publish commitment when processing attestations", "commitmentID", commitment.ID(), "peer", from, "error", err) return } - if commitment.AttestedWeight.Compute(func(currentWeight uint64) uint64 { - if !commitment.RequestAttestations.Get() { - a.LogTrace("received attestations for previously attested commitment", "commitment", commitment.LogName()) + if commitmentMetadata.AttestedWeight.Compute(func(currentWeight uint64) uint64 { + if !commitmentMetadata.RequestAttestations.Get() { + a.LogTrace("received attestations for previously attested commitment", "commitment", commitmentMetadata.LogName()) return currentWeight } - chain := commitment.Chain.Get() + chain := commitmentMetadata.Chain.Get() if chain == nil { - a.LogDebug("failed to find chain for commitment when processing attestations", "commitment", commitment.LogName()) + a.LogDebug("failed to find chain for commitment when processing attestations", "commitment", commitmentMetadata.LogName()) return currentWeight } commitmentVerifier, exists := a.commitmentVerifiers.Get(chain.ForkingPoint.Get().ID()) if !exists || commitmentVerifier == nil { - a.LogDebug("failed to retrieve commitment verifier", "commitment", commitment.LogName()) + a.LogDebug("failed to retrieve commitment verifier", "commitment", commitmentMetadata.LogName()) return currentWeight } - _, actualWeight, err := commitmentVerifier.verifyCommitment(commitment, attestations, merkleProof) + _, actualWeight, err := commitmentVerifier.verifyCommitment(commitmentMetadata, attestations, merkleProof) if err != nil { - a.LogError("failed to verify commitment", "commitment", commitment.LogName(), "error", err) + a.LogError("failed to verify commitment", "commitment", commitmentMetadata.LogName(), "error", err) return currentWeight } if actualWeight > currentWeight { - a.LogDebug("received response", "commitment", commitment.LogName(), "fromPeer", from) + a.LogDebug("received response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) } return actualWeight }) > 0 { - commitment.IsAttested.Set(true) + commitmentMetadata.IsAttested.Set(true) } }) } @@ -127,7 +127,7 @@ func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, return ierrors.Wrapf(err, "failed to load committed slot API") } - commitmentModel, err := slotAPI.Commitment() + commitment, err := slotAPI.Commitment() if err != nil { return ierrors.Wrapf(err, "failed to load commitment") } @@ -137,7 +137,7 @@ func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, return ierrors.Wrapf(err, "failed to load attestations") } - return a.protocol.Network.SendAttestations(commitmentModel, attestations, proof, from) + return a.protocol.Network.SendAttestations(commitment, attestations, proof, from) }, a, "commitmentID", commitmentID, "fromPeer", from) } diff --git a/pkg/protocol/protocol_commitments.go b/pkg/protocol/protocol_commitments.go index d1c7b9193..b91ae5c77 100644 --- a/pkg/protocol/protocol_commitments.go +++ b/pkg/protocol/protocol_commitments.go @@ -62,20 +62,19 @@ func (c *CommitmentsProtocol) SendRequest(commitmentID iotago.CommitmentID) { } // ProcessResponse processes the given commitment response. -func (c *CommitmentsProtocol) ProcessResponse(commitmentModel *model.Commitment, from peer.ID) { +func (c *CommitmentsProtocol) ProcessResponse(commitment *model.Commitment, from peer.ID) { c.workerPool.Submit(func() { - // Verify the commitment's version corresponds to the protocol version for the slot. - apiForSlot := c.protocol.APIForSlot(commitmentModel.Slot()) - if apiForSlot.Version() != commitmentModel.Commitment().ProtocolVersion { - c.LogDebug("received commitment with invalid protocol version", "commitment", commitmentModel.ID(), "version", commitmentModel.Commitment().ProtocolVersion, "expectedVersion", apiForSlot.Version(), "fromPeer", from) + // verify the commitment's version corresponds to the protocol version for the slot. + if apiForSlot := c.protocol.APIForSlot(commitment.Slot()); apiForSlot.Version() != commitment.Commitment().ProtocolVersion { + c.LogDebug("received commitment with invalid protocol version", "commitment", commitment.ID(), "version", commitment.Commitment().ProtocolVersion, "expectedVersion", apiForSlot.Version(), "fromPeer", from) return } - if commitment, published, err := c.protocol.Commitments.publishCommitment(commitmentModel); err != nil { + if commitmentMetadata, published, err := c.protocol.Commitments.publishCommitment(commitment); err != nil { c.LogError("failed to process commitment", "fromPeer", from, "err", err) } else if published { - c.LogTrace("received response", "commitment", commitment.LogName(), "fromPeer", from) + c.LogTrace("received response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) } }) } From 5528ccdc89c3b08a76ad6a5088117af0eff4aba0 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 8 Dec 2023 23:49:41 +0100 Subject: [PATCH 15/28] Feat: merged commitments --- pkg/protocol/commitments.go | 69 ++++++++++++++++++- pkg/protocol/protocol.go | 9 +-- pkg/protocol/protocol_commitments.go | 99 ---------------------------- 3 files changed, 68 insertions(+), 109 deletions(-) delete mode 100644 pkg/protocol/protocol_commitments.go diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 4c5e2038e..3a138472e 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -1,11 +1,15 @@ package protocol import ( + "github.com/libp2p/go-libp2p/core/peer" + + "github.com/iotaledger/hive.go/core/eventticker" "github.com/iotaledger/hive.go/ds/reactive" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/log" + "github.com/iotaledger/hive.go/runtime/workerpool" "github.com/iotaledger/iota-core/pkg/core/promise" "github.com/iotaledger/iota-core/pkg/model" "github.com/iotaledger/iota-core/pkg/protocol/engine" @@ -28,6 +32,12 @@ type Commitments struct { // It acts as a cache and a way to address commitments generically even if they are still unsolid. cachedRequests *shrinkingmap.ShrinkingMap[iotago.CommitmentID, *promise.Promise[*Commitment]] + // workerPool contains the worker pool that is used to process commitment requests and responses asynchronously. + workerPool *workerpool.WorkerPool + + // ticker contains the ticker that is used to send commitment requests. + ticker *eventticker.EventTicker[iotago.SlotIndex, iotago.CommitmentID] + // Logger contains a reference to the logger that is used by this component. log.Logger } @@ -39,18 +49,67 @@ func newCommitments(protocol *Protocol) *Commitments { Root: reactive.NewVariable[*Commitment](), protocol: protocol, cachedRequests: shrinkingmap.New[iotago.CommitmentID, *promise.Promise[*Commitment]](), + workerPool: protocol.Workers.CreatePool("Commitments"), + ticker: eventticker.New[iotago.SlotIndex, iotago.CommitmentID](protocol.Options.CommitmentRequesterOptions...), } shutdown := lo.Batch( c.initLogger(), c.initEngineCommitmentSynchronization(), + + c.ticker.Events.Tick.Hook(c.SendRequest).Unhook, ) - protocol.Shutdown.OnTrigger(shutdown) + protocol.Shutdown.OnTrigger(func() { + shutdown() + + c.ticker.Shutdown() + }) return c } +// SendRequest sends a commitment request for the given commitment ID to all peers. +func (c *Commitments) SendRequest(commitmentID iotago.CommitmentID) { + c.workerPool.Submit(func() { + c.protocol.Network.RequestSlotCommitment(commitmentID) + + c.LogDebug("request", "commitment", commitmentID) + }) +} + +// ProcessResponse processes the given commitment response. +func (c *Commitments) ProcessResponse(commitment *model.Commitment, from peer.ID) { + c.workerPool.Submit(func() { + // verify the commitment's version corresponds to the protocol version for the slot. + if apiForSlot := c.protocol.APIForSlot(commitment.Slot()); apiForSlot.Version() != commitment.Commitment().ProtocolVersion { + c.LogDebug("received commitment with invalid protocol version", "commitment", commitment.ID(), "version", commitment.Commitment().ProtocolVersion, "expectedVersion", apiForSlot.Version(), "fromPeer", from) + + return + } + + if commitmentMetadata, published, err := c.protocol.Commitments.publishCommitment(commitment); err != nil { + c.LogError("failed to process commitment", "fromPeer", from, "err", err) + } else if published { + c.LogTrace("received response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) + } + }) +} + +// ProcessRequest processes the given commitment request. +func (c *Commitments) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { + submitLoggedRequest(c.workerPool, func() error { + commitment, err := c.protocol.Commitments.Commitment(commitmentID) + if err != nil { + return ierrors.Wrap(err, "failed to load commitment") + } + + c.protocol.Network.SendSlotCommitment(commitment, from) + + return nil + }, c, "commitmentID", commitmentID, "fromPeer", from) +} + // Commitment returns the Commitment for the given commitmentID. It tries to retrieve the Commitment from the // cache first and falls back to serve it from the main engine if the Commitment is below the Root commitment. func (c *Commitments) Commitment(commitmentID iotago.CommitmentID) (commitment *model.Commitment, err error) { @@ -162,7 +221,7 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi return } - // publish the model + // publish the commitment commitmentMetadata, _, err := c.publishCommitment(commitment) if err != nil { c.LogError("failed to publish commitment from engine", "engine", engine.LogName(), "commitment", commitment, "err", err) @@ -218,7 +277,11 @@ func (c *Commitments) cachedRequest(commitmentID iotago.CommitmentID, requestIfM // start ticker if requested if lo.First(requestIfMissing) { - c.protocol.CommitmentsProtocol.StartTicker(cachedRequest, commitmentID) + c.ticker.StartTicker(commitmentID) + + cachedRequest.OnComplete(func() { + c.ticker.StopTicker(commitmentID) + }) } // handle successful resolutions diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index fd74b4b3f..294b08730 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -41,9 +41,6 @@ type Protocol struct { // BlocksProtocol contains the subcomponent that is responsible for handling block requests and responses. BlocksProtocol *BlocksProtocol - // CommitmentsProtocol contains the subcomponent that is responsible for handling commitment requests and responses. - CommitmentsProtocol *CommitmentsProtocol - // AttestationsProtocol contains the subcomponent that is responsible for handling attestation requests and // responses. AttestationsProtocol *AttestationsProtocol @@ -150,7 +147,6 @@ func (p *Protocol) LatestAPI() iotago.API { func (p *Protocol) initSubcomponents(networkEndpoint network.Endpoint) (shutdown func()) { p.Network = core.NewProtocol(networkEndpoint, p.Workers.CreatePool("NetworkProtocol"), p) p.BlocksProtocol = newBlocksProtocol(p) - p.CommitmentsProtocol = newCommitmentsProtocol(p) p.AttestationsProtocol = newAttestationsProtocol(p) p.WarpSyncProtocol = newWarpSyncProtocol(p) p.Commitments = newCommitments(p) @@ -159,7 +155,6 @@ func (p *Protocol) initSubcomponents(networkEndpoint network.Endpoint) (shutdown return func() { p.BlocksProtocol.Shutdown() - p.CommitmentsProtocol.Shutdown() p.AttestationsProtocol.Shutdown() p.WarpSyncProtocol.Shutdown() p.Network.Shutdown() @@ -196,8 +191,8 @@ func (p *Protocol) initNetwork() (shutdown func()) { p.Network.OnError(func(err error, peer peer.ID) { p.LogError("network error", "peer", peer, "error", err) }), p.Network.OnBlockReceived(p.BlocksProtocol.ProcessResponse), p.Network.OnBlockRequestReceived(p.BlocksProtocol.ProcessRequest), - p.Network.OnCommitmentReceived(p.CommitmentsProtocol.ProcessResponse), - p.Network.OnCommitmentRequestReceived(p.CommitmentsProtocol.ProcessRequest), + p.Network.OnCommitmentReceived(p.Commitments.ProcessResponse), + p.Network.OnCommitmentRequestReceived(p.Commitments.ProcessRequest), p.Network.OnAttestationsReceived(p.AttestationsProtocol.ProcessResponse), p.Network.OnAttestationsRequestReceived(p.AttestationsProtocol.ProcessRequest), p.Network.OnWarpSyncResponseReceived(p.WarpSyncProtocol.ProcessResponse), diff --git a/pkg/protocol/protocol_commitments.go b/pkg/protocol/protocol_commitments.go deleted file mode 100644 index b91ae5c77..000000000 --- a/pkg/protocol/protocol_commitments.go +++ /dev/null @@ -1,99 +0,0 @@ -package protocol - -import ( - "github.com/libp2p/go-libp2p/core/peer" - - "github.com/iotaledger/hive.go/core/eventticker" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/log" - "github.com/iotaledger/hive.go/runtime/workerpool" - "github.com/iotaledger/iota-core/pkg/core/promise" - "github.com/iotaledger/iota-core/pkg/model" - iotago "github.com/iotaledger/iota.go/v4" -) - -// CommitmentsProtocol is a subcomponent of the protocol that is responsible for handling commitment requests and -// responses. -type CommitmentsProtocol struct { - // protocol contains a reference to the Protocol instance that this component belongs to. - protocol *Protocol - - // workerPool contains the worker pool that is used to process commitment requests and responses asynchronously. - workerPool *workerpool.WorkerPool - - // ticker contains the ticker that is used to send commitment requests. - ticker *eventticker.EventTicker[iotago.SlotIndex, iotago.CommitmentID] - - // Logger embeds a logger that can be used to log messages emitted by this chain. - log.Logger -} - -// newCommitmentsProtocol creates a new commitment protocol instance for the given protocol. -func newCommitmentsProtocol(protocol *Protocol) *CommitmentsProtocol { - c := &CommitmentsProtocol{ - Logger: lo.Return1(protocol.Logger.NewChildLogger("Commitments")), - protocol: protocol, - workerPool: protocol.Workers.CreatePool("Commitments"), - ticker: eventticker.New[iotago.SlotIndex, iotago.CommitmentID](protocol.Options.CommitmentRequesterOptions...), - } - - c.ticker.Events.Tick.Hook(c.SendRequest) - - return c -} - -// StartTicker starts the ticker for the given commitment. -func (c *CommitmentsProtocol) StartTicker(commitmentPromise *promise.Promise[*Commitment], commitmentID iotago.CommitmentID) { - c.ticker.StartTicker(commitmentID) - - commitmentPromise.OnComplete(func() { - c.ticker.StopTicker(commitmentID) - }) -} - -// SendRequest sends a commitment request for the given commitment ID to all peers. -func (c *CommitmentsProtocol) SendRequest(commitmentID iotago.CommitmentID) { - c.workerPool.Submit(func() { - c.protocol.Network.RequestSlotCommitment(commitmentID) - - c.LogDebug("request", "commitment", commitmentID) - }) -} - -// ProcessResponse processes the given commitment response. -func (c *CommitmentsProtocol) ProcessResponse(commitment *model.Commitment, from peer.ID) { - c.workerPool.Submit(func() { - // verify the commitment's version corresponds to the protocol version for the slot. - if apiForSlot := c.protocol.APIForSlot(commitment.Slot()); apiForSlot.Version() != commitment.Commitment().ProtocolVersion { - c.LogDebug("received commitment with invalid protocol version", "commitment", commitment.ID(), "version", commitment.Commitment().ProtocolVersion, "expectedVersion", apiForSlot.Version(), "fromPeer", from) - - return - } - - if commitmentMetadata, published, err := c.protocol.Commitments.publishCommitment(commitment); err != nil { - c.LogError("failed to process commitment", "fromPeer", from, "err", err) - } else if published { - c.LogTrace("received response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) - } - }) -} - -// ProcessRequest processes the given commitment request. -func (c *CommitmentsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - submitLoggedRequest(c.workerPool, func() error { - commitment, err := c.protocol.Commitments.Commitment(commitmentID) - if err != nil { - return ierrors.Wrap(err, "failed to load commitment") - } - - c.protocol.Network.SendSlotCommitment(commitment, from) - - return nil - }, c, "commitmentID", commitmentID, "fromPeer", from) -} - -// Shutdown shuts down the commitment protocol and waits for all pending requests to be processed. -func (c *CommitmentsProtocol) Shutdown() { - c.ticker.Shutdown() -} From e5c2ea7d80e6f0073b96ea3b551dd96b17fd1c40 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 9 Dec 2023 00:28:30 +0100 Subject: [PATCH 16/28] Refactor: updated methods to be private --- pkg/protocol/commitments.go | 102 +++++++++++++++++++----------------- pkg/protocol/protocol.go | 4 +- 2 files changed, 56 insertions(+), 50 deletions(-) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 3a138472e..5e86345fc 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -56,60 +56,14 @@ func newCommitments(protocol *Protocol) *Commitments { shutdown := lo.Batch( c.initLogger(), c.initEngineCommitmentSynchronization(), - - c.ticker.Events.Tick.Hook(c.SendRequest).Unhook, + c.initTicker(), ) - protocol.Shutdown.OnTrigger(func() { - shutdown() - - c.ticker.Shutdown() - }) + protocol.Shutdown.OnTrigger(shutdown) return c } -// SendRequest sends a commitment request for the given commitment ID to all peers. -func (c *Commitments) SendRequest(commitmentID iotago.CommitmentID) { - c.workerPool.Submit(func() { - c.protocol.Network.RequestSlotCommitment(commitmentID) - - c.LogDebug("request", "commitment", commitmentID) - }) -} - -// ProcessResponse processes the given commitment response. -func (c *Commitments) ProcessResponse(commitment *model.Commitment, from peer.ID) { - c.workerPool.Submit(func() { - // verify the commitment's version corresponds to the protocol version for the slot. - if apiForSlot := c.protocol.APIForSlot(commitment.Slot()); apiForSlot.Version() != commitment.Commitment().ProtocolVersion { - c.LogDebug("received commitment with invalid protocol version", "commitment", commitment.ID(), "version", commitment.Commitment().ProtocolVersion, "expectedVersion", apiForSlot.Version(), "fromPeer", from) - - return - } - - if commitmentMetadata, published, err := c.protocol.Commitments.publishCommitment(commitment); err != nil { - c.LogError("failed to process commitment", "fromPeer", from, "err", err) - } else if published { - c.LogTrace("received response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) - } - }) -} - -// ProcessRequest processes the given commitment request. -func (c *Commitments) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - submitLoggedRequest(c.workerPool, func() error { - commitment, err := c.protocol.Commitments.Commitment(commitmentID) - if err != nil { - return ierrors.Wrap(err, "failed to load commitment") - } - - c.protocol.Network.SendSlotCommitment(commitment, from) - - return nil - }, c, "commitmentID", commitmentID, "fromPeer", from) -} - // Commitment returns the Commitment for the given commitmentID. It tries to retrieve the Commitment from the // cache first and falls back to serve it from the main engine if the Commitment is below the Root commitment. func (c *Commitments) Commitment(commitmentID iotago.CommitmentID) (commitment *model.Commitment, err error) { @@ -176,6 +130,17 @@ func (c *Commitments) initEngineCommitmentSynchronization() func() { }) } +// initTicker initializes the ticker that is used to send commitment requests. +func (c *Commitments) initTicker() (shutdown func()) { + unsubscribeFromTicker := c.ticker.Events.Tick.Hook(c.sendRequest).Unhook + + return func() { + unsubscribeFromTicker() + + c.ticker.Shutdown() + } +} + // publishRootCommitment publishes the root commitment of the main engine. func (c *Commitments) publishRootCommitment(mainChain *Chain, mainEngine *engine.Engine) func() { return mainEngine.RootCommitment.OnUpdate(func(_ *model.Commitment, rootCommitment *model.Commitment) { @@ -324,6 +289,47 @@ func (c *Commitments) initCommitment(commitment *Commitment, slotEvicted reactiv }) } +// sendRequest sends a commitment request for the given commitment ID to all peers. +func (c *Commitments) sendRequest(commitmentID iotago.CommitmentID) { + c.workerPool.Submit(func() { + c.protocol.Network.RequestSlotCommitment(commitmentID) + + c.LogDebug("request", "commitment", commitmentID) + }) +} + +// processResponse processes the given commitment response. +func (c *Commitments) processResponse(commitment *model.Commitment, from peer.ID) { + c.workerPool.Submit(func() { + // verify the commitment's version corresponds to the protocol version for the slot. + if apiForSlot := c.protocol.APIForSlot(commitment.Slot()); apiForSlot.Version() != commitment.Commitment().ProtocolVersion { + c.LogDebug("received commitment with invalid protocol version", "commitment", commitment.ID(), "version", commitment.Commitment().ProtocolVersion, "expectedVersion", apiForSlot.Version(), "fromPeer", from) + + return + } + + if commitmentMetadata, published, err := c.protocol.Commitments.publishCommitment(commitment); err != nil { + c.LogError("failed to process commitment", "fromPeer", from, "err", err) + } else if published { + c.LogTrace("received response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) + } + }) +} + +// processRequest processes the given commitment request. +func (c *Commitments) processRequest(commitmentID iotago.CommitmentID, from peer.ID) { + submitLoggedRequest(c.workerPool, func() error { + commitment, err := c.protocol.Commitments.Commitment(commitmentID) + if err != nil { + return ierrors.Wrap(err, "failed to load commitment") + } + + c.protocol.Network.SendSlotCommitment(commitment, from) + + return nil + }, c, "commitmentID", commitmentID, "fromPeer", from) +} + // targetEngine returns the engine that manages the data for the given commitment (or nil if no engine was found while // commitment IDs below the Root are always resolved against the main engine). func (c *Commitments) targetEngine(commitmentID iotago.CommitmentID) *engine.Engine { diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index 294b08730..1bebe5480 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -191,8 +191,8 @@ func (p *Protocol) initNetwork() (shutdown func()) { p.Network.OnError(func(err error, peer peer.ID) { p.LogError("network error", "peer", peer, "error", err) }), p.Network.OnBlockReceived(p.BlocksProtocol.ProcessResponse), p.Network.OnBlockRequestReceived(p.BlocksProtocol.ProcessRequest), - p.Network.OnCommitmentReceived(p.Commitments.ProcessResponse), - p.Network.OnCommitmentRequestReceived(p.Commitments.ProcessRequest), + p.Network.OnCommitmentReceived(p.Commitments.processResponse), + p.Network.OnCommitmentRequestReceived(p.Commitments.processRequest), p.Network.OnAttestationsReceived(p.AttestationsProtocol.ProcessResponse), p.Network.OnAttestationsRequestReceived(p.AttestationsProtocol.ProcessRequest), p.Network.OnWarpSyncResponseReceived(p.WarpSyncProtocol.ProcessResponse), From 6442740833882db4223bded0911ff87df10b22f0 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 9 Dec 2023 01:25:41 +0100 Subject: [PATCH 17/28] Refactor: cleaned up namespaces --- ...otocol_attestations.go => attestations.go} | 23 ++++++------ .../{protocol_blocks.go => blocks.go} | 20 +++++----- pkg/protocol/commitments.go | 2 +- pkg/protocol/protocol.go | 37 +++++++++---------- pkg/protocol/utils.go | 4 +- .../{protocol_warp_sync.go => warp_sync.go} | 22 +++++------ 6 files changed, 53 insertions(+), 55 deletions(-) rename pkg/protocol/{protocol_attestations.go => attestations.go} (87%) rename pkg/protocol/{protocol_blocks.go => blocks.go} (87%) rename pkg/protocol/{protocol_warp_sync.go => warp_sync.go} (90%) diff --git a/pkg/protocol/protocol_attestations.go b/pkg/protocol/attestations.go similarity index 87% rename from pkg/protocol/protocol_attestations.go rename to pkg/protocol/attestations.go index 7a24a4edd..b38fbe5cb 100644 --- a/pkg/protocol/protocol_attestations.go +++ b/pkg/protocol/attestations.go @@ -14,9 +14,8 @@ import ( "github.com/iotaledger/iota.go/v4/merklehasher" ) -// AttestationsProtocol is a subcomponent of the protocol that is responsible for handling attestation requests and -// responses. -type AttestationsProtocol struct { +// Attestations is a subcomponent of the protocol that is responsible for handling attestation requests and responses. +type Attestations struct { // protocol contains a reference to the Protocol instance that this component belongs to. protocol *Protocol @@ -33,9 +32,9 @@ type AttestationsProtocol struct { log.Logger } -// newAttestationsProtocol creates a new attestation protocol instance for the given protocol. -func newAttestationsProtocol(protocol *Protocol) *AttestationsProtocol { - a := &AttestationsProtocol{ +// newAttestations creates a new attestation protocol instance for the given protocol. +func newAttestations(protocol *Protocol) *Attestations { + a := &Attestations{ Logger: lo.Return1(protocol.Logger.NewChildLogger("Attestations")), protocol: protocol, workerPool: protocol.Workers.CreatePool("Attestations"), @@ -71,7 +70,7 @@ func newAttestationsProtocol(protocol *Protocol) *AttestationsProtocol { } // ProcessResponse processes the given attestation response. -func (a *AttestationsProtocol) ProcessResponse(commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { +func (a *Attestations) ProcessResponse(commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { a.workerPool.Submit(func() { commitmentMetadata, _, err := a.protocol.Commitments.publishCommitment(commitment) if err != nil { @@ -120,8 +119,8 @@ func (a *AttestationsProtocol) ProcessResponse(commitment *model.Commitment, att } // ProcessRequest processes the given attestation request. -func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - submitLoggedRequest(a.workerPool, func() error { +func (a *Attestations) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { + loggedWorkerPoolTask(a.workerPool, func() error { slotAPI, err := a.protocol.Commitments.targetEngine(commitmentID).CommittedSlot(commitmentID) if err != nil { return ierrors.Wrapf(err, "failed to load committed slot API") @@ -142,13 +141,13 @@ func (a *AttestationsProtocol) ProcessRequest(commitmentID iotago.CommitmentID, } // Shutdown shuts down the attestation protocol. -func (a *AttestationsProtocol) Shutdown() { +func (a *Attestations) Shutdown() { a.ticker.Shutdown() a.workerPool.Shutdown().ShutdownComplete.Wait() } // setupCommitmentVerifier sets up the commitment verifier for the given chain. -func (a *AttestationsProtocol) setupCommitmentVerifier(chain *Chain) (shutdown func()) { +func (a *Attestations) setupCommitmentVerifier(chain *Chain) (shutdown func()) { forkingPoint := chain.ForkingPoint.Get() if forkingPoint == nil { a.LogError("failed to retrieve forking point", "chain", chain.LogName()) @@ -184,7 +183,7 @@ func (a *AttestationsProtocol) setupCommitmentVerifier(chain *Chain) (shutdown f } // sendRequest sends an attestation request for the given commitment ID. -func (a *AttestationsProtocol) sendRequest(commitmentID iotago.CommitmentID) { +func (a *Attestations) sendRequest(commitmentID iotago.CommitmentID) { a.workerPool.Submit(func() { if commitmentMetadata, err := a.protocol.Commitments.Metadata(commitmentID, false); err == nil { a.protocol.Network.RequestAttestations(commitmentID) diff --git a/pkg/protocol/protocol_blocks.go b/pkg/protocol/blocks.go similarity index 87% rename from pkg/protocol/protocol_blocks.go rename to pkg/protocol/blocks.go index 22b2e900f..5134dc42c 100644 --- a/pkg/protocol/protocol_blocks.go +++ b/pkg/protocol/blocks.go @@ -15,8 +15,8 @@ import ( iotago "github.com/iotaledger/iota.go/v4" ) -// BlocksProtocol is a subcomponent of the protocol that is responsible for handling block requests and responses. -type BlocksProtocol struct { +// Blocks is a subcomponent of the protocol that is responsible for handling block requests and responses. +type Blocks struct { // protocol contains a reference to the Protocol instance that this component belongs to. protocol *Protocol @@ -31,9 +31,9 @@ type BlocksProtocol struct { log.Logger } -// newBlocksProtocol creates a new blocks protocol instance for the given protocol. -func newBlocksProtocol(protocol *Protocol) *BlocksProtocol { - b := &BlocksProtocol{ +// newBlocks creates a new blocks protocol instance for the given protocol. +func newBlocks(protocol *Protocol) *Blocks { + b := &Blocks{ Logger: lo.Return1(protocol.Logger.NewChildLogger("Blocks")), protocol: protocol, workerPool: protocol.Workers.CreatePool("Blocks"), @@ -66,7 +66,7 @@ func newBlocksProtocol(protocol *Protocol) *BlocksProtocol { } // SendRequest sends a request for the given block to all peers. -func (b *BlocksProtocol) SendRequest(blockID iotago.BlockID) { +func (b *Blocks) SendRequest(blockID iotago.BlockID) { b.workerPool.Submit(func() { b.protocol.Network.RequestBlock(blockID) @@ -75,7 +75,7 @@ func (b *BlocksProtocol) SendRequest(blockID iotago.BlockID) { } // SendResponse sends the given block to all peers. -func (b *BlocksProtocol) SendResponse(block *model.Block) { +func (b *Blocks) SendResponse(block *model.Block) { b.workerPool.Submit(func() { b.protocol.Network.SendBlock(block) @@ -84,7 +84,7 @@ func (b *BlocksProtocol) SendResponse(block *model.Block) { } // ProcessResponse processes the given block response. -func (b *BlocksProtocol) ProcessResponse(block *model.Block, from peer.ID) { +func (b *Blocks) ProcessResponse(block *model.Block, from peer.ID) { b.workerPool.Submit(func() { // abort if the commitment belongs to an evicted slot commitmentMetadata, err := b.protocol.Commitments.Metadata(block.ProtocolBlock().Header.SlotCommitmentID, true) @@ -110,7 +110,7 @@ func (b *BlocksProtocol) ProcessResponse(block *model.Block, from peer.ID) { } // ProcessRequest processes the given block request. -func (b *BlocksProtocol) ProcessRequest(blockID iotago.BlockID, from peer.ID) { +func (b *Blocks) ProcessRequest(blockID iotago.BlockID, from peer.ID) { b.workerPool.Submit(func() { block, exists := b.protocol.Engines.Main.Get().Block(blockID) if !exists { @@ -126,6 +126,6 @@ func (b *BlocksProtocol) ProcessRequest(blockID iotago.BlockID, from peer.ID) { } // Shutdown shuts down the blocks protocol and waits for all pending requests to be finished. -func (b *BlocksProtocol) Shutdown() { +func (b *Blocks) Shutdown() { b.workerPool.Shutdown().ShutdownComplete.Wait() } diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 5e86345fc..7ef4b9eb8 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -318,7 +318,7 @@ func (c *Commitments) processResponse(commitment *model.Commitment, from peer.ID // processRequest processes the given commitment request. func (c *Commitments) processRequest(commitmentID iotago.CommitmentID, from peer.ID) { - submitLoggedRequest(c.workerPool, func() error { + loggedWorkerPoolTask(c.workerPool, func() error { commitment, err := c.protocol.Commitments.Commitment(commitmentID) if err != nil { return ierrors.Wrap(err, "failed to load commitment") diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index 1bebe5480..ffeb1a7ea 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -38,15 +38,14 @@ type Protocol struct { // Chains contains the chains that are managed by the protocol. Chains *Chains - // BlocksProtocol contains the subcomponent that is responsible for handling block requests and responses. - BlocksProtocol *BlocksProtocol + // Blocks contains the subcomponent that is responsible for handling block requests and responses. + Blocks *Blocks - // AttestationsProtocol contains the subcomponent that is responsible for handling attestation requests and - // responses. - AttestationsProtocol *AttestationsProtocol + // Attestations contains the subcomponent that is responsible for handling attestation requests and responses. + Attestations *Attestations - // WarpSyncProtocol contains the subcomponent that is responsible for handling warp sync requests and responses. - WarpSyncProtocol *WarpSyncProtocol + // WarpSync contains the subcomponent that is responsible for handling warp sync requests and responses. + WarpSync *WarpSync // Engines contains the engines that are managed by the protocol. Engines *Engines @@ -146,17 +145,17 @@ func (p *Protocol) LatestAPI() iotago.API { // initSubcomponents initializes the subcomponents of the protocol and returns a function that shuts them down. func (p *Protocol) initSubcomponents(networkEndpoint network.Endpoint) (shutdown func()) { p.Network = core.NewProtocol(networkEndpoint, p.Workers.CreatePool("NetworkProtocol"), p) - p.BlocksProtocol = newBlocksProtocol(p) - p.AttestationsProtocol = newAttestationsProtocol(p) - p.WarpSyncProtocol = newWarpSyncProtocol(p) + p.Blocks = newBlocks(p) + p.Attestations = newAttestations(p) + p.WarpSync = newWarpSync(p) p.Commitments = newCommitments(p) p.Chains = newChains(p) p.Engines = newEngines(p) return func() { - p.BlocksProtocol.Shutdown() - p.AttestationsProtocol.Shutdown() - p.WarpSyncProtocol.Shutdown() + p.Blocks.Shutdown() + p.Attestations.Shutdown() + p.WarpSync.Shutdown() p.Network.Shutdown() p.Workers.WaitChildren() p.Engines.Shutdown.Trigger() @@ -189,14 +188,14 @@ func (p *Protocol) initGlobalEventsRedirection() (shutdown func()) { func (p *Protocol) initNetwork() (shutdown func()) { return lo.Batch( p.Network.OnError(func(err error, peer peer.ID) { p.LogError("network error", "peer", peer, "error", err) }), - p.Network.OnBlockReceived(p.BlocksProtocol.ProcessResponse), - p.Network.OnBlockRequestReceived(p.BlocksProtocol.ProcessRequest), + p.Network.OnBlockReceived(p.Blocks.ProcessResponse), + p.Network.OnBlockRequestReceived(p.Blocks.ProcessRequest), p.Network.OnCommitmentReceived(p.Commitments.processResponse), p.Network.OnCommitmentRequestReceived(p.Commitments.processRequest), - p.Network.OnAttestationsReceived(p.AttestationsProtocol.ProcessResponse), - p.Network.OnAttestationsRequestReceived(p.AttestationsProtocol.ProcessRequest), - p.Network.OnWarpSyncResponseReceived(p.WarpSyncProtocol.ProcessResponse), - p.Network.OnWarpSyncRequestReceived(p.WarpSyncProtocol.ProcessRequest), + p.Network.OnAttestationsReceived(p.Attestations.ProcessResponse), + p.Network.OnAttestationsRequestReceived(p.Attestations.ProcessRequest), + p.Network.OnWarpSyncResponseReceived(p.WarpSync.ProcessResponse), + p.Network.OnWarpSyncRequestReceived(p.WarpSync.ProcessRequest), ) } diff --git a/pkg/protocol/utils.go b/pkg/protocol/utils.go index f31af24c0..c97d5977b 100644 --- a/pkg/protocol/utils.go +++ b/pkg/protocol/utils.go @@ -5,8 +5,8 @@ import ( "github.com/iotaledger/hive.go/runtime/workerpool" ) -// submitLoggedRequest is a generic utility function that submits a request to the given worker pool logging the result. -func submitLoggedRequest(workerPool *workerpool.WorkerPool, processRequest func() error, logger log.Logger, loggerArgs ...any) { +// loggedWorkerPoolTask is a generic utility function that submits a request to the given worker pool logging the result. +func loggedWorkerPoolTask(workerPool *workerpool.WorkerPool, processRequest func() error, logger log.Logger, loggerArgs ...any) { workerPool.Submit(func() { if err := processRequest(); err != nil { logger.LogDebug("failed to answer request", append(loggerArgs, "err", err)...) diff --git a/pkg/protocol/protocol_warp_sync.go b/pkg/protocol/warp_sync.go similarity index 90% rename from pkg/protocol/protocol_warp_sync.go rename to pkg/protocol/warp_sync.go index a2aeea62f..473d23be5 100644 --- a/pkg/protocol/protocol_warp_sync.go +++ b/pkg/protocol/warp_sync.go @@ -19,8 +19,8 @@ import ( "github.com/iotaledger/iota.go/v4/merklehasher" ) -// WarpSyncProtocol is a subcomponent of the protocol that is responsible for handling warp sync requests and responses. -type WarpSyncProtocol struct { +// WarpSync is a subcomponent of the protocol that is responsible for handling warp sync requests and responses. +type WarpSync struct { // protocol contains a reference to the Protocol instance that this component belongs to. protocol *Protocol @@ -34,9 +34,9 @@ type WarpSyncProtocol struct { log.Logger } -// newWarpSyncProtocol creates a new warp sync protocol instance for the given protocol. -func newWarpSyncProtocol(protocol *Protocol) *WarpSyncProtocol { - c := &WarpSyncProtocol{ +// newWarpSync creates a new warp sync protocol instance for the given protocol. +func newWarpSync(protocol *Protocol) *WarpSync { + c := &WarpSync{ Logger: lo.Return1(protocol.Logger.NewChildLogger("WarpSync")), protocol: protocol, workerPool: protocol.Workers.CreatePool("WarpSync", workerpool.WithWorkerCount(1)), @@ -70,7 +70,7 @@ func newWarpSyncProtocol(protocol *Protocol) *WarpSyncProtocol { } // SendRequest sends a warp sync request for the given commitment ID to all peers. -func (w *WarpSyncProtocol) SendRequest(commitmentID iotago.CommitmentID) { +func (w *WarpSync) SendRequest(commitmentID iotago.CommitmentID) { w.workerPool.Submit(func() { if commitmentMetadata, err := w.protocol.Commitments.Metadata(commitmentID, false); err == nil { w.protocol.Network.SendWarpSyncRequest(commitmentID) @@ -81,7 +81,7 @@ func (w *WarpSyncProtocol) SendRequest(commitmentID iotago.CommitmentID) { } // SendResponse sends a warp sync response for the given commitment ID to the given peer. -func (w *WarpSyncProtocol) SendResponse(commitment *Commitment, blockIDsBySlotCommitment map[iotago.CommitmentID]iotago.BlockIDs, roots *iotago.Roots, transactionIDs iotago.TransactionIDs, to peer.ID) { +func (w *WarpSync) SendResponse(commitment *Commitment, blockIDsBySlotCommitment map[iotago.CommitmentID]iotago.BlockIDs, roots *iotago.Roots, transactionIDs iotago.TransactionIDs, to peer.ID) { w.workerPool.Submit(func() { w.protocol.Network.SendWarpSyncResponse(commitment.ID(), blockIDsBySlotCommitment, roots.TangleProof(), transactionIDs, roots.MutationProof(), to) @@ -90,7 +90,7 @@ func (w *WarpSyncProtocol) SendResponse(commitment *Commitment, blockIDsBySlotCo } // ProcessResponse processes the given warp sync response. -func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsBySlotCommitment map[iotago.CommitmentID]iotago.BlockIDs, proof *merklehasher.Proof[iotago.Identifier], transactionIDs iotago.TransactionIDs, mutationProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { +func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsBySlotCommitment map[iotago.CommitmentID]iotago.BlockIDs, proof *merklehasher.Proof[iotago.Identifier], transactionIDs iotago.TransactionIDs, mutationProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { w.workerPool.Submit(func() { commitmentMetadata, err := w.protocol.Commitments.Metadata(commitmentID) if err != nil { @@ -290,8 +290,8 @@ func (w *WarpSyncProtocol) ProcessResponse(commitmentID iotago.CommitmentID, blo } // ProcessRequest processes the given warp sync request. -func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - submitLoggedRequest(w.workerPool, func() (err error) { +func (w *WarpSync) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { + loggedWorkerPoolTask(w.workerPool, func() (err error) { slotAPI, err := w.protocol.Commitments.targetEngine(commitmentID).CommittedSlot(commitmentID) if err != nil { return ierrors.Wrap(err, "failed to load slot api") @@ -319,6 +319,6 @@ func (w *WarpSyncProtocol) ProcessRequest(commitmentID iotago.CommitmentID, from } // Shutdown shuts down the warp sync protocol. -func (w *WarpSyncProtocol) Shutdown() { +func (w *WarpSync) Shutdown() { w.ticker.Shutdown() } From 995bab5587b39f51904b9af6f86530b3bdd4728d Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 9 Dec 2023 01:55:18 +0100 Subject: [PATCH 18/28] Refactor: cleaned up slotcommittedapi --- pkg/protocol/attestations.go | 7 +------ pkg/protocol/engine/committed_slot_api.go | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/pkg/protocol/attestations.go b/pkg/protocol/attestations.go index b38fbe5cb..b54ce773c 100644 --- a/pkg/protocol/attestations.go +++ b/pkg/protocol/attestations.go @@ -126,12 +126,7 @@ func (a *Attestations) ProcessRequest(commitmentID iotago.CommitmentID, from pee return ierrors.Wrapf(err, "failed to load committed slot API") } - commitment, err := slotAPI.Commitment() - if err != nil { - return ierrors.Wrapf(err, "failed to load commitment") - } - - attestations, proof, err := slotAPI.Attestations() + commitment, attestations, proof, err := slotAPI.Attestations() if err != nil { return ierrors.Wrapf(err, "failed to load attestations") } diff --git a/pkg/protocol/engine/committed_slot_api.go b/pkg/protocol/engine/committed_slot_api.go index a82a82128..e88497883 100644 --- a/pkg/protocol/engine/committed_slot_api.go +++ b/pkg/protocol/engine/committed_slot_api.go @@ -38,24 +38,30 @@ func (c *CommittedSlotAPI) Commitment() (commitment *model.Commitment, err error return commitment, nil } -func (c *CommittedSlotAPI) Attestations() (attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], err error) { +// Attestations returns the commitment, attestations and the merkle proof of the slot. +func (c *CommittedSlotAPI) Attestations() (commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], err error) { + commitment, err = c.Commitment() + if err != nil { + return nil, nil, nil, ierrors.Wrap(err, "failed to load commitment") + } + if attestations, err = c.engine.Attestations.Get(c.CommitmentID.Slot()); err != nil { - return nil, nil, ierrors.Wrapf(err, "failed to load attestations") + return nil, nil, nil, ierrors.Wrap(err, "failed to load attestations") } rootsStorage, err := c.engine.Storage.Roots(c.CommitmentID.Slot()) if err != nil { - return nil, nil, ierrors.Wrapf(err, "failed to load roots storage") + return nil, nil, nil, ierrors.Wrapf(err, "failed to load roots storage") } roots, exists, err := rootsStorage.Load(c.CommitmentID) if err != nil { - return nil, nil, ierrors.Wrapf(err, "failed to load roots") + return nil, nil, nil, ierrors.Wrapf(err, "failed to load roots") } else if !exists { - return nil, nil, ierrors.New("roots not found") + return nil, nil, nil, ierrors.New("roots not found") } - return attestations, roots.AttestationsProof(), nil + return commitment, attestations, roots.AttestationsProof(), nil } // Roots returns the roots of the slot. From e8f286f2affebecab8240fda44b8f5c9e666008c Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sun, 10 Dec 2023 21:59:18 +0100 Subject: [PATCH 19/28] Refactor: more cleanup --- pkg/protocol/attestations.go | 135 ++++++++++++++++++----------------- pkg/protocol/blocks.go | 6 +- pkg/protocol/commitments.go | 77 ++++++++++---------- pkg/protocol/protocol.go | 4 +- pkg/protocol/warp_sync.go | 44 ++++++------ 5 files changed, 133 insertions(+), 133 deletions(-) diff --git a/pkg/protocol/attestations.go b/pkg/protocol/attestations.go index b54ce773c..5b749a2ef 100644 --- a/pkg/protocol/attestations.go +++ b/pkg/protocol/attestations.go @@ -69,70 +69,14 @@ func newAttestations(protocol *Protocol) *Attestations { return a } -// ProcessResponse processes the given attestation response. -func (a *Attestations) ProcessResponse(commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { - a.workerPool.Submit(func() { - commitmentMetadata, _, err := a.protocol.Commitments.publishCommitment(commitment) - if err != nil { - a.LogDebug("failed to publish commitment when processing attestations", "commitmentID", commitment.ID(), "peer", from, "error", err) - - return - } - - if commitmentMetadata.AttestedWeight.Compute(func(currentWeight uint64) uint64 { - if !commitmentMetadata.RequestAttestations.Get() { - a.LogTrace("received attestations for previously attested commitment", "commitment", commitmentMetadata.LogName()) - - return currentWeight - } - - chain := commitmentMetadata.Chain.Get() - if chain == nil { - a.LogDebug("failed to find chain for commitment when processing attestations", "commitment", commitmentMetadata.LogName()) - - return currentWeight - } - - commitmentVerifier, exists := a.commitmentVerifiers.Get(chain.ForkingPoint.Get().ID()) - if !exists || commitmentVerifier == nil { - a.LogDebug("failed to retrieve commitment verifier", "commitment", commitmentMetadata.LogName()) - - return currentWeight - } - - _, actualWeight, err := commitmentVerifier.verifyCommitment(commitmentMetadata, attestations, merkleProof) - if err != nil { - a.LogError("failed to verify commitment", "commitment", commitmentMetadata.LogName(), "error", err) - - return currentWeight - } - - if actualWeight > currentWeight { - a.LogDebug("received response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) - } - - return actualWeight - }) > 0 { - commitmentMetadata.IsAttested.Set(true) - } - }) -} - -// ProcessRequest processes the given attestation request. -func (a *Attestations) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { - loggedWorkerPoolTask(a.workerPool, func() error { - slotAPI, err := a.protocol.Commitments.targetEngine(commitmentID).CommittedSlot(commitmentID) - if err != nil { - return ierrors.Wrapf(err, "failed to load committed slot API") - } - - commitment, attestations, proof, err := slotAPI.Attestations() - if err != nil { - return ierrors.Wrapf(err, "failed to load attestations") - } +// Get returns the commitment, and its attestations (including the corresponding merkle proof). +func (a *Attestations) Get(commitmentID iotago.CommitmentID) (commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], err error) { + slotAPI, err := a.protocol.Commitments.targetEngine(commitmentID).CommittedSlot(commitmentID) + if err != nil { + return nil, nil, nil, ierrors.Wrapf(err, "failed to load committed slot API") + } - return a.protocol.Network.SendAttestations(commitment, attestations, proof, from) - }, a, "commitmentID", commitmentID, "fromPeer", from) + return slotAPI.Attestations() } // Shutdown shuts down the attestation protocol. @@ -180,12 +124,73 @@ func (a *Attestations) setupCommitmentVerifier(chain *Chain) (shutdown func()) { // sendRequest sends an attestation request for the given commitment ID. func (a *Attestations) sendRequest(commitmentID iotago.CommitmentID) { a.workerPool.Submit(func() { - if commitmentMetadata, err := a.protocol.Commitments.Metadata(commitmentID, false); err == nil { + if commitment, err := a.protocol.Commitments.Get(commitmentID, false); err == nil { a.protocol.Network.RequestAttestations(commitmentID) - a.LogDebug("request", "commitment", commitmentMetadata.LogName()) + a.LogDebug("request", "commitment", commitment.LogName()) } else { a.LogError("failed to load commitment", "commitmentID", commitmentID, "err", err) } }) } + +// processResponse processes the given attestation response. +func (a *Attestations) processResponse(commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { + a.workerPool.Submit(func() { + publishedCommitment, _, err := a.protocol.Commitments.publishCommitment(commitment) + if err != nil { + a.LogDebug("failed to publish commitment when processing attestations", "commitmentID", commitment.ID(), "peer", from, "error", err) + + return + } + + if publishedCommitment.AttestedWeight.Compute(func(currentWeight uint64) uint64 { + if !publishedCommitment.RequestAttestations.Get() { + a.LogTrace("received attestations for previously attested commitment", "commitment", publishedCommitment.LogName()) + + return currentWeight + } + + chain := publishedCommitment.Chain.Get() + if chain == nil { + a.LogDebug("failed to find chain for commitment when processing attestations", "commitment", publishedCommitment.LogName()) + + return currentWeight + } + + commitmentVerifier, exists := a.commitmentVerifiers.Get(chain.ForkingPoint.Get().ID()) + if !exists || commitmentVerifier == nil { + a.LogDebug("failed to retrieve commitment verifier", "commitment", publishedCommitment.LogName()) + + return currentWeight + } + + _, actualWeight, err := commitmentVerifier.verifyCommitment(publishedCommitment, attestations, merkleProof) + if err != nil { + a.LogError("failed to verify commitment", "commitment", publishedCommitment.LogName(), "error", err) + + return currentWeight + } + + if actualWeight > currentWeight { + a.LogDebug("received response", "commitment", publishedCommitment.LogName(), "fromPeer", from) + } + + return actualWeight + }) > 0 { + publishedCommitment.IsAttested.Set(true) + } + }) +} + +// processRequest processes the given attestation request. +func (a *Attestations) processRequest(commitmentID iotago.CommitmentID, from peer.ID) { + loggedWorkerPoolTask(a.workerPool, func() error { + commitment, attestations, proof, err := a.Get(commitmentID) + if err != nil { + return ierrors.Wrap(err, "failed to load attestations") + } + + return a.protocol.Network.SendAttestations(commitment, attestations, proof, from) + }, a, "commitmentID", commitmentID, "fromPeer", from) +} diff --git a/pkg/protocol/blocks.go b/pkg/protocol/blocks.go index 5134dc42c..7f336b535 100644 --- a/pkg/protocol/blocks.go +++ b/pkg/protocol/blocks.go @@ -87,7 +87,7 @@ func (b *Blocks) SendResponse(block *model.Block) { func (b *Blocks) ProcessResponse(block *model.Block, from peer.ID) { b.workerPool.Submit(func() { // abort if the commitment belongs to an evicted slot - commitmentMetadata, err := b.protocol.Commitments.Metadata(block.ProtocolBlock().Header.SlotCommitmentID, true) + commitment, err := b.protocol.Commitments.Get(block.ProtocolBlock().Header.SlotCommitmentID, true) if err != nil && ierrors.Is(ErrorSlotEvicted, err) { b.LogError("dropped block referencing unsolidifiable commitment", "commitmentID", block.ProtocolBlock().Header.SlotCommitmentID, "blockID", block.ID(), "err", err) @@ -95,7 +95,7 @@ func (b *Blocks) ProcessResponse(block *model.Block, from peer.ID) { } // add the block to the dropped blocks buffer if we could not dispatch it to the chain - if commitmentMetadata == nil || !commitmentMetadata.Chain.Get().DispatchBlock(block, from) { + if commitment == nil || !commitment.Chain.Get().DispatchBlock(block, from) { if !b.droppedBlocksBuffer.Add(block.ProtocolBlock().Header.SlotCommitmentID, types.NewTuple(block, from)) { b.LogError("failed to add dropped block referencing unsolid commitment to dropped blocks buffer", "commitmentID", block.ProtocolBlock().Header.SlotCommitmentID, "blockID", block.ID()) } else { @@ -105,7 +105,7 @@ func (b *Blocks) ProcessResponse(block *model.Block, from peer.ID) { return } - b.LogTrace("received block", "blockID", block.ID(), "commitment", commitmentMetadata.LogName()) + b.LogTrace("received block", "blockID", block.ID(), "commitment", commitment.LogName()) }) } diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 7ef4b9eb8..6d505f891 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -64,28 +64,10 @@ func newCommitments(protocol *Protocol) *Commitments { return c } -// Commitment returns the Commitment for the given commitmentID. It tries to retrieve the Commitment from the -// cache first and falls back to serve it from the main engine if the Commitment is below the Root commitment. -func (c *Commitments) Commitment(commitmentID iotago.CommitmentID) (commitment *model.Commitment, err error) { - commitmentMetadata, err := c.Metadata(commitmentID) - if err == nil { - return commitmentMetadata.Commitment, nil - } else if !ierrors.Is(err, ErrorCommitmentNotFound) || commitmentID.Slot() > c.Root.Get().Slot() { - return nil, ierrors.Wrapf(err, "failed to load commitment metadata") - } - - slotAPI, err := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID) - if err != nil { - return nil, ierrors.Wrapf(err, "failed to load engine API") - } - - return slotAPI.Commitment() -} - -// Metadata returns the protocol Commitment for the given commitmentID. If the Commitment is not available yet, it +// Get returns the protocol Commitment for the given commitmentID. If the Commitment is not available yet, it // will return an ErrorCommitmentNotFound. It is possible to trigger a request for the Commitment by passing true as the // second argument. -func (c *Commitments) Metadata(commitmentID iotago.CommitmentID, requestIfMissing ...bool) (commitmentMetadata *Commitment, err error) { +func (c *Commitments) Get(commitmentID iotago.CommitmentID, requestIfMissing ...bool) (commitment *Commitment, err error) { cachedRequest, exists := c.cachedRequests.Get(commitmentID) if !exists && lo.First(requestIfMissing) { if cachedRequest = c.cachedRequest(commitmentID, true); cachedRequest.WasRejected() { @@ -144,22 +126,22 @@ func (c *Commitments) initTicker() (shutdown func()) { // publishRootCommitment publishes the root commitment of the main engine. func (c *Commitments) publishRootCommitment(mainChain *Chain, mainEngine *engine.Engine) func() { return mainEngine.RootCommitment.OnUpdate(func(_ *model.Commitment, rootCommitment *model.Commitment) { - commitmentMetadata, published, err := c.publishCommitment(rootCommitment) + publishedCommitment, published, err := c.publishCommitment(rootCommitment) if err != nil { c.LogError("failed to publish new root commitment", "id", rootCommitment.ID(), "error", err) return } - commitmentMetadata.IsRoot.Set(true) + publishedCommitment.IsRoot.Set(true) if published { - commitmentMetadata.Chain.Set(mainChain) + publishedCommitment.Chain.Set(mainChain) } // TODO: USE SET HERE (debug eviction issues) - mainChain.ForkingPoint.DefaultTo(commitmentMetadata) + mainChain.ForkingPoint.DefaultTo(publishedCommitment) - c.Root.Set(commitmentMetadata) + c.Root.Set(publishedCommitment) }) } @@ -187,7 +169,7 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi } // publish the commitment - commitmentMetadata, _, err := c.publishCommitment(commitment) + publishedCommitment, _, err := c.publishCommitment(commitment) if err != nil { c.LogError("failed to publish commitment from engine", "engine", engine.LogName(), "commitment", commitment, "err", err) @@ -196,9 +178,9 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi // mark it as produced by ourselves and force it to be on the right chain (in case our chain produced a // different commitment than the one we erroneously expected it to be - we always trust our engine most). - commitmentMetadata.AttestedWeight.Set(commitmentMetadata.Weight.Get()) - commitmentMetadata.IsVerified.Set(true) - commitmentMetadata.forceChain(chain) + publishedCommitment.AttestedWeight.Set(publishedCommitment.Weight.Get()) + publishedCommitment.IsVerified.Set(true) + publishedCommitment.forceChain(chain) } }) } @@ -206,7 +188,7 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi // publishCommitment publishes the given commitment as a Commitment instance. If the Commitment was already // published, it will return the existing Commitment instance. Otherwise, it will create a new Commitment instance and // resolve the Promise that was created for it. -func (c *Commitments) publishCommitment(commitment *model.Commitment) (commitmentMetadata *Commitment, published bool, err error) { +func (c *Commitments) publishCommitment(commitment *model.Commitment) (publishedCommitment *Commitment, published bool, err error) { // retrieve promise and abort if it was already rejected cachedRequest := c.cachedRequest(commitment.ID()) if cachedRequest.WasRejected() { @@ -214,14 +196,14 @@ func (c *Commitments) publishCommitment(commitment *model.Commitment) (commitmen } // otherwise try to provideCommitment it and determine if we were the goroutine that resolved it - commitmentMetadata = newCommitment(c, commitment) - cachedRequest.Resolve(commitmentMetadata).OnSuccess(func(resolvedCommitment *Commitment) { - if published = resolvedCommitment == commitmentMetadata; !published { - commitmentMetadata = resolvedCommitment + publishedCommitment = newCommitment(c, commitment) + cachedRequest.Resolve(publishedCommitment).OnSuccess(func(resolvedCommitment *Commitment) { + if published = resolvedCommitment == publishedCommitment; !published { + publishedCommitment = resolvedCommitment } }) - return commitmentMetadata, published, nil + return publishedCommitment, published, nil } // cachedRequest returns a singleton Promise for the given commitmentID. If the Promise does not exist yet, it will be @@ -308,18 +290,33 @@ func (c *Commitments) processResponse(commitment *model.Commitment, from peer.ID return } - if commitmentMetadata, published, err := c.protocol.Commitments.publishCommitment(commitment); err != nil { + if publishedCommitment, published, err := c.protocol.Commitments.publishCommitment(commitment); err != nil { c.LogError("failed to process commitment", "fromPeer", from, "err", err) } else if published { - c.LogTrace("received response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) + c.LogTrace("received response", "commitment", publishedCommitment.LogName(), "fromPeer", from) } }) } // processRequest processes the given commitment request. func (c *Commitments) processRequest(commitmentID iotago.CommitmentID, from peer.ID) { + loadCommitment := func() (*model.Commitment, error) { + if commitment, err := c.Get(commitmentID); err == nil { + return commitment.Commitment, nil + } else if !ierrors.Is(err, ErrorCommitmentNotFound) || commitmentID.Slot() > c.Root.Get().Slot() { + return nil, ierrors.Wrap(err, "failed to load commitment metadata") + } + + slotAPI, err := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID) + if err != nil { + return nil, ierrors.Wrap(err, "failed to load engine API") + } + + return slotAPI.Commitment() + } + loggedWorkerPoolTask(c.workerPool, func() error { - commitment, err := c.protocol.Commitments.Commitment(commitmentID) + commitment, err := loadCommitment() if err != nil { return ierrors.Wrap(err, "failed to load commitment") } @@ -337,8 +334,8 @@ func (c *Commitments) targetEngine(commitmentID iotago.CommitmentID) *engine.Eng return c.protocol.Engines.Main.Get() } - if commitmentMetadata, err := c.Metadata(commitmentID); err == nil { - return commitmentMetadata.TargetEngine() + if commitment, err := c.Get(commitmentID); err == nil { + return commitment.TargetEngine() } else if !ierrors.Is(err, ErrorCommitmentNotFound) { c.LogDebug("failed to retrieve commitment", "commitmentID", commitmentID, "err", err) } diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index ffeb1a7ea..03e0ed602 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -192,8 +192,8 @@ func (p *Protocol) initNetwork() (shutdown func()) { p.Network.OnBlockRequestReceived(p.Blocks.ProcessRequest), p.Network.OnCommitmentReceived(p.Commitments.processResponse), p.Network.OnCommitmentRequestReceived(p.Commitments.processRequest), - p.Network.OnAttestationsReceived(p.Attestations.ProcessResponse), - p.Network.OnAttestationsRequestReceived(p.Attestations.ProcessRequest), + p.Network.OnAttestationsReceived(p.Attestations.processResponse), + p.Network.OnAttestationsRequestReceived(p.Attestations.processRequest), p.Network.OnWarpSyncResponseReceived(p.WarpSync.ProcessResponse), p.Network.OnWarpSyncRequestReceived(p.WarpSync.ProcessRequest), ) diff --git a/pkg/protocol/warp_sync.go b/pkg/protocol/warp_sync.go index 473d23be5..669caee28 100644 --- a/pkg/protocol/warp_sync.go +++ b/pkg/protocol/warp_sync.go @@ -72,11 +72,9 @@ func newWarpSync(protocol *Protocol) *WarpSync { // SendRequest sends a warp sync request for the given commitment ID to all peers. func (w *WarpSync) SendRequest(commitmentID iotago.CommitmentID) { w.workerPool.Submit(func() { - if commitmentMetadata, err := w.protocol.Commitments.Metadata(commitmentID, false); err == nil { - w.protocol.Network.SendWarpSyncRequest(commitmentID) + w.protocol.Network.SendWarpSyncRequest(commitmentID) - w.LogDebug("request", "commitment", commitmentMetadata.LogName()) - } + w.LogDebug("request", "commitmentID", commitmentID) }) } @@ -92,7 +90,7 @@ func (w *WarpSync) SendResponse(commitment *Commitment, blockIDsBySlotCommitment // ProcessResponse processes the given warp sync response. func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsBySlotCommitment map[iotago.CommitmentID]iotago.BlockIDs, proof *merklehasher.Proof[iotago.Identifier], transactionIDs iotago.TransactionIDs, mutationProof *merklehasher.Proof[iotago.Identifier], from peer.ID) { w.workerPool.Submit(func() { - commitmentMetadata, err := w.protocol.Commitments.Metadata(commitmentID) + commitment, err := w.protocol.Commitments.Get(commitmentID) if err != nil { if !ierrors.Is(err, ErrorCommitmentNotFound) { w.LogError("failed to load commitment for response", "commitmentID", commitmentID, "fromPeer", from, "err", err) @@ -103,9 +101,9 @@ func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsByS return } - chain := commitmentMetadata.Chain.Get() + chain := commitment.Chain.Get() if chain == nil { - w.LogTrace("failed to get chain for response", "commitment", commitmentMetadata.LogName(), "fromPeer", from) + w.LogTrace("failed to get chain for response", "commitment", commitment.LogName(), "fromPeer", from) return } @@ -116,16 +114,16 @@ func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsByS return } - targetEngine := commitmentMetadata.TargetEngine() + targetEngine := commitment.TargetEngine() if targetEngine == nil { - w.LogDebug("failed to get target engine for response", "commitment", commitmentMetadata.LogName()) + w.LogDebug("failed to get target engine for response", "commitment", commitment.LogName()) return } - commitmentMetadata.BlocksToWarpSync.Compute(func(blocksToWarpSync ds.Set[iotago.BlockID]) ds.Set[iotago.BlockID] { - if blocksToWarpSync != nil || !commitmentMetadata.WarpSyncBlocks.Get() { - w.LogTrace("response for already synced commitment", "commitment", commitmentMetadata.LogName(), "fromPeer", from) + commitment.BlocksToWarpSync.Compute(func(blocksToWarpSync ds.Set[iotago.BlockID]) ds.Set[iotago.BlockID] { + if blocksToWarpSync != nil || !commitment.WarpSyncBlocks.Get() { + w.LogTrace("response for already synced commitment", "commitment", commitment.LogName(), "fromPeer", from) return blocksToWarpSync } @@ -140,8 +138,8 @@ func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsByS } } - if !iotago.VerifyProof(proof, acceptedBlocks.Root(), commitmentMetadata.RootsID()) { - w.LogError("failed to verify blocks proof", "commitment", commitmentMetadata.LogName(), "blockIDs", blockIDsBySlotCommitment, "proof", proof, "fromPeer", from) + if !iotago.VerifyProof(proof, acceptedBlocks.Root(), commitment.RootsID()) { + w.LogError("failed to verify blocks proof", "commitment", commitment.LogName(), "blockIDs", blockIDsBySlotCommitment, "proof", proof, "fromPeer", from) return blocksToWarpSync } @@ -151,8 +149,8 @@ func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsByS _ = acceptedTransactionIDs.Add(transactionID) // a mapdb can never return an error } - if !iotago.VerifyProof(mutationProof, acceptedTransactionIDs.Root(), commitmentMetadata.RootsID()) { - w.LogError("failed to verify mutations proof", "commitment", commitmentMetadata.LogName(), "transactionIDs", transactionIDs, "proof", mutationProof, "fromPeer", from) + if !iotago.VerifyProof(mutationProof, acceptedTransactionIDs.Root(), commitment.RootsID()) { + w.LogError("failed to verify mutations proof", "commitment", commitment.LogName(), "transactionIDs", transactionIDs, "proof", mutationProof, "fromPeer", from) return blocksToWarpSync } @@ -233,7 +231,7 @@ func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsByS // Once all blocks are fully booked we can mark the commitment that is minCommittableAge older as this // commitment to be committable. - commitmentMetadata.IsSynced.OnUpdateOnce(func(_ bool, _ bool) { + commitment.IsSynced.OnUpdateOnce(func(_ bool, _ bool) { // update the flag in a worker since it can potentially cause a commit w.workerPool.Submit(func() { if committableCommitment, exists := chain.Commitment(commitmentID.Slot() - targetEngine.LatestAPI().ProtocolParameters().MinCommittableAge()); exists { @@ -243,16 +241,16 @@ func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsByS }) // force commit one by one and wait for the parent to be verified before we commit the next one - commitmentMetadata.Parent.WithNonEmptyValue(func(parent *Commitment) (teardown func()) { + commitment.Parent.WithNonEmptyValue(func(parent *Commitment) (teardown func()) { return parent.IsVerified.WithNonEmptyValue(func(_ bool) (teardown func()) { - return commitmentMetadata.IsCommittable.OnTrigger(commitmentFunc) + return commitment.IsCommittable.OnTrigger(commitmentFunc) }) }) if totalBlocks == 0 { // mark empty slots as committable and synced - commitmentMetadata.IsCommittable.Set(true) - commitmentMetadata.IsSynced.Set(true) + commitment.IsCommittable.Set(true) + commitment.IsSynced.Set(true) return blocksToWarpSync } @@ -277,12 +275,12 @@ func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsByS return } - commitmentMetadata.IsSynced.Set(true) + commitment.IsSynced.Set(true) }) } } - w.LogDebug("received response", "commitment", commitmentMetadata.LogName()) + w.LogDebug("received response", "commitment", commitment.LogName()) return blocksToWarpSync }) From e6c25bcbf99e7f426cc7d02740c864b68786cc95 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sun, 10 Dec 2023 23:11:27 +0100 Subject: [PATCH 20/28] Refactor: cleaned up attestations --- pkg/protocol/attestations.go | 71 ++++++++++++++++++++++-------------- pkg/protocol/protocol.go | 1 - 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/pkg/protocol/attestations.go b/pkg/protocol/attestations.go index 5b749a2ef..eb0519b44 100644 --- a/pkg/protocol/attestations.go +++ b/pkg/protocol/attestations.go @@ -22,8 +22,8 @@ type Attestations struct { // workerPool contains the worker pool that is used to process attestation requests and responses asynchronously. workerPool *workerpool.WorkerPool - // ticker contains the ticker that is used to send attestation requests. - ticker *eventticker.EventTicker[iotago.SlotIndex, iotago.CommitmentID] + // requester contains the ticker that is used to send attestation requests. + requester *eventticker.EventTicker[iotago.SlotIndex, iotago.CommitmentID] // commitmentVerifiers contains the commitment verifiers that are used to verify received attestations. commitmentVerifiers *shrinkingmap.ShrinkingMap[iotago.CommitmentID, *CommitmentVerifier] @@ -38,32 +38,17 @@ func newAttestations(protocol *Protocol) *Attestations { Logger: lo.Return1(protocol.Logger.NewChildLogger("Attestations")), protocol: protocol, workerPool: protocol.Workers.CreatePool("Attestations"), - ticker: eventticker.New[iotago.SlotIndex, iotago.CommitmentID](protocol.Options.AttestationRequesterOptions...), + requester: eventticker.New[iotago.SlotIndex, iotago.CommitmentID](protocol.Options.AttestationRequesterOptions...), commitmentVerifiers: shrinkingmap.New[iotago.CommitmentID, *CommitmentVerifier](), } - a.ticker.Events.Tick.Hook(a.sendRequest) - protocol.Constructed.OnTrigger(func() { - protocol.Chains.WithElements(func(chain *Chain) (shutdown func()) { - return chain.RequestAttestations.WithNonEmptyValue(func(requestAttestations bool) (shutdown func()) { - return a.setupCommitmentVerifier(chain) - }) - }) + shutdown := lo.Batch( + a.initCommitmentVerifiers(), + a.initRequester(), + ) - protocol.Commitments.WithElements(func(commitment *Commitment) (shutdown func()) { - return commitment.RequestAttestations.OnUpdate(func(_ bool, requestAttestations bool) { - if requestAttestations { - if commitment.CumulativeWeight() == 0 { - commitment.IsAttested.Set(true) - } else { - a.ticker.StartTicker(commitment.ID()) - } - } else { - a.ticker.StopTicker(commitment.ID()) - } - }) - }) + protocol.Shutdown.OnTrigger(shutdown) }) return a @@ -79,10 +64,42 @@ func (a *Attestations) Get(commitmentID iotago.CommitmentID) (commitment *model. return slotAPI.Attestations() } -// Shutdown shuts down the attestation protocol. -func (a *Attestations) Shutdown() { - a.ticker.Shutdown() - a.workerPool.Shutdown().ShutdownComplete.Wait() +// initCommitmentVerifiers initializes the commitment verifiers for all chains (once they are required). +func (a *Attestations) initCommitmentVerifiers() func() { + return a.protocol.Chains.WithElements(func(chain *Chain) (shutdown func()) { + return chain.RequestAttestations.WithNonEmptyValue(func(requestAttestations bool) (shutdown func()) { + return a.setupCommitmentVerifier(chain) + }) + }) +} + +// initRequester initializes the ticker that is used to send commitment requests. +func (a *Attestations) initRequester() (shutdown func()) { + unsubscribeFromTicker := lo.Batch( + a.protocol.Commitments.WithElements(func(commitment *Commitment) (shutdown func()) { + return commitment.RequestAttestations.WithNonEmptyValue(func(_ bool) (teardown func()) { + if commitment.CumulativeWeight() == 0 { + commitment.IsAttested.Set(true) + + return nil + } + + a.requester.StartTicker(commitment.ID()) + + return func() { + a.requester.StopTicker(commitment.ID()) + } + }) + }), + + a.requester.Events.Tick.Hook(a.sendRequest).Unhook, + ) + + return func() { + unsubscribeFromTicker() + + a.requester.Shutdown() + } } // setupCommitmentVerifier sets up the commitment verifier for the given chain. diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index 03e0ed602..7af24aeb1 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -154,7 +154,6 @@ func (p *Protocol) initSubcomponents(networkEndpoint network.Endpoint) (shutdown return func() { p.Blocks.Shutdown() - p.Attestations.Shutdown() p.WarpSync.Shutdown() p.Network.Shutdown() p.Workers.WaitChildren() From 24f650eb757837f299793581698b2389e9d483d2 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sun, 10 Dec 2023 23:28:23 +0100 Subject: [PATCH 21/28] Refactor: refactored api methids --- pkg/protocol/attestations.go | 4 +-- pkg/protocol/commitments.go | 35 +++++++++++------------ pkg/protocol/engine/committed_slot_api.go | 20 ++++++------- pkg/protocol/engine/engine.go | 4 +-- pkg/protocol/warp_sync.go | 8 +++--- pkg/testsuite/storage_commitments.go | 2 +- 6 files changed, 36 insertions(+), 37 deletions(-) diff --git a/pkg/protocol/attestations.go b/pkg/protocol/attestations.go index eb0519b44..e1064e93f 100644 --- a/pkg/protocol/attestations.go +++ b/pkg/protocol/attestations.go @@ -56,12 +56,12 @@ func newAttestations(protocol *Protocol) *Attestations { // Get returns the commitment, and its attestations (including the corresponding merkle proof). func (a *Attestations) Get(commitmentID iotago.CommitmentID) (commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], err error) { - slotAPI, err := a.protocol.Commitments.targetEngine(commitmentID).CommittedSlot(commitmentID) + commitmentAPI, err := a.protocol.Commitments.API(commitmentID) if err != nil { return nil, nil, nil, ierrors.Wrapf(err, "failed to load committed slot API") } - return slotAPI.Attestations() + return commitmentAPI.Attestations() } // initCommitmentVerifiers initializes the commitment verifiers for all chains (once they are required). diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 6d505f891..edeb475dc 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -82,6 +82,21 @@ func (c *Commitments) Get(commitmentID iotago.CommitmentID, requestIfMissing ... return cachedRequest.Result(), cachedRequest.Err() } +// API returns the API for the given commitmentID. If the Commitment is not available, it will return +// ErrorCommitmentNotFound. +func (c *Commitments) API(commitmentID iotago.CommitmentID) (*engine.CommitmentAPI, error) { + if commitmentID.Slot() <= c.Root.Get().Slot() { + return c.protocol.Engines.Main.Get().CommitmentAPI(commitmentID) + } + + commitment, err := c.Get(commitmentID) + if err != nil { + return nil, err + } + + return commitment.TargetEngine().CommitmentAPI(commitmentID) +} + // initLogger initializes the logger for this component. func (c *Commitments) initLogger() (shutdown func()) { c.Logger, shutdown = c.protocol.NewChildLogger("Commitments") @@ -307,12 +322,12 @@ func (c *Commitments) processRequest(commitmentID iotago.CommitmentID, from peer return nil, ierrors.Wrap(err, "failed to load commitment metadata") } - slotAPI, err := c.protocol.Engines.Main.Get().CommittedSlot(commitmentID) + commitmentAPI, err := c.protocol.Engines.Main.Get().CommitmentAPI(commitmentID) if err != nil { return nil, ierrors.Wrap(err, "failed to load engine API") } - return slotAPI.Commitment() + return commitmentAPI.Commitment() } loggedWorkerPoolTask(c.workerPool, func() error { @@ -326,19 +341,3 @@ func (c *Commitments) processRequest(commitmentID iotago.CommitmentID, from peer return nil }, c, "commitmentID", commitmentID, "fromPeer", from) } - -// targetEngine returns the engine that manages the data for the given commitment (or nil if no engine was found while -// commitment IDs below the Root are always resolved against the main engine). -func (c *Commitments) targetEngine(commitmentID iotago.CommitmentID) *engine.Engine { - if commitmentID.Slot() <= c.Root.Get().Slot() { - return c.protocol.Engines.Main.Get() - } - - if commitment, err := c.Get(commitmentID); err == nil { - return commitment.TargetEngine() - } else if !ierrors.Is(err, ErrorCommitmentNotFound) { - c.LogDebug("failed to retrieve commitment", "commitmentID", commitmentID, "err", err) - } - - return nil -} diff --git a/pkg/protocol/engine/committed_slot_api.go b/pkg/protocol/engine/committed_slot_api.go index e88497883..5fdd9994b 100644 --- a/pkg/protocol/engine/committed_slot_api.go +++ b/pkg/protocol/engine/committed_slot_api.go @@ -8,8 +8,8 @@ import ( "github.com/iotaledger/iota.go/v4/merklehasher" ) -// CommittedSlotAPI is a wrapper for the Engine that provides access to the data of a committed slot. -type CommittedSlotAPI struct { +// CommitmentAPI is a wrapper for the Engine that provides access to the data of a committed slot. +type CommitmentAPI struct { // engine is the Engine that is used to access the data. engine *Engine @@ -17,16 +17,16 @@ type CommittedSlotAPI struct { CommitmentID iotago.CommitmentID } -// NewCommittedSlotAPI creates a new CommittedSlotAPI. -func NewCommittedSlotAPI(engine *Engine, commitmentID iotago.CommitmentID) *CommittedSlotAPI { - return &CommittedSlotAPI{ +// NewCommittedSlotAPI creates a new CommitmentAPI. +func NewCommittedSlotAPI(engine *Engine, commitmentID iotago.CommitmentID) *CommitmentAPI { + return &CommitmentAPI{ engine: engine, CommitmentID: commitmentID, } } // Commitment returns the commitment of the slot. -func (c *CommittedSlotAPI) Commitment() (commitment *model.Commitment, err error) { +func (c *CommitmentAPI) Commitment() (commitment *model.Commitment, err error) { if commitment, err = c.engine.Storage.Commitments().Load(c.CommitmentID.Slot()); err != nil { return nil, ierrors.Wrapf(err, "failed to load commitment for slot %d", c.CommitmentID) } @@ -39,7 +39,7 @@ func (c *CommittedSlotAPI) Commitment() (commitment *model.Commitment, err error } // Attestations returns the commitment, attestations and the merkle proof of the slot. -func (c *CommittedSlotAPI) Attestations() (commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], err error) { +func (c *CommitmentAPI) Attestations() (commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], err error) { commitment, err = c.Commitment() if err != nil { return nil, nil, nil, ierrors.Wrap(err, "failed to load commitment") @@ -65,7 +65,7 @@ func (c *CommittedSlotAPI) Attestations() (commitment *model.Commitment, attesta } // Roots returns the roots of the slot. -func (c *CommittedSlotAPI) Roots() (committedRoots *iotago.Roots, err error) { +func (c *CommitmentAPI) Roots() (committedRoots *iotago.Roots, err error) { if c.engine.Storage.Settings().LatestCommitment().Slot() < c.CommitmentID.Slot() { return nil, ierrors.Errorf("slot %d is not committed yet", c.CommitmentID) } @@ -86,7 +86,7 @@ func (c *CommittedSlotAPI) Roots() (committedRoots *iotago.Roots, err error) { } // BlocksIDsBySlotCommitmentID returns the accepted block IDs of the slot grouped by their SlotCommitmentID. -func (c *CommittedSlotAPI) BlocksIDsBySlotCommitmentID() (map[iotago.CommitmentID]iotago.BlockIDs, error) { +func (c *CommitmentAPI) BlocksIDsBySlotCommitmentID() (map[iotago.CommitmentID]iotago.BlockIDs, error) { if c.engine.Storage.Settings().LatestCommitment().Slot() < c.CommitmentID.Slot() { return nil, ierrors.Errorf("slot %d is not committed yet", c.CommitmentID) } @@ -107,7 +107,7 @@ func (c *CommittedSlotAPI) BlocksIDsBySlotCommitmentID() (map[iotago.CommitmentI return blockIDsBySlotCommitmentID, nil } -func (c *CommittedSlotAPI) TransactionIDs() (iotago.TransactionIDs, error) { +func (c *CommitmentAPI) TransactionIDs() (iotago.TransactionIDs, error) { if c.engine.Storage.Settings().LatestCommitment().Slot() < c.CommitmentID.Slot() { return nil, ierrors.Errorf("slot %d is not committed yet", c.CommitmentID) } diff --git a/pkg/protocol/engine/engine.go b/pkg/protocol/engine/engine.go index b755d290e..b450f8f9e 100644 --- a/pkg/protocol/engine/engine.go +++ b/pkg/protocol/engine/engine.go @@ -325,8 +325,8 @@ func (e *Engine) LatestAPI() iotago.API { return e.Storage.Settings().APIProvider().LatestAPI() } -// CommittedSlot returns the committed slot for the given slot index. -func (e *Engine) CommittedSlot(commitmentID iotago.CommitmentID) (*CommittedSlotAPI, error) { +// CommitmentAPI returns the committed slot for the given slot index. +func (e *Engine) CommitmentAPI(commitmentID iotago.CommitmentID) (*CommitmentAPI, error) { if e == nil { return nil, ierrors.New("engine is nil") } diff --git a/pkg/protocol/warp_sync.go b/pkg/protocol/warp_sync.go index 669caee28..cd3e94fc0 100644 --- a/pkg/protocol/warp_sync.go +++ b/pkg/protocol/warp_sync.go @@ -290,22 +290,22 @@ func (w *WarpSync) ProcessResponse(commitmentID iotago.CommitmentID, blockIDsByS // ProcessRequest processes the given warp sync request. func (w *WarpSync) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID) { loggedWorkerPoolTask(w.workerPool, func() (err error) { - slotAPI, err := w.protocol.Commitments.targetEngine(commitmentID).CommittedSlot(commitmentID) + commitmentAPI, err := w.protocol.Commitments.API(commitmentID) if err != nil { return ierrors.Wrap(err, "failed to load slot api") } - blockIDsBySlotCommitment, err := slotAPI.BlocksIDsBySlotCommitmentID() + blockIDsBySlotCommitment, err := commitmentAPI.BlocksIDsBySlotCommitmentID() if err != nil { return ierrors.Wrap(err, "failed to get block ids") } - roots, err := slotAPI.Roots() + roots, err := commitmentAPI.Roots() if err != nil { return ierrors.Wrap(err, "failed to get roots") } - transactionIDs, err := slotAPI.TransactionIDs() + transactionIDs, err := commitmentAPI.TransactionIDs() if err != nil { return ierrors.Wrap(err, "failed to get transaction ids") } diff --git a/pkg/testsuite/storage_commitments.go b/pkg/testsuite/storage_commitments.go index c515a6972..0db0bd93e 100644 --- a/pkg/testsuite/storage_commitments.go +++ b/pkg/testsuite/storage_commitments.go @@ -68,7 +68,7 @@ func (t *TestSuite) AssertStorageCommitmentBlocks(slot iotago.SlotIndex, expecte return ierrors.Wrapf(err, "AssertStorageCommitmentBlocks: %s: error loading commitment for slot: %d", node.Name, slot) } - committedSlot, err := node.Protocol.Engines.Main.Get().CommittedSlot(storedCommitment.ID()) + committedSlot, err := node.Protocol.Engines.Main.Get().CommitmentAPI(storedCommitment.ID()) if err != nil { return ierrors.Wrapf(err, "AssertStorageCommitmentBlocks: %s: error getting committed slot for commitment: %s", node.Name, storedCommitment.ID()) } From 75ca7f2aac3378161107d42ea1dc3e9650652606 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 11 Dec 2023 02:12:47 +0100 Subject: [PATCH 22/28] Refactor: cleanup --- pkg/protocol/commitments.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index edeb475dc..7a1d39b44 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -35,8 +35,8 @@ type Commitments struct { // workerPool contains the worker pool that is used to process commitment requests and responses asynchronously. workerPool *workerpool.WorkerPool - // ticker contains the ticker that is used to send commitment requests. - ticker *eventticker.EventTicker[iotago.SlotIndex, iotago.CommitmentID] + // requester contains the ticker that is used to send commitment requests. + requester *eventticker.EventTicker[iotago.SlotIndex, iotago.CommitmentID] // Logger contains a reference to the logger that is used by this component. log.Logger @@ -50,13 +50,13 @@ func newCommitments(protocol *Protocol) *Commitments { protocol: protocol, cachedRequests: shrinkingmap.New[iotago.CommitmentID, *promise.Promise[*Commitment]](), workerPool: protocol.Workers.CreatePool("Commitments"), - ticker: eventticker.New[iotago.SlotIndex, iotago.CommitmentID](protocol.Options.CommitmentRequesterOptions...), + requester: eventticker.New[iotago.SlotIndex, iotago.CommitmentID](protocol.Options.CommitmentRequesterOptions...), } shutdown := lo.Batch( c.initLogger(), c.initEngineCommitmentSynchronization(), - c.initTicker(), + c.initRequester(), ) protocol.Shutdown.OnTrigger(shutdown) @@ -64,9 +64,9 @@ func newCommitments(protocol *Protocol) *Commitments { return c } -// Get returns the protocol Commitment for the given commitmentID. If the Commitment is not available yet, it -// will return an ErrorCommitmentNotFound. It is possible to trigger a request for the Commitment by passing true as the -// second argument. +// Get returns the Commitment for the given commitmentID. If the Commitment is not available yet, it will return an +// ErrorCommitmentNotFound. It is possible to trigger a request for the Commitment by passing true as the second +// argument. func (c *Commitments) Get(commitmentID iotago.CommitmentID, requestIfMissing ...bool) (commitment *Commitment, err error) { cachedRequest, exists := c.cachedRequests.Get(commitmentID) if !exists && lo.First(requestIfMissing) { @@ -127,14 +127,14 @@ func (c *Commitments) initEngineCommitmentSynchronization() func() { }) } -// initTicker initializes the ticker that is used to send commitment requests. -func (c *Commitments) initTicker() (shutdown func()) { - unsubscribeFromTicker := c.ticker.Events.Tick.Hook(c.sendRequest).Unhook +// initRequester initializes the requester that is used to request commitments from the network. +func (c *Commitments) initRequester() (shutdown func()) { + unsubscribeFromTicker := c.requester.Events.Tick.Hook(c.sendRequest).Unhook return func() { unsubscribeFromTicker() - c.ticker.Shutdown() + c.requester.Shutdown() } } @@ -239,10 +239,10 @@ func (c *Commitments) cachedRequest(commitmentID iotago.CommitmentID, requestIfM // start ticker if requested if lo.First(requestIfMissing) { - c.ticker.StartTicker(commitmentID) + c.requester.StartTicker(commitmentID) cachedRequest.OnComplete(func() { - c.ticker.StopTicker(commitmentID) + c.requester.StopTicker(commitmentID) }) } From f8dee7ec577bb84a4939b6e3b8329fd6dd7edc5d Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 11 Dec 2023 02:18:22 +0100 Subject: [PATCH 23/28] Feat: more cleanup --- pkg/protocol/commitments.go | 40 +++++++++---------- ...ommitted_slot_api.go => commitment_api.go} | 4 +- pkg/protocol/engine/engine.go | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) rename pkg/protocol/engine/{committed_slot_api.go => commitment_api.go} (97%) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 7a1d39b44..1cc380146 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -200,7 +200,7 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi }) } -// publishCommitment publishes the given commitment as a Commitment instance. If the Commitment was already +// publishCommitment publishes the given commitment and return a singleton Commitment instance as a Commitment instance. If the Commitment was already // published, it will return the existing Commitment instance. Otherwise, it will create a new Commitment instance and // resolve the Promise that was created for it. func (c *Commitments) publishCommitment(commitment *model.Commitment) (publishedCommitment *Commitment, published bool, err error) { @@ -266,7 +266,7 @@ func (c *Commitments) cachedRequest(commitmentID iotago.CommitmentID, requestIfM return cachedRequest } -// initCommitment initializes the given commitment. +// initCommitment initializes the given commitment in the protocol. func (c *Commitments) initCommitment(commitment *Commitment, slotEvicted reactive.Event) { commitment.LogDebug("created", "id", commitment.ID()) @@ -295,24 +295,6 @@ func (c *Commitments) sendRequest(commitmentID iotago.CommitmentID) { }) } -// processResponse processes the given commitment response. -func (c *Commitments) processResponse(commitment *model.Commitment, from peer.ID) { - c.workerPool.Submit(func() { - // verify the commitment's version corresponds to the protocol version for the slot. - if apiForSlot := c.protocol.APIForSlot(commitment.Slot()); apiForSlot.Version() != commitment.Commitment().ProtocolVersion { - c.LogDebug("received commitment with invalid protocol version", "commitment", commitment.ID(), "version", commitment.Commitment().ProtocolVersion, "expectedVersion", apiForSlot.Version(), "fromPeer", from) - - return - } - - if publishedCommitment, published, err := c.protocol.Commitments.publishCommitment(commitment); err != nil { - c.LogError("failed to process commitment", "fromPeer", from, "err", err) - } else if published { - c.LogTrace("received response", "commitment", publishedCommitment.LogName(), "fromPeer", from) - } - }) -} - // processRequest processes the given commitment request. func (c *Commitments) processRequest(commitmentID iotago.CommitmentID, from peer.ID) { loadCommitment := func() (*model.Commitment, error) { @@ -341,3 +323,21 @@ func (c *Commitments) processRequest(commitmentID iotago.CommitmentID, from peer return nil }, c, "commitmentID", commitmentID, "fromPeer", from) } + +// processResponse processes the given commitment response. +func (c *Commitments) processResponse(commitment *model.Commitment, from peer.ID) { + c.workerPool.Submit(func() { + // verify the commitment's version corresponds to the protocol version for the slot. + if apiForSlot := c.protocol.APIForSlot(commitment.Slot()); apiForSlot.Version() != commitment.Commitment().ProtocolVersion { + c.LogDebug("received commitment with invalid protocol version", "commitment", commitment.ID(), "version", commitment.Commitment().ProtocolVersion, "expectedVersion", apiForSlot.Version(), "fromPeer", from) + + return + } + + if publishedCommitment, published, err := c.protocol.Commitments.publishCommitment(commitment); err != nil { + c.LogError("failed to process commitment", "fromPeer", from, "err", err) + } else if published { + c.LogTrace("received response", "commitment", publishedCommitment.LogName(), "fromPeer", from) + } + }) +} diff --git a/pkg/protocol/engine/committed_slot_api.go b/pkg/protocol/engine/commitment_api.go similarity index 97% rename from pkg/protocol/engine/committed_slot_api.go rename to pkg/protocol/engine/commitment_api.go index 5fdd9994b..3de25b45e 100644 --- a/pkg/protocol/engine/committed_slot_api.go +++ b/pkg/protocol/engine/commitment_api.go @@ -17,8 +17,8 @@ type CommitmentAPI struct { CommitmentID iotago.CommitmentID } -// NewCommittedSlotAPI creates a new CommitmentAPI. -func NewCommittedSlotAPI(engine *Engine, commitmentID iotago.CommitmentID) *CommitmentAPI { +// NewCommitmentAPI creates a new CommitmentAPI. +func NewCommitmentAPI(engine *Engine, commitmentID iotago.CommitmentID) *CommitmentAPI { return &CommitmentAPI{ engine: engine, CommitmentID: commitmentID, diff --git a/pkg/protocol/engine/engine.go b/pkg/protocol/engine/engine.go index b450f8f9e..f6f241ecc 100644 --- a/pkg/protocol/engine/engine.go +++ b/pkg/protocol/engine/engine.go @@ -335,7 +335,7 @@ func (e *Engine) CommitmentAPI(commitmentID iotago.CommitmentID) (*CommitmentAPI return nil, ierrors.Errorf("slot %d is not committed yet", commitmentID.Slot()) } - return NewCommittedSlotAPI(e, commitmentID), nil + return NewCommitmentAPI(e, commitmentID), nil } func (e *Engine) WriteSnapshot(filePath string, targetSlot ...iotago.SlotIndex) (err error) { From 6e7ea88faa1e5491e183c91c4fcd7a82708462f6 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 11 Dec 2023 02:20:08 +0100 Subject: [PATCH 24/28] Refactor: cleanup --- pkg/protocol/commitments.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 1cc380146..99840f103 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -82,16 +82,16 @@ func (c *Commitments) Get(commitmentID iotago.CommitmentID, requestIfMissing ... return cachedRequest.Result(), cachedRequest.Err() } -// API returns the API for the given commitmentID. If the Commitment is not available, it will return +// API returns the CommitmentAPI for the given commitmentID. If the Commitment is not available, it will return // ErrorCommitmentNotFound. -func (c *Commitments) API(commitmentID iotago.CommitmentID) (*engine.CommitmentAPI, error) { +func (c *Commitments) API(commitmentID iotago.CommitmentID) (commitmentAPI *engine.CommitmentAPI, err error) { if commitmentID.Slot() <= c.Root.Get().Slot() { return c.protocol.Engines.Main.Get().CommitmentAPI(commitmentID) } commitment, err := c.Get(commitmentID) if err != nil { - return nil, err + return nil, ierrors.Wrap(err, "failed to load commitment") } return commitment.TargetEngine().CommitmentAPI(commitmentID) From ddee286f88c904da447c9b087b137b485ae9d9b5 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 11 Dec 2023 02:26:44 +0100 Subject: [PATCH 25/28] Feat: refactor --- pkg/protocol/attestations.go | 2 +- pkg/protocol/engine/commitment_api.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/protocol/attestations.go b/pkg/protocol/attestations.go index e1064e93f..3904f7eba 100644 --- a/pkg/protocol/attestations.go +++ b/pkg/protocol/attestations.go @@ -58,7 +58,7 @@ func newAttestations(protocol *Protocol) *Attestations { func (a *Attestations) Get(commitmentID iotago.CommitmentID) (commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], err error) { commitmentAPI, err := a.protocol.Commitments.API(commitmentID) if err != nil { - return nil, nil, nil, ierrors.Wrapf(err, "failed to load committed slot API") + return nil, nil, nil, ierrors.Wrap(err, "failed to load commitment API") } return commitmentAPI.Attestations() diff --git a/pkg/protocol/engine/commitment_api.go b/pkg/protocol/engine/commitment_api.go index 3de25b45e..8edd973a3 100644 --- a/pkg/protocol/engine/commitment_api.go +++ b/pkg/protocol/engine/commitment_api.go @@ -51,12 +51,12 @@ func (c *CommitmentAPI) Attestations() (commitment *model.Commitment, attestatio rootsStorage, err := c.engine.Storage.Roots(c.CommitmentID.Slot()) if err != nil { - return nil, nil, nil, ierrors.Wrapf(err, "failed to load roots storage") + return nil, nil, nil, ierrors.Wrap(err, "failed to load roots storage") } roots, exists, err := rootsStorage.Load(c.CommitmentID) if err != nil { - return nil, nil, nil, ierrors.Wrapf(err, "failed to load roots") + return nil, nil, nil, ierrors.Wrap(err, "failed to load roots") } else if !exists { return nil, nil, nil, ierrors.New("roots not found") } From 6ebac1b8900bfd1e90695968da415f6e20b3a4aa Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 11 Dec 2023 02:32:11 +0100 Subject: [PATCH 26/28] Refactor: fixed comment --- pkg/protocol/commitments.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index 99840f103..ec3b5a163 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -200,9 +200,9 @@ func (c *Commitments) publishEngineCommitments(chain *Chain, engine *engine.Engi }) } -// publishCommitment publishes the given commitment and return a singleton Commitment instance as a Commitment instance. If the Commitment was already -// published, it will return the existing Commitment instance. Otherwise, it will create a new Commitment instance and -// resolve the Promise that was created for it. +// publishCommitment publishes the given commitment and returns the singleton Commitment instance that is used to +// represent it in our data structure (together with a boolean that indicates if we were the first goroutine to publish +// the commitment). func (c *Commitments) publishCommitment(commitment *model.Commitment) (publishedCommitment *Commitment, published bool, err error) { // retrieve promise and abort if it was already rejected cachedRequest := c.cachedRequest(commitment.ID()) From 18e3c5bc79ed616c78cb661a46e723880ae1cc4e Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 11 Dec 2023 03:32:43 +0100 Subject: [PATCH 27/28] Refactor: added Mutations method --- pkg/protocol/engine/commitment_api.go | 20 ++++++++++++++++++++ pkg/protocol/warp_sync.go | 16 +++------------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/pkg/protocol/engine/commitment_api.go b/pkg/protocol/engine/commitment_api.go index 8edd973a3..6cef45933 100644 --- a/pkg/protocol/engine/commitment_api.go +++ b/pkg/protocol/engine/commitment_api.go @@ -64,6 +64,26 @@ func (c *CommitmentAPI) Attestations() (commitment *model.Commitment, attestatio return commitment, attestations, roots.AttestationsProof(), nil } +// Mutations returns all accepted block IDs, the tangle proof, all accepted transaction IDs and the ledger state +// mutation proof of the slot. +func (c *CommitmentAPI) Mutations() (acceptedBlocksBySlotCommitment map[iotago.CommitmentID]iotago.BlockIDs, acceptedBlocksProof *merklehasher.Proof[iotago.Identifier], acceptedTransactionIDs iotago.TransactionIDs, acceptedTransactionsProof *merklehasher.Proof[iotago.Identifier], err error) { + if acceptedBlocksBySlotCommitment, err = c.BlocksIDsBySlotCommitmentID(); err != nil { + return nil, nil, nil, nil, ierrors.Wrap(err, "failed to get block ids") + } + + roots, err := c.Roots() + if err != nil { + return nil, nil, nil, nil, ierrors.Wrap(err, "failed to get roots") + } + + acceptedTransactionIDs, err = c.TransactionIDs() + if err != nil { + return nil, nil, nil, nil, ierrors.Wrap(err, "failed to get transaction ids") + } + + return acceptedBlocksBySlotCommitment, roots.TangleProof(), acceptedTransactionIDs, roots.MutationProof(), nil +} + // Roots returns the roots of the slot. func (c *CommitmentAPI) Roots() (committedRoots *iotago.Roots, err error) { if c.engine.Storage.Settings().LatestCommitment().Slot() < c.CommitmentID.Slot() { diff --git a/pkg/protocol/warp_sync.go b/pkg/protocol/warp_sync.go index cd3e94fc0..05a047b28 100644 --- a/pkg/protocol/warp_sync.go +++ b/pkg/protocol/warp_sync.go @@ -295,22 +295,12 @@ func (w *WarpSync) ProcessRequest(commitmentID iotago.CommitmentID, from peer.ID return ierrors.Wrap(err, "failed to load slot api") } - blockIDsBySlotCommitment, err := commitmentAPI.BlocksIDsBySlotCommitmentID() + blocks, blocksProof, transactionIDs, transactionIDsProof, err := commitmentAPI.Mutations() if err != nil { - return ierrors.Wrap(err, "failed to get block ids") + return ierrors.Wrap(err, "failed to get mutations") } - roots, err := commitmentAPI.Roots() - if err != nil { - return ierrors.Wrap(err, "failed to get roots") - } - - transactionIDs, err := commitmentAPI.TransactionIDs() - if err != nil { - return ierrors.Wrap(err, "failed to get transaction ids") - } - - w.protocol.Network.SendWarpSyncResponse(commitmentID, blockIDsBySlotCommitment, roots.TangleProof(), transactionIDs, roots.MutationProof(), from) + w.protocol.Network.SendWarpSyncResponse(commitmentID, blocks, blocksProof, transactionIDs, transactionIDsProof, from) return nil }, w, "commitmentID", commitmentID, "fromPeer", from) From 67a1b3646ab430ebfcd5f63f2ed7c51d40ba3215 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:33:55 +0100 Subject: [PATCH 28/28] Refactor: fix comments --- pkg/protocol/commitments.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/protocol/commitments.go b/pkg/protocol/commitments.go index ec3b5a163..a53e33274 100644 --- a/pkg/protocol/commitments.go +++ b/pkg/protocol/commitments.go @@ -210,7 +210,7 @@ func (c *Commitments) publishCommitment(commitment *model.Commitment) (published return nil, false, ierrors.Wrapf(cachedRequest.Err(), "failed to request commitment %s", commitment.ID()) } - // otherwise try to provideCommitment it and determine if we were the goroutine that resolved it + // otherwise try to publish it and determine if we were the goroutine that published it publishedCommitment = newCommitment(c, commitment) cachedRequest.Resolve(publishedCommitment).OnSuccess(func(resolvedCommitment *Commitment) { if published = resolvedCommitment == publishedCommitment; !published {