1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-08-06 19:44:01 +08:00
Files
Marcin Rataj 2260e35df2 chore: upgrade to go 1.23 (#10486)
* chore: upgrade to go 1.23

https://tip.golang.org/doc/go1.23

* refactor: golangci-lint v1.60.2
2024-08-28 18:56:45 +02:00

270 lines
7.0 KiB
Go

package name
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"text/tabwriter"
"time"
"github.com/ipfs/boxo/ipns"
ipns_pb "github.com/ipfs/boxo/ipns/pb"
cmds "github.com/ipfs/go-ipfs-cmds"
cmdenv "github.com/ipfs/kubo/core/commands/cmdenv"
"google.golang.org/protobuf/proto"
)
type IpnsEntry struct {
Name string
Value string
}
var NameCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Publish and resolve IPNS names.",
ShortDescription: `
IPNS is a PKI namespace, where names are the hashes of public keys, and
the private key enables publishing new (signed) values. In both publish
and resolve, the default name used is the node's own PeerID,
which is the hash of its public key.
`,
LongDescription: `
IPNS is a PKI namespace, where names are the hashes of public keys, and
the private key enables publishing new (signed) values. In both publish
and resolve, the default name used is the node's own PeerID,
which is the hash of its public key.
You can use the 'ipfs key' commands to list and generate more names and their
respective keys.
Examples:
Publish an <ipfs-path> with your default name:
> ipfs name publish /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
Published to QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n: /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
Publish an <ipfs-path> with another name, added by an 'ipfs key' command:
> ipfs key gen --type=rsa --size=2048 mykey
> ipfs name publish --key=mykey /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
Published to QmSrPmbaUKA3ZodhzPWZnpFgcPMFWF4QsxXbkWfEptTBJd: /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
Resolve the value of your name:
> ipfs name resolve
/ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
Resolve the value of another name:
> ipfs name resolve QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
/ipfs/QmSiTko9JZyabH56y2fussEt1A5oDqsFXB3CkvAqraFryz
Resolve the value of a dnslink:
> ipfs name resolve ipfs.io
/ipfs/QmaBvfZooxWkrv7D3r8LS9moNjzD2o525XMZze69hhoxf5
`,
},
Subcommands: map[string]*cmds.Command{
"publish": PublishCmd,
"resolve": IpnsCmd,
"pubsub": IpnsPubsubCmd,
"inspect": IpnsInspectCmd,
},
}
type IpnsInspectValidation struct {
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.ValidityType
Validity *time.Time
Sequence *uint64
TTL *time.Duration
}
type IpnsInspectResult struct {
Entry IpnsInspectEntry
PbSize int
SignatureType string
HexDump string
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."),
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())
if err != nil {
return err
}
defer file.Close()
var b bytes.Buffer
_, err = io.Copy(&b, file)
if err != nil {
return err
}
rec, err := ipns.UnmarshalRecord(b.Bytes())
if err != nil {
return err
}
result := &IpnsInspectResult{
Entry: IpnsInspectEntry{},
}
// Best effort to get the fields. Show everything we can.
if v, err := rec.Value(); err == nil {
result.Entry.Value = v.String()
}
if v, err := rec.ValidityType(); err == nil {
result.Entry.ValidityType = &v
}
if v, err := rec.Validity(); err == nil {
result.Entry.Validity = &v
}
if v, err := rec.Sequence(); err == nil {
result.Entry.Sequence = &v
}
if v, err := rec.TTL(); err == nil {
result.Entry.TTL = &v
}
// 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)
if verify, ok := req.Options["verify"].(string); ok {
name, err := ipns.NameFromString(verify)
if err != nil {
return err
}
result.Validation = &IpnsInspectValidation{
Name: name.String(),
}
err = ipns.ValidateWithName(rec, name)
if err == nil {
result.Validation.Valid = true
} else {
result.Validation.Reason = err.Error()
}
}
if dump, ok := req.Options["dump"].(bool); ok && dump {
result.HexDump = hex.Dump(b.Bytes())
}
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()
if out.Entry.Value != "" {
fmt.Fprintf(tw, "Value:\t%q\n", out.Entry.Value)
}
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%q\n", out.Entry.Validity.Format(time.RFC3339Nano))
}
if out.Entry.Sequence != nil {
fmt.Fprintf(tw, "Sequence:\t%d\n", *out.Entry.Sequence)
}
if out.Entry.TTL != nil {
fmt.Fprintf(tw, "TTL:\t%s\n", out.Entry.TTL.String())
}
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()
fmt.Fprintf(w, "\nThis record was not verified. Pass '--verify k51...' to verify.\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, "\tName:\t%s\n", out.Validation.Name)
}
if out.HexDump != "" {
tw.Flush()
fmt.Fprintf(w, "\nHex Dump:\n%s", out.HexDump)
}
return nil
}),
},
}