Skip to content

Commit

Permalink
feat(relayer): HTTP api for exposing events table for bridge UI (#271)
Browse files Browse the repository at this point in the history
* WIP on HTTP api for exposing events table for bridge UI

* error checking

* escape string

* newHTTPServer tests
  • Loading branch information
cyberhorsey committed Nov 23, 2022
1 parent 0052673 commit 7b5e6b8
Show file tree
Hide file tree
Showing 17 changed files with 325 additions and 35 deletions.
15 changes: 12 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ module github.com/taikoxyz/taiko-mono

go 1.19

replace github.com/umbracle/ethgo => ../../ethgo

require (
github.com/cyberhorsey/errors v0.0.0-20220929234051-087d6d8bb841
github.com/cyberhorsey/webutils v0.0.0-20220929234343-770c4fa3beba
github.com/ethereum/go-ethereum v1.10.25
github.com/joho/godotenv v1.4.0
github.com/labstack/echo-contrib v0.13.0
github.com/labstack/echo/v4 v4.9.1
github.com/pkg/errors v0.9.1
github.com/pressly/goose v2.7.0+incompatible
Expand All @@ -27,40 +27,49 @@ require (
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Microsoft/hcsshim v0.9.4 // indirect
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chris-ramon/douceur v0.2.0 // indirect
github.com/containerd/cgroups v1.0.4 // indirect
github.com/containerd/containerd v1.6.8 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v20.10.17+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/go-ole/go-ole v1.2.1 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.4.0 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/labstack/echo-contrib v0.13.0 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/microcosm-cc/bluemonday v1.0.4 // indirect
github.com/moby/sys/mount v0.3.3 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/neko-neko/echo-logrus/v2 v2.0.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/opencontainers/runc v1.1.3 // indirect
Expand Down
42 changes: 38 additions & 4 deletions go.sum

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/relayer/.default.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ MYSQL_DATABASE=relayer
MYSQL_HOST=localhost:3306
RELAYER_ECDSA_KEY=
L1_BRIDGE_ADDRESS=0xB12d6112D64B213880Fa53F815aF1F29c91CaCe9
L2_BRIDGE_ADDRESS=0x4eA05A0f7713333AeB4bB73F17aEeFE146CF13E3
L2_BRIDGE_ADDRESS=0xA53a0f12879Aba509A24D2e5C959a36cdD18F9d0
L1_TAIKO_ADDRESS=0x9b557777Be33A8A2fE6aF93E017A0d139B439E5D
L2_TAIKO_ADDRESS=0x0027f309f7F94A8Efb6A3DBfb30827f1062803F4
L2_TAIKO_ADDRESS=0x80EDBd3f3618b348526F0970AEAFA52b6f204449
L1_RPC_URL=ws://34.132.67.34:8546
L2_RPC_URL=ws://ws.a1.testnet.taiko.xyz
BLOCK_BATCH_SIZE=2
Expand Down
23 changes: 20 additions & 3 deletions packages/relayer/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"strconv"
"strings"
"time"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -80,9 +81,7 @@ func Run(mode relayer.Mode, watchMode relayer.WatchMode, layer relayer.Layer) {
log.Fatal(err)
}

srv, err := http.NewServer(http.NewServerOpts{
Echo: echo.New(),
})
srv, err := newHTTPServer(db)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -318,3 +317,21 @@ func loadAndValidateEnv() error {

return errors.Errorf("Missing env vars: %v", missing)
}

func newHTTPServer(db relayer.DB) (*http.Server, error) {
eventRepo, err := repo.NewEventRepository(db)
if err != nil {
return nil, err
}

srv, err := http.NewServer(http.NewServerOpts{
EventRepo: eventRepo,
Echo: echo.New(),
CorsOrigins: strings.Split(os.Getenv("CORS_ORIGINS"), ","),
})
if err != nil {
return nil, err
}

return srv, nil
}
18 changes: 18 additions & 0 deletions packages/relayer/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,21 @@ func Test_makeIndexers(t *testing.T) {
})
}
}

