Skip to content

Commit 147ba60

Browse files
committed
feat: booster-http
1 parent 2f8d246 commit 147ba60

File tree

9 files changed

+644
-0
lines changed

9 files changed

+644
-0
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ boost: $(BUILD_DEPS)
8989
.PHONY: boost
9090
BINS+=boost boostx boostd
9191

92+
booster-http: $(BUILD_DEPS)
93+
rm -f booster-http
94+
$(GOCC) build $(GOFLAGS) -o booster-http ./cmd/booster-http
95+
.PHONY: booster-http
96+
BINS+=booster-http
97+
9298
devnet: $(BUILD_DEPS)
9399
rm -f devnet
94100
$(GOCC) build $(GOFLAGS) -o devnet ./cmd/devnet

api/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type Boost interface {
6666
PiecesListCidInfos(ctx context.Context) ([]cid.Cid, error) //perm:read
6767
PiecesGetPieceInfo(ctx context.Context, pieceCid cid.Cid) (*piecestore.PieceInfo, error) //perm:read
6868
PiecesGetCIDInfo(ctx context.Context, payloadCid cid.Cid) (*piecestore.CIDInfo, error) //perm:read
69+
PiecesGetMaxOffset(ctx context.Context, pieceCid cid.Cid) (uint64, error) //perm:read
6970

7071
// MethodGroup: Actor
7172
ActorSectorSize(context.Context, address.Address) (abi.SectorSize, error) //perm:read

api/proxy_gen.go

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/boostd/run.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ var runCmd = &cli.Command{
2828
Name: "pprof",
2929
Usage: "run pprof web server on localhost:6060",
3030
},
31+
&cli.BoolFlag{
32+
Name: "nosync",
33+
Usage: "dont wait for the full node to sync with the chain",
34+
},
3135
},
3236
Action: func(cctx *cli.Context) error {
3337
if cctx.Bool("pprof") {

cmd/booster-http/main.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package main
2+
3+
import (
4+
"os"
5+
6+
logging "github.com/ipfs/go-log/v2"
7+
"github.com/urfave/cli/v2"
8+
9+
"github.com/filecoin-project/boost/build"
10+
cliutil "github.com/filecoin-project/boost/cli/util"
11+
)
12+
13+
var log = logging.Logger("booster")
14+
15+
const (
16+
FlagBoostRepo = "boost-repo"
17+
)
18+
19+
func main() {
20+
app := &cli.App{
21+
Name: "booster-http",
22+
Usage: "HTTP endpoint for retrieval from Filecoin",
23+
EnableBashCompletion: true,
24+
Version: build.UserVersion(),
25+
Flags: []cli.Flag{
26+
&cli.StringFlag{
27+
Name: FlagBoostRepo,
28+
EnvVars: []string{"BOOST_PATH"},
29+
Usage: "boost repo path",
30+
Value: "~/.boost",
31+
},
32+
cliutil.FlagVeryVerbose,
33+
},
34+
Commands: []*cli.Command{
35+
runCmd,
36+
},
37+
}
38+
app.Setup()
39+
40+
if err := app.Run(os.Args); err != nil {
41+
os.Stderr.WriteString("Error: " + err.Error() + "\n")
42+
}
43+
}
44+
45+
func before(cctx *cli.Context) error {
46+
_ = logging.SetLogLevel("booster", "INFO")
47+
48+
if cliutil.IsVeryVerbose {
49+
_ = logging.SetLogLevel("booster", "DEBUG")
50+
}
51+
52+
return nil
53+
}

cmd/booster-http/run.go

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"github.com/filecoin-project/boost/api"
8+
bclient "github.com/filecoin-project/boost/api/client"
9+
cliutil "github.com/filecoin-project/boost/cli/util"
10+
"github.com/filecoin-project/dagstore/mount"
11+
"github.com/filecoin-project/go-fil-markets/piecestore"
12+
"github.com/filecoin-project/go-jsonrpc"
13+
"github.com/filecoin-project/go-state-types/abi"
14+
lapi "github.com/filecoin-project/lotus/api"
15+
"github.com/filecoin-project/lotus/api/client"
16+
"github.com/filecoin-project/lotus/api/v0api"
17+
"github.com/filecoin-project/lotus/api/v1api"
18+
lcli "github.com/filecoin-project/lotus/cli"
19+
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
20+
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
21+
"github.com/filecoin-project/lotus/markets/dagstore"
22+
"github.com/filecoin-project/lotus/markets/sectoraccessor"
23+
lotus_modules "github.com/filecoin-project/lotus/node/modules"
24+
"github.com/filecoin-project/lotus/node/modules/dtypes"
25+
"github.com/filecoin-project/lotus/node/repo"
26+
"github.com/ipfs/go-cid"
27+
"github.com/urfave/cli/v2"
28+
"net/http"
29+
_ "net/http/pprof"
30+
"strings"
31+
)
32+
33+
var runCmd = &cli.Command{
34+
Name: "run",
35+
Usage: "Start a booster-http process",
36+
Before: before,
37+
Flags: []cli.Flag{
38+
&cli.BoolFlag{
39+
Name: "pprof",
40+
Usage: "run pprof web server on localhost:6070",
41+
},
42+
&cli.StringFlag{
43+
Name: "base-path",
44+
Usage: "the base path at which to run the web server",
45+
Value: "",
46+
},
47+
&cli.UintFlag{
48+
Name: "port",
49+
Usage: "the port the web server listens on",
50+
Value: 7777,
51+
},
52+
&cli.StringFlag{
53+
Name: "api-boost",
54+
Usage: "the endpoint for the boost API",
55+
Required: true,
56+
},
57+
&cli.StringFlag{
58+
Name: "api-fullnode",
59+
Usage: "the endpoint for the full node API",
60+
Required: true,
61+
},
62+
&cli.StringFlag{
63+
Name: "api-sealer",
64+
Usage: "the endpoint for the sealer API",
65+
Required: true,
66+
},
67+
},
68+
Action: func(cctx *cli.Context) error {
69+
if cctx.Bool("pprof") {
70+
go func() {
71+
err := http.ListenAndServe("localhost:6070", nil)
72+
if err != nil {
73+
log.Error(err)
74+
}
75+
}()
76+
}
77+
78+
// Connect to the Boost API
79+
ctx := lcli.ReqContext(cctx)
80+
boostApiInfo := cctx.String("api-boost")
81+
bapi, bcloser, err := getBoostApi(ctx, boostApiInfo)
82+
if err != nil {
83+
return fmt.Errorf("getting boost API: %w", err)
84+
}
85+
defer bcloser()
86+
87+
// Connect to the full node API
88+
fnApiInfo := cctx.String("api-fullnode")
89+
fullnodeApi, ncloser, err := getFullNodeApi(ctx, fnApiInfo)
90+
defer ncloser()
91+
92+
// Connect to the sealing API
93+
sealingApiInfo := cctx.String("api-sealer")
94+
sauth, err := storageAuthWithURL(sealingApiInfo)
95+
if err != nil {
96+
return fmt.Errorf("parsing sealing API endpoint: %w", err)
97+
}
98+
sealingService, sealerCloser, err := getMinerApi(ctx, sealingApiInfo)
99+
defer sealerCloser()
100+
101+
maddr, err := sealingService.ActorAddress(ctx)
102+
log.Infof("Miner address: %s", maddr)
103+
104+
// Use an in-memory repo because we don't need any functions
105+
// of a real repo, we just need to supply something that satisfies
106+
// the LocalStorage interface to the store
107+
memRepo := repo.NewMemory(nil)
108+
lr, err := memRepo.Lock(repo.StorageMiner)
109+
if err != nil {
110+
return fmt.Errorf("locking mem repo: %w", err)
111+
}
112+
defer lr.Close()
113+
114+
// Create the store interface
115+
var urls []string
116+
lstor, err := stores.NewLocal(ctx, lr, sealingService, urls)
117+
if err != nil {
118+
return fmt.Errorf("creating new local store: %w", err)
119+
}
120+
storage := lotus_modules.RemoteStorage(lstor, sealingService, sauth, sectorstorage.Config{
121+
// TODO: Not sure if I need this, or any of the other fields in this struct
122+
ParallelFetchLimit: 1,
123+
})
124+
// Create the piece provider and sector accessors
125+
pp := sectorstorage.NewPieceProvider(storage, sealingService, sealingService)
126+
sa := sectoraccessor.NewSectorAccessor(dtypes.MinerAddress(maddr), sealingService, pp, fullnodeApi)
127+
// Create the server API
128+
sapi := serverApi{ctx: ctx, bapi: bapi, sa: sa}
129+
server := NewHttpServer(cctx.String("base-path"), cctx.Int("port"), sapi)
130+
131+
// Start the server
132+
log.Infof("Starting booster-http node on port %d with base path '%s'",
133+
cctx.Int("port"), cctx.String("base-path"))
134+
server.Start(ctx)
135+
136+
// Monitor for shutdown.
137+
<-ctx.Done()
138+
139+
log.Info("Shutting down...")
140+
141+
err = server.Stop()
142+
if err != nil {
143+
return err
144+
}
145+
log.Info("Graceful shutdown successful")
146+
147+
// Sync all loggers.
148+
_ = log.Sync() //nolint:errcheck
149+
150+
return nil
151+
},
152+
}
153+
154+
func storageAuthWithURL(apiInfo string) (sectorstorage.StorageAuth, error) {
155+
s := strings.Split(apiInfo, ":")
156+
if len(s) != 2 {
157+
return nil, errors.New("unexpected format of `apiInfo`")
158+
}
159+
headers := http.Header{}
160+
headers.Add("Authorization", "Bearer "+s[0])
161+
return sectorstorage.StorageAuth(headers), nil
162+
}
163+
164+
type serverApi struct {
165+
ctx context.Context
166+
bapi api.Boost
167+
sa dagstore.SectorAccessor
168+
}
169+
170+
var _ HttpServerApi = (*serverApi)(nil)
171+
172+
func (s serverApi) GetMaxPieceOffset(pieceCid cid.Cid) (uint64, error) {
173+
return s.bapi.PiecesGetMaxOffset(s.ctx, pieceCid)
174+
}
175+
176+
func (s serverApi) GetPieceInfo(pieceCID cid.Cid) (*piecestore.PieceInfo, error) {
177+
return s.bapi.PiecesGetPieceInfo(s.ctx, pieceCID)
178+
}
179+
180+
func (s serverApi) IsUnsealed(ctx context.Context, sectorID abi.SectorNumber, offset abi.UnpaddedPieceSize, length abi.UnpaddedPieceSize) (bool, error) {
181+
return s.sa.IsUnsealed(ctx, sectorID, offset, length)
182+
}
183+
184+
func (s serverApi) UnsealSectorAt(ctx context.Context, sectorID abi.SectorNumber, offset abi.UnpaddedPieceSize, length abi.UnpaddedPieceSize) (mount.Reader, error) {
185+
return s.sa.UnsealSectorAt(ctx, sectorID, offset, length)
186+
}
187+
188+
func getBoostApi(ctx context.Context, ai string) (api.Boost, jsonrpc.ClientCloser, error) {
189+
ai = strings.TrimPrefix(strings.TrimSpace(ai), "BOOST_API_INFO=")
190+
info := cliutil.ParseApiInfo(ai)
191+
addr, err := info.DialArgs("v0")
192+
if err != nil {
193+
return nil, nil, fmt.Errorf("could not get DialArgs: %w", err)
194+
}
195+
196+
log.Infof("Using boost API at %s", addr)
197+
api, closer, err := bclient.NewBoostRPCV0(ctx, addr, info.AuthHeader())
198+
if err != nil {
199+
return nil, nil, fmt.Errorf("creating full node service API: %w", err)
200+
}
201+
202+
return api, closer, nil
203+
}
204+
205+
func getFullNodeApi(ctx context.Context, ai string) (v1api.FullNode, jsonrpc.ClientCloser, error) {
206+
ai = strings.TrimPrefix(strings.TrimSpace(ai), "FULLNODE_API_INFO=")
207+
info := cliutil.ParseApiInfo(ai)
208+
addr, err := info.DialArgs("v1")
209+
if err != nil {
210+
return nil, nil, fmt.Errorf("could not get DialArgs: %w", err)
211+
}
212+
213+
log.Infof("Using full node API at %s", addr)
214+
api, closer, err := client.NewFullNodeRPCV1(ctx, addr, info.AuthHeader())
215+
if err != nil {
216+
return nil, nil, fmt.Errorf("creating full node service API: %w", err)
217+
}
218+
219+
v, err := api.Version(ctx)
220+
if err != nil {
221+
return nil, nil, fmt.Errorf("checking full node service API version: %w", err)
222+
}
223+
224+
if !v.APIVersion.EqMajorMinor(lapi.FullAPIVersion1) {
225+
return nil, nil, fmt.Errorf("full node service API version didn't match (expected %s, remote %s)", lapi.FullAPIVersion1, v.APIVersion)
226+
}
227+
228+
return api, closer, nil
229+
}
230+
231+
func getMinerApi(ctx context.Context, ai string) (v0api.StorageMiner, jsonrpc.ClientCloser, error) {
232+
ai = strings.TrimPrefix(strings.TrimSpace(ai), "MINER_API_INFO=")
233+
info := cliutil.ParseApiInfo(ai)
234+
addr, err := info.DialArgs("v0")
235+
if err != nil {
236+
return nil, nil, fmt.Errorf("could not get DialArgs: %w", err)
237+
}
238+
239+
log.Infof("Using sealing API at %s", addr)
240+
api, closer, err := client.NewStorageMinerRPCV0(ctx, addr, info.AuthHeader())
241+
if err != nil {
242+
return nil, nil, fmt.Errorf("creating miner service API: %w", err)
243+
}
244+
245+
v, err := api.Version(ctx)
246+
if err != nil {
247+
return nil, nil, fmt.Errorf("checking miner service API version: %w", err)
248+
}
249+
250+
if !v.APIVersion.EqMajorMinor(lapi.MinerAPIVersion0) {
251+
return nil, nil, fmt.Errorf("miner service API version didn't match (expected %s, remote %s)", lapi.MinerAPIVersion0, v.APIVersion)
252+
}
253+
254+
return api, closer, nil
255+
}

0 commit comments

Comments
 (0)