Skip to content
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

feat: implement delegated PUTs #40

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
155 changes: 108 additions & 47 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,43 @@
"encoding/json"
"errors"
"fmt"
"io"
"os"
"time"

"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/routing/http/client"
"github.com/ipfs/boxo/routing/http/types"
"github.com/ipfs/boxo/routing/http/types/iter"
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/peer"
)

func findProviders(ctx context.Context, key cid.Cid, endpoint string, prettyOutput bool) error {
type askClient struct {
drc *client.Client
out io.Writer
pretty bool
}

func newAskClient(endpoint string, prettyOutput bool, out io.Writer) (*askClient, error) {

Check warning on line 26 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L26

Added line #L26 was not covered by tests
drc, err := client.New(endpoint)
if err != nil {
return err
return nil, err

Check warning on line 29 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L29

Added line #L29 was not covered by tests
}

recordsIter, err := drc.FindProviders(ctx, key)
if out == nil {
out = os.Stdout

Check warning on line 33 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L32-L33

Added lines #L32 - L33 were not covered by tests
}

return &askClient{
drc: drc,
pretty: prettyOutput,
out: out,
}, nil

Check warning on line 40 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L36-L40

Added lines #L36 - L40 were not covered by tests
}

func (a *askClient) findProviders(ctx context.Context, key cid.Cid) error {
recordsIter, err := a.drc.FindProviders(ctx, key)

Check warning on line 44 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L43-L44

Added lines #L43 - L44 were not covered by tests
if err != nil {
return err
}
Expand All @@ -40,33 +60,24 @@
return nil
}

if prettyOutput {
if a.pretty {

Check warning on line 63 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L63

Added line #L63 was not covered by tests
switch res.Val.GetSchema() {
case types.SchemaPeer:
record := res.Val.(*types.PeerRecord)
fmt.Fprintln(os.Stdout, record.ID)
fmt.Fprintln(os.Stdout, "\tProtocols:", record.Protocols)
fmt.Fprintln(os.Stdout, "\tAddresses:", record.Addrs)

//lint:ignore SA1019 // ignore staticcheck
case types.SchemaBitswap:
//lint:ignore SA1019 // ignore staticcheck
record := res.Val.(*types.BitswapRecord)
fmt.Fprintln(os.Stdout, record.ID)
fmt.Fprintln(os.Stdout, "\tProtocol:", record.Protocol)
fmt.Fprintln(os.Stdout, "\tAddresses:", record.Addrs)

fmt.Fprintln(a.out, record.ID)
fmt.Fprintln(a.out, "\tProtocols:", record.Protocols)
fmt.Fprintln(a.out, "\tAddresses:", record.Addrs)

Check warning on line 69 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L67-L69

Added lines #L67 - L69 were not covered by tests
default:
// This is an unknown schema. Let's just print it raw.
err := json.NewEncoder(os.Stdout).Encode(res.Val)
err := json.NewEncoder(a.out).Encode(res.Val)

Check warning on line 72 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L72

Added line #L72 was not covered by tests
if err != nil {
return err
}
}

fmt.Fprintln(os.Stdout)
fmt.Fprintln(a.out)

Check warning on line 78 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L78

Added line #L78 was not covered by tests
} else {
err := json.NewEncoder(os.Stdout).Encode(res.Val)
err := json.NewEncoder(a.out).Encode(res.Val)

Check warning on line 80 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L80

Added line #L80 was not covered by tests
if err != nil {
return err
}
Expand All @@ -76,13 +87,56 @@
return nil
}

func findPeers(ctx context.Context, pid peer.ID, endpoint string, prettyOutput bool) error {
drc, err := client.New(endpoint)
func (a *askClient) provide(ctx context.Context, records ...*types.AnnouncementRecord) error {
for _, rec := range records {
err := rec.Verify()
if err != nil {
return err

Check warning on line 94 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L90-L94

Added lines #L90 - L94 were not covered by tests
}
}

recordsIter, err := a.drc.ProvideRecords(ctx, records...)

Check warning on line 98 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L98

Added line #L98 was not covered by tests
if err != nil {
return err
}
defer recordsIter.Close()
return a.printProvideResult(recordsIter)

Check warning on line 103 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L102-L103

Added lines #L102 - L103 were not covered by tests
}

func (a *askClient) printProvideResult(recordsIter iter.ResultIter[*types.AnnouncementResponseRecord]) error {
for recordsIter.Next() {
res := recordsIter.Val()

Check warning on line 108 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L106-L108

Added lines #L106 - L108 were not covered by tests

// Check for error, but do not complain if we exceeded the timeout. We are
// expecting that to happen: we explicitly defined a timeout.
if res.Err != nil {
if !errors.Is(res.Err, context.DeadlineExceeded) {
return res.Err

Check warning on line 114 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L112-L114

Added lines #L112 - L114 were not covered by tests
}

return nil

Check warning on line 117 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L117

Added line #L117 was not covered by tests
}

if a.pretty {
if res.Val.Error != "" {
fmt.Fprintf(a.out, "Error: %s", res.Val.Error)
} else {
fmt.Fprintf(a.out, "TTL: %s", res.Val.TTL)

Check warning on line 124 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L120-L124

Added lines #L120 - L124 were not covered by tests
}
fmt.Fprintln(a.out)
} else {
err := json.NewEncoder(a.out).Encode(res.Val)
if err != nil {
return err

Check warning on line 130 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L126-L130

Added lines #L126 - L130 were not covered by tests
}
}
}

recordsIter, err := drc.FindPeers(ctx, pid)
return nil

Check warning on line 135 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L135

Added line #L135 was not covered by tests
}

func (a *askClient) findPeers(ctx context.Context, pid peer.ID) error {
recordsIter, err := a.drc.FindPeers(ctx, pid)

Check warning on line 139 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L138-L139

Added lines #L138 - L139 were not covered by tests
if err != nil {
return err
}
Expand All @@ -101,13 +155,13 @@
return nil
}

if prettyOutput {
fmt.Fprintln(os.Stdout, res.Val.ID)
fmt.Fprintln(os.Stdout, "\tProtocols:", res.Val.Protocols)
fmt.Fprintln(os.Stdout, "\tAddresses:", res.Val.Addrs)
fmt.Fprintln(os.Stdout)
if a.pretty {
fmt.Fprintln(a.out, res.Val.ID)
fmt.Fprintln(a.out, "\tProtocols:", res.Val.Protocols)
fmt.Fprintln(a.out, "\tAddresses:", res.Val.Addrs)
fmt.Fprintln(a.out)

Check warning on line 162 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L158-L162

Added lines #L158 - L162 were not covered by tests
} else {
err := json.NewEncoder(os.Stdout).Encode(res.Val)
err := json.NewEncoder(a.out).Encode(res.Val)

Check warning on line 164 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L164

Added line #L164 was not covered by tests
if err != nil {
return err
}
Expand All @@ -117,18 +171,30 @@
return nil
}

func getIPNS(ctx context.Context, name ipns.Name, endpoint string, prettyOutput bool) error {
drc, err := client.New(endpoint)
func (a *askClient) providePeer(ctx context.Context, records ...*types.AnnouncementRecord) error {
for _, rec := range records {
err := rec.Verify()
if err != nil {
return err

Check warning on line 178 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L174-L178

Added lines #L174 - L178 were not covered by tests
}
}

recordsIter, err := a.drc.ProvidePeerRecords(ctx, records...)

Check warning on line 182 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L182

Added line #L182 was not covered by tests
if err != nil {
return err
}
defer recordsIter.Close()

Check warning on line 186 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L186

Added line #L186 was not covered by tests

rec, err := drc.GetIPNS(ctx, name)
return a.printProvideResult(recordsIter)

Check warning on line 188 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L188

Added line #L188 was not covered by tests
}

func (a *askClient) getIPNS(ctx context.Context, name ipns.Name) error {
rec, err := a.drc.GetIPNS(ctx, name)

Check warning on line 192 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L191-L192

Added lines #L191 - L192 were not covered by tests
if err != nil {
return err
}

if prettyOutput {
if a.pretty {

Check warning on line 197 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L197

Added line #L197 was not covered by tests
v, err := rec.Value()
if err != nil {
return err
Expand All @@ -144,19 +210,19 @@
return err
}

fmt.Printf("/ipns/%s\n", name)
fmt.Fprintf(a.out, "/ipns/%s\n", name)

Check warning on line 213 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L213

Added line #L213 was not covered by tests

// Since [client.Client.GetIPNS] verifies if the retrieved record is valid, we
// do not need to verify it again. However, if you were not using this specific
// client, but using some other tool, you should always validate the IPNS Record
// using the [ipns.Validate] or [ipns.ValidateWithName] functions.
fmt.Println("\tSignature Validated")
fmt.Println("\tValue:", v.String())
fmt.Println("\tSequence:", seq)
fmt.Println("\tValidityType : EOL/End-of-Life")
fmt.Println("\tValidity:", eol.Format(time.RFC3339))
fmt.Fprintln(a.out, "\tSignature Validated")
fmt.Fprintln(a.out, "\tValue:", v.String())
fmt.Fprintln(a.out, "\tSequence:", seq)
fmt.Fprintln(a.out, "\tValidityType : EOL/End-of-Life")
fmt.Fprintln(a.out, "\tValidity:", eol.Format(time.RFC3339))

Check warning on line 223 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L219-L223

Added lines #L219 - L223 were not covered by tests
if ttl, err := rec.TTL(); err == nil {
fmt.Println("\tTTL:", ttl.String())
fmt.Fprintln(a.out, "\tTTL:", ttl.String())

Check warning on line 225 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L225

Added line #L225 was not covered by tests
}

return nil
Expand All @@ -167,20 +233,15 @@
return err
}

_, err = os.Stdout.Write(raw)
_, err = a.out.Write(raw)

Check warning on line 236 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L236

Added line #L236 was not covered by tests
return err
}

func putIPNS(ctx context.Context, name ipns.Name, record []byte, endpoint string) error {
drc, err := client.New(endpoint)
if err != nil {
return err
}

func (a *askClient) putIPNS(ctx context.Context, name ipns.Name, record []byte) error {

Check warning on line 240 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L240

Added line #L240 was not covered by tests
rec, err := ipns.UnmarshalRecord(record)
if err != nil {
return err
}

return drc.PutIPNS(ctx, name, rec)
return a.drc.PutIPNS(ctx, name, rec)

Check warning on line 246 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L246

Added line #L246 was not covered by tests
}
22 changes: 22 additions & 0 deletions docs/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- [Configuration](#configuration)
- [`SOMEGUY_LISTEN_ADDRESS`](#someguy_listen_address)
- [`SOMEGUY_ACCELERATED_DHT`](#someguy_accelerated_dht)
- [`SOMEGUY_PUT_ENABLED`](#someguy_put_enabled)
- [`SOMEGUY_DATADIR`](#someguy_datadir)
- [`SOMEGUY_PROVIDER_ENDPOINTS`](#someguy_provider_endpoints)
- [`SOMEGUY_PEER_ENDPOINTS`](#someguy_peer_endpoints)
- [`SOMEGUY_IPNS_ENDPOINTS`](#someguy_ipns_endpoints)
Expand All @@ -28,6 +30,26 @@ Whether or not the Accelerated DHT is enabled or not.

Default: `true`

### `SOMEGUY_PUT_ENABLED`

Whether or not to accept Delegated Routing V1 PUT requests. Affects all PUT requests:
provider records, peer records and IPNS records.

By default, PUT requests are ignored. Therefore, they will neither be stored locally,
nor sent to other remote endpoints.

Default: `false`

### `SOMEGUY_DATADIR`

Used in conjunction with [`SOMEGUY_PUT_ENABLED`](#someguy_put_enabled).

The LevelDB data directory to persist PUT records. When receiving PUT requests,
the records will be stored in this database. The database is queried for GET
requests.

Default: none

### `SOMEGUY_PROVIDER_ENDPOINTS`

Comma-separated list of other Delegated Routing V1 endpoints to proxy provider requests to.
Expand Down
11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ go 1.21
require (
github.com/CAFxX/httpcompression v0.0.9
github.com/felixge/httpsnoop v1.0.4
github.com/ipfs/boxo v0.19.1-0.20240415103851-7f9506844904
github.com/ipfs/boxo v0.19.1-0.20240507133722-cc217db63f3b
github.com/ipfs/go-cid v0.4.1
github.com/ipfs/go-datastore v0.6.0
github.com/ipfs/go-ds-leveldb v0.5.0
github.com/ipfs/go-log/v2 v2.5.1
github.com/libp2p/go-libp2p v0.33.2
github.com/libp2p/go-libp2p-kad-dht v0.25.2
Expand All @@ -16,14 +18,15 @@ require (
github.com/multiformats/go-multihash v0.2.3
github.com/prometheus/client_golang v1.19.0
github.com/rs/cors v1.10.1
github.com/samber/lo v1.39.0
github.com/slok/go-http-metrics v0.11.0
github.com/stretchr/testify v1.9.0
github.com/urfave/cli/v2 v2.27.1
)

require (
github.com/Jorropo/jsync v1.0.1 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
Expand All @@ -42,6 +45,7 @@ require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
github.com/google/uuid v1.6.0 // indirect
Expand All @@ -51,7 +55,6 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/ipfs/go-datastore v0.6.0 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipld/go-ipld-prime v0.21.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
Expand Down Expand Up @@ -101,9 +104,9 @@ require (
github.com/quic-go/webtransport-go v0.7.0 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/samber/lo v1.39.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
go.opencensus.io v0.24.0 // indirect
Expand Down
Loading
Loading