func Test_newHTTPServer(t *testing.T) {
db, cancel, err := testMysql(t)
if err != nil {
t.Fatal(err)
}

defer cancel()

srv, err := newHTTPServer(db)
assert.Nil(t, err)
assert.NotNil(t, srv)
}

func Test_newHTTPServer_nilDB(t *testing.T) {
_, err := newHTTPServer(nil)
assert.NotNil(t, err)
}
9 changes: 5 additions & 4 deletions packages/relayer/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ var (
"ERR_NO_BLOCK_REPOSITORY",
"BlockRepository is required",
)
ErrNoProver = errors.Validation.NewWithKeyAndDetail("ERR_NO_PROVER", "Prover is required")
ErrNoRPCClient = errors.Validation.NewWithKeyAndDetail("ERR_NO_RPC_CLIENT", "RPCClient is required")
ErrNoBridge = errors.Validation.NewWithKeyAndDetail("ERR_NO_BRIDGE", "Bridge is required")
ErrNoTaikoL2 = errors.Validation.NewWithKeyAndDetail("ERR_NO_TAIKO_L2", "TaikoL2 is required")
ErrNoCORSOrigins = errors.Validation.NewWithKeyAndDetail("ERR_NO_CORS_ORIGINS", "CORS Origins are required")
ErrNoProver = errors.Validation.NewWithKeyAndDetail("ERR_NO_PROVER", "Prover is required")
ErrNoRPCClient = errors.Validation.NewWithKeyAndDetail("ERR_NO_RPC_CLIENT", "RPCClient is required")
ErrNoBridge = errors.Validation.NewWithKeyAndDetail("ERR_NO_BRIDGE", "Bridge is required")
ErrNoTaikoL2 = errors.Validation.NewWithKeyAndDetail("ERR_NO_TAIKO_L2", "TaikoL2 is required")

ErrInvalidConfirmations = errors.Validation.NewWithKeyAndDetail(
"ERR_INVALID_CONFIRMATIONS",
Expand Down
7 changes: 5 additions & 2 deletions packages/relayer/event.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package relayer

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/common"
"gorm.io/datatypes"
)

Expand Down Expand Up @@ -47,6 +49,7 @@ type SaveEventOpts struct {

// EventRepository is used to interact with events in the store
type EventRepository interface {
Save(opts SaveEventOpts) (*Event, error)
UpdateStatus(id int, status EventStatus) error
Save(ctx context.Context, opts SaveEventOpts) (*Event, error)
UpdateStatus(ctx context.Context, id int, status EventStatus) error
FindAllByAddress(ctx context.Context, chainID *big.Int, address common.Address) ([]*Event, error)
}
28 changes: 28 additions & 0 deletions packages/relayer/http/get_events_by_address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package http

import (
"errors"
"html"
"math/big"
"net/http"

"github.com/cyberhorsey/webutils"
"github.com/ethereum/go-ethereum/common"
"github.com/labstack/echo/v4"
)

func (srv *Server) GetEventsByAddress(c echo.Context) error {
chainID, ok := new(big.Int).SetString(c.QueryParam("chainID"), 10)
if !ok {
return webutils.LogAndRenderErrors(c, http.StatusUnprocessableEntity, errors.New("invalid chain id"))
}

address := html.EscapeString(c.QueryParam("address"))

events, err := srv.eventRepo.FindAllByAddress(c.Request().Context(), chainID, common.HexToAddress(address))
if err != nil {
return webutils.LogAndRenderErrors(c, http.StatusUnprocessableEntity, err)
}

return c.JSON(http.StatusOK, events)
}
71 changes: 71 additions & 0 deletions packages/relayer/http/get_events_by_address_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package http

import (
"context"
"fmt"
"math/big"
"net/http"
"net/http/httptest"
"testing"

"github.com/cyberhorsey/webutils/testutils"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/taikoxyz/taiko-mono/packages/relayer"
)

func Test_GetEventsByAddress(t *testing.T) {
srv := newTestServer("")

_, err := srv.eventRepo.Save(context.Background(), relayer.SaveEventOpts{
Name: "name",
Data: `{"Owner": "0x0000000000000000000000000000000000000123"}`,
ChainID: big.NewInt(167001),
Status: relayer.EventStatusNew,
})

assert.Equal(t, nil, err)

tests := []struct {
name string
address string
chainID string
wantStatus int
wantBodyRegexpMatches []string
}{
{
"successEmptyList",
"0x456",
"167001",
http.StatusOK,
[]string{`\[\]`},
},
{
"success",
"0x0000000000000000000000000000000000000123",
"167001",
http.StatusOK,
[]string{`[{"id":780800018316137516,"name":"name",
"data":{"Owner":"0x0000000000000000000000000000000000000123"},"status":0,"chainID":167001}]`},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := testutils.NewUnauthenticatedRequest(
echo.GET,
fmt.Sprintf("/events?address=%v&chainID=%v",
tt.address,
tt.chainID),

nil,
)

rec := httptest.NewRecorder()

srv.ServeHTTP(rec, req)

testutils.AssertStatusAndBody(t, rec, tt.wantStatus, tt.wantBodyRegexpMatches)
})
}
}
2 changes: 2 additions & 0 deletions packages/relayer/http/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ package http

