feat(container): versioning for the CBOR container
This commit is contained in:
committed by
Michael Muré
parent
9057cbcba6
commit
25ca34923f
@@ -40,6 +40,13 @@ func TestCarRoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func FuzzCarRoundTrip(f *testing.F) {
|
||||
// Note: this fuzzing is somewhat broken.
|
||||
// After some time, the fuzzer discover that a varint can be serialized in different
|
||||
// ways that lead to the same integer value. This means that the CAR format can have
|
||||
// multiple legal binary representation for the exact same data, which is what we are
|
||||
// trying to detect here. Ideally, the format would be stricter, but that's how things
|
||||
// are.
|
||||
|
||||
example, err := os.ReadFile("testdata/sample-v1.car")
|
||||
require.NoError(f, err)
|
||||
|
||||
|
||||
@@ -98,15 +98,36 @@ func FromCbor(r io.Reader) (Reader, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n.Kind() != datamodel.Kind_List {
|
||||
return nil, fmt.Errorf("not a list")
|
||||
if n.Kind() != datamodel.Kind_Map {
|
||||
return nil, fmt.Errorf("invalid container format: expected map")
|
||||
}
|
||||
if n.Length() != 1 {
|
||||
return nil, fmt.Errorf("invalid container format: expected single version key")
|
||||
}
|
||||
|
||||
ctn := make(Reader, n.Length())
|
||||
// get the first (and only) key-value pair
|
||||
it := n.MapIterator()
|
||||
key, tokensNode, err := it.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
it := n.ListIterator()
|
||||
for !it.Done() {
|
||||
_, val, err := it.Next()
|
||||
version, err := key.AsString()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid container format: version must be string")
|
||||
}
|
||||
if version != currentContainerVersion {
|
||||
return nil, fmt.Errorf("unsupported container version: %s", version)
|
||||
}
|
||||
|
||||
if tokensNode.Kind() != datamodel.Kind_List {
|
||||
return nil, fmt.Errorf("invalid container format: tokens must be a list")
|
||||
}
|
||||
|
||||
ctn := make(Reader, tokensNode.Length())
|
||||
it2 := tokensNode.ListIterator()
|
||||
for !it2.Done() {
|
||||
_, val, err := it2.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -176,3 +176,30 @@ func randToken() (*delegation.Token, cid.Cid, []byte) {
|
||||
}
|
||||
return t, c, b
|
||||
}
|
||||
|
||||
func FuzzContainerRead(f *testing.F) {
|
||||
// Generate a corpus
|
||||
for tokenCount := 0; tokenCount < 10; tokenCount++ {
|
||||
writer := NewWriter()
|
||||
for i := 0; i < tokenCount; i++ {
|
||||
_, c, data := randToken()
|
||||
writer.AddSealed(c, data)
|
||||
}
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err := writer.ToCbor(buf)
|
||||
require.NoError(f, err)
|
||||
|
||||
f.Add(buf.Bytes())
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
start := time.Now()
|
||||
|
||||
// search for panics
|
||||
_, _ = FromCbor(bytes.NewReader(data))
|
||||
|
||||
if time.Since(start) > 100*time.Millisecond {
|
||||
panic("too long")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
|
||||
// TODO: should we have a multibase to wrap the cbor? but there is no reader/write in go-multibase :-(
|
||||
|
||||
const currentContainerVersion = "ctn-v1"
|
||||
|
||||
// Writer is a token container writer. It provides a convenient way to aggregate and serialize tokens together.
|
||||
type Writer map[cid.Cid][]byte
|
||||
|
||||
@@ -43,10 +45,12 @@ func (ctn Writer) ToCarBase64(w io.Writer) error {
|
||||
}
|
||||
|
||||
func (ctn Writer) ToCbor(w io.Writer) error {
|
||||
node, err := qp.BuildList(basicnode.Prototype.Any, int64(len(ctn)), func(la datamodel.ListAssembler) {
|
||||
for _, bytes := range ctn {
|
||||
qp.ListEntry(la, qp.Bytes(bytes))
|
||||
}
|
||||
node, err := qp.BuildMap(basicnode.Prototype.Any, 1, func(ma datamodel.MapAssembler) {
|
||||
qp.MapEntry(ma, currentContainerVersion, qp.List(int64(len(ctn)), func(la datamodel.ListAssembler) {
|
||||
for _, bytes := range ctn {
|
||||
qp.ListEntry(la, qp.Bytes(bytes))
|
||||
}
|
||||
}))
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user