Skip to content

Commit

Permalink
feat(ipns): refactored IPNS package with lean records
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Jun 16, 2023
1 parent 2e721ad commit d5d3b91
Show file tree
Hide file tree
Showing 14 changed files with 357 additions and 451 deletions.
3 changes: 1 addition & 2 deletions cmd/ipfs/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
fsrepo "github.com/ipfs/kubo/repo/fsrepo"

options "github.com/ipfs/boxo/coreiface/options"
nsopts "github.com/ipfs/boxo/coreiface/options/namesys"
"github.com/ipfs/boxo/files"
cmds "github.com/ipfs/go-ipfs-cmds"
config "github.com/ipfs/kubo/config"
Expand Down Expand Up @@ -263,5 +262,5 @@ func initializeIpnsKeyspace(repoRoot string) error {
return err
}

return nd.Namesys.Publish(ctx, nd.PrivateKey, path.FromCid(emptyDir.Cid()), nsopts.PublishCompatibleWithV1(true))
return nd.Namesys.Publish(ctx, nd.PrivateKey, path.FromCid(emptyDir.Cid()))
}
163 changes: 73 additions & 90 deletions core/commands/name/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@ package name

import (
"bytes"
"encoding/json"
"encoding/hex"
"fmt"
"io"
"text/tabwriter"
"time"

"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/crypto"
mbase "github.com/multiformats/go-multibase"
"google.golang.org/protobuf/proto"
)

Expand Down Expand Up @@ -83,27 +79,25 @@ Resolve the value of a dnslink:
}

type IpnsInspectValidation struct {
Valid bool
Reason string
PublicKey string
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.IpnsRecord_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
Version string
HexDump string
Validation *IpnsInspectValidation
}

Expand Down Expand Up @@ -135,6 +129,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("verbose", "Show a full hex dump of the raw Protobuf record."),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
file, err := cmdenv.GetFileArg(req.Files.Entries())
Expand All @@ -150,96 +145,76 @@ Passing --verify will verify signature against provided public key.
return err
}

// Here we use the old school raw Protobuf pbRecord because we want to inspect it,
// aka, we need to be able to see all of its contents inside. While the boxo/ipns
// provides a good abstraction over the Record type, it doesn't allow for introspection
// of the raw value of the IpnsEntry.
var pbRecord ipns_pb.IpnsRecord
err = proto.Unmarshal(b.Bytes(), &pbRecord)
if err != nil {
return err
}

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{
Value: string(pbRecord.Value),
ValidityType: pbRecord.ValidityType,
Sequence: *pbRecord.Sequence,
TTL: pbRecord.Ttl,
PublicKey: encoder.Encode(pbRecord.PubKey),
SignatureV1: encoder.Encode(pbRecord.SignatureV1),
SignatureV2: encoder.Encode(pbRecord.SignatureV2),
Data: nil,
},
Entry: IpnsInspectEntry{},
}

if len(pbRecord.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(pbRecord.Data, dagcbor.Decode)
if err != nil {
return err
}
// Best effort to get the fields. Show everything we can.
if v, err := rec.Value(); err == nil {
result.Entry.Value = &v
}

var buf bytes.Buffer
err = dagjson.Encode(node, &buf)
if err != nil {
return err
}
if v, err := rec.ValidityType(); err == nil {
result.Entry.ValidityType = &v
}

err = json.Unmarshal(buf.Bytes(), &result.Entry.Data)
if err != nil {
return err
}
if v, err := rec.Validity(); err == nil {
result.Entry.Validity = &v
}

validity, err := rec.Validity()
if err == nil {
result.Entry.Validity = &validity
if v, err := rec.Sequence(); err == nil {
result.Entry.Sequence = &v
}

verify, ok := req.Options["verify"].(string)
if ok {
name, err := ipns.NameFromString(verify)
if err != nil {
return err
}
if v, err := rec.TTL(); err == nil {
result.Entry.TTL = &v
}

pk, err := ipns.ExtractPublicKey(rec, name)
if err != nil {
return err
// 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 pbRecord.SignatureV1 != nil || pbRecord.Value != nil {
if pbRecord.Data != nil {
result.Version = "V1+V2"
} else {
result.Version = "V2"
}
} else if pbRecord.Data != nil {
result.Version = "V2"
} else {
result.Version = "Unknown"
}

bytes, err := crypto.MarshalPublicKey(pk)
if verify, ok := req.Options["verify"].(string); ok {
name, err := ipns.NameFromString(verify)
if err != nil {
return err
}

result.Validation = &IpnsInspectValidation{
PublicKey: encoder.Encode(bytes),
Name: name.String(),
}

err = ipns.Validate(rec, pk)
err = ipns.ValidateWithName(rec, name)
if err == nil {
result.Validation.Valid = true
} else {
result.Validation.Reason = err.Error()
}
}

if verbose, ok := req.Options["verbose"].(bool); ok && verbose {
result.HexDump = hex.Dump(b.Bytes())
}

return cmds.EmitOnce(res, result)
},
Type: IpnsInspectResult{},
Expand All @@ -248,24 +223,25 @@ 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.RFC3339))
}
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))