func (srv *Server) configureRoutes() {
srv.echo.GET("/healthz", srv.Health)

srv.echo.GET("/events", srv.GetEventsByAddress)
}
17 changes: 15 additions & 2 deletions packages/relayer/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ import (
"os"

"github.com/labstack/echo/v4/middleware"
"github.com/taikoxyz/taiko-mono/packages/relayer"

echoprom "github.com/labstack/echo-contrib/prometheus"
echo "github.com/labstack/echo/v4"
)

type Server struct {
echo *echo.Echo
echo *echo.Echo
eventRepo relayer.EventRepository
}

type NewServerOpts struct {
Echo *echo.Echo
EventRepo relayer.EventRepository
CorsOrigins []string
}

Expand All @@ -26,6 +29,14 @@ func (opts NewServerOpts) Validate() error {
return ErrNoHTTPFramework
}

if opts.EventRepo == nil {
return relayer.ErrNoEventRepository
}

if opts.CorsOrigins == nil {
return relayer.ErrNoCORSOrigins
}

return nil
}

Expand All @@ -35,14 +46,16 @@ func NewServer(opts NewServerOpts) (*Server, error) {
}

srv := &Server{
echo: opts.Echo,
echo: opts.Echo,
eventRepo: opts.EventRepo,
}

corsOrigins := opts.CorsOrigins
if corsOrigins == nil {
corsOrigins = []string{"*"}
}

srv.configureRoutes()
srv.configureMiddleware(corsOrigins)

return srv, nil
Expand Down
21 changes: 18 additions & 3 deletions packages/relayer/http/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import (
"github.com/joho/godotenv"
echo "github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/taikoxyz/taiko-mono/packages/relayer"
"github.com/taikoxyz/taiko-mono/packages/relayer/mock"
"github.com/taikoxyz/taiko-mono/packages/relayer/repo"
)

func newTestServer(url string) *Server {
_ = godotenv.Load("../.test.env")

srv := &Server{
echo: echo.New(),
echo: echo.New(),
eventRepo: mock.NewEventRepository(),
}

srv.configureMiddleware([]string{"*"})
Expand All @@ -35,20 +39,31 @@ func Test_NewServer(t *testing.T) {
"success",
NewServerOpts{
Echo: echo.New(),
EventRepo: &repo.EventRepository{},
CorsOrigins: make([]string, 0),
},
nil,
},
{
"noEventRepo",
NewServerOpts{
Echo: echo.New(),
CorsOrigins: make([]string, 0),
},
relayer.ErrNoEventRepository,
},
{
"noCorsOrigins",
NewServerOpts{
Echo: echo.New(),
Echo: echo.New(),
EventRepo: &repo.EventRepository{},
},
nil,
relayer.ErrNoCORSOrigins,
},
{
"noHttpFramework",
NewServerOpts{
EventRepo: &repo.EventRepository{},
CorsOrigins: make([]string, 0),
},
ErrNoHTTPFramework,
Expand Down
Loading

0 comments on commit 7b5e6b8

Please sign in to comment.