Skip to content

Commit c39499a

Browse files
committed
feat: delegated PUTs
1 parent 42106a1 commit c39499a

6 files changed

+308
-64
lines changed

client.go

+108-47
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,43 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"io"
89
"os"
910
"time"
1011

1112
"github.com/ipfs/boxo/ipns"
1213
"github.com/ipfs/boxo/routing/http/client"
1314
"github.com/ipfs/boxo/routing/http/types"
15+
"github.com/ipfs/boxo/routing/http/types/iter"
1416
"github.com/ipfs/go-cid"
1517
"github.com/libp2p/go-libp2p/core/peer"
1618
)
1719

18-
func findProviders(ctx context.Context, key cid.Cid, endpoint string, prettyOutput bool) error {
20+
type askClient struct {
21+
drc *client.Client
22+
out io.Writer
23+
pretty bool
24+
}
25+
26+
func newAskClient(endpoint string, prettyOutput bool, out io.Writer) (*askClient, error) {
1927
drc, err := client.New(endpoint)
2028
if err != nil {
21-
return err
29+
return nil, err
2230
}
2331

24-
recordsIter, err := drc.FindProviders(ctx, key)
32+
if out == nil {
33+
out = os.Stdout
34+
}
35+
36+
return &askClient{
37+
drc: drc,
38+
pretty: prettyOutput,
39+
out: out,
40+
}, nil
41+
}
42+
43+
func (a *askClient) findProviders(ctx context.Context, key cid.Cid) error {
44+
recordsIter, err := a.drc.FindProviders(ctx, key)
2545
if err != nil {
2646
return err
2747
}
@@ -40,33 +60,24 @@ func findProviders(ctx context.Context, key cid.Cid, endpoint string, prettyOutp
4060
return nil
4161
}
4262

43-
if prettyOutput {
63+
if a.pretty {
4464
switch res.Val.GetSchema() {
4565
case types.SchemaPeer:
4666
record := res.Val.(*types.PeerRecord)
47-
fmt.Fprintln(os.Stdout, record.ID)
48-
fmt.Fprintln(os.Stdout, "\tProtocols:", record.Protocols)
49-
fmt.Fprintln(os.Stdout, "\tAddresses:", record.Addrs)
50-
51-
//lint:ignore SA1019 // ignore staticcheck
52-
case types.SchemaBitswap:
53-
//lint:ignore SA1019 // ignore staticcheck
54-
record := res.Val.(*types.BitswapRecord)
55-
fmt.Fprintln(os.Stdout, record.ID)
56-
fmt.Fprintln(os.Stdout, "\tProtocol:", record.Protocol)
57-
fmt.Fprintln(os.Stdout, "\tAddresses:", record.Addrs)
58-
67+
fmt.Fprintln(a.out, record.ID)
68+
fmt.Fprintln(a.out, "\tProtocols:", record.Protocols)
69+
fmt.Fprintln(a.out, "\tAddresses:", record.Addrs)
5970
default:
6071
// This is an unknown schema. Let's just print it raw.
61-
err := json.NewEncoder(os.Stdout).Encode(res.Val)
72+
err := json.NewEncoder(a.out).Encode(res.Val)
6273
if err != nil {
6374
return err
6475
}
6576
}
6677

67-
fmt.Fprintln(os.Stdout)
78+
fmt.Fprintln(a.out)
6879
} else {
69-
err := json.NewEncoder(os.Stdout).Encode(res.Val)
80+
err := json.NewEncoder(a.out).Encode(res.Val)
7081
if err != nil {
7182
return err
7283
}
@@ -76,13 +87,56 @@ func findProviders(ctx context.Context, key cid.Cid, endpoint string, prettyOutp
7687
return nil
7788
}
7889

79-
func findPeers(ctx context.Context, pid peer.ID, endpoint string, prettyOutput bool) error {
80-
drc, err := client.New(endpoint)
90+
func (a *askClient) provide(ctx context.Context, records ...*types.AnnouncementRecord) error {
91+
for _, rec := range records {
92+
err := rec.Verify()
93+
if err != nil {
94+
return err
95+
}
96+
}
97+
98+
recordsIter, err := a.drc.ProvideRecords(ctx, records...)
8199
if err != nil {
82100
return err
83101
}
102+
defer recordsIter.Close()
103+
return a.printProvideResult(recordsIter)
104+
}
105+
106+
func (a *askClient) printProvideResult(recordsIter iter.ResultIter[*types.AnnouncementResponseRecord]) error {
107+
for recordsIter.Next() {
108+
res := recordsIter.Val()
109+
110+
// Check for error, but do not complain if we exceeded the timeout. We are
111+
// expecting that to happen: we explicitly defined a timeout.
112+
if res.Err != nil {
113+
if !errors.Is(res.Err, context.DeadlineExceeded) {
114+
return res.Err
115+
}
116+
117+
return nil
118+
}
119+
120+
if a.pretty {
121+
if res.Val.Error != "" {
122+
fmt.Fprintf(a.out, "Error: %s", res.Val.Error)
123+
} else {
124+
fmt.Fprintf(a.out, "TTL: %s", res.Val.TTL)
125+
}
126+
fmt.Fprintln(a.out)
127+
} else {
128+
err := json.NewEncoder(a.out).Encode(res.Val)
129+
if err != nil {
130+
return err
131+
}
132+
}
133+
}
84134

85-
recordsIter, err := drc.FindPeers(ctx, pid)
135+
return nil
136+
}
137+
138+
func (a *askClient) findPeers(ctx context.Context, pid peer.ID) error {
139+
recordsIter, err := a.drc.FindPeers(ctx, pid)
86140
if err != nil {
87141
return err
88142
}
@@ -101,13 +155,13 @@ func findPeers(ctx context.Context, pid peer.ID, endpoint string, prettyOutput b
101155
return nil
102156
}
103157

104-
if prettyOutput {
105-
fmt.Fprintln(os.Stdout, res.Val.ID)
106-
fmt.Fprintln(os.Stdout, "\tProtocols:", res.Val.Protocols)
107-
fmt.Fprintln(os.Stdout, "\tAddresses:", res.Val.Addrs)
108-
fmt.Fprintln(os.Stdout)
158+
if a.pretty {
159+
fmt.Fprintln(a.out, res.Val.ID)
160+
fmt.Fprintln(a.out, "\tProtocols:", res.Val.Protocols)
161+
fmt.Fprintln(a.out, "\tAddresses:", res.Val.Addrs)
162+
fmt.Fprintln(a.out)
109163
} else {
110-
err := json.NewEncoder(os.Stdout).Encode(res.Val)
164+
err := json.NewEncoder(a.out).Encode(res.Val)
111165
if err != nil {
112166
return err
113167
}
@@ -117,18 +171,30 @@ func findPeers(ctx context.Context, pid peer.ID, endpoint string, prettyOutput b
117171
return nil
118172
}
119173

120-
func getIPNS(ctx context.Context, name ipns.Name, endpoint string, prettyOutput bool) error {
121-
drc, err := client.New(endpoint)
174+
func (a *askClient) providePeer(ctx context.Context, records ...*types.AnnouncementRecord) error {
175+
for _, rec := range records {
176+
err := rec.Verify()
177+
if err != nil {
178+
return err
179+
}
180+
}
181+
182+
recordsIter, err := a.drc.ProvidePeerRecords(ctx, records...)
122183
if err != nil {
123184
return err
124185
}
186+
defer recordsIter.Close()
125187

126-
rec, err := drc.GetIPNS(ctx, name)
188+
return a.printProvideResult(recordsIter)
189+
}
190+
191+
func (a *askClient) getIPNS(ctx context.Context, name ipns.Name) error {
192+
rec, err := a.drc.GetIPNS(ctx, name)
127193
if err != nil {
128194
return err
129195
}
130196

131-
if prettyOutput {
197+
if a.pretty {
132198
v, err := rec.Value()
133199
if err != nil {
134200
return err
@@ -144,19 +210,19 @@ func getIPNS(ctx context.Context, name ipns.Name, endpoint string, prettyOutput
144210
return err
145211
}
146212

147-
fmt.Printf("/ipns/%s\n", name)
213+
fmt.Fprintf(a.out, "/ipns/%s\n", name)
148214

149215
// Since [client.Client.GetIPNS] verifies if the retrieved record is valid, we
150216
// do not need to verify it again. However, if you were not using this specific
151217
// client, but using some other tool, you should always validate the IPNS Record
152218
// using the [ipns.Validate] or [ipns.ValidateWithName] functions.
153-
fmt.Println("\tSignature Validated")
154-
fmt.Println("\tValue:", v.String())
155-
fmt.Println("\tSequence:", seq)
156-
fmt.Println("\tValidityType : EOL/End-of-Life")
157-
fmt.Println("\tValidity:", eol.Format(time.RFC3339))
219+
fmt.Fprintln(a.out, "\tSignature Validated")
220+
fmt.Fprintln(a.out, "\tValue:", v.String())
221+
fmt.Fprintln(a.out, "\tSequence:", seq)
222+
fmt.Fprintln(a.out, "\tValidityType : EOL/End-of-Life")
223+
fmt.Fprintln(a.out, "\tValidity:", eol.Format(time.RFC3339))
158224
if ttl, err := rec.TTL(); err == nil {
159-
fmt.Println("\tTTL:", ttl.String())
225+
fmt.Fprintln(a.out, "\tTTL:", ttl.String())
160226
}
161227

162228
return nil
@@ -167,20 +233,15 @@ func getIPNS(ctx context.Context, name ipns.Name, endpoint string, prettyOutput
167233
return err
168234
}
169235

170-
_, err = os.Stdout.Write(raw)
236+
_, err = a.out.Write(raw)
171237
return err
172238
}
173239

174-
func putIPNS(ctx context.Context, name ipns.Name, record []byte, endpoint string) error {
175-
drc, err := client.New(endpoint)
176-
if err != nil {
177-
return err
178-
}
179-
240+
func (a *askClient) putIPNS(ctx context.Context, name ipns.Name, record []byte) error {
180241
rec, err := ipns.UnmarshalRecord(record)
181242
if err != nil {
182243
return err
183244
}
184245

185-
return drc.PutIPNS(ctx, name, rec)
246+
return a.drc.PutIPNS(ctx, name, rec)
186247
}

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.21
55
require (
66
github.com/CAFxX/httpcompression v0.0.9
77
github.com/felixge/httpsnoop v1.0.4
8-
github.com/ipfs/boxo v0.18.1-0.20240307083455-b6b7771fbca0
8+
github.com/ipfs/boxo v0.18.1-0.20240319120907-07fc4754a2d2
99
github.com/ipfs/go-cid v0.4.1
1010
github.com/ipfs/go-log/v2 v2.5.1
1111
github.com/libp2p/go-libp2p v0.33.0

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
186186
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
187187
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
188188
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
189-
github.com/ipfs/boxo v0.18.1-0.20240307083455-b6b7771fbca0 h1:4OinZKXAVTOisDaml2XC9dxE6zIWmRFQkxD3zqNKaHw=
190-
github.com/ipfs/boxo v0.18.1-0.20240307083455-b6b7771fbca0/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80=
189+
github.com/ipfs/boxo v0.18.1-0.20240319120907-07fc4754a2d2 h1:zDZQC27xqgBWKfu/dU5d7KcREgAYPlVRRdkeM8EYt+k=
190+
github.com/ipfs/boxo v0.18.1-0.20240319120907-07fc4754a2d2/go.mod h1:V5gJzbIMwKEXrg3IdvAxIdF7UPgU4RsXmNGS8MQ/0D4=
191191
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
192192
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
193193
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=

main.go

+78-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package main
22

33
import (
4+
"encoding/json"
45
"errors"
56
"log"
67
"os"
78

89
"github.com/ipfs/boxo/ipns"
10+
"github.com/ipfs/boxo/routing/http/types"
911
"github.com/ipfs/go-cid"
1012
"github.com/libp2p/go-libp2p/core/peer"
1113
"github.com/multiformats/go-multibase"
@@ -85,7 +87,39 @@ func main() {
8587
if err != nil {
8688
return err
8789
}
88-
return findProviders(ctx.Context, c, ctx.String("endpoint"), ctx.Bool("pretty"))
90+
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
91+
if err != nil {
92+
return err
93+
}
94+
return cl.findProviders(ctx.Context, c)
95+
},
96+
},
97+
{
98+
Name: "provide",
99+
Usage: "provide <record-path...>",
100+
UsageText: "Provide a one or multiple announcement records to the network",
101+
Action: func(ctx *cli.Context) error {
102+
if ctx.NArg() < 1 {
103+
return errors.New("invalid command, see help")
104+
}
105+
records := []*types.AnnouncementRecord{}
106+
for _, recordPath := range ctx.Args().Slice() {
107+
recordData, err := os.ReadFile(recordPath)
108+
if err != nil {
109+
return err
110+
}
111+
var record *types.AnnouncementRecord
112+
err = json.Unmarshal(recordData, &record)
113+
if err != nil {
114+
return err
115+
}
116+
records = append(records, record)
117+
}
118+
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
119+
if err != nil {
120+
return err
121+
}
122+
return cl.provide(ctx.Context, records...)
89123
},
90124
},
91125
{
@@ -101,7 +135,39 @@ func main() {
101135
if err != nil {
102136
return err
103137
}
104-
return findPeers(ctx.Context, pid, ctx.String("endpoint"), ctx.Bool("pretty"))
138+
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
139+
if err != nil {
140+
return err
141+
}
142+
return cl.findPeers(ctx.Context, pid)
143+
},
144+
},
145+
{
146+
Name: "providepeers",
147+
Usage: "providepeers <record-path...>",
148+
UsageText: "Provide a one or multiple peer announcement records to the network",
149+
Action: func(ctx *cli.Context) error {
150+
if ctx.NArg() < 1 {
151+
return errors.New("invalid command, see help")
152+
}
153+
records := []*types.AnnouncementRecord{}
154+
for _, recordPath := range ctx.Args().Slice() {
155+
recordData, err := os.ReadFile(recordPath)
156+
if err != nil {
157+
return err
158+
}
159+
var record *types.AnnouncementRecord
160+
err = json.Unmarshal(recordData, &record)
161+
if err != nil {
162+
return err
163+
}
164+
records = append(records, record)
165+
}
166+
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
167+
if err != nil {
168+
return err
169+
}
170+
return cl.providePeer(ctx.Context, records...)
105171
},
106172
},
107173
{
@@ -117,7 +183,11 @@ func main() {
117183
if err != nil {
118184
return err
119185
}
120-
return getIPNS(ctx.Context, name, ctx.String("endpoint"), ctx.Bool("pretty"))
186+
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
187+
if err != nil {
188+
return err
189+
}
190+
return cl.getIPNS(ctx.Context, name)
121191
},
122192
},
123193
{
@@ -139,7 +209,11 @@ func main() {
139209
if err != nil {
140210
return err
141211
}
142-
return putIPNS(ctx.Context, name, recBytes, ctx.String("endpoint"))
212+
cl, err := newAskClient(ctx.String("endpoint"), ctx.Bool("pretty"), os.Stdout)
213+
if err != nil {
214+
return err
215+
}
216+
return cl.putIPNS(ctx.Context, name, recBytes)
143217
},
144218
},
145219
},

0 commit comments

Comments
 (0)