Skip to content

Nitro integration work #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 31 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7a19a72
Add go-nitro to deps
lalexgap May 26, 2023
84cf486
Implement a basic intregation of nitro payments into boost
lalexgap May 30, 2023
d86cb4a
add cors support
lalexgap Jun 6, 2023
559eaa9
use go-nitro main
lalexgap Jun 6, 2023
1ea0aa6
Replace checkPaymentChannelBalance panic with error
bitwiseguy Jun 14, 2023
8ba8220
Use consistent error handling within handleByPieceCid method
bitwiseguy Jun 15, 2023
24e0211
Merge pull request #13 from statechannels/dont-panic-gk
geoknee Jun 16, 2023
e5d1944
Upgrade nitro
geoknee Jun 22, 2023
938b11b
Merge pull request #14 from statechannels/upgrade-nitro
geoknee Jun 23, 2023
6261c48
remove checks about zero channel id
geoknee Jun 26, 2023
76dc127
Merge pull request #16 from statechannels/remove-check
geoknee Jun 26, 2023
ee081dc
Merge remote-tracking branch 'origin/main' into nitro-integration
geoknee Jul 5, 2023
f7d1b8e
use docker host url by default
lalexgap Jul 6, 2023
79f2234
update to latest go-nitro
lalexgap Jul 5, 2023
47107d0
remove piece check
lalexgap Jul 5, 2023
e87a2ad
update ipfs endpoint to check voucher
lalexgap Jul 5, 2023
852323c
little bit of cleanup
lalexgap Jul 5, 2023
3ef5119
reduce expected amount
lalexgap Jul 5, 2023
ac7b03a
logging
lalexgap Jul 6, 2023
bfc5069
clean up error handling
lalexgap Jul 10, 2023
70131ab
clean up
lalexgap Jul 10, 2023
042c327
enable nitro inside docker entrypoint
geoknee Jun 22, 2023
97dfb2d
Merge pull request #17 from statechannels/voucher-support
lalexgap Jul 10, 2023
6e6e2b7
support errors returned from the rpc client
lalexgap Jul 11, 2023
546cf77
Merge pull request #19 from statechannels/error-handling
lalexgap Jul 11, 2023
a2514db
enable nitro integration using env var
lalexgap Jul 12, 2023
2778b34
add brief readme entry
lalexgap Jul 12, 2023
8a16d31
Merge pull request #26 from statechannels/enable-with-env-var
lalexgap Jul 13, 2023
3cbc1ef
lower resource class to xlarge
lalexgap Jul 13, 2023
023155e
update comment
lalexgap Jul 13, 2023
2d532f0
Merge pull request #30 from statechannels/circleci
lalexgap Jul 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ executors:
golang:
docker:
- image: cimg/go:1.19.7
resource_class: 2xlarge
resource_class: xlarge
ubuntu:
docker:
- image: ubuntu:20.04
Expand Down Expand Up @@ -266,7 +266,7 @@ workflows:
ci:
jobs:
- lint-all:
concurrency: "16" # expend all docker 2xlarge CPUs.
concurrency: "8" # expend all docker xlarge CPUs.
- mod-tidy-check
- gofmt
- cbor-check
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,15 @@ make devnet/down

