From 5156f2116256c45b45720161d1169eab85378f48 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Tue, 20 Jun 2023 14:24:31 +0200 Subject: [PATCH] feat(ipns): records with V2-only signatures (#9932) --- client/rpc/name.go | 24 +- config/routing.go | 11 +- config/routing_test.go | 77 +----- core/commands/dht_test.go | 4 +- core/commands/name/name.go | 159 ++++++------ core/commands/name/publish.go | 21 +- core/core_test.go | 296 --------------------- core/coreapi/name.go | 38 +-- docs/changelogs/v0.22.md | 10 + docs/examples/kubo-as-a-library/go.mod | 4 +- docs/examples/kubo-as-a-library/go.sum | 8 +- go.mod | 10 +- go.sum | 8 +- routing/delegated.go | 79 ------ routing/delegated_test.go | 112 ++++---- routing/wrapper.go | 28 -- test/cli/fixtures/TestName.car | Bin 0 -> 421 bytes test/cli/name_test.go | 266 +++++++++++++++++++ test/dependencies/go.mod | 2 +- test/dependencies/go.sum | 4 +- test/sharness/t0100-name.sh | 345 ------------------------- 21 files changed, 458 insertions(+), 1048 deletions(-) create mode 100644 test/cli/fixtures/TestName.car create mode 100644 test/cli/name_test.go delete mode 100755 test/sharness/t0100-name.sh diff --git a/client/rpc/name.go b/client/rpc/name.go index e6c726c5811..5ad9d16cb8a 100644 --- a/client/rpc/name.go +++ b/client/rpc/name.go @@ -10,29 +10,20 @@ import ( caopts "github.com/ipfs/boxo/coreiface/options" nsopts "github.com/ipfs/boxo/coreiface/options/namesys" "github.com/ipfs/boxo/coreiface/path" + "github.com/ipfs/boxo/ipns" ) type NameAPI HttpApi type ipnsEntry struct { - JName string `json:"Name"` - JValue string `json:"Value"` - - path path.Path -} - -func (e *ipnsEntry) Name() string { - return e.JName + Name string `json:"Name"` + Value string `json:"Value"` } -func (e *ipnsEntry) Value() path.Path { - return e.path -} - -func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.NamePublishOption) (iface.IpnsEntry, error) { +func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.NamePublishOption) (ipns.Name, error) { options, err := caopts.NamePublishOptions(opts...) if err != nil { - return nil, err + return ipns.Name{}, err } req := api.core().Request("name/publish", p.String()). @@ -47,10 +38,9 @@ func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.Nam var out ipnsEntry if err := req.Exec(ctx, &out); err != nil { - return nil, err + return ipns.Name{}, err } - out.path = path.New(out.JValue) - return &out, out.path.IsValid() + return ipns.NameFromString(out.Name) } func (api *NameAPI) Search(ctx context.Context, name string, opts ...caopts.NameResolveOption) (<-chan iface.IpnsResult, error) { diff --git a/config/routing.go b/config/routing.go index 7f5e48aa262..ede8f0f9ea9 100644 --- a/config/routing.go +++ b/config/routing.go @@ -28,7 +28,7 @@ type Router struct { Type RouterType // Parameters are extra configuration that this router might need. - // A common one for reframe router is "Endpoint". + // A common one for HTTP router is "Endpoint". Parameters interface{} } @@ -81,8 +81,6 @@ func (r *RouterParser) UnmarshalJSON(b []byte) error { switch out.Type { case RouterTypeHTTP: p = &HTTPRouterParams{} - case RouterTypeReframe: - p = &ReframeRouterParams{} case RouterTypeDHT: p = &DHTRouterParams{} case RouterTypeSequential: @@ -106,7 +104,6 @@ func (r *RouterParser) UnmarshalJSON(b []byte) error { type RouterType string const ( - RouterTypeReframe RouterType = "reframe" // More info here: https://github.com/ipfs/specs/tree/main/reframe . Actually deprecated. RouterTypeHTTP RouterType = "http" // HTTP JSON API for delegated routing systems (IPIP-337). RouterTypeDHT RouterType = "dht" // DHT router. RouterTypeSequential RouterType = "sequential" // Router helper to execute several routers sequentially. @@ -133,12 +130,6 @@ const ( var MethodNameList = []MethodName{MethodNameProvide, MethodNameFindPeers, MethodNameFindProviders, MethodNameGetIPNS, MethodNamePutIPNS} -type ReframeRouterParams struct { - // Endpoint is the URL where the routing implementation will point to get the information. - // Usually used for reframe Routers. - Endpoint string -} - type HTTPRouterParams struct { // Endpoint is the URL where the routing implementation will point to get the information. Endpoint string diff --git a/config/routing_test.go b/config/routing_test.go index 49068f976d9..4dfc54ccbd2 100644 --- a/config/routing_test.go +++ b/config/routing_test.go @@ -23,12 +23,6 @@ func TestRouterParameters(t *testing.T) { PublicIPNetwork: false, }, }}, - "router-reframe": {Router{ - Type: RouterTypeReframe, - Parameters: ReframeRouterParams{ - Endpoint: "reframe-endpoint", - }, - }}, "router-parallel": {Router{ Type: RouterTypeParallel, Parameters: ComposableRouterParams{ @@ -39,7 +33,7 @@ func TestRouterParameters(t *testing.T) { IgnoreErrors: true, }, { - RouterName: "router-reframe", + RouterName: "router-dht", Timeout: Duration{10 * time.Second}, IgnoreErrors: false, ExecuteAfter: &OptionalDuration{&sec}, @@ -58,7 +52,7 @@ func TestRouterParameters(t *testing.T) { IgnoreErrors: true, }, { - RouterName: "router-reframe", + RouterName: "router-dht", Timeout: Duration{10 * time.Second}, IgnoreErrors: false, }, @@ -69,7 +63,7 @@ func TestRouterParameters(t *testing.T) { }, Methods: Methods{ MethodNameFindPeers: { - RouterName: "router-reframe", + RouterName: "router-dht", }, MethodNameFindProviders: { RouterName: "router-dht", @@ -99,9 +93,6 @@ func TestRouterParameters(t *testing.T) { dhtp := r2.Routers["router-dht"].Parameters require.IsType(&DHTRouterParams{}, dhtp) - rp := r2.Routers["router-reframe"].Parameters - require.IsType(&ReframeRouterParams{}, rp) - sp := r2.Routers["router-sequential"].Parameters require.IsType(&ComposableRouterParams{}, sp) @@ -109,68 +100,24 @@ func TestRouterParameters(t *testing.T) { require.IsType(&ComposableRouterParams{}, pp) } -func TestRouterMissingParameters(t *testing.T) { - require := require.New(t) - - r := Routing{ - Type: NewOptionalString("custom"), - Routers: map[string]RouterParser{ - "router-wrong-reframe": {Router{ - Type: RouterTypeReframe, - Parameters: DHTRouterParams{ - Mode: "auto", - AcceleratedDHTClient: true, - PublicIPNetwork: false, - }, - }}, - }, - Methods: Methods{ - MethodNameFindPeers: { - RouterName: "router-wrong-reframe", - }, - MethodNameFindProviders: { - RouterName: "router-wrong-reframe", - }, - MethodNameGetIPNS: { - RouterName: "router-wrong-reframe", - }, - MethodNameProvide: { - RouterName: "router-wrong-reframe", - }, - MethodNamePutIPNS: { - RouterName: "router-wrong-reframe", - }, - }, - } - - out, err := json.Marshal(r) - require.NoError(err) - - r2 := &Routing{} - - err = json.Unmarshal(out, r2) - require.NoError(err) - require.Empty(r2.Routers["router-wrong-reframe"].Parameters.(*ReframeRouterParams).Endpoint) -} - func TestMethods(t *testing.T) { require := require.New(t) methodsOK := Methods{ MethodNameFindPeers: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameFindProviders: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameGetIPNS: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameProvide: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNamePutIPNS: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, } @@ -178,16 +125,16 @@ func TestMethods(t *testing.T) { methodsMissing := Methods{ MethodNameFindPeers: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameGetIPNS: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameProvide: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNamePutIPNS: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, } diff --git a/core/commands/dht_test.go b/core/commands/dht_test.go index 961d906ab78..d6a87c954b2 100644 --- a/core/commands/dht_test.go +++ b/core/commands/dht_test.go @@ -12,7 +12,7 @@ import ( func TestKeyTranslation(t *testing.T) { pid := test.RandPeerIDFatal(t) pkname := namesys.PkKeyForID(pid) - ipnsname := ipns.RecordKey(pid) + ipnsname := ipns.NameFromPeer(pid).RoutingKey() pkk, err := escapeDhtKey("/pk/" + pid.Pretty()) if err != nil { @@ -28,7 +28,7 @@ func TestKeyTranslation(t *testing.T) { t.Fatal("keys didn't match!") } - if ipnsk != ipnsname { + if ipnsk != string(ipnsname) { t.Fatal("keys didn't match!") } } diff --git a/core/commands/name/name.go b/core/commands/name/name.go index 94f05290cfc..3bcb5c8e415 100644 --- a/core/commands/name/name.go +++ b/core/commands/name/name.go @@ -2,23 +2,18 @@ package name import ( "bytes" - "encoding/json" + "encoding/hex" "fmt" "io" - "strings" "text/tabwriter" "time" - "github.com/gogo/protobuf/proto" "github.com/ipfs/boxo/ipns" ipns_pb "github.com/ipfs/boxo/ipns/pb" + "github.com/ipfs/boxo/path" cmds "github.com/ipfs/go-ipfs-cmds" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" - "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/codec/dagcbor" - "github.com/ipld/go-ipld-prime/codec/dagjson" - "github.com/libp2p/go-libp2p/core/peer" - mbase "github.com/multiformats/go-multibase" + "google.golang.org/protobuf/proto" ) type IpnsEntry struct { @@ -84,28 +79,27 @@ Resolve the value of a dnslink: } type IpnsInspectValidation struct { - Valid bool - Reason string - PublicKey peer.ID + Valid bool + Reason string + Name string } // IpnsInspectEntry contains the deserialized values from an IPNS Entry: // https://github.com/ipfs/specs/blob/main/ipns/IPNS.md#record-serialization-format type IpnsInspectEntry struct { - Value string - ValidityType *ipns_pb.IpnsEntry_ValidityType + Value *path.Path + ValidityType *ipns.ValidityType Validity *time.Time - Sequence uint64 - TTL *uint64 - PublicKey string - SignatureV1 string - SignatureV2 string - Data interface{} + Sequence *uint64 + TTL *time.Duration } type IpnsInspectResult struct { - Entry IpnsInspectEntry - Validation *IpnsInspectValidation + Entry IpnsInspectEntry + PbSize int + SignatureType string + HexDump string + Validation *IpnsInspectValidation } var IpnsInspectCmd = &cmds.Command{ @@ -136,6 +130,7 @@ Passing --verify will verify signature against provided public key. }, Options: []cmds.Option{ cmds.StringOption("verify", "CID of the public IPNS key to validate against."), + cmds.BoolOption("dump", "Include a full hex dump of the raw Protobuf record.").WithDefault(true), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { file, err := cmdenv.GetFileArg(req.Files.Entries()) @@ -151,71 +146,62 @@ Passing --verify will verify signature against provided public key. return err } - var entry ipns_pb.IpnsEntry - err = proto.Unmarshal(b.Bytes(), &entry) + rec, err := ipns.UnmarshalRecord(b.Bytes()) if err != nil { return err } - encoder, err := mbase.EncoderByName("base64") - if err != nil { - return err + result := &IpnsInspectResult{ + Entry: IpnsInspectEntry{}, } - result := &IpnsInspectResult{ - Entry: IpnsInspectEntry{ - Value: string(entry.Value), - ValidityType: entry.ValidityType, - Sequence: *entry.Sequence, - TTL: entry.Ttl, - PublicKey: encoder.Encode(entry.PubKey), - SignatureV1: encoder.Encode(entry.SignatureV1), - SignatureV2: encoder.Encode(entry.SignatureV2), - Data: nil, - }, + // Best effort to get the fields. Show everything we can. + if v, err := rec.Value(); err == nil { + result.Entry.Value = &v } - if len(entry.Data) != 0 { - // This is hacky. The variable node (datamodel.Node) doesn't directly marshal - // to JSON. Therefore, we need to first decode from DAG-CBOR, then encode in - // DAG-JSON and finally unmarshal it from JSON. Since DAG-JSON is a subset - // of JSON, that should work. Then, we can store the final value in the - // result.Entry.Data for further inspection. - node, err := ipld.Decode(entry.Data, dagcbor.Decode) - if err != nil { - return err - } + if v, err := rec.ValidityType(); err == nil { + result.Entry.ValidityType = &v + } - var buf bytes.Buffer - err = dagjson.Encode(node, &buf) - if err != nil { - return err - } + if v, err := rec.Validity(); err == nil { + result.Entry.Validity = &v + } - err = json.Unmarshal(buf.Bytes(), &result.Entry.Data) - if err != nil { - return err - } + if v, err := rec.Sequence(); err == nil { + result.Entry.Sequence = &v + } + + if v, err := rec.TTL(); err == nil { + result.Entry.TTL = &v } - validity, err := ipns.GetEOL(&entry) - if err == nil { - result.Entry.Validity = &validity + // Here we need the raw protobuf just to decide the version. + var pbRecord ipns_pb.IpnsRecord + err = proto.Unmarshal(b.Bytes(), &pbRecord) + if err != nil { + return err + } + if len(pbRecord.SignatureV1) != 0 || len(pbRecord.Value) != 0 { + result.SignatureType = "V1+V2" + } else if pbRecord.Data != nil { + result.SignatureType = "V2" + } else { + result.SignatureType = "Unknown" } + result.PbSize = proto.Size(&pbRecord) - verify, ok := req.Options["verify"].(string) - if ok { - key := strings.TrimPrefix(verify, "/ipns/") - id, err := peer.Decode(key) + if verify, ok := req.Options["verify"].(string); ok { + name, err := ipns.NameFromString(verify) if err != nil { return err } result.Validation = &IpnsInspectValidation{ - PublicKey: id, + Name: name.String(), } - err = ipns.ValidateWithPeerID(id, &entry) + err = ipns.ValidateWithName(rec, name) if err == nil { result.Validation.Valid = true } else { @@ -223,6 +209,10 @@ Passing --verify will verify signature against provided public key. } } + if dump, ok := req.Options["dump"].(bool); ok && dump { + result.HexDump = hex.Dump(b.Bytes()) + } + return cmds.EmitOnce(res, result) }, Type: IpnsInspectResult{}, @@ -231,24 +221,28 @@ Passing --verify will verify signature against provided public key. tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0) defer tw.Flush() - fmt.Fprintf(tw, "Value:\t%q\n", string(out.Entry.Value)) - fmt.Fprintf(tw, "Validity Type:\t%q\n", out.Entry.ValidityType) + if out.Entry.Value != nil { + fmt.Fprintf(tw, "Value:\t%q\n", out.Entry.Value.String()) + } + + if out.Entry.ValidityType != nil { + fmt.Fprintf(tw, "Validity Type:\t%q\n", *out.Entry.ValidityType) + } + if out.Entry.Validity != nil { - fmt.Fprintf(tw, "Validity:\t%s\n", out.Entry.Validity.Format(time.RFC3339Nano)) + fmt.Fprintf(tw, "Validity:\t%q\n", out.Entry.Validity.Format(time.RFC3339Nano)) } - fmt.Fprintf(tw, "Sequence:\t%d\n", out.Entry.Sequence) - if out.Entry.TTL != nil { - fmt.Fprintf(tw, "TTL:\t%d\n", *out.Entry.TTL) + + if out.Entry.Sequence != nil { + fmt.Fprintf(tw, "Sequence:\t%d\n", *out.Entry.Sequence) } - fmt.Fprintf(tw, "PublicKey:\t%q\n", out.Entry.PublicKey) - fmt.Fprintf(tw, "Signature V1:\t%q\n", out.Entry.SignatureV1) - fmt.Fprintf(tw, "Signature V2:\t%q\n", out.Entry.SignatureV2) - data, err := json.Marshal(out.Entry.Data) - if err != nil { - return err + if out.Entry.TTL != nil { + fmt.Fprintf(tw, "TTL:\t%s\n", out.Entry.TTL.String()) } - fmt.Fprintf(tw, "Data:\t%s\n", string(data)) + + fmt.Fprintf(tw, "Protobuf Size:\t%d\n", out.PbSize) + fmt.Fprintf(tw, "Signature Type:\t%s\n", out.SignatureType) if out.Validation == nil { tw.Flush() @@ -261,7 +255,14 @@ Passing --verify will verify signature against provided public key. if out.Validation.Reason != "" { fmt.Fprintf(tw, "\tReason:\t%s\n", out.Validation.Reason) } - fmt.Fprintf(tw, "\tPublicKey:\t%s\n", out.Validation.PublicKey) + fmt.Fprintf(tw, "\tName:\t%s\n", out.Validation.Name) + } + + if out.HexDump != "" { + tw.Flush() + + fmt.Fprintf(w, "\nHex Dump:\n") + fmt.Fprintf(w, out.HexDump) } return nil diff --git a/core/commands/name/publish.go b/core/commands/name/publish.go index b104aaeb594..bb4a4b70887 100644 --- a/core/commands/name/publish.go +++ b/core/commands/name/publish.go @@ -13,7 +13,6 @@ import ( path "github.com/ipfs/boxo/coreiface/path" cmds "github.com/ipfs/go-ipfs-cmds" ke "github.com/ipfs/kubo/core/commands/keyencode" - peer "github.com/libp2p/go-libp2p/core/peer" ) var ( @@ -28,6 +27,7 @@ const ( ttlOptionName = "ttl" keyOptionName = "key" quieterOptionName = "quieter" + v1compatOptionName = "v1compat" ) var PublishCmd = &cmds.Command{ @@ -83,6 +83,7 @@ Alternatively, publish an using a valid PeerID (as listed by cmds.StringOption(ttlOptionName, "Time duration this record should be cached for. Uses the same syntax as the lifetime option. (caution: experimental)"), cmds.StringOption(keyOptionName, "k", "Name of the key to be used or a valid PeerID, as listed by 'ipfs key list -l'.").WithDefault("self"), cmds.BoolOption(quieterOptionName, "Q", "Write only final hash."), + cmds.BoolOption(v1compatOptionName, "Produce a backward-compatible IPNS Record by including fields for both V1 and V2 signatures.").WithDefault(true), ke.OptionIPNSBase, }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { @@ -90,12 +91,9 @@ Alternatively, publish an using a valid PeerID (as listed by if err != nil { return err } - keyEnc, err := ke.KeyEncoderFromString(req.Options[ke.OptionIPNSBase.Name()].(string)) - if err != nil { - return err - } allowOffline, _ := req.Options[allowOfflineOptionName].(bool) + compatibleWithV1, _ := req.Options[v1compatOptionName].(bool) kname, _ := req.Options[keyOptionName].(string) validTimeOpt, _ := req.Options[lifeTimeOptionName].(string) @@ -108,6 +106,7 @@ Alternatively, publish an using a valid PeerID (as listed by options.Name.AllowOffline(allowOffline), options.Name.Key(kname), options.Name.ValidTime(validTime), + options.Name.CompatibleWithV1(compatibleWithV1), } if ttl, found := req.Options[ttlOptionName].(string); found { @@ -128,7 +127,7 @@ Alternatively, publish an using a valid PeerID (as listed by } } - out, err := api.Name().Publish(req.Context, p, opts...) + name, err := api.Name().Publish(req.Context, p, opts...) if err != nil { if err == iface.ErrOffline { err = errAllowOffline @@ -136,15 +135,9 @@ Alternatively, publish an using a valid PeerID (as listed by return err } - // parse path, extract cid, re-base cid, reconstruct path - pid, err := peer.Decode(out.Name()) - if err != nil { - return err - } - return cmds.EmitOnce(res, &IpnsEntry{ - Name: keyEnc.FormatID(pid), - Value: out.Value().String(), + Name: name.String(), + Value: p.String(), }) }, Encoders: cmds.EncoderMap{ diff --git a/core/core_test.go b/core/core_test.go index 2d7e8927cf3..42dd543d767 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -1,28 +1,14 @@ package core import ( - "crypto/rand" - "errors" - "fmt" - "net/http/httptest" - "path" "testing" - "time" context "context" - "github.com/ipfs/boxo/ipns" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-delegated-routing/client" - "github.com/ipfs/kubo/core/node/libp2p" "github.com/ipfs/kubo/repo" - "github.com/libp2p/go-libp2p/core/crypto" - peer "github.com/libp2p/go-libp2p/core/peer" - "github.com/stretchr/testify/require" datastore "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - drs "github.com/ipfs/go-delegated-routing/server" config "github.com/ipfs/kubo/config" ) @@ -79,285 +65,3 @@ var testIdentity = config.Identity{ PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt", PrivKey: "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", } - -var errNotSupported = errors.New("method not supported") - -func TestDelegatedRoutingSingle(t *testing.T) { - require := require.New(t) - - pID1, priv1, err := GeneratePeerID() - require.NoError(err) - - pID2, _, err := GeneratePeerID() - require.NoError(err) - - theID := path.Join("/ipns", string(pID1)) - theErrorID := path.Join("/ipns", string(pID2)) - - d := &delegatedRoutingService{ - goodPeerID: pID1, - badPeerID: pID2, - pk1: priv1, - } - - url := StartRoutingServer(t, d) - n := GetNode(t, url) - - ctx := context.Background() - - v, err := n.Routing.GetValue(ctx, theID) - require.NoError(err) - require.NotNil(v) - require.Contains(string(v), "RECORD FROM SERVICE 0") - - v, err = n.Routing.GetValue(ctx, theErrorID) - require.Nil(v) - require.Error(err) - - err = n.Routing.PutValue(ctx, theID, v) - require.NoError(err) - -} - -func TestDelegatedRoutingMulti(t *testing.T) { - require := require.New(t) - - pID1, priv1, err := GeneratePeerID() - require.NoError(err) - - pID2, priv2, err := GeneratePeerID() - require.NoError(err) - - theID1 := path.Join("/ipns", string(pID1)) - theID2 := path.Join("/ipns", string(pID2)) - - d1 := &delegatedRoutingService{ - goodPeerID: pID1, - badPeerID: pID2, - pk1: priv1, - serviceID: 1, - } - - url1 := StartRoutingServer(t, d1) - - d2 := &delegatedRoutingService{ - goodPeerID: pID2, - badPeerID: pID1, - pk1: priv2, - serviceID: 2, - } - - url2 := StartRoutingServer(t, d2) - - n := GetNode(t, url1, url2) - - ctx := context.Background() - - v, err := n.Routing.GetValue(ctx, theID1) - require.NoError(err) - require.NotNil(v) - require.Contains(string(v), "RECORD FROM SERVICE 1") - - v, err = n.Routing.GetValue(ctx, theID2) - require.NoError(err) - require.NotNil(v) - require.Contains(string(v), "RECORD FROM SERVICE 2") -} - -func StartRoutingServer(t *testing.T, d drs.DelegatedRoutingService) string { - t.Helper() - - f := drs.DelegatedRoutingAsyncHandler(d) - svr := httptest.NewServer(f) - t.Cleanup(func() { - svr.Close() - }) - - return svr.URL -} - -func GetNode(t *testing.T, reframeURLs ...string) *IpfsNode { - t.Helper() - - routers := make(config.Routers) - var routerNames []string - for i, ru := range reframeURLs { - rn := fmt.Sprintf("reframe-%d", i) - routerNames = append(routerNames, rn) - routers[rn] = - config.RouterParser{ - Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ - Endpoint: ru, - }, - }, - } - } - - var crs []config.ConfigRouter - for _, rn := range routerNames { - crs = append(crs, config.ConfigRouter{ - RouterName: rn, - IgnoreErrors: true, - Timeout: config.Duration{Duration: time.Minute}, - }) - } - - const parallelRouterName = "parallel-router" - - routers[parallelRouterName] = config.RouterParser{ - Router: config.Router{ - Type: config.RouterTypeParallel, - Parameters: &config.ComposableRouterParams{ - Routers: crs, - }, - }, - } - cfg := config.Config{ - Identity: testIdentity, - Addresses: config.Addresses{ - Swarm: []string{"/ip4/0.0.0.0/tcp/0", "/ip4/0.0.0.0/udp/0/quic"}, - API: []string{"/ip4/127.0.0.1/tcp/0"}, - }, - Routing: config.Routing{ - Type: config.NewOptionalString("custom"), - Routers: routers, - Methods: config.Methods{ - config.MethodNameFindPeers: config.Method{ - RouterName: parallelRouterName, - }, - config.MethodNameFindProviders: config.Method{ - RouterName: parallelRouterName, - }, - config.MethodNameGetIPNS: config.Method{ - RouterName: parallelRouterName, - }, - config.MethodNameProvide: config.Method{ - RouterName: parallelRouterName, - }, - config.MethodNamePutIPNS: config.Method{ - RouterName: parallelRouterName, - }, - }, - }, - } - - r := &repo.Mock{ - C: cfg, - D: syncds.MutexWrap(datastore.NewMapDatastore()), - } - - n, err := NewNode(context.Background(), - &BuildCfg{ - Repo: r, - Online: true, - Routing: libp2p.ConstructDelegatedRouting( - cfg.Routing.Routers, - cfg.Routing.Methods, - cfg.Identity.PeerID, - cfg.Addresses, - cfg.Identity.PrivKey, - ), - }, - ) - require.NoError(t, err) - - return n -} - -func GeneratePeerID() (peer.ID, crypto.PrivKey, error) { - priv, pk, err := crypto.GenerateEd25519Key(rand.Reader) - if err != nil { - return peer.ID(""), nil, err - } - - pid, err := peer.IDFromPublicKey(pk) - return pid, priv, err -} - -type delegatedRoutingService struct { - goodPeerID, badPeerID peer.ID - pk1 crypto.PrivKey - serviceID int -} - -func (drs *delegatedRoutingService) FindProviders(ctx context.Context, key cid.Cid) (<-chan client.FindProvidersAsyncResult, error) { - return nil, errNotSupported -} - -func (drs *delegatedRoutingService) Provide(ctx context.Context, req *client.ProvideRequest) (<-chan client.ProvideAsyncResult, error) { - return nil, errNotSupported -} - -func (drs *delegatedRoutingService) GetIPNS(ctx context.Context, id []byte) (<-chan client.GetIPNSAsyncResult, error) { - ctx, cancel := context.WithCancel(ctx) - ch := make(chan client.GetIPNSAsyncResult) - go func() { - defer close(ch) - defer cancel() - - var out client.GetIPNSAsyncResult - switch peer.ID(id) { - case drs.goodPeerID: - ie, err := ipns.Create(drs.pk1, []byte(fmt.Sprintf("RECORD FROM SERVICE %d", drs.serviceID)), 0, time.Now().Add(10*time.Hour), 100*time.Hour) - if err != nil { - log.Fatal(err) - } - ieb, err := ie.Marshal() - if err != nil { - log.Fatal(err) - } - - out = client.GetIPNSAsyncResult{ - Record: ieb, - Err: nil, - } - case drs.badPeerID: - out = client.GetIPNSAsyncResult{ - Record: nil, - Err: errors.New("THE ERROR"), - } - default: - return - } - - select { - case <-ctx.Done(): - return - case ch <- out: - } - }() - - return ch, nil - -} - -func (drs *delegatedRoutingService) PutIPNS(ctx context.Context, id []byte, record []byte) (<-chan client.PutIPNSAsyncResult, error) { - ctx, cancel := context.WithCancel(ctx) - ch := make(chan client.PutIPNSAsyncResult) - go func() { - defer close(ch) - defer cancel() - - var out client.PutIPNSAsyncResult - switch peer.ID(id) { - case drs.goodPeerID: - out = client.PutIPNSAsyncResult{} - case drs.badPeerID: - out = client.PutIPNSAsyncResult{ - Err: fmt.Errorf("THE ERROR %d", drs.serviceID), - } - default: - return - } - - select { - case <-ctx.Done(): - return - case ch <- out: - } - }() - - return ch, nil -} diff --git a/core/coreapi/name.go b/core/coreapi/name.go index f07cfcd0c7a..3bf59a00c4e 100644 --- a/core/coreapi/name.go +++ b/core/coreapi/name.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/ipfs/boxo/ipns" keystore "github.com/ipfs/boxo/keystore" "github.com/ipfs/boxo/namesys" "github.com/ipfs/kubo/tracing" @@ -23,33 +24,18 @@ import ( type NameAPI CoreAPI -type ipnsEntry struct { - name string - value path.Path -} - -// Name returns the ipnsEntry name. -func (e *ipnsEntry) Name() string { - return e.name -} - -// Value returns the ipnsEntry value. -func (e *ipnsEntry) Value() path.Path { - return e.value -} - // Publish announces new IPNS name and returns the new IPNS entry. -func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.NamePublishOption) (coreiface.IpnsEntry, error) { +func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.NamePublishOption) (ipns.Name, error) { ctx, span := tracing.Span(ctx, "CoreAPI.NameAPI", "Publish", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() if err := api.checkPublishAllowed(); err != nil { - return nil, err + return ipns.Name{}, err } options, err := caopts.NamePublishOptions(opts...) if err != nil { - return nil, err + return ipns.Name{}, err } span.SetAttributes( attribute.Bool("allowoffline", options.AllowOffline), @@ -62,23 +48,24 @@ func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.Nam err = api.checkOnline(options.AllowOffline) if err != nil { - return nil, err + return ipns.Name{}, err } pth, err := ipath.ParsePath(p.String()) if err != nil { - return nil, err + return ipns.Name{}, err } k, err := keylookup(api.privateKey, api.repo.Keystore(), options.Key) if err != nil { - return nil, err + return ipns.Name{}, err } eol := time.Now().Add(options.ValidTime) publishOptions := []nsopts.PublishOption{ nsopts.PublishWithEOL(eol), + nsopts.PublishCompatibleWithV1(options.CompatibleWithV1), } if options.TTL != nil { @@ -87,18 +74,15 @@ func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.Nam err = api.namesys.Publish(ctx, k, pth, publishOptions...) if err != nil { - return nil, err + return ipns.Name{}, err } pid, err := peer.IDFromPrivateKey(k) if err != nil { - return nil, err + return ipns.Name{}, err } - return &ipnsEntry{ - name: coreiface.FormatKeyID(pid), - value: p, - }, nil + return ipns.NameFromPeer(pid), nil } func (api *NameAPI) Search(ctx context.Context, name string, opts ...caopts.NameResolveOption) (<-chan coreiface.IpnsResult, error) { diff --git a/docs/changelogs/v0.22.md b/docs/changelogs/v0.22.md index 6fabdad7d39..633741c8810 100644 --- a/docs/changelogs/v0.22.md +++ b/docs/changelogs/v0.22.md @@ -6,6 +6,7 @@ - [Overview](#overview) - [๐Ÿ”ฆ Highlights](#-highlights) + - [`ipfs name publish` now supports V2 only IPNS records](#ipfs-name-publish-now-supports-v2-only-ipns-records) - [๐Ÿ“ Changelog](#-changelog) - [๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors](#-contributors) @@ -13,6 +14,15 @@ ### ๐Ÿ”ฆ Highlights +#### `ipfs name publish` now supports V2 only IPNS records + +When publishing an IPNS record, you are now able to create v2 only records +by passing `--v1compat=false`. By default, we still create V1+V2 records, such +that there is the highest chance of backwards compatibility. The goal is to move +to V2 only in the future. + +**TODO**: add links to IPIP https://github.com/ipfs/specs/issues/376 + ### ๐Ÿ“ Changelog ### ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 0fda8880c9a..abbd3eb72fc 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -7,7 +7,7 @@ go 1.18 replace github.com/ipfs/kubo => ./../../.. require ( - github.com/ipfs/boxo v0.10.1 + github.com/ipfs/boxo v0.10.2-0.20230620120822-417c5f7d61ff github.com/ipfs/kubo v0.0.0-00010101000000-000000000000 github.com/libp2p/go-libp2p v0.27.7 github.com/multiformats/go-multiaddr v0.9.0 @@ -65,7 +65,6 @@ require ( github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-cidutil v0.1.0 // indirect github.com/ipfs/go-datastore v0.6.0 // indirect - github.com/ipfs/go-delegated-routing v0.8.0 // indirect github.com/ipfs/go-ds-badger v0.3.0 // indirect github.com/ipfs/go-ds-flatfs v0.5.1 // indirect github.com/ipfs/go-ds-leveldb v0.5.0 // indirect @@ -88,7 +87,6 @@ require ( github.com/ipfs/go-metrics-interface v0.0.1 // indirect github.com/ipfs/go-peertaskqueue v0.8.1 // indirect github.com/ipfs/go-unixfsnode v1.7.1 // indirect - github.com/ipld/edelweiss v0.2.0 // indirect github.com/ipld/go-codec-dagpb v1.6.0 // indirect github.com/ipld/go-ipld-prime v0.20.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index d18d571e43f..c438e313896 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -320,8 +320,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.10.1 h1:q0ZhbyN6iNZLipd6txt1xotCiP/icfvdAQ4YpUi+cL4= -github.com/ipfs/boxo v0.10.1/go.mod h1:1qgKq45mPRCxf4ZPoJV2lnXxyxucigILMJOrQrVivv8= +github.com/ipfs/boxo v0.10.2-0.20230620120822-417c5f7d61ff h1:QnYD2h1e55nX9lSl5k8YVij1VIOICR7lPJlhbKOQjNM= +github.com/ipfs/boxo v0.10.2-0.20230620120822-417c5f7d61ff/go.mod h1:OGMmq97krQBiKx8LRGyf5DgWHeu+PDdIHNN2YnQlWjs= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= @@ -344,8 +344,6 @@ github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRV github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= -github.com/ipfs/go-delegated-routing v0.8.0 h1:faiRi4k8YioTxU2x7+pnrLQjR7jIQhGWN2JvCwcQ/aU= -github.com/ipfs/go-delegated-routing v0.8.0/go.mod h1:18Dds6ZoNTsff9S/7R49Nh2t2YNXIIKR/RLQmBZdjjY= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= @@ -413,8 +411,6 @@ github.com/ipfs/go-unixfs v0.4.5 h1:wj8JhxvV1G6CD7swACwSKYa+NgtdWC1RUit+gFnymDU= github.com/ipfs/go-unixfsnode v1.7.1 h1:RRxO2b6CSr5UQ/kxnGzaChTjp5LWTdf3Y4n8ANZgB/s= github.com/ipfs/go-unixfsnode v1.7.1/go.mod h1:PVfoyZkX1B34qzT3vJO4nsLUpRCyhnMuHBznRcXirlk= github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= -github.com/ipld/edelweiss v0.2.0 h1:KfAZBP8eeJtrLxLhi7r3N0cBCo7JmwSRhOJp3WSpNjk= -github.com/ipld/edelweiss v0.2.0/go.mod h1:FJAzJRCep4iI8FOFlRriN9n0b7OuX3T/S9++NpBDmA4= github.com/ipld/go-car/v2 v2.9.1-0.20230325062757-fff0e4397a3d h1:22g+x1tgWSXK34i25qjs+afr7basaneEkHaglBshd2g= github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= diff --git a/go.mod b/go.mod index 1b675c12ee7..6730b19145a 100644 --- a/go.mod +++ b/go.mod @@ -13,15 +13,13 @@ require ( github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 github.com/fsnotify/fsnotify v1.6.0 - github.com/gogo/protobuf v1.3.2 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/ipfs/boxo v0.10.1 + github.com/ipfs/boxo v0.10.2-0.20230620120822-417c5f7d61ff github.com/ipfs/go-block-format v0.1.2 github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-cidutil v0.1.0 github.com/ipfs/go-datastore v0.6.0 - github.com/ipfs/go-delegated-routing v0.8.0 github.com/ipfs/go-detect-race v0.0.1 github.com/ipfs/go-ds-badger v0.3.0 github.com/ipfs/go-ds-flatfs v0.5.1 @@ -86,6 +84,7 @@ require ( golang.org/x/mod v0.10.0 golang.org/x/sync v0.2.0 golang.org/x/sys v0.9.0 + google.golang.org/protobuf v1.30.0 ) require ( @@ -116,6 +115,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect 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/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -140,7 +140,6 @@ require ( github.com/ipfs/go-ipld-cbor v0.0.6 // indirect github.com/ipfs/go-libipfs v0.7.0 // indirect github.com/ipfs/go-peertaskqueue v0.8.1 // indirect - github.com/ipld/edelweiss v0.2.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/klauspost/compress v1.16.5 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect @@ -227,7 +226,6 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/grpc v1.55.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -235,4 +233,4 @@ require ( nhooyr.io/websocket v1.8.7 // indirect ) -go 1.18 +go 1.19 diff --git a/go.sum b/go.sum index 6ba0186971d..87987a0dfbb 100644 --- a/go.sum +++ b/go.sum @@ -355,8 +355,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.10.1 h1:q0ZhbyN6iNZLipd6txt1xotCiP/icfvdAQ4YpUi+cL4= -github.com/ipfs/boxo v0.10.1/go.mod h1:1qgKq45mPRCxf4ZPoJV2lnXxyxucigILMJOrQrVivv8= +github.com/ipfs/boxo v0.10.2-0.20230620120822-417c5f7d61ff h1:QnYD2h1e55nX9lSl5k8YVij1VIOICR7lPJlhbKOQjNM= +github.com/ipfs/boxo v0.10.2-0.20230620120822-417c5f7d61ff/go.mod h1:OGMmq97krQBiKx8LRGyf5DgWHeu+PDdIHNN2YnQlWjs= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= @@ -379,8 +379,6 @@ github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRV github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= -github.com/ipfs/go-delegated-routing v0.8.0 h1:faiRi4k8YioTxU2x7+pnrLQjR7jIQhGWN2JvCwcQ/aU= -github.com/ipfs/go-delegated-routing v0.8.0/go.mod h1:18Dds6ZoNTsff9S/7R49Nh2t2YNXIIKR/RLQmBZdjjY= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= @@ -452,8 +450,6 @@ github.com/ipfs/go-unixfs v0.4.5 h1:wj8JhxvV1G6CD7swACwSKYa+NgtdWC1RUit+gFnymDU= github.com/ipfs/go-unixfsnode v1.7.1 h1:RRxO2b6CSr5UQ/kxnGzaChTjp5LWTdf3Y4n8ANZgB/s= github.com/ipfs/go-unixfsnode v1.7.1/go.mod h1:PVfoyZkX1B34qzT3vJO4nsLUpRCyhnMuHBznRcXirlk= github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= -github.com/ipld/edelweiss v0.2.0 h1:KfAZBP8eeJtrLxLhi7r3N0cBCo7JmwSRhOJp3WSpNjk= -github.com/ipld/edelweiss v0.2.0/go.mod h1:FJAzJRCep4iI8FOFlRriN9n0b7OuX3T/S9++NpBDmA4= github.com/ipld/go-car v0.5.0 h1:kcCEa3CvYMs0iE5BzD5sV7O2EwMiCIp3uF8tA6APQT8= github.com/ipld/go-car/v2 v2.9.1-0.20230325062757-fff0e4397a3d h1:22g+x1tgWSXK34i25qjs+afr7basaneEkHaglBshd2g= github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= diff --git a/routing/delegated.go b/routing/delegated.go index 90a9c08071b..26340fb7201 100644 --- a/routing/delegated.go +++ b/routing/delegated.go @@ -10,8 +10,6 @@ import ( drclient "github.com/ipfs/boxo/routing/http/client" "github.com/ipfs/boxo/routing/http/contentrouter" "github.com/ipfs/go-datastore" - drc "github.com/ipfs/go-delegated-routing/client" - drp "github.com/ipfs/go-delegated-routing/gen/proto" logging "github.com/ipfs/go-log" version "github.com/ipfs/kubo" "github.com/ipfs/kubo/config" @@ -25,7 +23,6 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" ma "github.com/multiformats/go-multiaddr" - "github.com/multiformats/go-multicodec" "go.opencensus.io/stats/view" ) @@ -96,8 +93,6 @@ func parse(visited map[string]bool, switch cfg.Type { case config.RouterTypeHTTP: router, err = httpRoutingFromConfig(cfg.Router, extraHTTP) - case config.RouterTypeReframe: - router, err = reframeRoutingFromConfig(cfg.Router, extraHTTP) case config.RouterTypeDHT: router, err = dhtRoutingFromConfig(cfg.Router, extraDHT) case config.RouterTypeParallel: @@ -232,67 +227,6 @@ func httpRoutingFromConfig(conf config.Router, extraHTTP *ExtraHTTPParams) (rout }, nil } -func reframeRoutingFromConfig(conf config.Router, extraReframe *ExtraHTTPParams) (routing.Routing, error) { - var dr drp.DelegatedRouting_Client - - params := conf.Parameters.(*config.ReframeRouterParams) - - if params.Endpoint == "" { - return nil, NewParamNeededErr("Endpoint", conf.Type) - } - - // Increase per-host connection pool since we are making lots of concurrent requests. - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.MaxIdleConns = 500 - transport.MaxIdleConnsPerHost = 100 - - delegateHTTPClient := &http.Client{ - Transport: transport, - } - dr, err := drp.New_DelegatedRouting_Client(params.Endpoint, - drp.DelegatedRouting_Client_WithHTTPClient(delegateHTTPClient), - ) - if err != nil { - return nil, err - } - - var c *drc.Client - - err = view.Register(drc.DefaultViews...) - if err != nil { - return nil, fmt.Errorf("registering delegated routing views: %w", err) - } - - // this path is for tests only - if extraReframe == nil { - c, err = drc.NewClient(dr, nil, nil) - if err != nil { - return nil, err - } - } else { - prov, err := createProvider(extraReframe.PeerID, extraReframe.Addrs) - if err != nil { - return nil, err - } - - key, err := decodePrivKey(extraReframe.PrivKeyB64) - if err != nil { - return nil, err - } - - c, err = drc.NewClient(dr, prov, key) - if err != nil { - return nil, err - } - } - - crc := drc.NewContentRoutingClient(c) - return &reframeRoutingWrapper{ - Client: c, - ContentRoutingClient: crc, - }, nil -} - func decodePrivKey(keyB64 string) (ic.PrivKey, error) { pk, err := base64.StdEncoding.DecodeString(keyB64) if err != nil { @@ -324,19 +258,6 @@ func createAddrInfo(peerID string, addrs []string) (peer.AddrInfo, error) { }, nil } -func createProvider(peerID string, addrs []string) (*drc.Provider, error) { - addrInfo, err := createAddrInfo(peerID, addrs) - if err != nil { - return nil, err - } - return &drc.Provider{ - Peer: addrInfo, - ProviderProto: []drc.TransferProtocol{ - {Codec: multicodec.TransportBitswap}, - }, - }, nil -} - type ExtraDHTParams struct { BootstrapPeers []peer.AddrInfo Host host.Host diff --git a/routing/delegated_test.go b/routing/delegated_test.go index ee7543114d9..da0210f5ebb 100644 --- a/routing/delegated_test.go +++ b/routing/delegated_test.go @@ -1,68 +1,27 @@ package routing import ( + "crypto/rand" "encoding/base64" "testing" "github.com/ipfs/kubo/config" - crypto "github.com/libp2p/go-libp2p/core/crypto" - peer "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" ) -func TestReframeRoutingFromConfig(t *testing.T) { +func TestParser(t *testing.T) { require := require.New(t) - r, err := reframeRoutingFromConfig(config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{}, - }, nil) - - require.Nil(r) - require.EqualError(err, "configuration param 'Endpoint' is needed for reframe delegated routing types") - - r, err = reframeRoutingFromConfig(config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ - Endpoint: "test", - }, - }, nil) - - require.NoError(err) - require.NotNil(r) - - priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, 2048) - require.NoError(err) - - id, err := peer.IDFromPublicKey(pub) - require.NoError(err) - - privM, err := crypto.MarshalPrivateKey(priv) + pid, sk, err := generatePeerID() require.NoError(err) - r, err = reframeRoutingFromConfig(config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ - Endpoint: "test", - }, - }, &ExtraHTTPParams{ - PeerID: id.String(), - Addrs: []string{"/ip4/0.0.0.0/tcp/4001"}, - PrivKeyB64: base64.StdEncoding.EncodeToString(privM), - }) - - require.NotNil(r) - require.NoError(err) -} - -func TestParser(t *testing.T) { - require := require.New(t) - router, err := Parse(config.Routers{ "r1": config.RouterParser{ Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ + Type: config.RouterTypeHTTP, + Parameters: &config.HTTPRouterParams{ Endpoint: "testEndpoint", }, }, @@ -95,7 +54,10 @@ func TestParser(t *testing.T) { config.MethodNameProvide: config.Method{ RouterName: "r2", }, - }, &ExtraDHTParams{}, nil) + }, &ExtraDHTParams{}, &ExtraHTTPParams{ + PeerID: string(pid), + PrivKeyB64: sk, + }) require.NoError(err) @@ -109,27 +71,30 @@ func TestParser(t *testing.T) { func TestParserRecursive(t *testing.T) { require := require.New(t) + pid, sk, err := generatePeerID() + require.NoError(err) + router, err := Parse(config.Routers{ - "reframe1": config.RouterParser{ + "http1": config.RouterParser{ Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ + Type: config.RouterTypeHTTP, + Parameters: &config.HTTPRouterParams{ Endpoint: "testEndpoint1", }, }, }, - "reframe2": config.RouterParser{ + "http2": config.RouterParser{ Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ + Type: config.RouterTypeHTTP, + Parameters: &config.HTTPRouterParams{ Endpoint: "testEndpoint2", }, }, }, - "reframe3": config.RouterParser{ + "http3": config.RouterParser{ Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ + Type: config.RouterTypeHTTP, + Parameters: &config.HTTPRouterParams{ Endpoint: "testEndpoint3", }, }, @@ -140,10 +105,10 @@ func TestParserRecursive(t *testing.T) { Parameters: &config.ComposableRouterParams{ Routers: []config.ConfigRouter{ { - RouterName: "reframe1", + RouterName: "http1", }, { - RouterName: "reframe2", + RouterName: "http2", }, }, }, @@ -158,7 +123,7 @@ func TestParserRecursive(t *testing.T) { RouterName: "composable1", }, { - RouterName: "reframe3", + RouterName: "http3", }, }, }, @@ -180,7 +145,10 @@ func TestParserRecursive(t *testing.T) { config.MethodNameProvide: config.Method{ RouterName: "composable2", }, - }, &ExtraDHTParams{}, nil) + }, &ExtraDHTParams{}, &ExtraHTTPParams{ + PeerID: string(pid), + PrivKeyB64: sk, + }) require.NoError(err) @@ -237,3 +205,23 @@ func TestParserRecursiveLoop(t *testing.T) { require.ErrorContains(err, "dependency loop creating router with name \"composable2\"") } + +func generatePeerID() (string, string, error) { + sk, pk, err := crypto.GenerateEd25519Key(rand.Reader) + if err != nil { + return "", "", err + } + + bytes, err := crypto.MarshalPrivateKey(sk) + if err != nil { + return "", "", err + } + + enc := base64.StdEncoding.EncodeToString(bytes) + if err != nil { + return "", "", err + } + + pid, err := peer.IDFromPublicKey(pk) + return pid.String(), enc, err +} diff --git a/routing/wrapper.go b/routing/wrapper.go index d4215ca9c90..f6a753843cd 100644 --- a/routing/wrapper.go +++ b/routing/wrapper.go @@ -3,39 +3,11 @@ package routing import ( "context" - "github.com/ipfs/go-cid" - drc "github.com/ipfs/go-delegated-routing/client" routinghelpers "github.com/libp2p/go-libp2p-routing-helpers" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" ) -var _ routing.Routing = &reframeRoutingWrapper{} -var _ routinghelpers.ProvideManyRouter = &reframeRoutingWrapper{} - -// reframeRoutingWrapper is a wrapper needed to construct the routing.Routing interface from -// delegated-routing library. -type reframeRoutingWrapper struct { - *drc.Client - *drc.ContentRoutingClient -} - -func (c *reframeRoutingWrapper) Provide(ctx context.Context, id cid.Cid, announce bool) error { - return c.ContentRoutingClient.Provide(ctx, id, announce) -} - -func (c *reframeRoutingWrapper) FindProvidersAsync(ctx context.Context, cid cid.Cid, count int) <-chan peer.AddrInfo { - return c.ContentRoutingClient.FindProvidersAsync(ctx, cid, count) -} - -func (c *reframeRoutingWrapper) Bootstrap(ctx context.Context) error { - return nil -} - -func (c *reframeRoutingWrapper) FindPeer(ctx context.Context, id peer.ID) (peer.AddrInfo, error) { - return peer.AddrInfo{}, routing.ErrNotSupported -} - type ProvideManyRouter interface { routinghelpers.ProvideManyRouter routing.Routing diff --git a/test/cli/fixtures/TestName.car b/test/cli/fixtures/TestName.car new file mode 100644 index 0000000000000000000000000000000000000000..5e3fd57b264508c46f73cbccc7a5e215e41658e0 GIT binary patch literal 421 zcmcColvbS!!+Z+3iVHF56Bq#a{Zm0QZ&DJ^|Br{{|^0i zp%>E2Qj3Z+^Ya)x7>Q9Xq|2qkSO_$B&fevpjLjaK+2!z-Sbo0g7jUdE?>00RLC%|>(rgQUzL_E-u$B1_QT@^ i7B>TBY!2+yiUj$NkRr02Ye0+#!QN9S&o9bJ;Q|08?65BY literal 0 HcmV?d00001 diff --git a/test/cli/name_test.go b/test/cli/name_test.go new file mode 100644 index 00000000000..e0f2f090962 --- /dev/null +++ b/test/cli/name_test.go @@ -0,0 +1,266 @@ +package cli + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "strings" + "testing" + + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/kubo/core/commands/name" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/require" +) + +func TestName(t *testing.T) { + const ( + fixturePath = "fixtures/TestName.car" + fixtureCid = "bafybeidg3uxibfrt7uqh7zd5yaodetik7wjwi4u7rwv2ndbgj6ec7lsv2a" + dagCid = "bafyreidgts62p4rtg3rtmptmbv2dt46zjzin275fr763oku3wfod3quzay" + ) + + makeDaemon := func(t *testing.T, initArgs []string) *harness.Node { + node := harness.NewT(t).NewNode().Init(append([]string{"--profile=test"}, initArgs...)...) + r, err := os.Open(fixturePath) + require.Nil(t, err) + defer r.Close() + err = node.IPFSDagImport(r, fixtureCid) + require.NoError(t, err) + return node + } + + testPublishingWithSelf := func(keyType string) { + t.Run("Publishing with self (keyType = "+keyType+")", func(t *testing.T) { + t.Parallel() + + args := []string{} + if keyType != "default" { + args = append(args, "-a="+keyType) + } + + node := makeDaemon(t, args) + name := ipns.NameFromPeer(node.PeerID()) + + t.Run("Publishing a CID", func(t *testing.T) { + publishPath := "/ipfs/" + fixtureCid + + res := node.IPFS("name", "publish", "--allow-offline", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + + res = node.IPFS("name", "resolve", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + t.Run("Publishing a CID with -Q option", func(t *testing.T) { + publishPath := "/ipfs/" + fixtureCid + + res := node.IPFS("name", "publish", "--allow-offline", "-Q", publishPath) + require.Equal(t, name.String()+"\n", res.Stdout.String()) + + res = node.IPFS("name", "resolve", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + t.Run("Publishing a CID+subpath", func(t *testing.T) { + publishPath := "/ipfs/" + fixtureCid + "/hello" + + res := node.IPFS("name", "publish", "--allow-offline", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + + res = node.IPFS("name", "resolve", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + t.Run("Publishing nothing fails", func(t *testing.T) { + res := node.RunIPFS("name", "publish") + require.Error(t, res.Err) + require.Equal(t, 1, res.ExitCode()) + require.Contains(t, res.Stderr.String(), `argument "ipfs-path" is required`) + }) + + t.Run("Publishing with IPLD works", func(t *testing.T) { + publishPath := "/ipld/" + dagCid + "/thing" + res := node.IPFS("name", "publish", "--allow-offline", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + + res = node.IPFS("name", "resolve", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + publishPath := "/ipfs/" + fixtureCid + res := node.IPFS("name", "publish", "--allow-offline", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + + t.Run("Resolving self offline succeeds (daemon off)", func(t *testing.T) { + res = node.IPFS("name", "resolve", "--offline", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + + // Test without cache. + res = node.IPFS("name", "resolve", "--offline", "-n", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + node.StartDaemon() + + t.Run("Resolving self offline succeeds (daemon on)", func(t *testing.T) { + res = node.IPFS("name", "resolve", "--offline", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + + // Test without cache. + res = node.IPFS("name", "resolve", "--offline", "-n", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + }) + } + + testPublishingWithSelf("default") + testPublishingWithSelf("rsa") + testPublishingWithSelf("ed25519") + + testPublishWithKey := func(name string, keyArgs ...string) { + t.Run(name, func(t *testing.T) { + t.Parallel() + node := makeDaemon(t, nil) + + keyGenArgs := []string{"key", "gen"} + keyGenArgs = append(keyGenArgs, keyArgs...) + keyGenArgs = append(keyGenArgs, "key") + + res := node.IPFS(keyGenArgs...) + key := strings.TrimSpace(res.Stdout.String()) + + publishPath := "/ipfs/" + fixtureCid + name, err := ipns.NameFromString(key) + require.NoError(t, err) + + res = node.IPFS("name", "publish", "--allow-offline", "--key="+key, publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + }) + } + + testPublishWithKey("Publishing with RSA (with b58mh) Key", "--ipns-base=b58mh", "--type=rsa", "--size=2048") + testPublishWithKey("Publishing with ED25519 (with b58mh) Key", "--ipns-base=b58mh", "--type=ed25519") + testPublishWithKey("Publishing with ED25519 (with base36) Key", "--ipns-base=base36", "--type=ed25519") + + t.Run("Fails to publish in offline mode", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t, nil).StartDaemon("--offline") + res := node.RunIPFS("name", "publish", "/ipfs/"+fixtureCid) + require.Error(t, res.Err) + require.Equal(t, 1, res.ExitCode()) + require.Contains(t, res.Stderr.String(), `can't publish while offline`) + }) + + t.Run("Publish V2-only record", func(t *testing.T) { + t.Parallel() + + node := makeDaemon(t, nil).StartDaemon() + ipnsName := ipns.NameFromPeer(node.PeerID()).String() + ipnsPath := ipns.NamespacePrefix + ipnsName + publishPath := "/ipfs/" + fixtureCid + + res := node.IPFS("name", "publish", "--ttl=30m", "--v1compat=false", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", ipnsName, publishPath), res.Stdout.String()) + + res = node.IPFS("name", "resolve", ipnsPath) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + + res = node.IPFS("routing", "get", ipnsPath) + record := res.Stdout.Bytes() + + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect") + out := res.Stdout.String() + require.Contains(t, out, "This record was not validated.") + require.Contains(t, out, publishPath) + require.Contains(t, out, "30m") + + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--verify="+ipnsPath) + out = res.Stdout.String() + require.Contains(t, out, "Valid: true") + require.Contains(t, out, "Signature Type: V2") + require.Contains(t, out, fmt.Sprintf("Protobuf Size: %d", len(record))) + }) + + t.Run("Publish with TTL and inspect record", func(t *testing.T) { + t.Parallel() + + node := makeDaemon(t, nil).StartDaemon() + ipnsPath := ipns.NamespacePrefix + ipns.NameFromPeer(node.PeerID()).String() + publishPath := "/ipfs/" + fixtureCid + + _ = node.IPFS("name", "publish", "--ttl=30m", publishPath) + res := node.IPFS("routing", "get", ipnsPath) + record := res.Stdout.Bytes() + + t.Run("Inspect record shows correct TTL and that it is not validated", func(t *testing.T) { + t.Parallel() + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect") + out := res.Stdout.String() + require.Contains(t, out, "This record was not validated.") + require.Contains(t, out, publishPath) + require.Contains(t, out, "30m") + require.Contains(t, out, "Signature Type: V1+V2") + require.Contains(t, out, fmt.Sprintf("Protobuf Size: %d", len(record))) + }) + + t.Run("Inspect record shows valid with correct name", func(t *testing.T) { + t.Parallel() + res := node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--enc=json", "--verify="+ipnsPath) + val := name.IpnsInspectResult{} + err := json.Unmarshal(res.Stdout.Bytes(), &val) + require.NoError(t, err) + require.True(t, val.Validation.Valid) + }) + + t.Run("Inspect record shows invalid with wrong name", func(t *testing.T) { + t.Parallel() + res := node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--enc=json", "--verify=12D3KooWRirYjmmQATx2kgHBfky6DADsLP7ex1t7BRxJ6nqLs9WH") + val := name.IpnsInspectResult{} + err := json.Unmarshal(res.Stdout.Bytes(), &val) + require.NoError(t, err) + require.False(t, val.Validation.Valid) + }) + }) + + t.Run("Inspect with verification using wrong RSA key errors", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t, nil).StartDaemon() + + // Prepare RSA Key 1 + res := node.IPFS("key", "gen", "--type=rsa", "--size=4096", "key1") + key1 := strings.TrimSpace(res.Stdout.String()) + name1, err := ipns.NameFromString(key1) + require.NoError(t, err) + + // Prepare RSA Key 2 + res = node.IPFS("key", "gen", "--type=rsa", "--size=4096", "key2") + key2 := strings.TrimSpace(res.Stdout.String()) + name2, err := ipns.NameFromString(key2) + require.NoError(t, err) + + // Publish using Key 1 + publishPath := "/ipfs/" + fixtureCid + res = node.IPFS("name", "publish", "--allow-offline", "--key="+key1, publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name1.String(), publishPath), res.Stdout.String()) + + // Get IPNS Record + res = node.IPFS("routing", "get", ipns.NamespacePrefix+name1.String()) + record := res.Stdout.Bytes() + + // Validate with correct key succeeds + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--verify="+name1.String(), "--enc=json") + val := name.IpnsInspectResult{} + err = json.Unmarshal(res.Stdout.Bytes(), &val) + require.NoError(t, err) + require.True(t, val.Validation.Valid) + + // Validate with wrong key fails + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--verify="+name2.String(), "--enc=json") + val = name.IpnsInspectResult{} + err = json.Unmarshal(res.Stdout.Bytes(), &val) + require.NoError(t, err) + require.False(t, val.Validation.Valid) + }) +} diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index f119128d2d1..7d7cdcaffc1 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -7,7 +7,7 @@ replace github.com/ipfs/kubo => ../../ require ( github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd github.com/golangci/golangci-lint v1.49.0 - github.com/ipfs/boxo v0.10.1 + github.com/ipfs/boxo v0.10.2-0.20230620120822-417c5f7d61ff github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-cidutil v0.1.0 github.com/ipfs/go-datastore v0.6.0 diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 5c3a5ef9459..cb27e68af40 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -413,8 +413,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.10.1 h1:q0ZhbyN6iNZLipd6txt1xotCiP/icfvdAQ4YpUi+cL4= -github.com/ipfs/boxo v0.10.1/go.mod h1:1qgKq45mPRCxf4ZPoJV2lnXxyxucigILMJOrQrVivv8= +github.com/ipfs/boxo v0.10.2-0.20230620120822-417c5f7d61ff h1:QnYD2h1e55nX9lSl5k8YVij1VIOICR7lPJlhbKOQjNM= +github.com/ipfs/boxo v0.10.2-0.20230620120822-417c5f7d61ff/go.mod h1:OGMmq97krQBiKx8LRGyf5DgWHeu+PDdIHNN2YnQlWjs= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.1.2 h1:GAjkfhVx1f4YTODS6Esrj1wt2HhrtwTnhEr+DyPUaJo= diff --git a/test/sharness/t0100-name.sh b/test/sharness/t0100-name.sh deleted file mode 100755 index f49df341754..00000000000 --- a/test/sharness/t0100-name.sh +++ /dev/null @@ -1,345 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2014 Jeromy Johnson -# MIT Licensed; see the LICENSE file in this repository. -# - -test_description="Test ipfs repo operations" - -. lib/test-lib.sh - -test_name_with_self() { - SELF_ALG=$1 - - test_expect_success "ipfs init (variant self $SELF_ALG)" ' - export IPFS_PATH="$(pwd)/.ipfs" && - case $SELF_ALG in - default) - ipfs init --empty-repo=false --profile=test > /dev/null - ;; - rsa) - ipfs init --empty-repo=false --profile=test -a=rsa > /dev/null - ;; - ed25519) - ipfs init --empty-repo=false --profile=test -a=ed25519 > /dev/null - ;; - esac && - export PEERID=`ipfs key list --ipns-base=base36 -l | grep self | cut -d " " -f1` && - test_check_peerid "${PEERID}" - ' - - # test publishing a hash - - test_expect_success "'ipfs name publish --allow-offline' succeeds" ' - ipfs name publish --allow-offline "/ipfs/$HASH_WELCOME_DOCS" >publish_out - ' - - test_expect_success "publish output looks good" ' - echo "Published to ${PEERID}: /ipfs/$HASH_WELCOME_DOCS" >expected1 && - test_cmp expected1 publish_out - ' - - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$PEERID" >output - ' - - test_expect_success "resolve output looks good" ' - printf "/ipfs/%s\n" "$HASH_WELCOME_DOCS" >expected2 && - test_cmp expected2 output - ' - - # test publishing with -Q option - - test_expect_success "'ipfs name publish --quieter' succeeds" ' - ipfs name publish --allow-offline -Q "/ipfs/$HASH_WELCOME_DOCS" >publish_out - ' - - test_expect_success "publish --quieter output looks good" ' - echo "${PEERID}" >expected1 && - test_cmp expected1 publish_out - ' - - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$PEERID" >output - ' - - test_expect_success "resolve output looks good" ' - printf "/ipfs/%s\n" "$HASH_WELCOME_DOCS" >expected2 && - test_cmp expected2 output - ' - - # now test with a path - - test_expect_success "'ipfs name publish --allow-offline' succeeds" ' - ipfs name publish --allow-offline "/ipfs/$HASH_WELCOME_DOCS/help" >publish_out - ' - - test_expect_success "publish a path looks good" ' - echo "Published to ${PEERID}: /ipfs/$HASH_WELCOME_DOCS/help" >expected3 && - test_cmp expected3 publish_out - ' - - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$PEERID" >output - ' - - test_expect_success "resolve output looks good" ' - printf "/ipfs/%s/help\n" "$HASH_WELCOME_DOCS" >expected4 && - test_cmp expected4 output - ' - - test_expect_success "ipfs cat on published content succeeds" ' - ipfs cat "/ipfs/$HASH_WELCOME_DOCS/help" >expected && - ipfs cat "/ipns/$PEERID" >actual && - test_cmp expected actual - ' - - # publish with an explicit node ID - - test_expect_failure "'ipfs name publish --allow-offline ' succeeds" ' - echo ipfs name publish --allow-offline "${PEERID}" "/ipfs/$HASH_WELCOME_DOCS" && - ipfs name publish --allow-offline "${PEERID}" "/ipfs/$HASH_WELCOME_DOCS" >actual_node_id_publish - ' - - test_expect_failure "publish with our explicit node ID looks good" ' - echo "Published to ${PEERID}: /ipfs/$HASH_WELCOME_DOCS" >expected_node_id_publish && - test_cmp expected_node_id_publish actual_node_id_publish - ' - - # test publishing with B36CID and B58MH resolve to the same B36CID - - test_expect_success "verify self key output" ' - B58MH_ID=`ipfs key list --ipns-base=b58mh -l | grep self | cut -d " " -f1` && - B36CID_ID=`ipfs key list --ipns-base=base36 -l | grep self | cut -d " " -f1` && - test_check_peerid "${B58MH_ID}" && - test_check_peerid "${B36CID_ID}" - ' - - test_expect_success "'ipfs name publish --allow-offline --key= ' succeeds" ' - ipfs name publish --allow-offline --key=${B58MH_ID} "/ipfs/$HASH_WELCOME_DOCS" >b58mh_published_id_base36 && - ipfs name publish --allow-offline --key=${B36CID_ID} "/ipfs/$HASH_WELCOME_DOCS" >base36_published_id_base36 && - ipfs name publish --allow-offline --key=${B58MH_ID} --ipns-base=b58mh "/ipfs/$HASH_WELCOME_DOCS" >b58mh_published_id_b58mh && - ipfs name publish --allow-offline --key=${B36CID_ID} --ipns-base=b58mh "/ipfs/$HASH_WELCOME_DOCS" >base36_published_id_b58mh - ' - - test_expect_success "publish an explicit node ID as two key in B58MH and B36CID, name looks good" ' - echo "Published to ${B36CID_ID}: /ipfs/$HASH_WELCOME_DOCS" >expected_published_id_base36 && - echo "Published to ${B58MH_ID}: /ipfs/$HASH_WELCOME_DOCS" >expected_published_id_b58mh && - test_cmp expected_published_id_base36 b58mh_published_id_base36 && - test_cmp expected_published_id_base36 base36_published_id_base36 && - test_cmp expected_published_id_b58mh b58mh_published_id_b58mh && - test_cmp expected_published_id_b58mh base36_published_id_b58mh - ' - - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$B36CID_ID" >output - ' - - test_expect_success "resolve output looks good" ' - printf "/ipfs/%s\n" "$HASH_WELCOME_DOCS" >expected2 && - test_cmp expected2 output - ' - - # test IPNS + IPLD - - test_expect_success "'ipfs dag put' succeeds" ' - HELLO_HASH="$(echo "\"hello world\"" | ipfs dag put)" && - OBJECT_HASH="$(echo "{\"thing\": {\"/\": \"${HELLO_HASH}\" }}" | ipfs dag put)" - ' - test_expect_success "'ipfs name publish --allow-offline /ipld/...' succeeds" ' - test_check_peerid "${PEERID}" && - ipfs name publish --allow-offline "/ipld/$OBJECT_HASH/thing" >publish_out - ' - test_expect_success "publish a path looks good" ' - echo "Published to ${PEERID}: /ipld/$OBJECT_HASH/thing" >expected3 && - test_cmp expected3 publish_out - ' - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$PEERID" >output - ' - test_expect_success "resolve output looks good (IPNS + IPLD)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - # test publishing nothing - - test_expect_success "'ipfs name publish' fails" ' - printf '' | test_expect_code 1 ipfs name publish --allow-offline >publish_out 2>&1 - ' - - test_expect_success "publish output has the correct error" ' - grep "argument \"ipfs-path\" is required" publish_out - ' - - test_expect_success "'ipfs name publish' fails" ' - printf '' | test_expect_code 1 ipfs name publish -Q --allow-offline >publish_out 2>&1 - ' - - test_expect_success "publish output has the correct error" ' - grep "argument \"ipfs-path\" is required" publish_out - ' - - test_expect_success "'ipfs name publish --help' succeeds" ' - ipfs name publish --help - ' - - # test offline resolve - - test_expect_success "'ipfs name resolve --offline' succeeds" ' - ipfs name resolve --offline "$PEERID" >output - ' - test_expect_success "resolve output looks good (offline resolve)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - test_expect_success "'ipfs name resolve --offline -n' succeeds" ' - ipfs name resolve --offline -n "$PEERID" >output - ' - test_expect_success "resolve output looks good (offline resolve, -n)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - test_launch_ipfs_daemon - - test_expect_success "'ipfs name resolve --offline' succeeds" ' - ipfs name resolve --offline "$PEERID" >output - ' - test_expect_success "resolve output looks good (with daemon)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - test_expect_success "'ipfs name resolve --offline -n' succeeds" ' - ipfs name resolve --offline -n "$PEERID" >output - ' - test_expect_success "resolve output looks good (with daemon, -n)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - test_expect_success "empty request to name publish doesn't panic and returns error" ' - curl -X POST "http://$API_ADDR/api/v0/name/publish" > curl_out || true && - grep "argument \"ipfs-path\" is required" curl_out - ' - - # Test Publishing with TTL and Inspecting Records - test_expect_success "'ipfs name publish --ttl=30m' succeeds" ' - ipfs name publish --ttl=30m --allow-offline "/ipfs/$HASH_WELCOME_DOCS" - ' - - test_expect_success "retrieve IPNS key for further inspection" ' - ipfs routing get "/ipns/$PEERID" > ipns_record - ' - - test_expect_success "'ipfs name inspect' has correct TTL (30m)" ' - ipfs name inspect < ipns_record > verify_output && - test_should_contain "This record was not validated." verify_output && - test_should_contain "$HASH_WELCOME_DOCS" verify_output && - test_should_contain "1800000000000" verify_output - ' - - test_expect_success "'ipfs name inspect --verify' has '.Validation.Validity' set to 'true' with correct Peer ID" ' - ipfs name inspect --verify $PEERID --enc json < ipns_record | jq -e ".Validation.Valid == true and .Entry.TTL == .Entry.Data.TTL" - ' - - test_expect_success "'ipfs name inspect --verify' has '.Validation.Validity' set to 'false' with incorrect Peer ID" ' - ipfs name inspect --verify 12D3KooWRirYjmmQATx2kgHBfky6DADsLP7ex1t7BRxJ6nqLs9WH --enc json < ipns_record | jq -e ".Validation.Valid == false" - ' - - test_kill_ipfs_daemon - - # Test daemon in offline mode - test_launch_ipfs_daemon_without_network - - test_expect_success "'ipfs name publish' fails offline mode" ' - test_expect_code 1 ipfs name publish "/ipfs/$HASH_WELCOME_DOCS" - ' - - test_kill_ipfs_daemon - - test_expect_success "clean up ipfs dir" ' - rm -rf "$IPFS_PATH" - ' -} -test_name_with_self 'default' -test_name_with_self 'rsa' -test_name_with_self 'ed25519' - -test_name_with_key() { - GEN_ALG=$1 - - test_expect_success "ipfs init (key variant $GEN_ALG)" ' - export IPFS_PATH="$(pwd)/.ipfs" && - ipfs init --empty-repo=false --profile=test > /dev/null - ' - - test_expect_success "'prepare keys" ' - case $GEN_ALG in - rsa) - export KEY=`ipfs key gen --ipns-base=b58mh --type=rsa --size=2048 key` && - export KEY_B36CID=`ipfs key list --ipns-base=base36 -l | grep key | cut -d " " -f1` - ;; - ed25519_b58) - export KEY=`ipfs key gen --ipns-base=b58mh --type=ed25519 key` - export KEY_B36CID=`ipfs key list --ipns-base=base36 -l | grep key | cut -d " " -f1` - ;; - ed25519_b36) - export KEY=`ipfs key gen --ipns-base=base36 --type=ed25519 key` - export KEY_B36CID=$KEY - ;; - esac && - test_check_peerid "${KEY}" - ' - - # publish with an explicit node ID as key name - - test_expect_success "'ipfs name publish --allow-offline --key= ' succeeds" ' - ipfs name publish --allow-offline --key=${KEY} "/ipfs/$HASH_WELCOME_DOCS" >actual_node_id_publish - ' - - test_expect_success "publish an explicit node ID as key name looks good" ' - echo "Published to ${KEY_B36CID}: /ipfs/$HASH_WELCOME_DOCS" >expected_node_id_publish && - test_cmp expected_node_id_publish actual_node_id_publish - ' - - # cleanup - test_expect_success "clean up ipfs dir" ' - rm -rf "$IPFS_PATH" - ' -} -test_name_with_key 'rsa' -test_name_with_key 'ed25519_b58' -test_name_with_key 'ed25519_b36' - - -# `ipfs name inspect --verify` using the wrong RSA key should not succeed - -test_init_ipfs --empty-repo=false -test_launch_ipfs_daemon - -test_expect_success "prepare RSA keys" ' - export KEY_1=`ipfs key gen --type=rsa --size=4096 key1` && - export KEY_2=`ipfs key gen --type=rsa --size=4096 key2` && - export PEERID_1=`ipfs key list --ipns-base=base36 -l | grep key1 | cut -d " " -f1` && - export PEERID_2=`ipfs key list --ipns-base=base36 -l | grep key2 | cut -d " " -f1` -' - -test_expect_success "ipfs name publish --allow-offline --key= ' succeeds" ' - ipfs name publish --allow-offline --key=${KEY_1} "/ipfs/$( echo "helloworld" | ipfs add --inline -q )" && - ipfs routing get "/ipns/$PEERID_1" > ipns_record -' - -test_expect_success "ipfs name inspect --verify' has '.Validation.Validity' set to 'true' with correct Peer ID" ' - ipfs name inspect --verify $PEERID_1 --enc json < ipns_record | jq -e ".Validation.Valid == true" -' - -test_expect_success "ipfs name inspect --verify' has '.Validation.Validity' set to 'false' when we verify the wrong Peer ID" ' - ipfs name inspect --verify $PEERID_2 --enc json < ipns_record | jq -e ".Validation.Valid == false" -' - -test_kill_ipfs_daemon - -test_done