Merge pull request #3780 from filecoin-project/feat/lotus-gateway

implement initial lotus-gateway program
This commit is contained in:
Łukasz Magiera 2020-09-15 12:07:59 +02:00 committed by GitHub
commit e22aa74865
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 209 additions and 0 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@
/lotus-fountain
/lotus-stats
/lotus-bench
/lotus-gateway
/bench.json
/lotuspond/front/node_modules
/lotuspond/front/build

View File

@ -92,6 +92,12 @@ lotus-shed: $(BUILD_DEPS)
.PHONY: lotus-shed
BINS+=lotus-shed
lotus-gateway: $(BUILD_DEPS)
rm -f lotus-gateway
go build $(GOFLAGS) -o lotus-gateway ./cmd/lotus-gateway
.PHONY: lotus-gateway
BINS+=lotus-gateway
build: lotus lotus-miner lotus-worker
@[[ $$(type -P "lotus") ]] && echo "Caution: you have \
an existing lotus binary in your PATH. This may cause problems if you don't run 'sudo make install'" || true

90
cmd/lotus-gateway/api.go Normal file
View File

@ -0,0 +1,90 @@
package main
import (
"context"
"fmt"
"time"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-cid"
"go.opencensus.io/trace"
)
const LookbackCap = time.Hour
var (
ErrLookbackTooLong = fmt.Errorf("lookbacks of more than %s are disallowed", LookbackCap)
)
type GatewayAPI struct {
api api.FullNode
}
func (a *GatewayAPI) getTipsetTimestamp(ctx context.Context, tsk types.TipSetKey) (time.Time, error) {
if tsk.IsEmpty() {
return time.Now(), nil
}
ts, err := a.api.ChainGetTipSet(ctx, tsk)
if err != nil {
return time.Time{}, err
}
return time.Unix(int64(ts.Blocks()[0].Timestamp), 0), nil
}
func (a *GatewayAPI) checkTipset(ctx context.Context, ts types.TipSetKey) error {
when, err := a.getTipsetTimestamp(ctx, ts)
if err != nil {
return err
}
if time.Since(when) > time.Hour {
return ErrLookbackTooLong
}
return nil
}
func (a *GatewayAPI) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) {
ctx, span := trace.StartSpan(ctx, "StateGetActor")
defer span.End()
if err := a.checkTipset(ctx, ts); err != nil {
return nil, fmt.Errorf("bad tipset: %w", err)
}
return a.api.StateGetActor(ctx, actor, ts)
}
func (a *GatewayAPI) ChainHead(ctx context.Context) (*types.TipSet, error) {
ctx, span := trace.StartSpan(ctx, "ChainHead")
defer span.End()
// TODO: cache and invalidate cache when timestamp is up (or have internal ChainNotify)
return a.api.ChainHead(ctx)
}
func (a *GatewayAPI) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) {
ctx, span := trace.StartSpan(ctx, "ChainGetTipSet")
defer span.End()
if err := a.checkTipset(ctx, tsk); err != nil {
return nil, fmt.Errorf("bad tipset: %w", err)
}
// TODO: since we're limiting lookbacks, should just cache this (could really even cache the json response bytes)
return a.api.ChainGetTipSet(ctx, tsk)
}
func (a *GatewayAPI) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) {
ctx, span := trace.StartSpan(ctx, "MpoolPush")
defer span.End()
// TODO: additional anti-spam checks
return a.api.MpoolPush(ctx, sm)
}

112
cmd/lotus-gateway/main.go Normal file
View File

@ -0,0 +1,112 @@
package main
import (
"context"
"net"
"net/http"
"os"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/build"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/lib/lotuslog"
logging "github.com/ipfs/go-log"
"github.com/gorilla/mux"
"github.com/urfave/cli/v2"
)
var log = logging.Logger("gateway")
func main() {
lotuslog.SetupLogLevels()
local := []*cli.Command{
runCmd,
}
app := &cli.App{
Name: "lotus-gateway",
Usage: "Public API server for lotus",
Version: build.UserVersion(),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
EnvVars: []string{"LOTUS_PATH"},
Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME
},
},
Commands: local,
}
app.Setup()
if err := app.Run(os.Args); err != nil {
log.Warnf("%+v", err)
return
}
}
var runCmd = &cli.Command{
Name: "run",
Usage: "Start api server",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "listen",
Usage: "host address and port the api server will listen on",
Value: "0.0.0.0:2346",
},
},
Action: func(cctx *cli.Context) error {
log.Info("Starting lotus gateway")
ctx := lcli.ReqContext(cctx)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
address := cctx.String("listen")
mux := mux.NewRouter()
log.Info("Setting up API endpoint at " + address)
rpcServer := jsonrpc.NewServer()
rpcServer.Register("Filecoin", &GatewayAPI{api: api})
mux.Handle("/rpc/v0", rpcServer)
mux.PathPrefix("/").Handler(http.DefaultServeMux)
/*ah := &auth.Handler{
Verify: nodeApi.AuthVerify,
Next: mux.ServeHTTP,
}*/
srv := &http.Server{
Handler: mux,
BaseContext: func(listener net.Listener) context.Context {
return ctx
},
}
go func() {
<-ctx.Done()
log.Warn("Shutting down...")
if err := srv.Shutdown(context.TODO()); err != nil {
log.Errorf("shutting down RPC server failed: %s", err)
}
log.Warn("Graceful shutdown successful")
}()
nl, err := net.Listen("tcp", address)
if err != nil {
return err
}
return srv.Serve(nl)
},
}