From 953bdf15a11c558428fd35366dfe2525f7dcfbf4 Mon Sep 17 00:00:00 2001 From: Will Scott Date: Sun, 6 Mar 2022 18:07:55 +0100 Subject: [PATCH 1/2] relaxed reification If a node is not an immediate pbnode, but can be built as such, then reifiy appropriately. This allows a dag-pb node to have been serialized to cbor or json, and still be interpreted as unixfs data. --- reification.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/reification.go b/reification.go index fc79291..90e83df 100644 --- a/reification.go +++ b/reification.go @@ -17,7 +17,12 @@ import ( func Reify(lnkCtx ipld.LinkContext, maybePBNodeRoot ipld.Node, lsys *ipld.LinkSystem) (ipld.Node, error) { pbNode, ok := maybePBNodeRoot.(dagpb.PBNode) if !ok { - return maybePBNodeRoot, nil + // see if the node has the right structure anyway + pbb := dagpb.Type.PBNode.NewBuilder() + if err := pbb.AssignNode(maybePBNodeRoot); err != nil { + return maybePBNodeRoot, nil + } + pbNode = pbb.Build().(dagpb.PBNode) } if !pbNode.FieldData().Exists() { // no data field, therefore, not UnixFS From e943499ee6cd0b0ddecde91be76b5261c9b9f3b4 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 27 Jun 2022 21:41:08 +1000 Subject: [PATCH 2/2] add ReifyDagPB, add tests, update docs --- README.md | 2 +- file/file_test.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++ reification.go | 16 +++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2146b6c..d11c22f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This is an IPLD ADL that provides string based pathing for protobuf nodes. The top level node behaves like a map where LookupByString returns the Hash property on the Link in the protobufs list of Links whos Name property matches the key. This should enable selector traversals that work based of paths. -Note that while it works internally with go-codec-dagpb, the Reify method (used to get a UnixFSNode from a DagPB node should actually work successfully with go-ipld-prime-proto nodes) +Note that while it works internally with go-codec-dagpb, the `Reify()` method (used to get a UnixFSNode from a DagPB node) should actually work successfully with any schema-compliant go-ipld-prime `Node`. Using `ReifyDagPB()` will require that the incoming `Node` be of type `PBNode` from go-codec-dagpb. ## License diff --git a/file/file_test.go b/file/file_test.go index ee01a71..af40343 100644 --- a/file/file_test.go +++ b/file/file_test.go @@ -13,7 +13,9 @@ import ( "github.com/ipld/go-car/v2/blockstore" dagpb "github.com/ipld/go-codec-dagpb" "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/datamodel" cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/ipld/go-ipld-prime/node/basicnode" ) func TestRootV0File(t *testing.T) { @@ -32,6 +34,74 @@ func TestRootV0File(t *testing.T) { } } +func TestBasicnodeReify(t *testing.T) { + baseFile := "./fixtures/QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o.car" + root, ls := open(baseFile, t) + nb := basicnode.Prototype.Any.NewBuilder() + err := datamodel.Copy(root, nb) + if err != nil { + t.Fatal(err) + } + basicroot := nb.Build() + file, err := unixfsnode.Reify(ipld.LinkContext{}, basicroot, ls) + if err != nil { + t.Fatal(err) + } + if file == basicroot { + t.Fatal("node pass-through with Reify()") + } + fc, err := file.AsBytes() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(fc, []byte("hello world\n")) { + t.Errorf("file content does not match: %s", string(fc)) + } +} + +func TestReifyDagPB(t *testing.T) { + baseFile := "./fixtures/QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o.car" + root, ls := open(baseFile, t) + file, err := unixfsnode.Reify(ipld.LinkContext{}, root, ls) + if err != nil { + t.Fatal(err) + } + if file == root { + t.Fatal("node pass-through with Reify()") + } + fc, err := file.AsBytes() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(fc, []byte("hello world\n")) { + t.Errorf("file content does not match: %s", string(fc)) + } +} + +func TestBasicnodeReifyDagPBFails(t *testing.T) { + baseFile := "./fixtures/QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o.car" + root, ls := open(baseFile, t) + nb := basicnode.Prototype.Any.NewBuilder() + err := datamodel.Copy(root, nb) + if err != nil { + t.Fatal(err) + } + basicroot := nb.Build() + // we expect this to pass through without being interpreted as UnixFS since + // ReifyDagPB is strict + file, err := unixfsnode.ReifyDagPB(ipld.LinkContext{}, basicroot, ls) + if err != nil { + t.Fatal(err) + } + if file != basicroot { + t.Fatal("node did not pass-through Reify") + } + _, err = file.AsBytes() + if err == nil { + t.Fatal("should not be able to AsBytes() unreified node") + } +} + func TestNamedV0File(t *testing.T) { baseFile := "./fixtures/QmT8EC9sJq63SkDZ1mWLbWWyVV66PuqyHWpKkH4pcVyY4H.car" root, ls := open(baseFile, t) diff --git a/reification.go b/reification.go index 90e83df..7f07e8b 100644 --- a/reification.go +++ b/reification.go @@ -12,6 +12,22 @@ import ( "github.com/ipld/go-ipld-prime" ) +// ReifyDagPB looks at an ipld Node and tries to interpret it as a UnixFSNode +// if successful, it returns the UnixFSNode. +// +// ReifyDagPB strictly requires that an incoming node be a +// github.com/ipld/go-codec-dagpb#PBNode type in order to reify it. Use this +// reification method if you want to apply the strict form of the UnixFS +// specification by type checking (note that a type check of PBNode does not +// guarantee that the data was DAG-PB encoded, nor does the reverse hold as +// there is currently no way to determine original codec by inspecting a Node). +func ReifyDagPB(lnkCtx ipld.LinkContext, maybePBNodeRoot ipld.Node, lsys *ipld.LinkSystem) (ipld.Node, error) { + if _, ok := maybePBNodeRoot.(dagpb.PBNode); !ok { + return maybePBNodeRoot, nil + } + return Reify(lnkCtx, maybePBNodeRoot, lsys) +} + // Reify looks at an ipld Node and tries to interpret it as a UnixFSNode // if successful, it returns the UnixFSNode func Reify(lnkCtx ipld.LinkContext, maybePBNodeRoot ipld.Node, lsys *ipld.LinkSystem) (ipld.Node, error) {