diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index ec4cc1bb6dc..8f94e5d6413 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -173,6 +173,7 @@ func TestCommands(t *testing.T) { "/multibase/transcode", "/multibase/list", "/name", + "/name/inspect", "/name/publish", "/name/pubsub", "/name/pubsub/cancel", diff --git a/core/commands/name/name.go b/core/commands/name/name.go index d9e3de57fab..7999aee3ddc 100644 --- a/core/commands/name/name.go +++ b/core/commands/name/name.go @@ -1,7 +1,25 @@ package name import ( - "github.com/ipfs/go-ipfs-cmds" + "bytes" + "encoding/json" + "fmt" + "io" + "strings" + "text/tabwriter" + "time" + + "github.com/gogo/protobuf/proto" + cmds "github.com/ipfs/go-ipfs-cmds" + "github.com/ipfs/go-ipns" + ipns_pb "github.com/ipfs/go-ipns/pb" + 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" + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + mbase "github.com/multiformats/go-multibase" ) type IpnsEntry struct { @@ -62,5 +80,204 @@ Resolve the value of a dnslink: "publish": PublishCmd, "resolve": IpnsCmd, "pubsub": IpnsPubsubCmd, + "inspect": IpnsInspectCmd, + }, +} + +type IpnsInspectValidation struct { + Valid bool + Reason string + PublicKey peer.ID +} + +// 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 + Validity *time.Time + Sequence uint64 + TTL *uint64 + PublicKey string + SignatureV1 string + SignatureV2 string + Data interface{} +} + +type IpnsInspectResult struct { + Entry IpnsInspectEntry + Validation *IpnsInspectValidation +} + +var IpnsInspectCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Inspects an IPNS Record", + ShortDescription: ` +Prints values inside of IPNS Record protobuf and its DAG-CBOR Data field. +Passing --verify will verify signature against provided public key. +`, + LongDescription: ` +Prints values inside of IPNS Record protobuf and its DAG-CBOR Data field. + +The input can be a file or STDIN, the output can be JSON: + + $ ipfs routing get "/ipns/$PEERID" > ipns_record + $ ipfs name inspect --enc=json < ipns_record + +Values in PublicKey, SignatureV1 and SignatureV2 fields are raw bytes encoded +in Multibase. The Data field is DAG-CBOR represented as DAG-JSON. + +Passing --verify will verify signature against provided public key. + +`, + }, + Arguments: []cmds.Argument{ + cmds.FileArg("record", true, false, "The IPNS record payload to be verified.").EnableStdin(), + }, + Options: []cmds.Option{ + cmds.StringOption("verify", "CID of the public IPNS key to validate against."), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + file, err := cmdenv.GetFileArg(req.Files.Entries()) + if err != nil { + return err + } + defer file.Close() + + var b bytes.Buffer + + _, err = io.Copy(&b, file) + if err != nil { + return err + } + + var entry ipns_pb.IpnsEntry + err = proto.Unmarshal(b.Bytes(), &entry) + if err != nil { + return err + } + + encoder, err := mbase.EncoderByName("base64") + if err != nil { + return err + } + + 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, + }, + } + + 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 + } + + var buf bytes.Buffer + err = dagjson.Encode(node, &buf) + if err != nil { + return err + } + + err = json.Unmarshal(buf.Bytes(), &result.Entry.Data) + if err != nil { + return err + } + } + + validity, err := ipns.GetEOL(&entry) + if err == nil { + result.Entry.Validity = &validity + } + + verify, ok := req.Options["verify"].(string) + if ok { + key := strings.TrimPrefix(verify, "/ipns/") + id, err := peer.Decode(key) + if err != nil { + return err + } + + result.Validation = &IpnsInspectValidation{ + PublicKey: id, + } + + pub, err := id.ExtractPublicKey() + if err != nil { + // Make sure it works with all those RSA that cannot be embedded into the + // Peer ID. + if len(entry.PubKey) > 0 { + pub, err = ic.UnmarshalPublicKey(entry.PubKey) + } + } + if err != nil { + return err + } + + err = ipns.Validate(pub, &entry) + if err == nil { + result.Validation.Valid = true + } else { + result.Validation.Reason = err.Error() + } + } + + return cmds.EmitOnce(res, result) + }, + Type: IpnsInspectResult{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *IpnsInspectResult) error { + 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.Validity != nil { + fmt.Fprintf(tw, "Validity:\t%s\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) + } + 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 + } + fmt.Fprintf(tw, "Data:\t%s\n", string(data)) + + if out.Validation == nil { + tw.Flush() + fmt.Fprintf(w, "\nThis record was not validated.\n") + } else { + tw.Flush() + fmt.Fprintf(w, "\nValidation results:\n") + + fmt.Fprintf(tw, "\tValid:\t%v\n", out.Validation.Valid) + if out.Validation.Reason != "" { + fmt.Fprintf(tw, "\tReason:\t%s\n", out.Validation.Reason) + } + fmt.Fprintf(tw, "\tPublicKey:\t%s\n", out.Validation.PublicKey) + } + + return nil + }), }, } diff --git a/core/coreapi/name.go b/core/coreapi/name.go index 69dc1137bf0..87af34af476 100644 --- a/core/coreapi/name.go +++ b/core/coreapi/name.go @@ -15,6 +15,7 @@ import ( ipath "github.com/ipfs/go-path" coreiface "github.com/ipfs/interface-go-ipfs-core" caopts "github.com/ipfs/interface-go-ipfs-core/options" + nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys" path "github.com/ipfs/interface-go-ipfs-core/path" ci "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" @@ -37,8 +38,6 @@ func (e *ipnsEntry) Value() path.Path { return e.value } -type requestContextKey string - // 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) { ctx, span := tracing.Span(ctx, "CoreAPI.NameAPI", "Publish", trace.WithAttributes(attribute.String("path", p.String()))) @@ -76,13 +75,17 @@ func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.Nam return nil, err } + eol := time.Now().Add(options.ValidTime) + + publishOptions := []nsopts.PublishOption{ + nsopts.PublishWithEOL(eol), + } + if options.TTL != nil { - // nolint: staticcheck // non-backward compatible change - ctx = context.WithValue(ctx, requestContextKey("ipns-publish-ttl"), *options.TTL) + publishOptions = append(publishOptions, nsopts.PublishWithTTL(*options.TTL)) } - eol := time.Now().Add(options.ValidTime) - err = api.namesys.PublishWithEOL(ctx, k, pth, eol) + err = api.namesys.Publish(ctx, k, pth, publishOptions...) if err != nil { return nil, err } diff --git a/core/corehttp/gateway_test.go b/core/corehttp/gateway_test.go index 74723579d6a..877ac9739ae 100644 --- a/core/corehttp/gateway_test.go +++ b/core/corehttp/gateway_test.go @@ -9,7 +9,6 @@ import ( "regexp" "strings" "testing" - "time" namesys "github.com/ipfs/go-namesys" version "github.com/ipfs/kubo" @@ -68,11 +67,7 @@ func (m mockNamesys) ResolveAsync(ctx context.Context, name string, opts ...nsop return out } -func (m mockNamesys) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { - return errors.New("not implemented for mockNamesys") -} - -func (m mockNamesys) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, _ time.Time) error { +func (m mockNamesys) Publish(ctx context.Context, name ci.PrivKey, value path.Path, opts ...nsopts.PublishOption) error { return errors.New("not implemented for mockNamesys") } diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 66cc238d6b0..29b99699e07 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -8,8 +8,8 @@ replace github.com/ipfs/kubo => ./../../.. require ( github.com/ipfs/go-libipfs v0.3.0 - github.com/ipfs/interface-go-ipfs-core v0.8.2 - github.com/ipfs/kubo v0.14.0-rc1 + github.com/ipfs/interface-go-ipfs-core v0.9.0 + github.com/ipfs/kubo v0.0.0-00010101000000-000000000000 github.com/libp2p/go-libp2p v0.24.2 github.com/multiformats/go-multiaddr v0.8.0 ) @@ -101,7 +101,7 @@ require ( github.com/ipfs/go-merkledag v0.9.0 // indirect github.com/ipfs/go-metrics-interface v0.0.1 // indirect github.com/ipfs/go-mfs v0.2.1 // indirect - github.com/ipfs/go-namesys v0.6.0 // indirect + github.com/ipfs/go-namesys v0.7.0 // indirect github.com/ipfs/go-path v0.3.0 // indirect github.com/ipfs/go-peertaskqueue v0.8.0 // indirect github.com/ipfs/go-unixfs v0.4.2 // indirect @@ -209,7 +209,7 @@ require ( golang.org/x/tools v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect - google.golang.org/grpc v1.47.0 // indirect + google.golang.org/grpc v1.46.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect lukechampine.com/blake3 v1.1.7 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 073bf7072b5..75135d8bed5 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -575,8 +575,8 @@ github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fG github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/ipfs/go-mfs v0.2.1 h1:5jz8+ukAg/z6jTkollzxGzhkl3yxm022Za9f2nL5ab8= github.com/ipfs/go-mfs v0.2.1/go.mod h1:Woj80iuw4ajDnIP6+seRaoHpPsc9hmL0pk/nDNDWP88= -github.com/ipfs/go-namesys v0.6.0 h1:w4+Wq9bCILnuZRT1RBBdzZQFqtJeDG1duzN8mIDnHZ0= -github.com/ipfs/go-namesys v0.6.0/go.mod h1:0L+3CHBgHxr08Cg+chVo9Ew285PGQfToThjll4g0/d4= +github.com/ipfs/go-namesys v0.7.0 h1:xqosk71GIVRkFDtF2UNRcXn4LdNeo7tzuy8feHD6NbU= +github.com/ipfs/go-namesys v0.7.0/go.mod h1:KYSZBVZG3VJC34EfqqJPG7T48aWgxseoMPAPA5gLyyQ= github.com/ipfs/go-path v0.2.1/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= github.com/ipfs/go-path v0.3.0 h1:tkjga3MtpXyM5v+3EbRvOHEoo+frwi4oumw5K+KYWyA= github.com/ipfs/go-path v0.3.0/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= @@ -594,8 +594,8 @@ github.com/ipfs/go-unixfsnode v1.5.1/go.mod h1:ed79DaG9IEuZITJVQn4U6MZDftv6I3ygU github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= -github.com/ipfs/interface-go-ipfs-core v0.8.2 h1:WDeCBnE4MENVOXbtfwwdAPJ2nBBS8PTmhZWWpm24HRM= -github.com/ipfs/interface-go-ipfs-core v0.8.2/go.mod h1:F3EcmDy53GFkF0H3iEJpfJC320fZ/4G60eftnItrrJ0= +github.com/ipfs/interface-go-ipfs-core v0.9.0 h1:+RCouVtSU/SldgkqWufjIu1smmGaSyKgUIfbYwLukgI= +github.com/ipfs/interface-go-ipfs-core v0.9.0/go.mod h1:F3EcmDy53GFkF0H3iEJpfJC320fZ/4G60eftnItrrJ0= 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.4.0 h1:U6W7F1aKF/OJMHovnOVdst2cpQE5GhmHibQkAixgNcQ= @@ -1779,9 +1779,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/go.mod b/go.mod index f0b992f3088..fbc56cc8456 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 github.com/fsnotify/fsnotify v1.6.0 github.com/gabriel-vasile/mimetype v1.4.1 + 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/go-bitswap v0.11.0 @@ -55,13 +56,13 @@ require ( github.com/ipfs/go-metrics-interface v0.0.1 github.com/ipfs/go-metrics-prometheus v0.0.2 github.com/ipfs/go-mfs v0.2.1 - github.com/ipfs/go-namesys v0.6.0 + github.com/ipfs/go-namesys v0.7.0 github.com/ipfs/go-path v0.3.0 github.com/ipfs/go-pinning-service-http-client v0.1.2 github.com/ipfs/go-unixfs v0.4.2 github.com/ipfs/go-unixfsnode v1.5.1 github.com/ipfs/go-verifcid v0.0.2 - github.com/ipfs/interface-go-ipfs-core v0.8.2 + github.com/ipfs/interface-go-ipfs-core v0.9.0 github.com/ipld/go-car v0.4.0 github.com/ipld/go-car/v2 v2.5.1 github.com/ipld/go-codec-dagpb v1.5.0 @@ -142,7 +143,6 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // 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.2 // indirect diff --git a/go.sum b/go.sum index 057b7f21b8d..27674871fd1 100644 --- a/go.sum +++ b/go.sum @@ -599,8 +599,8 @@ github.com/ipfs/go-metrics-prometheus v0.0.2 h1:9i2iljLg12S78OhC6UAiXi176xvQGiZa github.com/ipfs/go-metrics-prometheus v0.0.2/go.mod h1:ELLU99AQQNi+zX6GCGm2lAgnzdSH3u5UVlCdqSXnEks= github.com/ipfs/go-mfs v0.2.1 h1:5jz8+ukAg/z6jTkollzxGzhkl3yxm022Za9f2nL5ab8= github.com/ipfs/go-mfs v0.2.1/go.mod h1:Woj80iuw4ajDnIP6+seRaoHpPsc9hmL0pk/nDNDWP88= -github.com/ipfs/go-namesys v0.6.0 h1:w4+Wq9bCILnuZRT1RBBdzZQFqtJeDG1duzN8mIDnHZ0= -github.com/ipfs/go-namesys v0.6.0/go.mod h1:0L+3CHBgHxr08Cg+chVo9Ew285PGQfToThjll4g0/d4= +github.com/ipfs/go-namesys v0.7.0 h1:xqosk71GIVRkFDtF2UNRcXn4LdNeo7tzuy8feHD6NbU= +github.com/ipfs/go-namesys v0.7.0/go.mod h1:KYSZBVZG3VJC34EfqqJPG7T48aWgxseoMPAPA5gLyyQ= github.com/ipfs/go-path v0.2.1/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= github.com/ipfs/go-path v0.3.0 h1:tkjga3MtpXyM5v+3EbRvOHEoo+frwi4oumw5K+KYWyA= github.com/ipfs/go-path v0.3.0/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= @@ -620,8 +620,8 @@ github.com/ipfs/go-unixfsnode v1.5.1/go.mod h1:ed79DaG9IEuZITJVQn4U6MZDftv6I3ygU github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= -github.com/ipfs/interface-go-ipfs-core v0.8.2 h1:WDeCBnE4MENVOXbtfwwdAPJ2nBBS8PTmhZWWpm24HRM= -github.com/ipfs/interface-go-ipfs-core v0.8.2/go.mod h1:F3EcmDy53GFkF0H3iEJpfJC320fZ/4G60eftnItrrJ0= +github.com/ipfs/interface-go-ipfs-core v0.9.0 h1:+RCouVtSU/SldgkqWufjIu1smmGaSyKgUIfbYwLukgI= +github.com/ipfs/interface-go-ipfs-core v0.9.0/go.mod h1:F3EcmDy53GFkF0H3iEJpfJC320fZ/4G60eftnItrrJ0= 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.4.0 h1:U6W7F1aKF/OJMHovnOVdst2cpQE5GhmHibQkAixgNcQ= diff --git a/test/sharness/t0100-name.sh b/test/sharness/t0100-name.sh index 934cc012740..44c2afb2d69 100755 --- a/test/sharness/t0100-name.sh +++ b/test/sharness/t0100-name.sh @@ -225,6 +225,30 @@ test_name_with_self() { 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