Skip to content

Commit

Permalink
feat: implement delegated PUTs
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Apr 15, 2024
1 parent 16875cc commit 8654be1
Show file tree
Hide file tree
Showing 8 changed files with 975 additions and 141 deletions.
155 changes: 108 additions & 47 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,43 @@ import (
"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 @@ func findProviders(ctx context.Context, key cid.Cid, endpoint string, prettyOutp
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 @@ func findProviders(ctx context.Context, key cid.Cid, endpoint string, prettyOutp
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 @@ func findPeers(ctx context.Context, pid peer.ID, endpoint string, prettyOutput b
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 @@ func findPeers(ctx context.Context, pid peer.ID, endpoint string, prettyOutput b
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 @@ func getIPNS(ctx context.Context, name ipns.Name, endpoint string, prettyOutput
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 @@ func getIPNS(ctx context.Context, name ipns.Name, endpoint string, prettyOutput
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.20240415145225-0915caffc979
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

0 comments on commit 8654be1

Please sign in to comment.