Skip to content

Commit

Permalink
Add API for getting honest vs all edges (#507)
Browse files Browse the repository at this point in the history
  • Loading branch information
amsanghi authored Dec 19, 2023
1 parent 6282cd5 commit 781682d
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 11 deletions.
5 changes: 4 additions & 1 deletion api/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package api

import (
"context"
"github.com/ethereum/go-ethereum/common"

protocol "github.com/OffchainLabs/bold/chain-abstraction"
)

type EdgesProvider interface {
GetEdges() []protocol.SpecEdge
GetHonestEdges() []protocol.SpecEdge
GetEdges(ctx context.Context) ([]protocol.SpecEdge, error)
GetEdge(ctx context.Context, hash common.Hash) (protocol.SpecEdge, error)
}

type AssertionsProvider interface {
Expand Down
18 changes: 17 additions & 1 deletion api/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package api_test
import (
"context"
"errors"
"fmt"

"github.com/OffchainLabs/bold/api"
protocol "github.com/OffchainLabs/bold/chain-abstraction"

"github.com/ethereum/go-ethereum/common"
)

var _ = api.EdgesProvider(&FakeEdgesProvider{})
Expand All @@ -15,10 +18,23 @@ type FakeEdgesProvider struct {
Edges []protocol.SpecEdge
}

func (f *FakeEdgesProvider) GetEdges() []protocol.SpecEdge {
func (f *FakeEdgesProvider) GetHonestEdges() []protocol.SpecEdge {
return f.Edges
}

func (f *FakeEdgesProvider) GetEdges(ctx context.Context) ([]protocol.SpecEdge, error) {
return f.Edges, nil
}

func (f *FakeEdgesProvider) GetEdge(ctx context.Context, edgeId common.Hash) (protocol.SpecEdge, error) {
for _, e := range f.Edges {
if e.Id().Hash == edgeId {
return e, nil
}
}
return nil, fmt.Errorf("no edge found with id %#x", edgeId)
}

type FakeAssertionProvider struct {
Hashes []protocol.AssertionHash
AssertionCreationInfos []*protocol.AssertionCreatedInfo
Expand Down
46 changes: 42 additions & 4 deletions api/method_edges.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,37 @@ package api
import (
"net/http"
"sort"

"github.com/gorilla/mux"

"github.com/ethereum/go-ethereum/common"
)

func (s *Server) listHonestEdgesHandler(w http.ResponseWriter, r *http.Request) {
e, err := convertSpecEdgeEdgesToEdges(r.Context(), s.edges.GetHonestEdges())
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}

// TODO: Allow params to sort by other fields
sort.Slice(e, func(i, j int) bool {
return e[i].CreatedAtBlock < e[j].CreatedAtBlock
})

if err := writeJSONResponse(w, 200, e); err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
}

func (s *Server) listEdgesHandler(w http.ResponseWriter, r *http.Request) {
e, err := convertSpecEdgeEdgesToEdges(r.Context(), s.edges.GetEdges())
specEdges, err := s.edges.GetEdges(r.Context())
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
e, err := convertSpecEdgeEdgesToEdges(r.Context(), specEdges)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
Expand All @@ -24,8 +51,19 @@ func (s *Server) listEdgesHandler(w http.ResponseWriter, r *http.Request) {
}

func (s *Server) getEdgeHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
if _, err := w.Write([]byte("not implemented")); err != nil {
log.Error("failed to write response body", "err", err)
edgeId := mux.Vars(r)["id"]
specEdge, err := s.edges.GetEdge(r.Context(), common.HexToHash(edgeId))
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
edge, err := convertSpecEdgeEdgeToEdge(r.Context(), specEdge)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
if err := writeJSONResponse(w, 200, edge); err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
}
4 changes: 2 additions & 2 deletions api/method_edges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ func TestGetEdge(t *testing.T) {
s.Router().ServeHTTP(rr, req)

// Check the status code is what we expect.
if status := rr.Code; status != http.StatusNotImplemented {
if status := rr.Code; status != http.StatusInternalServerError {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusNotImplemented)
status, http.StatusInternalServerError)
}
}

Expand Down
1 change: 1 addition & 0 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func (s *Server) registerMethods() error {
s.router.HandleFunc("/assertions/{id}", s.getAssertionHandler).Methods("GET")

// Edges
s.router.HandleFunc("/honest-edges", s.listHonestEdgesHandler).Methods("GET")
s.router.HandleFunc("/edges", s.listEdgesHandler).Methods("GET")
s.router.HandleFunc("/edges/{id}", s.getEdgeHandler).Methods("GET")

Expand Down
106 changes: 104 additions & 2 deletions challenge-manager/chain-watcher/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,110 @@ func (w *Watcher) Start(ctx context.Context) {
}
}

// GetEdges returns all edges in the watcher.
func (w *Watcher) GetEdges() []protocol.SpecEdge {
func (w *Watcher) GetEdge(ctx context.Context, edgeId common.Hash) (protocol.SpecEdge, error) {
challengeManager, err := w.chain.SpecChallengeManager(ctx)
if err != nil {
return nil, err
}
edgeOpt, err := challengeManager.GetEdge(ctx, protocol.EdgeId{Hash: edgeId})
if err != nil {
return nil, err
}
if edgeOpt.IsNone() {
return nil, fmt.Errorf("no edge found with id %#x", edgeId)
}
return edgeOpt.Unwrap(), nil
}

func (w *Watcher) GetEdges(ctx context.Context) ([]protocol.SpecEdge, error) {
scanRange, err := retry.UntilSucceeds(ctx, func() (filterRange, error) {
return w.getStartEndBlockNum(ctx)
})
if err != nil {
return nil, err
}
fromBlock := scanRange.startBlockNum
toBlock := scanRange.endBlockNum

// Get a challenge manager instance and filterer.
challengeManager, err := retry.UntilSucceeds(ctx, func() (protocol.SpecChallengeManager, error) {
return w.chain.SpecChallengeManager(ctx)
})
if err != nil {
return nil, err
}
filterer, err := retry.UntilSucceeds(ctx, func() (*challengeV2gen.EdgeChallengeManagerFilterer, error) {
return challengeV2gen.NewEdgeChallengeManagerFilterer(challengeManager.Address(), w.backend)
})
if err != nil {
return nil, err
}
filterOpts := &bind.FilterOpts{
Start: fromBlock,
End: &toBlock,
Context: ctx,
}

return retry.UntilSucceeds(ctx, func() ([]protocol.SpecEdge, error) {
return w.getAllEdges(ctx, filterer, filterOpts)
})
}

func (w *Watcher) getAllEdges(
ctx context.Context,
filterer *challengeV2gen.EdgeChallengeManagerFilterer,
filterOpts *bind.FilterOpts,
) ([]protocol.SpecEdge, error) {
it, err := filterer.FilterEdgeAdded(filterOpts, nil, nil, nil)
if err != nil {
return nil, err
}
defer func() {
if err = it.Close(); err != nil {
srvlog.Error("Could not close filter iterator", log.Ctx{"err": err})
}
}()
edges := make([]protocol.SpecEdge, 0)
for it.Next() {
if it.Error() != nil {
return nil, errors.Wrapf(
err,
"got iterator error when scanning edge creations from block %d to %d",
filterOpts.Start,
*filterOpts.End,
)
}
edge, err := retry.UntilSucceeds(ctx, func() (protocol.SpecEdge, error) {
return w.getEdgeFromEvent(ctx, it.Event)
})
if err != nil {
return nil, err
}
edges = append(edges, edge)
}
return edges, nil
}

func (w *Watcher) getEdgeFromEvent(
ctx context.Context,
event *challengeV2gen.EdgeChallengeManagerEdgeAdded,
) (protocol.SpecEdge, error) {
challengeManager, err := w.chain.SpecChallengeManager(ctx)
if err != nil {
return nil, err
}
edgeOpt, err := challengeManager.GetEdge(ctx, protocol.EdgeId{Hash: event.EdgeId})
if err != nil {
return nil, err
}
if edgeOpt.IsNone() {
return nil, fmt.Errorf("no edge found with id %#x", event.EdgeId)
}
return edgeOpt.Unwrap(), nil
}

// GetHonestEdges returns all edges in the watcher.
func (w *Watcher) GetHonestEdges() []protocol.SpecEdge {
syncEdges := make([]protocol.SpecEdge, 0)
//nolint:err
_ = w.challenges.ForEach(func(AssertionHash protocol.AssertionHash, t *trackedChallenge) error {
Expand Down
2 changes: 1 addition & 1 deletion testing/endtoend/basic_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ func testSyncAliceStopsBobRemains(t *testing.T, be backend.Backend, s *Challenge
}
logs, err := bob.ChallengeManager().FilterEdgeConfirmedByTime(filterOpts, nil, nil)
require.NoError(t, err)
edges := bob.Watcher().GetEdges()
edges := bob.Watcher().GetHonestEdges()
for logs.Next() {
for _, edge := range edges {
if edge.Id().Hash == logs.Event.EdgeId {
Expand Down

0 comments on commit 781682d

Please sign in to comment.