rm -rf ~/.cache/filecoin-proof-parameters
```
### Nitro payment integration

To enable nitro payment integration for `booster-http` the `BOOSTER_HTTP_NITRO_ENABLED` env var must be set. The env var `BOOSTER_HTTP_NITRO_ENDPOINT` can also be set to specify a specific nitro RPC endpoint; if its not set a default endpoint of `host.docker.internal:4007/api/v1` is used.

```bash
docker stop m booster-http
docker rm booster-http
BOOSTER_HTTP_NITRO_ENABLED=true BOOSTER_HTTP_NITRO_ENDPOINT=someurl:4007/api/v1 docker compose -f ./docker/devnet/docker-compose.yaml up -d
```

## License

Expand Down
76 changes: 71 additions & 5 deletions cmd/booster-http/gateway_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@ package main

import (
"fmt"
"math/big"
"mime"
"net/http"
"net/url"
"strings"

"github.com/ethereum/go-ethereum/common"
"github.com/ipfs/boxo/gateway"
"github.com/statechannels/go-nitro/rpc"
"github.com/statechannels/go-nitro/types"

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

"github.com/statechannels/go-nitro/crypto"
"github.com/statechannels/go-nitro/payments"
)

type gatewayHandler struct {
gwh http.Handler
supportedFormats map[string]struct{}
nitroRpcClient *rpc.RpcClient
}

func newGatewayHandler(gw *gateway.BlocksBackend, supportedFormats []string) http.Handler {
func newGatewayHandler(gw *gateway.BlocksBackend, supportedFormats []string, nitroRpcClient *rpc.RpcClient) http.Handler {
headers := map[string][]string{}
gateway.AddAccessControlHeaders(headers)

Expand All @@ -23,12 +34,11 @@ func newGatewayHandler(gw *gateway.BlocksBackend, supportedFormats []string) htt
fmtsMap[f] = struct{}{}
}

// TODO: For the integration demo, we need to allow CORS requests to the gateway.
return &gatewayHandler{
gwh: gateway.NewHandler(gateway.Config{
Headers: headers,
DeserializedResponses: true,
}, gw),
gwh: &corsHandler{gateway.NewHandler(gateway.Config{Headers: headers, DeserializedResponses: true}, gw)},
supportedFormats: fmtsMap,
nitroRpcClient: nitroRpcClient,
}
}

Expand All @@ -47,10 +57,66 @@ func (h *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

if h.nitroRpcClient != nil {
// This the payment we expect to receive for the file.
const expectedPayment = int64(5)

params, _ := url.ParseQuery(r.URL.RawQuery)

v, err := parseVoucher(params)
if err != nil {
webError(w, fmt.Errorf("could not parse voucher: %w", err), http.StatusBadRequest)
return
}

s, err := h.nitroRpcClient.ReceiveVoucher(v)

if err != nil {
webError(w, fmt.Errorf("error processing voucher %w", err), http.StatusBadRequest)
return
}

// s.Delta is amount our balance increases by adding this voucher
// AKA the payment amount we received in the request for this file
if s.Delta.Cmp(big.NewInt(expectedPayment)) < 0 {
webError(w, fmt.Errorf("payment of %d required, the voucher only resulted in a payment of %d", expectedPayment, s.Delta.Uint64()), http.StatusPaymentRequired)
return
}
}

h.gwh.ServeHTTP(w, r)
}

// parseVoucher takes in an a collection of query params and parses out a voucher.
func parseVoucher(params url.Values) (payments.Voucher, error) {
if !params.Has("channelId") {
return payments.Voucher{}, fmt.Errorf("a valid channel id must be provided")
}
if !params.Has("amount") {
return payments.Voucher{}, fmt.Errorf("a valid amount must be provided")
}
if !params.Has("signature") {
return payments.Voucher{}, fmt.Errorf("a valid signature must be provided")
}
rawChId := params.Get("channelId")
rawAmt := params.Get("amount")
amount := big.NewInt(0)
amount.SetString(rawAmt, 10)
rawSignature := params.Get("signature")

v := payments.Voucher{
ChannelId: types.Destination(common.HexToHash(rawChId)),
Amount: amount,
Signature: crypto.SplitSignature(hexutil.MustDecode(rawSignature)),
}
return v, nil
}

func webError(w http.ResponseWriter, err error, code int) {
// TODO: This is a hack to allow CORS requests to the gateway for the boost integration demo.
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "*")
fmt.Printf("ERROR CODE %d\n", code)
http.Error(w, err.Error(), code)
}

Expand Down
6 changes: 3 additions & 3 deletions cmd/booster-http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const testFile = "test/test_file"
func TestNewHttpServer(t *testing.T) {
// Create a new mock Http server
ctrl := gomock.NewController(t)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mocks_booster_http.NewMockHttpServerApi(ctrl), nil)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mocks_booster_http.NewMockHttpServerApi(ctrl), nil, nil)
err := httpServer.Start(context.Background())
require.NoError(t, err)
waitServerUp(t, 7777)
Expand All @@ -42,7 +42,7 @@ func TestHttpGzipResponse(t *testing.T) {
// Create a new mock Http server with custom functions
ctrl := gomock.NewController(t)
mockHttpServer := mocks_booster_http.NewMockHttpServerApi(ctrl)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mockHttpServer, nil)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mockHttpServer, nil, nil)
err := httpServer.Start(context.Background())
require.NoError(t, err)
waitServerUp(t, 7777)
Expand Down Expand Up @@ -109,7 +109,7 @@ func TestHttpInfo(t *testing.T) {

// Create a new mock Http server
ctrl := gomock.NewController(t)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mocks_booster_http.NewMockHttpServerApi(ctrl), nil)
httpServer := NewHttpServer("", "0.0.0.0", 7777, mocks_booster_http.NewMockHttpServerApi(ctrl), nil, nil)
err := httpServer.Start(context.Background())
require.NoError(t, err)
waitServerUp(t, 7777)
Expand Down
18 changes: 18 additions & 0 deletions cmd/booster-http/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ var runCmd = &cli.Command{
Usage: "Start a booster-http process",
Before: before,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "nitro-enabled",
Usage: "enables nitro micro payments",
Value: false,
},
&cli.StringFlag{
Name: "nitro-endpoint",
Usage: "the endpoint for the nitro server",
Value: "host.docker.internal:4007/api/v1",
},

&cli.BoolFlag{
Name: "pprof",
Usage: "run pprof web server on localhost:6070",
Expand Down Expand Up @@ -204,13 +215,20 @@ var runCmd = &cli.Command{
filtered := filters.NewFilteredBlockstore(rbs, multiFilter)
opts.Blockstore = filtered
}

nitroOpts := &NitroOptions{
Enabled: cctx.Bool("nitro-enabled"),
Endpoint: cctx.String("nitro-endpoint"),
}

sapi := serverApi{ctx: ctx, bapi: bapi, sa: sa}
server := NewHttpServer(
cctx.String("base-path"),
cctx.String("address"),
cctx.Int("port"),
sapi,
opts,
nitroOpts,
)

// Start the server
Expand Down
25 changes: 22 additions & 3 deletions cmd/booster-http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/ipfs/boxo/gateway"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
nrpc "github.com/statechannels/go-nitro/rpc"
"go.opencensus.io/stats"
)

Expand Down Expand Up @@ -53,6 +54,8 @@ type HttpServer struct {
ctx context.Context
cancel context.CancelFunc
server *http.Server

nitroRpcClient *nrpc.RpcClient
}

type HttpServerApi interface {
Expand All @@ -67,11 +70,26 @@ type HttpServerOptions struct {
SupportedResponseFormats []string
}

func NewHttpServer(path string, listenAddr string, port int, api HttpServerApi, opts *HttpServerOptions) *HttpServer {
type NitroOptions struct {
Enabled bool
Endpoint string
}

func NewHttpServer(path string, listenAddr string, port int, api HttpServerApi, opts *HttpServerOptions, nitroOpts *NitroOptions) *HttpServer {
if opts == nil {
opts = &HttpServerOptions{ServePieces: true}
}
return &HttpServer{path: path, listenAddr: listenAddr, port: port, api: api, opts: *opts, idxPage: parseTemplate(*opts)}
var rpcClient *nrpc.RpcClient
var err error
if nitroOpts != nil && nitroOpts.Enabled {

rpcClient, err = nrpc.NewHttpRpcClient(nitroOpts.Endpoint)
if err != nil {
panic(err)
}
}
return &HttpServer{path: path, port: port, api: api, opts: *opts, idxPage: parseTemplate(*opts), nitroRpcClient: rpcClient}

}

func (s *HttpServer) pieceBasePath() string {
Expand All @@ -96,7 +114,7 @@ func (s *HttpServer) Start(ctx context.Context) error {
if err != nil {
return fmt.Errorf("creating blocks gateway: %w", err)
}
handler.Handle(s.ipfsBasePath(), newGatewayHandler(gw, s.opts.SupportedResponseFormats))
handler.Handle(s.ipfsBasePath(), newGatewayHandler(gw, s.opts.SupportedResponseFormats, s.nitroRpcClient))
}

handler.HandleFunc("/", s.handleIndex)
Expand Down Expand Up @@ -156,6 +174,7 @@ func (s *HttpServer) handleByPieceCid(w http.ResponseWriter, r *http.Request) {
return
}

// TODO: Update to parse out multiple url params
pieceCidStr := r.URL.Path[prefixLen:]
pieceCid, err := cid.Parse(pieceCidStr)
if err != nil {
Expand Down
21 changes: 20 additions & 1 deletion cmd/booster-http/util.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package main

import "fmt"
import (
"fmt"
"net/http"
)

func addCommas(count uint64) string {
str := fmt.Sprintf("%d", count)
Expand All @@ -9,3 +12,19 @@ func addCommas(count uint64) string {
}
return str
}

type corsHandler struct {
sub http.Handler
}

func (h *corsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT")
w.Header().Set("Access-Control-Allow-Headers", "*")
if r.Method == "OPTIONS" {
_, _ = w.Write([]byte("OK"))
return
}

h.sub.ServeHTTP(w, r)
}
3 changes: 2 additions & 1 deletion docker/devnet/booster-http/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ echo $MINER_API_INFO
echo $BOOST_API_INFO

echo Starting booster-http...
exec booster-http run --serve-files=true --api-boost=$BOOST_API_INFO --api-fullnode=$FULLNODE_API_INFO --api-storage=$MINER_API_INFO --tracing
exec booster-http run --serve-files=true --api-boost=$BOOST_API_INFO --api-fullnode=$FULLNODE_API_INFO --api-storage=$MINER_API_INFO --tracing \
--nitro-enabled=$BOOSTER_HTTP_NITRO_ENABLED --nitro-endpoint=$BOOSTER_HTTP_NITRO_ENDPOINT
2 changes: 2 additions & 0 deletions docker/devnet/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ services:
- BOOST_PATH=/var/lib/boost
- LOTUS_PATH=/var/lib/lotus
- LOTUS_MINER_PATH=/var/lib/lotus-miner
- BOOSTER_HTTP_NITRO_ENABLED=${BOOSTER_HTTP_NITRO_ENABLED:-false}
- BOOSTER_HTTP_NITRO_ENDPOINT=${BOOSTER_HTTP_NITRO_ENDPOINT:-host.docker.internal:4007/api/v1}
restart: unless-stopped
logging: *default-logging
volumes:
Expand Down
Loading