From 1cfe2fdb0184836302f83eb6b13c9616d07247e8 Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Fri, 3 May 2024 15:36:57 -0500 Subject: [PATCH] update how root nodes are confirmed --- challenge-manager/chain-watcher/watcher.go | 38 +++++++++ challenge-manager/challenge-tree/paths.go | 28 +++---- .../challenge-tree/paths_test.go | 20 ++--- challenge-manager/challenge-tree/tree.go | 4 + challenge-manager/edge-tracker/tracker.go | 81 +++++-------------- 5 files changed, 87 insertions(+), 84 deletions(-) diff --git a/challenge-manager/chain-watcher/watcher.go b/challenge-manager/chain-watcher/watcher.go index ee7bb2090..bc3f12ff9 100644 --- a/challenge-manager/chain-watcher/watcher.go +++ b/challenge-manager/chain-watcher/watcher.go @@ -409,6 +409,44 @@ func (w *Watcher) ComputeAncestors( return chal.honestEdgeTree.ComputeAncestors(ctx, edgeId, blockHeader.Number.Uint64()) } +func (w *Watcher) IsConfirmableEssentialNode( + ctx context.Context, + challengedAssertionHash protocol.AssertionHash, + essentialNodeId protocol.EdgeId, + confirmationThreshold uint64, +) (confirmable bool, essentialPaths []challengetree.EssentialPath, timer uint64, err error) { + chal, ok := w.challenges.TryGet(challengedAssertionHash) + if !ok { + err = fmt.Errorf( + "could not get challenge for top level assertion %#x", + challengedAssertionHash, + ) + return + } + blockHeader, err := w.chain.Backend().HeaderByNumber(ctx, util.GetSafeBlockNumber()) + if err != nil { + return + } + if !blockHeader.Number.IsUint64() { + err = errors.New("block number is not uint64") + return + } + essentialNode, ok := chal.honestEdgeTree.GetEdge(essentialNodeId) + if !ok { + err = fmt.Errorf("could not get essential node with id %#x", essentialNodeId.Hash) + return + } + confirmable, essentialPaths, timer, err = chal.honestEdgeTree.IsConfirmableEssentialNode( + ctx, + challengetree.IsConfirmableArgs{ + EssentialNode: essentialNode.Id(), + BlockNum: blockHeader.Number.Uint64(), + ConfirmationThreshold: confirmationThreshold, + }, + ) + return +} + func (w *Watcher) ComputeRootInheritedTimer( ctx context.Context, challengedAssertionHash protocol.AssertionHash, diff --git a/challenge-manager/challenge-tree/paths.go b/challenge-manager/challenge-tree/paths.go index 793772ad0..2ca3047a6 100644 --- a/challenge-manager/challenge-tree/paths.go +++ b/challenge-manager/challenge-tree/paths.go @@ -11,12 +11,12 @@ import ( "github.com/OffchainLabs/bold/containers/option" ) -type essentialPath []protocol.EdgeId +type EssentialPath []protocol.EdgeId -type isConfirmableArgs struct { - essentialNode protocol.EdgeId - confirmationThreshold uint64 - blockNum uint64 +type IsConfirmableArgs struct { + EssentialNode protocol.EdgeId + ConfirmationThreshold uint64 + BlockNum uint64 } // Find all the paths down from an essential node, and @@ -32,16 +32,16 @@ type isConfirmableArgs struct { // essential node is then confirmable. func (ht *RoyalChallengeTree) IsConfirmableEssentialNode( ctx context.Context, - args isConfirmableArgs, -) (bool, []essentialPath, uint64, error) { - essentialNode, ok := ht.edges.TryGet(args.essentialNode) + args IsConfirmableArgs, +) (bool, []EssentialPath, uint64, error) { + essentialNode, ok := ht.edges.TryGet(args.EssentialNode) if !ok { return false, nil, 0, fmt.Errorf("essential node not found") } essentialPaths, essentialTimers, err := ht.findEssentialPaths( ctx, essentialNode, - args.blockNum, + args.BlockNum, ) if err != nil { return false, nil, 0, err @@ -66,7 +66,7 @@ func (ht *RoyalChallengeTree) IsConfirmableEssentialNode( return false, nil, 0, fmt.Errorf("no path weights computed") } minWeight := pathWeights.Pop() - allEssentialPathsConfirmable := minWeight >= args.confirmationThreshold + allEssentialPathsConfirmable := minWeight >= args.ConfirmationThreshold return allEssentialPathsConfirmable, essentialPaths, minWeight, nil } @@ -81,13 +81,13 @@ func (ht *RoyalChallengeTree) findEssentialPaths( ctx context.Context, essentialNode protocol.ReadOnlyEdge, blockNum uint64, -) ([]essentialPath, []essentialLocalTimers, error) { - allPaths := make([]essentialPath, 0) +) ([]EssentialPath, []essentialLocalTimers, error) { + allPaths := make([]EssentialPath, 0) allTimers := make([]essentialLocalTimers, 0) type visited struct { essentialNode protocol.ReadOnlyEdge - path essentialPath + path EssentialPath localTimers essentialLocalTimers } stack := newStack[*visited]() @@ -99,7 +99,7 @@ func (ht *RoyalChallengeTree) findEssentialPaths( stack.push(&visited{ essentialNode: essentialNode, - path: essentialPath{essentialNode.Id()}, + path: EssentialPath{essentialNode.Id()}, localTimers: essentialLocalTimers{localTimer}, }) diff --git a/challenge-manager/challenge-tree/paths_test.go b/challenge-manager/challenge-tree/paths_test.go index 1f09a436b..56e3502b9 100644 --- a/challenge-manager/challenge-tree/paths_test.go +++ b/challenge-manager/challenge-tree/paths_test.go @@ -19,8 +19,8 @@ func TestIsConfirmableEssentialNode(t *testing.T) { // understand the setup of the challenge tree. _, _, _, err := tree.IsConfirmableEssentialNode( ctx, - isConfirmableArgs{ - essentialNode: protocol.EdgeId{}, + IsConfirmableArgs{ + EssentialNode: protocol.EdgeId{}, }, ) require.ErrorContains(t, err, "essential node not found") @@ -32,10 +32,10 @@ func TestIsConfirmableEssentialNode(t *testing.T) { blockNum := uint64(10) isConfirmable, _, minPathWeight, err := tree.IsConfirmableEssentialNode( ctx, - isConfirmableArgs{ - confirmationThreshold: 10, - essentialNode: essentialHonestRoot.Id(), - blockNum: blockNum, + IsConfirmableArgs{ + ConfirmationThreshold: 10, + EssentialNode: essentialHonestRoot.Id(), + BlockNum: blockNum, }, ) require.NoError(t, err) @@ -47,10 +47,10 @@ func TestIsConfirmableEssentialNode(t *testing.T) { blockNum = uint64(14) isConfirmable, _, minPathWeight, err = tree.IsConfirmableEssentialNode( ctx, - isConfirmableArgs{ - confirmationThreshold: 10, - essentialNode: essentialHonestRoot.Id(), - blockNum: blockNum, + IsConfirmableArgs{ + ConfirmationThreshold: 10, + EssentialNode: essentialHonestRoot.Id(), + BlockNum: blockNum, }, ) require.NoError(t, err) diff --git a/challenge-manager/challenge-tree/tree.go b/challenge-manager/challenge-tree/tree.go index 193cb971a..df2478af7 100644 --- a/challenge-manager/challenge-tree/tree.go +++ b/challenge-manager/challenge-tree/tree.go @@ -104,6 +104,10 @@ func (ht *RoyalChallengeTree) GetEdges() *threadsafe.Map[protocol.EdgeId, protoc return ht.edges } +func (ht *RoyalChallengeTree) GetEdge(edgeId protocol.EdgeId) (protocol.SpecEdge, bool) { + return ht.edges.TryGet(edgeId) +} + func (ht *RoyalChallengeTree) HasRoyalEdge(edgeId protocol.EdgeId) bool { return ht.edges.Has(edgeId) } diff --git a/challenge-manager/edge-tracker/tracker.go b/challenge-manager/edge-tracker/tracker.go index 64b955010..c6e93197d 100644 --- a/challenge-manager/edge-tracker/tracker.go +++ b/challenge-manager/edge-tracker/tracker.go @@ -12,6 +12,7 @@ import ( "time" protocol "github.com/OffchainLabs/bold/chain-abstraction" + challengetree "github.com/OffchainLabs/bold/challenge-manager/challenge-tree" "github.com/OffchainLabs/bold/containers" "github.com/OffchainLabs/bold/containers/fsm" "github.com/OffchainLabs/bold/containers/option" @@ -49,10 +50,12 @@ type RoyalChallengeWriter interface { AddVerifiedHonestEdge( ctx context.Context, verifiedHonest protocol.VerifiedRoyalEdge, ) error - ComputeRootInheritedTimer( + IsConfirmableEssentialNode( ctx context.Context, challengedAssertionHash protocol.AssertionHash, - ) (protocol.InheritedTimer, error) + essentialNodeId protocol.EdgeId, + confirmationThreshold uint64, + ) (confirmable bool, essentialPaths []challengetree.EssentialPath, timer uint64, err error) } type ChallengeTracker interface { @@ -426,18 +429,6 @@ func (et *Tracker) tryToConfirmEdge(ctx context.Context) (bool, error) { return false, err } fields := et.uniqueTrackerLogFields() - start := time.Now() - computedTimer, err := et.chainWatcher.ComputeRootInheritedTimer(ctx, assertionHash) - if err != nil { - fields["error"] = err - srvlog.Error("Could not update time cache") - return false, errors.Wrap(err, "could not update edge inherited timer") - } - end := time.Since(start) - onchainTimer, err := et.edge.SafeHeadInheritedTimer(ctx) - if err != nil { - return false, errors.Wrap(err, "could not get edge onchain inherited timer") - } manager, err := et.chain.SpecChallengeManager(ctx) if err != nil { return false, errors.Wrap(err, "could not get challenge manager") @@ -446,53 +437,23 @@ func (et *Tracker) tryToConfirmEdge(ctx context.Context) (bool, error) { if err != nil { return false, errors.Wrap(err, "could not check the challenge period length") } - localFields := log.Ctx{ - "localTimer": computedTimer, - "onchainTimer": onchainTimer, - "confirmableAfter": chalPeriod, - "edgeId": fmt.Sprintf("%#x", et.edge.Id().Bytes()[:4]), - "took": end, - "fromBatch": et.associatedAssertionMetadata.FromBatch, - "toBatch": et.associatedAssertionMetadata.ToBatch, - "claimedAssertion": fmt.Sprintf("%#x", et.associatedAssertionMetadata.ClaimedAssertionHash[:4]), - } - srvlog.Info("Updated edge timer", localFields) - // Short circuit early if the edge is confirmable. - // We have a few things to check here: - // First, if the edge's onchain timer is greater than a challenge period, then we can - // immediately confirm by time by sending a transaction. - if onchainTimer >= protocol.InheritedTimer(chalPeriod) { - srvlog.Info("Onchain timer is greater than challenge period, now confirming edge by time", localFields) - if _, err := et.edge.ConfirmByTimer(ctx); err != nil { - return false, errors.Wrapf( - err, - "could not confirm by timer: got timer %d, chal period %d", - onchainTimer, - chalPeriod, - ) - } - srvlog.Info("Confirmed edge by time", fields) - confirmedCounter.Inc(1) - return true, nil + start := time.Now() + isConfirmable, essentialPaths, timer, err := et.chainWatcher.IsConfirmableEssentialNode( + ctx, + assertionHash, + et.edge.Id(), + chalPeriod, + ) + if err != nil { + fields["error"] = err + srvlog.Error("Could not check if essential node is confirmable") + return false, errors.Wrap(err, "not check if essential node is confirmable") } - // Otherwise, if the locally cached timer is greater than a challenge period, it means - // we need to trigger a confirmation job that will propagate updates to the whole royal - // challenge tree onchain until the edge has an onchain timer >= a challenge period. - // We let our confirmer dependency take care of this confirmatin job. - if uint64(computedTimer) >= chalPeriod { - srvlog.Info("Local computed timer big enough to confirm edge", localFields) - if err := et.challengeConfirmer.beginConfirmationJob( - ctx, - assertionHash, - et.edge, - chalPeriod, - ); err != nil { - return false, errors.Wrap( - err, - "could not complete confirmation job for royal, block challenge edge", - ) - } - // The edge is now confirmed. + end := time.Since(start) + _ = end + _ = essentialPaths + _ = timer + if isConfirmable { return true, nil } return false, nil