if out.Validation == nil {
tw.Flush()
Expand All @@ -278,7 +254,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
Expand Down
9 changes: 9 additions & 0 deletions docs/changelogs/v0.22.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ Imported 1 blocks (1618 bytes)
[exit code 1]
```

#### `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.

### 📝 Changelog

### 👨‍👩‍👧‍👦 Contributors
2 changes: 1 addition & 1 deletion docs/examples/kubo-as-a-library/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ go 1.18
replace github.com/ipfs/kubo => ./../../..

require (
github.com/ipfs/boxo v0.10.1-0.20230615083040-d5e141ea82cd
github.com/ipfs/boxo v0.10.1-0.20230616065146-5ebb39f47b57
github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
github.com/libp2p/go-libp2p v0.27.6
github.com/multiformats/go-multiaddr v0.9.0
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/kubo-as-a-library/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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-0.20230615083040-d5e141ea82cd h1:mR5STWmJw1n+JZ2t7Jx0vPxp+iL9vuAM4RFxoqFyww0=
github.com/ipfs/boxo v0.10.1-0.20230615083040-d5e141ea82cd/go.mod h1:IwBbXi5P7fA0HzLhsw/FtAj9RAMacODuOCPPsBcvqcE=
github.com/ipfs/boxo v0.10.1-0.20230616065146-5ebb39f47b57 h1:Q4hRoDmqqy8twq1kJS7nyzTwSNJLAiizR5/5Dw8hs/I=
github.com/ipfs/boxo v0.10.1-0.20230616065146-5ebb39f47b57/go.mod h1:IwBbXi5P7fA0HzLhsw/FtAj9RAMacODuOCPPsBcvqcE=
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=
Expand Down
3 changes: 1 addition & 2 deletions fuse/ipns/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package ipns
import (
"context"

nsopts "github.com/ipfs/boxo/coreiface/options/namesys"
ft "github.com/ipfs/boxo/ipld/unixfs"
nsys "github.com/ipfs/boxo/namesys"
path "github.com/ipfs/boxo/path"
Expand Down Expand Up @@ -31,5 +30,5 @@ func InitializeKeyspace(n *core.IpfsNode, key ci.PrivKey) error {

pub := nsys.NewIpnsPublisher(n.Routing, n.Repo.Datastore())

return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid()), nsopts.PublishCompatibleWithV1(true))
return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid()))
}
2 changes: 1 addition & 1 deletion fuse/ipns/ipns_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ type Root struct {

func ipnsPubFunc(ipfs iface.CoreAPI, key iface.Key) mfs.PubFunc {
return func(ctx context.Context, c cid.Cid) error {
_, err := ipfs.Name().Publish(ctx, path.IpfsPath(c), options.Name.Key(key.Name()), options.Name.CompatibleWithV1(true))
_, err := ipfs.Name().Publish(ctx, path.IpfsPath(c), options.Name.Key(key.Name()))
return err
}
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/fsnotify/fsnotify v1.6.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-multierror v1.1.1
github.com/ipfs/boxo v0.10.1-0.20230615083040-d5e141ea82cd
github.com/ipfs/boxo v0.10.1-0.20230616065146-5ebb39f47b57
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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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-0.20230615083040-d5e141ea82cd h1:mR5STWmJw1n+JZ2t7Jx0vPxp+iL9vuAM4RFxoqFyww0=
github.com/ipfs/boxo v0.10.1-0.20230615083040-d5e141ea82cd/go.mod h1:IwBbXi5P7fA0HzLhsw/FtAj9RAMacODuOCPPsBcvqcE=
github.com/ipfs/boxo v0.10.1-0.20230616065146-5ebb39f47b57 h1:Q4hRoDmqqy8twq1kJS7nyzTwSNJLAiizR5/5Dw8hs/I=
github.com/ipfs/boxo v0.10.1-0.20230616065146-5ebb39f47b57/go.mod h1:IwBbXi5P7fA0HzLhsw/FtAj9RAMacODuOCPPsBcvqcE=
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=
Expand Down
Binary file added test/cli/fixtures/TestName.car
Binary file not shown.
Loading

0 comments on commit d5d3b91

Please sign in to comment.