diff --git a/did/didtest/crypto.go b/did/didtest/crypto.go index 3d9de8d..13b43c2 100644 --- a/did/didtest/crypto.go +++ b/did/didtest/crypto.go @@ -85,7 +85,11 @@ func (p Persona) Name() string { // PrivKey returns the Ed25519 private key for the Persona. func (p Persona) PrivKey() crypto.PrivKey { - return privKeys[p] + res, ok := privKeys[p] + if !ok { + panic(fmt.Sprintf("Unknown persona: %v", p)) + } + return res } // PubKey returns the Ed25519 public key for the Persona. diff --git a/pkg/container/reader.go b/pkg/container/reader.go index 74ba06a..1ca3982 100644 --- a/pkg/container/reader.go +++ b/pkg/container/reader.go @@ -1,6 +1,7 @@ package container import ( + "bytes" "encoding/base64" "errors" "fmt" @@ -71,33 +72,13 @@ func (ctn Reader) GetInvocation() (*invocation.Token, error) { return nil, ErrNotFound } -func FromCar(r io.Reader) (Reader, error) { - _, it, err := readCar(r) - if err != nil { - return nil, err - } - - ctn := make(Reader) - - for block, err := range it { - if err != nil { - return nil, err - } - - err = ctn.addToken(block.data) - if err != nil { - return nil, err - } - } - - return ctn, nil +// FromCbor decodes a DAG-CBOR encoded container. +func FromCbor(data []byte) (Reader, error) { + return FromCborReader(bytes.NewReader(data)) } -func FromCarBase64(r io.Reader) (Reader, error) { - return FromCar(base64.NewDecoder(base64.StdEncoding, r)) -} - -func FromCbor(r io.Reader) (Reader, error) { +// FromCborReader is the same as FromCbor, but with an io.Reader. +func FromCborReader(r io.Reader) (Reader, error) { n, err := ipld.DecodeStreaming(r, dagcbor.Decode) if err != nil { return nil, err @@ -147,8 +128,52 @@ func FromCbor(r io.Reader) (Reader, error) { return ctn, nil } -func FromCborBase64(r io.Reader) (Reader, error) { - return FromCbor(base64.NewDecoder(base64.StdEncoding, r)) +// FromCborBase64 decodes a base64 DAG-CBOR encoded container. +func FromCborBase64(data []byte) (Reader, error) { + return FromCborBase64Reader(bytes.NewReader(data)) +} + +// FromCborBase64Reader is the same as FromCborBase64, but with an io.Reader. +func FromCborBase64Reader(r io.Reader) (Reader, error) { + return FromCborReader(base64.NewDecoder(base64.StdEncoding, r)) +} + +// FromCar decodes a CAR file encoded container. +func FromCar(data []byte) (Reader, error) { + return FromCarReader(bytes.NewReader(data)) +} + +// FromCarReader is the same as FromCar, but with an io.Reader. +func FromCarReader(r io.Reader) (Reader, error) { + _, it, err := readCar(r) + if err != nil { + return nil, err + } + + ctn := make(Reader) + + for block, err := range it { + if err != nil { + return nil, err + } + + err = ctn.addToken(block.data) + if err != nil { + return nil, err + } + } + + return ctn, nil +} + +// FromCarBase64 decodes a base64 CAR file encoded container. +func FromCarBase64(data []byte) (Reader, error) { + return FromCarReader(bytes.NewReader(data)) +} + +// FromCarBase64Reader is the same as FromCarBase64, but with an io.Reader. +func FromCarBase64Reader(r io.Reader) (Reader, error) { + return FromCarReader(base64.NewDecoder(base64.StdEncoding, r)) } func (ctn Reader) addToken(data []byte) error { diff --git a/pkg/container/serial_test.go b/pkg/container/serial_test.go index 36560b0..86db649 100644 --- a/pkg/container/serial_test.go +++ b/pkg/container/serial_test.go @@ -26,10 +26,10 @@ func TestContainerRoundTrip(t *testing.T) { writer func(ctn Writer, w io.Writer) error reader func(io.Reader) (Reader, error) }{ - {"car", Writer.ToCar, FromCar}, - {"carBase64", Writer.ToCarBase64, FromCarBase64}, - {"cbor", Writer.ToCbor, FromCbor}, - {"cborBase64", Writer.ToCborBase64, FromCborBase64}, + {"car", Writer.ToCarWriter, FromCarReader}, + {"carBase64", Writer.ToCarBase64Writer, FromCarBase64Reader}, + {"cbor", Writer.ToCborWriter, FromCborReader}, + {"cborBase64", Writer.ToCborBase64Writer, FromCborBase64Reader}, } { t.Run(tc.name, func(t *testing.T) { tokens := make(map[cid.Cid]*delegation.Token) @@ -98,10 +98,10 @@ func BenchmarkContainerSerialisation(b *testing.B) { writer func(ctn Writer, w io.Writer) error reader func(io.Reader) (Reader, error) }{ - {"car", Writer.ToCar, FromCar}, - {"carBase64", Writer.ToCarBase64, FromCarBase64}, - {"cbor", Writer.ToCbor, FromCbor}, - {"cborBase64", Writer.ToCborBase64, FromCborBase64}, + {"car", Writer.ToCarWriter, FromCarReader}, + {"carBase64", Writer.ToCarBase64Writer, FromCarBase64Reader}, + {"cbor", Writer.ToCborWriter, FromCborReader}, + {"cborBase64", Writer.ToCborBase64Writer, FromCborBase64Reader}, } { writer := NewWriter() @@ -185,18 +185,17 @@ func FuzzContainerRead(f *testing.F) { _, c, data := randToken() writer.AddSealed(c, data) } - buf := bytes.NewBuffer(nil) - err := writer.ToCbor(buf) + data, err := writer.ToCbor() require.NoError(f, err) - f.Add(buf.Bytes()) + f.Add(data) } f.Fuzz(func(t *testing.T, data []byte) { start := time.Now() // search for panics - _, _ = FromCbor(bytes.NewReader(data)) + _, _ = FromCbor(data) if time.Since(start) > 100*time.Millisecond { panic("too long") diff --git a/pkg/container/writer.go b/pkg/container/writer.go index e8d8c32..7f70f5f 100644 --- a/pkg/container/writer.go +++ b/pkg/container/writer.go @@ -1,6 +1,7 @@ package container import ( + "bytes" "encoding/base64" "io" @@ -12,10 +13,6 @@ import ( "github.com/ipld/go-ipld-prime/node/basicnode" ) -// 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 @@ -28,27 +25,24 @@ func (ctn Writer) AddSealed(cid cid.Cid, data []byte) { ctn[cid] = data } -func (ctn Writer) ToCar(w io.Writer) error { - return writeCar(w, nil, func(yield func(carBlock, error) bool) { - for c, bytes := range ctn { - if !yield(carBlock{c: c, data: bytes}, nil) { - return - } - } - }) +const currentContainerVersion = "ctn-v1" + +// ToCbor encode the container into a DAG-CBOR binary format. +func (ctn Writer) ToCbor() ([]byte, error) { + var buf bytes.Buffer + err := ctn.ToCborWriter(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil } -func (ctn Writer) ToCarBase64(w io.Writer) error { - w2 := base64.NewEncoder(base64.StdEncoding, w) - defer w2.Close() - return ctn.ToCar(w2) -} - -func (ctn Writer) ToCbor(w io.Writer) error { +// ToCborWriter is the same as ToCbor, but with an io.Writer. +func (ctn Writer) ToCborWriter(w io.Writer) error { 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)) + for _, data := range ctn { + qp.ListEntry(la, qp.Bytes(data)) } })) }) @@ -58,8 +52,57 @@ func (ctn Writer) ToCbor(w io.Writer) error { return ipld.EncodeStreaming(w, node, dagcbor.Encode) } -func (ctn Writer) ToCborBase64(w io.Writer) error { +// ToCborBase64 encode the container into a base64 encoded DAG-CBOR binary format. +func (ctn Writer) ToCborBase64() ([]byte, error) { + var buf bytes.Buffer + err := ctn.ToCborBase64Writer(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// ToCborBase64Writer is the same as ToCborBase64, but with an io.Writer. +func (ctn Writer) ToCborBase64Writer(w io.Writer) error { w2 := base64.NewEncoder(base64.StdEncoding, w) defer w2.Close() - return ctn.ToCbor(w2) + return ctn.ToCborWriter(w2) +} + +// ToCar encode the container into a CAR file. +func (ctn Writer) ToCar() ([]byte, error) { + var buf bytes.Buffer + err := ctn.ToCarWriter(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// ToCarWriter is the same as ToCar, but with an io.Writer. +func (ctn Writer) ToCarWriter(w io.Writer) error { + return writeCar(w, nil, func(yield func(carBlock, error) bool) { + for c, data := range ctn { + if !yield(carBlock{c: c, data: data}, nil) { + return + } + } + }) +} + +// ToCarBase64 encode the container into a base64 encoded CAR file. +func (ctn Writer) ToCarBase64() ([]byte, error) { + var buf bytes.Buffer + err := ctn.ToCarBase64Writer(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// ToCarBase64Writer is the same as ToCarBase64, but with an io.Writer. +func (ctn Writer) ToCarBase64Writer(w io.Writer) error { + w2 := base64.NewEncoder(base64.StdEncoding, w) + defer w2.Close() + return ctn.ToCarWriter(w2) }