Commit 7106176e authored by Eric Myhre's avatar Eric Myhre

Test that dagcbor and dagjson roundtrip cidlinks.

Including one interesting fix for dagjson.

Since json can include whitespace -- and especially since our
implementation currently uses prettyprinted json with quite a bit of
said whitespace -- it's important to handle it consistently.

We had a fun issue here: the json would be emitted with a trailing
linebreak (as is generally what you want for printing to a terminal,
etc!)... and thus hashed with it.  Then, when loading the object,
our parser will load exactly every byte needed to parse the object,
then stop.  Which... will cause it to return right before consuming
that trailing linebreak.

Which would cause that trailing linebreak to not be fed into the
hasher, since we've carefully used a system which tees exactly the
bytes consumed by the parser into the the hasher.

So of course the link hash validation would fail.  Woowee.

I documented some of these details in an issue on the specs repo:
https://github.com/ipld/specs/issues/108
There's not a super clear resolution over there as yet, but there seems
to be a general agreement that whitespace should be tolerated, so...
let's do so.

As of this patch, the dagjson unmarshaller will consume all additional
whitespace after finishing consumption of the json object iself.

Dagcbor doesn't need a similar fix: there's no such thing as any
possibility of other nonsemantic bytes, so there's nothing to absorb;
and if we don't reach the end of the reader... we technically don't
*care*: given the same reader over the same data being used to load
the same link, we'll behave consistently; and therefore it follows that
any additional bytes in the reader are unobservable to our universe.

An earlier (and badly broken) draft of this attempted to put the
read-to-end behavior in the cidlink package, but in addition to being
unnecessary for dagcbor as described above, it also would've been
simply *wrong*: the whitespace slurp is specific to dagjson.
Signed-off-by: default avatarEric Myhre <hash@exultant.us>
parent 00a58f7a
package dagcbor
import (
"bytes"
"context"
"io"
"testing"
. "github.com/warpfork/go-wish"
cid "github.com/ipfs/go-cid"
ipld "github.com/ipld/go-ipld-prime"
ipldfree "github.com/ipld/go-ipld-prime/impl/free"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
)
func TestRoundtripCidlink(t *testing.T) {
lb := cidlink.LinkBuilder{cid.Prefix{
Version: 1,
Codec: 0x71,
MhType: 0x17,
MhLength: 4,
}}
buf := bytes.Buffer{}
lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n,
func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) {
return &buf, func(lnk ipld.Link) error { return nil }, nil
},
)
Wish(t, err, ShouldEqual, nil)
n2, err := lnk.Load(context.Background(), ipld.LinkContext{}, ipldfree.NodeBuilder(),
func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) {
return bytes.NewBuffer(buf.Bytes()), nil
},
)
Wish(t, err, ShouldEqual, nil)
Wish(t, n2, ShouldEqual, n)
}
package dagjson
import (
"fmt"
"io"
"github.com/polydawn/refmt/json"
......@@ -22,7 +23,33 @@ func init() {
func Decoder(nb ipld.NodeBuilder, r io.Reader) (ipld.Node, error) {
// Shell out directly to generic builder path.
// (There's not really any fastpaths of note for json.)
return Unmarshal(nb, json.NewDecoder(r))
n, err := Unmarshal(nb, json.NewDecoder(r))
if err != nil {
return n, err
}
// Slurp any remaining whitespace.
// (This is relevant if our reader is tee'ing bytes to a hasher, and
// the json contained any trailing whitespace.)
// (We can't actually support multiple objects per reader from here;
// we can't unpeek if we find a non-whitespace token, so our only
// option is to error if this reader seems to contain more content.)
var buf [1]byte
for {
_, err := r.Read(buf[:])
switch buf[0] {
case ' ', '\t', '\r', '\n': // continue
default:
return n, fmt.Errorf("unexpected content after end of json object")
}
if err == nil {
continue
} else if err == io.EOF {
return n, nil
} else {
return n, err
}
}
return n, err
}
func Encoder(n ipld.Node, w io.Writer) error {
......
package dagjson
import (
"bytes"
"context"
"io"
"testing"
. "github.com/warpfork/go-wish"
cid "github.com/ipfs/go-cid"
ipld "github.com/ipld/go-ipld-prime"
ipldfree "github.com/ipld/go-ipld-prime/impl/free"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
)
func TestRoundtripCidlink(t *testing.T) {
lb := cidlink.LinkBuilder{cid.Prefix{
Version: 1,
Codec: 0x0129,
MhType: 0x17,
MhLength: 4,
}}
buf := bytes.Buffer{}
lnk, err := lb.Build(context.Background(), ipld.LinkContext{}, n,
func(ipld.LinkContext) (io.Writer, ipld.StoreCommitter, error) {
return &buf, func(lnk ipld.Link) error { return nil }, nil
},
)
Wish(t, err, ShouldEqual, nil)
n2, err := lnk.Load(context.Background(), ipld.LinkContext{}, ipldfree.NodeBuilder(),
func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) {
return bytes.NewBuffer(buf.Bytes()), nil
},
)
Wish(t, err, ShouldEqual, nil)
Wish(t, n2, ShouldEqual, n)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment