Skip to content

Commit

Permalink
First pass API for introspection of edges and assertions (#371)
Browse files Browse the repository at this point in the history
  • Loading branch information
prestonvanloon authored Jul 24, 2023
1 parent ff3a977 commit 7a2277e
Show file tree
Hide file tree
Showing 21 changed files with 807 additions and 2 deletions.
43 changes: 43 additions & 0 deletions api/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "api",
srcs = [
"data.go",
"edges.go",
"log.go",
"method_assertions.go",
"method_edges.go",
"method_healthz.go",
"server.go",
],
importpath = "github.com/OffchainLabs/challenge-protocol-v2/api",
visibility = ["//visibility:public"],
deps = [
"//chain-abstraction:protocol",
"@com_github_ethereum_go_ethereum//common",
"@com_github_ethereum_go_ethereum//log",
"@com_github_gorilla_mux//:mux",
"@org_golang_x_sync//errgroup",
],
)

go_test(
name = "api_test",
srcs = [
"data_test.go",
"edges_test.go",
"method_assertions_test.go",
"method_edges_test.go",
"method_healthz_test.go",
"server_helper_test.go",
"server_test.go",
],
embed = [":api"],
deps = [
"//chain-abstraction:protocol",
"//challenge-manager/challenge-tree/mock",
"@com_github_gorilla_mux//:mux",
"@in_gopkg_d4l3k_messagediff_v1//:messagediff_v1",
],
)
9 changes: 9 additions & 0 deletions api/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package api

Check failure on line 1 in api/data.go

View workflow job for this annotation

GitHub Actions / Lint

: # github.com/OffchainLabs/challenge-protocol-v2/api [github.com/OffchainLabs/challenge-protocol-v2/api.test]

import (
protocol "github.com/OffchainLabs/challenge-protocol-v2/chain-abstraction"
)

type DataAccessor interface {
GetEdges() []protocol.SpecEdge
}
16 changes: 16 additions & 0 deletions api/data_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package api_test

import (
"github.com/OffchainLabs/challenge-protocol-v2/api"

Check failure on line 4 in api/data_test.go

View workflow job for this annotation

GitHub Actions / Lint

could not import github.com/OffchainLabs/challenge-protocol-v2/api (-: # github.com/OffchainLabs/challenge-protocol-v2/api [github.com/OffchainLabs/challenge-protocol-v2/api.test]
protocol "github.com/OffchainLabs/challenge-protocol-v2/chain-abstraction"
)

var _ = api.DataAccessor(&FakeDataAccessor{})

type FakeDataAccessor struct {
Edges []protocol.SpecEdge
}

func (f *FakeDataAccessor) GetEdges() []protocol.SpecEdge {
return f.Edges
}
186 changes: 186 additions & 0 deletions api/edges.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package api

import (
"context"
"fmt"

protocol "github.com/OffchainLabs/challenge-protocol-v2/chain-abstraction"
"github.com/ethereum/go-ethereum/common"
"golang.org/x/sync/errgroup"
)

type Edge struct {
ID common.Hash `json:"id"`
Type string `json:"type"`
StartCommitment *Commitment `json:"startCommitment"`
EndCommitment *Commitment `json:"endCommitment"`
CreatedAtBlock uint64 `json:"createdAtBlock"`
MutualID common.Hash `json:"mutualId"`
OriginID common.Hash `json:"originId"`
ClaimID common.Hash `json:"claimId"`
HasChildren bool `json:"hasChildren"`
LowerChildID common.Hash `json:"lowerChildId"`
UpperChildID common.Hash `json:"upperChildId"`
MiniStaker common.Address `json:"miniStaker"`
AssertionHash common.Hash `json:"assertionHash"`
TimeUnrivaled uint64 `json:"timeUnrivaled"`
HasRival bool `json:"hasRival"`
Status string `json:"status"`
HasLengthOneRival bool `json:"hasLengthOneRival"`
TopLevelClaimHeight protocol.OriginHeights `json:"topLevelClaimHeight"`

// Validator's point of view
// IsHonest bool `json:"isHonest"`
// AgreesWithStartCommitment `json:"agreesWithStartCommitment"`
}

type Commitment struct {
Height uint64 `json:"height"`
Hash common.Hash `json:"hash"`
}

func convertSpecEdgeEdgesToEdges(ctx context.Context, e []protocol.SpecEdge) ([]*Edge, error) {
// Convert concurrently as some of the underlying methods are API calls.
eg, ctx := errgroup.WithContext(ctx)

edges := make([]*Edge, len(e))
for i, edge := range e {
index := i
ee := edge

eg.Go(func() (err error) {
edges[index], err = convertSpecEdgeEdgeToEdge(ctx, ee)
return
})
}
return edges, eg.Wait()
}

func convertSpecEdgeEdgeToEdge(ctx context.Context, e protocol.SpecEdge) (*Edge, error) {
edge := &Edge{
ID: common.Hash(e.Id()),
Type: e.GetType().String(),
StartCommitment: toCommitment(e.StartCommitment),
EndCommitment: toCommitment(e.EndCommitment),
MutualID: common.Hash(e.MutualId()),
OriginID: common.Hash(e.OriginId()),
ClaimID: func() common.Hash {
if !e.ClaimId().IsNone() {
return common.Hash(e.ClaimId().Unwrap())
}
return common.Hash{}
}(),
MiniStaker: func() common.Address {
if !e.MiniStaker().IsNone() {
return common.Address(e.MiniStaker().Unwrap())
}
return common.Address{}
}(),
CreatedAtBlock: func() uint64 {
cab, err := e.CreatedAtBlock()
if err != nil {
return 0
}
return cab
}(),
}

// The following methods include calls to the backend, so we run them concurrently.
// Note: No rate limiting currently in place.
eg, ctx := errgroup.WithContext(ctx)

eg.Go(func() error {
hasChildren, err := e.HasChildren(ctx)
if err != nil {
return fmt.Errorf("failed to get edge children: %w", err)
}
edge.HasChildren = hasChildren
return nil
})

eg.Go(func() error {
lowerChild, err := e.LowerChild(ctx)
if err != nil {
return fmt.Errorf("failed to get edge lower child: %w", err)
}
if !lowerChild.IsNone() {
edge.LowerChildID = common.Hash(lowerChild.Unwrap())
}
return nil
})

eg.Go(func() error {
upperChild, err := e.UpperChild(ctx)
if err != nil {
return fmt.Errorf("failed to get edge upper child: %w", err)
}
if !upperChild.IsNone() {
edge.UpperChildID = common.Hash(upperChild.Unwrap())
}
return nil
})

eg.Go(func() error {
ah, err := e.AssertionHash(ctx)
if err != nil {
return fmt.Errorf("failed to get edge assertion hash: %w", err)
}
edge.AssertionHash = common.Hash(ah)

Check failure on line 128 in api/edges.go

View workflow job for this annotation

GitHub Actions / Gosec scan

cannot convert ah (variable of type protocol.AssertionHash) to type common.Hash

Check failure on line 128 in api/edges.go

View workflow job for this annotation

GitHub Actions / Build and Test

cannot convert ah (variable of type protocol.AssertionHash) to type common.Hash

Check failure on line 128 in api/edges.go

View workflow job for this annotation

GitHub Actions / Lint

cannot convert ah (variable of type protocol.AssertionHash) to type common.Hash (typecheck)

Check failure on line 128 in api/edges.go

View workflow job for this annotation

GitHub Actions / Lint

cannot convert ah (variable of type protocol.AssertionHash) to type common.Hash) (typecheck)

Check failure on line 128 in api/edges.go

View workflow job for this annotation

GitHub Actions / Lint

cannot convert ah (variable of type protocol.AssertionHash) to type common.Hash) (typecheck)
return nil
})

eg.Go(func() error {
timeUnrivaled, err := e.TimeUnrivaled(ctx)
if err != nil {
return fmt.Errorf("failed to get edge time unrivaled: %w", err)
}
edge.TimeUnrivaled = timeUnrivaled
return nil
})

eg.Go(func() error {
hasRival, err := e.HasRival(ctx)
if err != nil {
return fmt.Errorf("failed to get edge has rival: %w", err)
}
edge.HasRival = hasRival
return nil
})

eg.Go(func() error {
status, err := e.Status(ctx)
if err != nil {
return fmt.Errorf("failed to get edge status: %w", err)
}
edge.Status = status.String()
return nil
})

eg.Go(func() error {
hasLengthOneRival, err := e.HasLengthOneRival(ctx)
if err != nil {
return fmt.Errorf("failed to get edge has length one rival: %w", err)
}
edge.HasLengthOneRival = hasLengthOneRival
return nil
})

eg.Go(func() error {
topLevelClaimHeight, err := e.TopLevelClaimHeight(ctx)
if err != nil {
return fmt.Errorf("failed to get edge top level claim height: %w", err)
}
edge.TopLevelClaimHeight = topLevelClaimHeight
return nil
})

return edge, eg.Wait()
}

func toCommitment(fn func() (protocol.Height, common.Hash)) *Commitment {
h, hs := fn()
return &Commitment{
Height: uint64(h),
Hash: hs,
}
}
37 changes: 37 additions & 0 deletions api/edges_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package api_test

import (
"github.com/OffchainLabs/challenge-protocol-v2/api"
protocol "github.com/OffchainLabs/challenge-protocol-v2/chain-abstraction"
"github.com/OffchainLabs/challenge-protocol-v2/challenge-manager/challenge-tree/mock"
)

func edgesToMockEdges(e []*api.Edge) []*mock.Edge {
me := make([]*mock.Edge, len(e))
for i, ee := range e {
me[i] = edgeToMockEdge(ee)
}
return me
}

func edgeToMockEdge(e *api.Edge) *mock.Edge {
return &mock.Edge{
ID: mock.EdgeId(e.ID.Bytes()),
EdgeType: func() protocol.EdgeType {
et, err := protocol.EdgeTypeFromString(e.Type)
if err != nil {
panic(err)
}
return et
}(),
StartHeight: e.StartCommitment.Height,
StartCommit: mock.Commit(e.StartCommitment.Hash.Bytes()),
EndHeight: e.EndCommitment.Height,
EndCommit: mock.Commit(e.EndCommitment.Hash.Bytes()),
OriginID: mock.OriginId(e.OriginID.Bytes()),
ClaimID: string(e.ClaimID.Bytes()),
LowerChildID: mock.EdgeId(e.LowerChildID.Bytes()),
UpperChildID: mock.EdgeId(e.UpperChildID.Bytes()),
CreationBlock: e.CreatedAtBlock,
}
}
7 changes: 7 additions & 0 deletions api/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package api

import gethLog "github.com/ethereum/go-ethereum/log"

var (
log = gethLog.New("service", "api")
)
17 changes: 17 additions & 0 deletions api/method_assertions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package api

import "net/http"

func listAssertionsHandler(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)
}
}

func getAssertionHandler(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)
}
}
49 changes: 49 additions & 0 deletions api/method_assertions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package api_test

import (
"net/http"
"net/http/httptest"
"testing"
)

func TestListAssertions(t *testing.T) {
s, _ := NewTestServer(t)

req, err := http.NewRequest("GET", "/assertions", nil)
if err != nil {
t.Fatal(err)
}

// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()

// Serve the request with the http recorder.
s.Router().ServeHTTP(rr, req)

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

func TestGetAssertion(t *testing.T) {
s, _ := NewTestServer(t)

req, err := http.NewRequest("GET", "/assertions/foo", nil)
if err != nil {
t.Fatal(err)
}

// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()

// Serve the request with the http recorder.
s.Router().ServeHTTP(rr, req)

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

0 comments on commit 7a2277e

Please sign in to comment.