Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement non-dag cbor codec #153

Merged
merged 1 commit into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions codec/cbor/multicodec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cbor

import (
"io"

"github.com/polydawn/refmt/cbor"

"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec/dagcbor"
"github.com/ipld/go-ipld-prime/multicodec"
)

var (
_ ipld.Decoder = Decode
_ ipld.Encoder = Encode
)

func init() {
multicodec.RegisterEncoder(0x51, Encode)
multicodec.RegisterDecoder(0x51, Decode)
}

func Decode(na ipld.NodeAssembler, r io.Reader) error {
return dagcbor.Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r), false)
}

func Encode(n ipld.Node, w io.Writer) error {
return dagcbor.Marshal(n, cbor.NewEncoder(w), false)
}
13 changes: 8 additions & 5 deletions codec/dagcbor/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import (
// This should be identical to the general feature in the parent package,
// except for the `case ipld.Kind_Link` block,
// which is dag-cbor's special sauce for schemafree links.
func Marshal(n ipld.Node, sink shared.TokenSink) error {
func Marshal(n ipld.Node, sink shared.TokenSink, allowLinks bool) error {
var tk tok.Token
return marshal(n, &tk, sink)
return marshal(n, &tk, sink, allowLinks)
}

func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink, allowLinks bool) error {
switch n.Kind() {
case ipld.Kind_Invalid:
return fmt.Errorf("cannot traverse a node that is absent")
Expand Down Expand Up @@ -47,7 +47,7 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
if _, err := sink.Step(tk); err != nil {
return err
}
if err := marshal(v, tk, sink); err != nil {
if err := marshal(v, tk, sink, allowLinks); err != nil {
return err
}
}
Expand All @@ -69,7 +69,7 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
if err != nil {
return err
}
if err := marshal(v, tk, sink); err != nil {
if err := marshal(v, tk, sink, allowLinks); err != nil {
return err
}
}
Expand Down Expand Up @@ -123,6 +123,9 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
_, err = sink.Step(tk)
return err
case ipld.Kind_Link:
if !allowLinks {
return fmt.Errorf("cannot Marshal ipld links to CBOR")
}
v, err := n.AsLink()
if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions codec/dagcbor/multicodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func Decode(na ipld.NodeAssembler, r io.Reader) error {
return na2.DecodeDagCbor(r)
}
// Okay, generic builder path.
return Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r))
return Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r), true)
}

func Encode(n ipld.Node, w io.Writer) error {
Expand All @@ -40,5 +40,5 @@ func Encode(n ipld.Node, w io.Writer) error {
return n2.EncodeDagCbor(w)
}
// Okay, generic inspection path.
return Marshal(n, cbor.NewEncoder(w))
return Marshal(n, cbor.NewEncoder(w), true)
}
19 changes: 11 additions & 8 deletions codec/dagcbor/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ const (
// except for the `case tok.TBytes` block,
// which has dag-cbor's special sauce for detecting schemafree links.

func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource) error {
func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, allowLinks bool) error {
// Have a gas budget, which will be decremented as we allocate memory, and an error returned when execeeded (or about to be exceeded).
// This is a DoS defense mechanism.
// It's *roughly* in units of bytes (but only very, VERY roughly) -- it also treats words as 1 in many cases.
// FUTURE: this ought be configurable somehow. (How, and at what granularity though?)
var gas int = 1048576 * 10
return unmarshal1(na, tokSrc, &gas)
return unmarshal1(na, tokSrc, &gas, allowLinks)
}

func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int) error {
func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int, allowLinks bool) error {
var tk tok.Token
done, err := tokSrc.Step(&tk)
if err != nil {
Expand All @@ -45,12 +45,12 @@ func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int) erro
if done && !tk.Type.IsValue() {
return fmt.Errorf("unexpected eof")
}
return unmarshal2(na, tokSrc, &tk, gas)
return unmarshal2(na, tokSrc, &tk, gas, allowLinks)
}

// starts with the first token already primed. Necessary to get recursion
// to flow right without a peek+unpeek system.
func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, gas *int) error {
func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, gas *int, allowLinks bool) error {
// FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want).
switch tk.Type {
case tok.TMapOpen:
Expand Down Expand Up @@ -97,7 +97,7 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token,
if err != nil { // return in error if the key was rejected
return err
}
err = unmarshal1(mva, tokSrc, gas)
err = unmarshal1(mva, tokSrc, gas, allowLinks)
if err != nil { // return in error if some part of the recursion errored
return err
}
Expand Down Expand Up @@ -140,7 +140,7 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token,
if observedLen > expectLen {
return fmt.Errorf("unexpected continuation of array elements beyond declared length")
}
err := unmarshal2(la.AssembleValue(), tokSrc, tk, gas)
err := unmarshal2(la.AssembleValue(), tokSrc, tk, gas, allowLinks)
if err != nil { // return in error if some part of the recursion errored
return err
}
Expand All @@ -166,14 +166,17 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token,
}
switch tk.Tag {
case linkTag:
if !allowLinks {
return fmt.Errorf("unhandled cbor tag %d", tk.Tag)
}
if len(tk.Bytes) < 1 || tk.Bytes[0] != 0 {
return ErrInvalidMultibase
}
elCid, err := cid.Cast(tk.Bytes[1:])
if err != nil {
return err
}
return na.AssignLink(cidlink.Link{elCid})
return na.AssignLink(cidlink.Link{Cid: elCid})
default:
return fmt.Errorf("unhandled cbor tag %d", tk.Tag)
}
Expand Down