Skip to content

Commit

Permalink
feat: delegated PUTs
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Mar 19, 2024
1 parent 42106a1 commit c39499a
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 64 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 34 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L32-L34

Added lines #L32 - L34 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 95 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L90-L95

Added lines #L90 - L95 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 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 115 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L106-L115

Added lines #L106 - L115 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)
}
fmt.Fprintln(a.out)
} else {
err := json.NewEncoder(a.out).Encode(res.Val)
if err != nil {
return err
}

Check warning on line 131 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L120-L131

Added lines #L120 - L131 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 179 in client.go

View check run for this annotation

Codecov / codecov/patch

client.go#L174-L179

Added lines #L174 - L179 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
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21
require (
github.com/CAFxX/httpcompression v0.0.9
github.com/felixge/httpsnoop v1.0.4
github.com/ipfs/boxo v0.18.1-0.20240307083455-b6b7771fbca0
github.com/ipfs/boxo v0.18.1-0.20240319120907-07fc4754a2d2
github.com/ipfs/go-cid v0.4.1
github.com/ipfs/go-log/v2 v2.5.1
github.com/libp2p/go-libp2p v0.33.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/ipfs/boxo v0.18.1-0.20240307083455-b6b7771fbca0 h1:4OinZKXAVTOisDaml2XC9dxE6zIWmRFQkxD3zqNKaHw=
github.com/ipfs/boxo v0.18.1-0.20240307083455-b6b7771fbca0/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80=
github.com/ipfs/boxo v0.18.1-0.20240319120907-07fc4754a2d2 h1:zDZQC27xqgBWKfu/dU5d7KcREgAYPlVRRdkeM8EYt+k=
github.com/ipfs/boxo v0.18.1-0.20240319120907-07fc4754a2d2/go.mod h1:V5gJzbIMwKEXrg3IdvAxIdF7UPgU4RsXmNGS8MQ/0D4=
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
Expand Down
82 changes: 78 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package main

import (
"encoding/json"
"errors"
"log"
"os"

"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/routing/http/types"
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multibase"
Expand Down Expand Up @@ -85,7 +87,39 @@ func main() {
if err != nil {
return err
}
return findProviders(ctx.Context, c, ctx.String("endpoint"), ctx.Bool("pretty"))
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
if err != nil {
return err
}
return cl.findProviders(ctx.Context, c)

Check warning on line 94 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L90-L94

Added lines #L90 - L94 were not covered by tests
},
},
{
Name: "provide",
Usage: "provide <record-path...>",
UsageText: "Provide a one or multiple announcement records to the network",
Action: func(ctx *cli.Context) error {
if ctx.NArg() < 1 {
return errors.New("invalid command, see help")
}
records := []*types.AnnouncementRecord{}
for _, recordPath := range ctx.Args().Slice() {
recordData, err := os.ReadFile(recordPath)
if err != nil {
return err
}
var record *types.AnnouncementRecord
err = json.Unmarshal(recordData, &record)
if err != nil {
return err
}
records = append(records, record)

Check warning on line 116 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L101-L116

Added lines #L101 - L116 were not covered by tests
}
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
if err != nil {
return err
}
return cl.provide(ctx.Context, records...)

Check warning on line 122 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L118-L122

Added lines #L118 - L122 were not covered by tests
},
},
{
Expand All @@ -101,7 +135,39 @@ func main() {
if err != nil {
return err
}
return findPeers(ctx.Context, pid, ctx.String("endpoint"), ctx.Bool("pretty"))
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
if err != nil {
return err
}
return cl.findPeers(ctx.Context, pid)

Check warning on line 142 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L138-L142

Added lines #L138 - L142 were not covered by tests
},
},
{
Name: "providepeers",
Usage: "providepeers <record-path...>",
UsageText: "Provide a one or multiple peer announcement records to the network",
Action: func(ctx *cli.Context) error {
if ctx.NArg() < 1 {
return errors.New("invalid command, see help")
}
records := []*types.AnnouncementRecord{}
for _, recordPath := range ctx.Args().Slice() {
recordData, err := os.ReadFile(recordPath)
if err != nil {
return err
}
var record *types.AnnouncementRecord
err = json.Unmarshal(recordData, &record)
if err != nil {
return err
}
records = append(records, record)

Check warning on line 164 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L149-L164

Added lines #L149 - L164 were not covered by tests
}
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
if err != nil {
return err
}
return cl.providePeer(ctx.Context, records...)

Check warning on line 170 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L166-L170

Added lines #L166 - L170 were not covered by tests
},
},
{
Expand All @@ -117,7 +183,11 @@ func main() {
if err != nil {
return err
}
return getIPNS(ctx.Context, name, ctx.String("endpoint"), ctx.Bool("pretty"))
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
if err != nil {
return err
}
return cl.getIPNS(ctx.Context, name)

Check warning on line 190 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L186-L190

Added lines #L186 - L190 were not covered by tests
},
},
{
Expand All @@ -139,7 +209,11 @@ func main() {
if err != nil {
return err
}
return putIPNS(ctx.Context, name, recBytes, ctx.String("endpoint"))
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
if err != nil {
return err
}
return cl.putIPNS(ctx.Context, name, recBytes)

Check warning on line 216 in main.go

View check run for this annotation

Codecov / codecov/patch

main.go#L212-L216

Added lines #L212 - L216 were not covered by tests
},
},
},
Expand Down
Loading

0 comments on commit c39499a

Please sign in to comment.