From 043c9b160d37b3e9ed94135b28b7bf9bb5ff8e85 Mon Sep 17 00:00:00 2001 From: Steve Moyer Date: Thu, 19 Sep 2024 13:29:33 -0400 Subject: [PATCH 1/8] feat(cid): calculate the CID for decoded and newly created Tokeners --- delegation/delegation.go | 22 ++++++++++ delegation/delegation_test.go | 7 +++ delegation/ipld.go | 42 ++++++++++++------ delegation/schema_test.go | 39 +++-------------- internal/envelope/cid.go | 49 +++++++++++++++++++++ internal/envelope/cid_test.go | 25 +++++++++++ internal/envelope/example_test.go | 1 + internal/envelope/ipld.go | 71 ++++++++++++++++++++++--------- internal/envelope/ipld_test.go | 44 ++++++++++++++++--- 9 files changed, 230 insertions(+), 70 deletions(-) create mode 100644 internal/envelope/cid.go create mode 100644 internal/envelope/cid_test.go diff --git a/delegation/delegation.go b/delegation/delegation.go index b89560d..e0bbefa 100644 --- a/delegation/delegation.go +++ b/delegation/delegation.go @@ -6,11 +6,13 @@ import ( "fmt" "time" + "github.com/ipfs/go-cid" "github.com/libp2p/go-libp2p/core/crypto" "github.com/ucan-wg/go-ucan/capability/command" "github.com/ucan-wg/go-ucan/capability/policy" "github.com/ucan-wg/go-ucan/did" + "github.com/ucan-wg/go-ucan/internal/envelope" "github.com/ucan-wg/go-ucan/pkg/meta" ) @@ -33,6 +35,8 @@ type Token struct { notBefore *time.Time // The timestamp at which the Invocation becomes invalid expiration *time.Time + // The CID of the Token when enclosed in an Envelope and encoded to DAG-CBOR + cid cid.Cid } // New creates a validated Token from the provided parameters and options. @@ -69,6 +73,18 @@ func New(privKey crypto.PrivKey, aud did.DID, cmd command.Command, pol policy.Po return nil, err } + cbor, err := tkn.ToDagCbor(privKey) + if err != nil { + return nil, err + } + + id, err := envelope.CIDFromBytes(cbor) + if err != nil { + return nil, err + } + + tkn.cid = id + return tkn, nil } @@ -132,6 +148,12 @@ func (t *Token) Expiration() *time.Time { return t.expiration } +// CID returns the content identifier of the Token model when enclosed +// in an Envelope and encoded to DAG-CBOR. +func (t *Token) CID() cid.Cid { + return t.cid +} + func (t *Token) validate() error { var errs error diff --git a/delegation/delegation_test.go b/delegation/delegation_test.go index 27e8954..9a2a6ad 100644 --- a/delegation/delegation_test.go +++ b/delegation/delegation_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/libp2p/go-libp2p/core/crypto" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gotest.tools/v3/golden" @@ -13,6 +14,7 @@ import ( "github.com/ucan-wg/go-ucan/capability/policy" "github.com/ucan-wg/go-ucan/delegation" "github.com/ucan-wg/go-ucan/did" + "github.com/ucan-wg/go-ucan/internal/envelope" ) const ( @@ -64,6 +66,9 @@ const ( ] ] ` + + newCID = "zdpuAn9JgGPvnt2WCmTaKktZdbuvcVGTg9bUT5kQaufwUtZ6e" + rootCID = "zdpuAkgGmUp5JrXvehGuuw9JA8DLQKDaxtK3R8brDQQVC2i5X" ) func TestConstructors(t *testing.T) { @@ -101,6 +106,7 @@ func TestConstructors(t *testing.T) { t.Log(string(data)) golden.Assert(t, string(data), "new.dagjson") + assert.Equal(t, newCID, envelope.CIDToBase58BTC(dlg.CID())) }) t.Run("Root", func(t *testing.T) { @@ -120,6 +126,7 @@ func TestConstructors(t *testing.T) { t.Log(string(data)) golden.Assert(t, string(data), "root.dagjson") + assert.Equal(t, rootCID, envelope.CIDToBase58BTC(dlg.CID())) }) } diff --git a/delegation/ipld.go b/delegation/ipld.go index a900238..c2620bd 100644 --- a/delegation/ipld.go +++ b/delegation/ipld.go @@ -3,6 +3,7 @@ package delegation import ( "io" + "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/codec" "github.com/ipld/go-ipld-prime/codec/dagcbor" @@ -100,19 +101,19 @@ func (t *Token) ToIPLD(privKey crypto.PrivKey) (datamodel.Node, error) { // // An error is returned if the conversion fails, or if the resulting // View is invalid. -func Decode(b []byte, decFn codec.Decoder) (*Token, error) { +func Decode(b []byte, decFn codec.Decoder) (*Token, cid.Cid, error) { node, err := ipld.Decode(b, decFn) if err != nil { - return nil, err + return nil, cid.Undef, err } return FromIPLD(node) } // DecodeReader is the same as Decode, but accept an io.Reader. -func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error) { +func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, cid.Cid, error) { node, err := ipld.DecodeStreaming(r, decFn) if err != nil { - return nil, err + return nil, cid.Undef, err } return FromIPLD(node) } @@ -121,12 +122,22 @@ func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error) { // // An error is returned if the conversion fails, or if the resulting // View is invalid. -func FromDagCbor(data []byte) (*Token, error) { - return Decode(data, dagcbor.Decode) +func FromDagCbor(data []byte) (*Token, cid.Cid, error) { + pay, id, err := envelope.FromDagCbor[*tokenPayloadModel](data) + if err != nil { + return nil, cid.Undef, err + } + + tkn, err := tokenFromModel(*pay) + if err != nil { + return nil, cid.Undef, err + } + + return tkn, id, err } // FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. -func FromDagCborReader(r io.Reader) (*Token, error) { +func FromDagCborReader(r io.Reader) (*Token, cid.Cid, error) { return DecodeReader(r, dagcbor.Decode) } @@ -134,12 +145,12 @@ func FromDagCborReader(r io.Reader) (*Token, error) { // // An error is returned if the conversion fails, or if the resulting // View is invalid. -func FromDagJson(data []byte) (*Token, error) { +func FromDagJson(data []byte) (*Token, cid.Cid, error) { return Decode(data, dagjson.Decode) } // FromDagJsonReader is the same as FromDagJson, but accept an io.Reader. -func FromDagJsonReader(r io.Reader) (*Token, error) { +func FromDagJsonReader(r io.Reader) (*Token, cid.Cid, error) { return DecodeReader(r, dagjson.Decode) } @@ -147,11 +158,16 @@ func FromDagJsonReader(r io.Reader) (*Token, error) { // // An error is returned if the conversion fails, or if the resulting // View is invalid. -func FromIPLD(node datamodel.Node) (*Token, error) { - tkn, _, err := envelope.FromIPLD[*tokenPayloadModel](node) // TODO add CID to view +func FromIPLD(node datamodel.Node) (*Token, cid.Cid, error) { + pay, id, err := envelope.FromIPLD[*tokenPayloadModel](node) // TODO add CID to view if err != nil { - return nil, err + return nil, cid.Undef, err } - return tokenFromModel(*tkn) + tkn, err := tokenFromModel(*pay) + if err != nil { + return nil, cid.Undef, err + } + + return tkn, id, err } diff --git a/delegation/schema_test.go b/delegation/schema_test.go index 0f534df..f63a968 100644 --- a/delegation/schema_test.go +++ b/delegation/schema_test.go @@ -10,43 +10,14 @@ import ( "gotest.tools/v3/golden" "github.com/ucan-wg/go-ucan/delegation" + "github.com/ucan-wg/go-ucan/internal/envelope" ) //go:embed delegation.ipldsch var schemaBytes []byte func TestSchemaRoundTrip(t *testing.T) { - // const delegationJson = ` - // { - // "aud":"did:key:def456", - // "cmd":"/foo/bar", - // "exp":123456, - // "iss":"did:key:abc123", - // "meta":{ - // "bar":"baaar", - // "foo":"fooo" - // }, - // "nbf":123456, - // "nonce":{ - // "/":{ - // "bytes":"c3VwZXItcmFuZG9t" - // } - // }, - // "pol":[ - // ["==", ".status", "draft"], - // ["all", ".reviewer", [ - // ["like", ".email", "*@example.com"]] - // ], - // ["any", ".tags", [ - // ["or", [ - // ["==", ".", "news"], - // ["==", ".", "press"]] - // ]] - // ] - // ], - // "sub":"" - // } - // ` + t.Parallel() delegationJson := golden.Get(t, "new.dagjson") privKey := privKey(t, issuerPrivKeyCfg) @@ -54,20 +25,22 @@ func TestSchemaRoundTrip(t *testing.T) { // format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson // function: DecodeDagJson() EncodeDagCbor() DecodeDagCbor() EncodeDagJson() - p1, err := delegation.FromDagJson([]byte(delegationJson)) + p1, id1, err := delegation.FromDagJson([]byte(delegationJson)) require.NoError(t, err) + require.Equal(t, newCID, envelope.CIDToBase58BTC(id1)) cborBytes, err := p1.ToDagCbor(privKey) require.NoError(t, err) fmt.Println("cborBytes length", len(cborBytes)) fmt.Println("cbor", string(cborBytes)) - p2, err := delegation.FromDagCbor(cborBytes) + p2, id2, err := delegation.FromDagCbor(cborBytes) require.NoError(t, err) fmt.Println("read Cbor", p2) readJson, err := p2.ToDagJson(privKey) require.NoError(t, err) + require.Equal(t, newCID, envelope.CIDToBase58BTC(id2)) fmt.Println("readJson length", len(readJson)) fmt.Println("json: ", string(readJson)) diff --git a/internal/envelope/cid.go b/internal/envelope/cid.go new file mode 100644 index 0000000..169d7c7 --- /dev/null +++ b/internal/envelope/cid.go @@ -0,0 +1,49 @@ +package envelope + +import ( + "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/multiformats/go-multibase" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" +) + +var b58BTCEnc = multibase.MustNewEncoder(multibase.Base58BTC) + +// CIDToBase56BTC is a utility method to convert a CIDv1 to the canonical +// string representation used by UCAN. +func CIDToBase58BTC(id cid.Cid) string { + return id.Encode(b58BTCEnc) +} + +// CID returns the UCAN content identifier a Tokener. +func CID(privKey crypto.PrivKey, token Tokener) (cid.Cid, error) { + data, err := ToDagCbor(privKey, token) + if err != nil { + return cid.Undef, err + } + + return CIDFromBytes(data) +} + +// CIDFromBytes returns the UCAN content identifier for an arbitrary slice +// of bytes. +func CIDFromBytes(b []byte) (cid.Cid, error) { + return cid.V1Builder{ + Codec: uint64(multicodec.DagCbor), + MhType: multihash.SHA2_256, + MhLength: 0, + }.Sum(b) +} + +// CIDFromIPLD returns the UCAN content identifier for an ipld.Node. +func CIDFromIPLD(node ipld.Node) (cid.Cid, error) { + data, err := ipld.Encode(node, dagcbor.Encode) + if err != nil { + return cid.Undef, nil + } + + return CIDFromBytes(data) +} diff --git a/internal/envelope/cid_test.go b/internal/envelope/cid_test.go new file mode 100644 index 0000000..3ff9d13 --- /dev/null +++ b/internal/envelope/cid_test.go @@ -0,0 +1,25 @@ +package envelope_test + +import ( + "testing" + + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/ucan-wg/go-ucan/internal/envelope" + "gotest.tools/v3/golden" +) + +func TestCid(t *testing.T) { + t.Parallel() + + expData := golden.Get(t, "example.dagcbor") + expHash, err := multihash.Sum(expData, uint64(multicodec.Sha2_256), -1) + require.NoError(t, err) + + id, err := envelope.CID(examplePrivKey(t), newExample(t)) + require.NoError(t, err) + assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) + assert.Equal(t, expHash, id.Hash()) +} diff --git a/internal/envelope/example_test.go b/internal/envelope/example_test.go index d5a7ba8..f8fa05a 100644 --- a/internal/envelope/example_test.go +++ b/internal/envelope/example_test.go @@ -21,6 +21,7 @@ import ( ) const ( + exampleCID = "zdpuAyw6R5HvKSPzztuzXNYFx3ZGoMHMuAsXL6u3xLGQriRXQ" exampleDID = "did:key:z6MkpuK2Amsu1RqcLGgmHHQHhvmeXCCBVsM4XFSg2cCyg4Nh" exampleGreeting = "world" examplePrivKeyCfg = "CAESQP9v2uqECTuIi45dyg3znQvsryvf2IXmOF/6aws6aCehm0FVrj0zHR5RZSDxWNjcpcJqsGym3sjCungX9Zt5oA4=" diff --git a/internal/envelope/ipld.go b/internal/envelope/ipld.go index d12b1be..6aa3a2d 100644 --- a/internal/envelope/ipld.go +++ b/internal/envelope/ipld.go @@ -90,7 +90,24 @@ func DecodeReader[T Tokener](r io.Reader, decFn codec.Decoder) (T, cid.Cid, erro // An error is returned if the conversion fails, or if the resulting // Tokener is invalid. func FromDagCbor[T Tokener](b []byte) (T, cid.Cid, error) { - return Decode[T](b, dagcbor.Decode) + undef := *new(T) + + node, err := ipld.Decode(b, dagcbor.Decode) + if err != nil { + return undef, cid.Undef, err + } + + id, err := CIDFromBytes(b) + if err != nil { + return undef, cid.Undef, err + } + + tkn, err := fromIPLD[T](node) + if err != nil { + return undef, cid.Undef, err + } + + return tkn, id, nil } // FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. @@ -118,29 +135,45 @@ func FromDagJsonReader[T Tokener](r io.Reader) (T, cid.Cid, error) { func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) { undef := *new(T) - signatureNode, err := node.LookupByIndex(0) + id, err := CIDFromIPLD(node) if err != nil { return undef, cid.Undef, err } + tkn, err := fromIPLD[T](node) + if err != nil { + return undef, cid.Undef, err + } + + return tkn, id, nil +} + +func fromIPLD[T Tokener](node datamodel.Node) (T, error) { + undef := *new(T) + + signatureNode, err := node.LookupByIndex(0) + if err != nil { + return undef, err + } + signature, err := signatureNode.AsBytes() if err != nil { - return undef, cid.Undef, err + return undef, err } sigPayloadNode, err := node.LookupByIndex(1) if err != nil { - return undef, cid.Undef, err + return undef, err } varsigHeaderNode, err := sigPayloadNode.LookupByString(varsigHeaderKey) if err != nil { - return undef, cid.Undef, err + return undef, err } tokenPayloadNode, err := sigPayloadNode.LookupByString(undef.Tag()) if err != nil { - return undef, cid.Undef, err + return undef, err } // This needs to be done before converting this node to it's schema @@ -148,7 +181,7 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) { // to use the wire name). issuerNode, err := tokenPayloadNode.LookupByString("iss") if err != nil { - return undef, cid.Undef, err + return undef, err } // ^^^ @@ -160,7 +193,7 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) { err = nb.AssignNode(tokenPayloadNode) if err != nil { - return undef, cid.Undef, err + return undef, err } tokenPayloadNode = nb.Build() @@ -168,12 +201,12 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) { tokenPayload := bindnode.Unwrap(tokenPayloadNode) if tokenPayload == nil { - return undef, cid.Undef, errors.New("failed to Unwrap the TokenPayload") + return undef, errors.New("failed to Unwrap the TokenPayload") } tkn, ok := tokenPayload.(T) if !ok { - return undef, cid.Undef, errors.New("failed to assert the TokenPayload type as *token.Token") + return undef, errors.New("failed to assert the TokenPayload type as *token.Token") } // Check that the issuer's DID contains a public key with a type that @@ -181,45 +214,45 @@ func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) { // vvv issuer, err := issuerNode.AsString() if err != nil { - return undef, cid.Undef, err + return undef, err } issuerDID, err := did.Parse(issuer) if err != nil { - return undef, cid.Undef, err + return undef, err } issuerPubKey, err := issuerDID.PubKey() if err != nil { - return undef, cid.Undef, err + return undef, err } issuerVarsigHeader, err := varsig.Encode(issuerPubKey.Type()) if err != nil { - return undef, cid.Undef, err + return undef, err } varsigHeader, err := varsigHeaderNode.AsBytes() if err != nil { - return undef, cid.Undef, err + return undef, err } if string(varsigHeader) != string(issuerVarsigHeader) { - return undef, cid.Undef, errors.New("the VarsigHeader key type doesn't match the issuer's key type") + return undef, errors.New("the VarsigHeader key type doesn't match the issuer's key type") } data, err := ipld.Encode(sigPayloadNode, dagcbor.Encode) if err != nil { - return undef, cid.Undef, err + return undef, err } ok, err = issuerPubKey.Verify(data, signature) if err != nil || !ok { - return undef, cid.Undef, errors.New("failed to verify the token's signature") + return undef, errors.New("failed to verify the token's signature") } // ^^^ - return tkn, cid.Undef, nil + return tkn, nil } // Encode marshals a Tokener to the format specified by the provided diff --git a/internal/envelope/ipld_test.go b/internal/envelope/ipld_test.go index 132106d..24001a8 100644 --- a/internal/envelope/ipld_test.go +++ b/internal/envelope/ipld_test.go @@ -59,35 +59,69 @@ func TestEncode(t *testing.T) { func TestRoundtrip(t *testing.T) { t.Parallel() + t.Run("via FromDagCbor/ToDagCbor", func(t *testing.T) { + t.Parallel() + + dataIn := golden.Get(t, exampleDAGCBORFilename) + + tkn, id, err := envelope.FromDagCbor[*Example](dataIn) + require.NoError(t, err) + assert.Equal(t, exampleGreeting, tkn.Hello) + assert.Equal(t, exampleDID, tkn.Issuer) + assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) + + dataOut, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t)) + require.NoError(t, err) + assert.Equal(t, dataIn, dataOut) + }) + t.Run("via FromDagCborReader/ToDagCborWriter", func(t *testing.T) { t.Parallel() data := golden.Get(t, exampleDAGCBORFilename) - tkn, _, err := envelope.FromDagCborReader[*Example](bytes.NewReader(data)) + tkn, id, err := envelope.FromDagCborReader[*Example](bytes.NewReader(data)) require.NoError(t, err) assert.Equal(t, exampleGreeting, tkn.Hello) assert.Equal(t, exampleDID, tkn.Issuer) + assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) w := &bytes.Buffer{} require.NoError(t, envelope.ToDagCborWriter(w, examplePrivKey(t), newExample(t))) assert.Equal(t, data, w.Bytes()) }) - t.Run("via FromDagCbor/ToDagCbor", func(t *testing.T) { + t.Run("via FromDagJson/ToDagJson", func(t *testing.T) { t.Parallel() - dataIn := golden.Get(t, exampleDAGCBORFilename) + dataIn := golden.Get(t, exampleDAGJSONFilename) - tkn, _, err := envelope.FromDagCbor[*Example](dataIn) + tkn, id, err := envelope.FromDagJson[*Example](dataIn) require.NoError(t, err) assert.Equal(t, exampleGreeting, tkn.Hello) assert.Equal(t, exampleDID, tkn.Issuer) + assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) - dataOut, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t)) + dataOut, err := envelope.ToDagJson(examplePrivKey(t), newExample(t)) require.NoError(t, err) assert.Equal(t, dataIn, dataOut) }) + + t.Run("via FromDagJsonReader/ToDagJsonrWriter", func(t *testing.T) { + t.Parallel() + + data := golden.Get(t, exampleDAGJSONFilename) + + tkn, id, err := envelope.FromDagJsonReader[*Example](bytes.NewReader(data)) + require.NoError(t, err) + assert.Equal(t, exampleGreeting, tkn.Hello) + assert.Equal(t, exampleDID, tkn.Issuer) + assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) + + w := &bytes.Buffer{} + require.NoError(t, envelope.ToDagJsonWriter(w, examplePrivKey(t), newExample(t))) + assert.Equal(t, data, w.Bytes()) + }) } func TestFromIPLD_with_invalid_signature(t *testing.T) { From b14671009cd8376b3e378440ae3a60a313b27d97 Mon Sep 17 00:00:00 2001 From: Steve Moyer Date: Tue, 24 Sep 2024 06:46:48 -0400 Subject: [PATCH 2/8] refactor(delegate): calculate CID in methods that explicitly state that they include that function --- delegation/ipld.go | 223 +++++++++++++++++++++------------ delegation/schema_test.go | 71 ++++++++--- go.mod | 2 +- internal/envelope/cid.go | 95 ++++++++++++-- internal/envelope/cid_test.go | 57 +++++++++ internal/envelope/ipld.go | 42 +++---- internal/envelope/ipld_test.go | 39 ++++-- 7 files changed, 384 insertions(+), 145 deletions(-) diff --git a/delegation/ipld.go b/delegation/ipld.go index c2620bd..8f4bdf6 100644 --- a/delegation/ipld.go +++ b/delegation/ipld.go @@ -15,10 +15,77 @@ import ( "github.com/ucan-wg/go-ucan/internal/envelope" ) +// Seal wraps the delegation token in an envelope, generates the signature, +// encodes the result to DAG-CBOR and calculates the CID of the resulting +// binary data. +func (t *Token) Seal(privKey crypto.PrivKey) ([]byte, cid.Cid, error) { + data, err := t.ToDagCbor(privKey) + if err != nil { + return nil, cid.Undef, err + } + + id, err := envelope.CIDFromBytes(data) + if err != nil { + return nil, cid.Undef, err + } + + return data, id, nil +} + +// SealWriter is the same as Seal but accepts an io.Writer. +func (t *Token) SealWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error) { + cidWriter := envelope.NewCIDWriter(w) + + if err := t.ToDagCborWriter(cidWriter, privKey); err != nil { + return cid.Undef, err + } + + return cidWriter.CID() +} + +// Unseal decodes the provided binary data from the DAG-CBOR format, +// verifies that the envelope's signature is correct based on the public +// key taken from the issuer (iss) field and calculates the CID of the +// incoming data. +func Unseal(data []byte) (*Token, error) { + tkn, err := FromDagCbor(data) + if err != nil { + return nil, err + } + + id, err := envelope.CIDFromBytes(data) + if err != nil { + return nil, err + } + + tkn.cid = id + + return tkn, nil +} + +// UnsealReader is the same as Unseal but accepts an io.Reader. +func UnsealReader(r io.Reader) (*Token, error) { + cidReader := envelope.NewCIDReader(r) + + tkn, err := FromDagCborReader(cidReader) + if err != nil { + return nil, err + } + + id, err := cidReader.CID() + if err != nil { + return nil, err + } + + tkn.cid = id + + return tkn, nil +} + // Encode marshals a View to the format specified by the provided // codec.Encoder. func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error) { - node, err := t.ToIPLD(privKey) + node, err := t.toIPLD(privKey) if err != nil { return nil, err } @@ -28,7 +95,7 @@ func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, err // EncodeWriter is the same as Encode but accepts an io.Writer. func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Encoder) error { - node, err := t.ToIPLD(privKey) + node, err := t.toIPLD(privKey) if err != nil { return err } @@ -56,9 +123,81 @@ func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error { return t.EncodeWriter(w, privKey, dagjson.Encode) } -// ToIPLD wraps the View in an IPLD datamodel.Node. -func (t *Token) ToIPLD(privKey crypto.PrivKey) (datamodel.Node, error) { +// Decode unmarshals the input data using the format specified by the +// provided codec.Decoder into a View. +// +// An error is returned if the conversion fails, or if the resulting +// View is invalid. +func Decode(b []byte, decFn codec.Decoder) (*Token, error) { + node, err := ipld.Decode(b, decFn) + if err != nil { + return nil, err + } + return fromIPLD(node) +} + +// DecodeReader is the same as Decode, but accept an io.Reader. +func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error) { + node, err := ipld.DecodeStreaming(r, decFn) + if err != nil { + return nil, err + } + return fromIPLD(node) +} + +// FromDagCbor unmarshals the input data into a View. +// +// An error is returned if the conversion fails, or if the resulting +// View is invalid. +func FromDagCbor(data []byte) (*Token, error) { + pay, err := envelope.FromDagCbor[*tokenPayloadModel](data) + if err != nil { + return nil, err + } + + tkn, err := tokenFromModel(*pay) + if err != nil { + return nil, err + } + + return tkn, err +} + +// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. +func FromDagCborReader(r io.Reader) (*Token, error) { + return DecodeReader(r, dagcbor.Decode) +} + +// FromDagJson unmarshals the input data into a View. +// +// An error is returned if the conversion fails, or if the resulting +// View is invalid. +func FromDagJson(data []byte) (*Token, error) { + return Decode(data, dagjson.Decode) +} + +// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader. +func FromDagJsonReader(r io.Reader) (*Token, error) { + return DecodeReader(r, dagjson.Decode) +} + +func fromIPLD(node datamodel.Node) (*Token, error) { + pay, err := envelope.FromIPLD[*tokenPayloadModel](node) + if err != nil { + return nil, err + } + + tkn, err := tokenFromModel(*pay) + if err != nil { + return nil, err + } + + return tkn, err +} + +func (t *Token) toIPLD(privKey crypto.PrivKey) (datamodel.Node, error) { var sub *string + if t.subject != did.Undef { s := t.subject.String() sub = &s @@ -95,79 +234,3 @@ func (t *Token) ToIPLD(privKey crypto.PrivKey) (datamodel.Node, error) { return envelope.ToIPLD(privKey, model) } - -// Decode unmarshals the input data using the format specified by the -// provided codec.Decoder into a View. -// -// An error is returned if the conversion fails, or if the resulting -// View is invalid. -func Decode(b []byte, decFn codec.Decoder) (*Token, cid.Cid, error) { - node, err := ipld.Decode(b, decFn) - if err != nil { - return nil, cid.Undef, err - } - return FromIPLD(node) -} - -// DecodeReader is the same as Decode, but accept an io.Reader. -func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, cid.Cid, error) { - node, err := ipld.DecodeStreaming(r, decFn) - if err != nil { - return nil, cid.Undef, err - } - return FromIPLD(node) -} - -// FromDagCbor unmarshals the input data into a View. -// -// An error is returned if the conversion fails, or if the resulting -// View is invalid. -func FromDagCbor(data []byte) (*Token, cid.Cid, error) { - pay, id, err := envelope.FromDagCbor[*tokenPayloadModel](data) - if err != nil { - return nil, cid.Undef, err - } - - tkn, err := tokenFromModel(*pay) - if err != nil { - return nil, cid.Undef, err - } - - return tkn, id, err -} - -// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. -func FromDagCborReader(r io.Reader) (*Token, cid.Cid, error) { - return DecodeReader(r, dagcbor.Decode) -} - -// FromDagJson unmarshals the input data into a View. -// -// An error is returned if the conversion fails, or if the resulting -// View is invalid. -func FromDagJson(data []byte) (*Token, cid.Cid, error) { - return Decode(data, dagjson.Decode) -} - -// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader. -func FromDagJsonReader(r io.Reader) (*Token, cid.Cid, error) { - return DecodeReader(r, dagjson.Decode) -} - -// FromIPLD unwraps a View from the provided IPLD datamodel.Node -// -// An error is returned if the conversion fails, or if the resulting -// View is invalid. -func FromIPLD(node datamodel.Node) (*Token, cid.Cid, error) { - pay, id, err := envelope.FromIPLD[*tokenPayloadModel](node) // TODO add CID to view - if err != nil { - return nil, cid.Undef, err - } - - tkn, err := tokenFromModel(*pay) - if err != nil { - return nil, cid.Undef, err - } - - return tkn, id, err -} diff --git a/delegation/schema_test.go b/delegation/schema_test.go index f63a968..62274c5 100644 --- a/delegation/schema_test.go +++ b/delegation/schema_test.go @@ -1,11 +1,13 @@ package delegation_test import ( + "bytes" _ "embed" "fmt" "testing" "github.com/ipld/go-ipld-prime" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gotest.tools/v3/golden" @@ -22,29 +24,62 @@ func TestSchemaRoundTrip(t *testing.T) { delegationJson := golden.Get(t, "new.dagjson") privKey := privKey(t, issuerPrivKeyCfg) - // format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson - // function: DecodeDagJson() EncodeDagCbor() DecodeDagCbor() EncodeDagJson() + t.Run("via buffers", func(t *testing.T) { + t.Parallel() - p1, id1, err := delegation.FromDagJson([]byte(delegationJson)) - require.NoError(t, err) - require.Equal(t, newCID, envelope.CIDToBase58BTC(id1)) + // format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson + // function: DecodeDagJson() Seal() Unseal() EncodeDagJson() - cborBytes, err := p1.ToDagCbor(privKey) - require.NoError(t, err) - fmt.Println("cborBytes length", len(cborBytes)) - fmt.Println("cbor", string(cborBytes)) + p1, err := delegation.FromDagJson([]byte(delegationJson)) + require.NoError(t, err) - p2, id2, err := delegation.FromDagCbor(cborBytes) - require.NoError(t, err) - fmt.Println("read Cbor", p2) + cborBytes, id, err := p1.Seal(privKey) + require.NoError(t, err) + assert.Equal(t, newCID, envelope.CIDToBase58BTC(id)) + fmt.Println("cborBytes length", len(cborBytes)) + fmt.Println("cbor", string(cborBytes)) - readJson, err := p2.ToDagJson(privKey) - require.NoError(t, err) - require.Equal(t, newCID, envelope.CIDToBase58BTC(id2)) - fmt.Println("readJson length", len(readJson)) - fmt.Println("json: ", string(readJson)) + p2, err := delegation.Unseal(cborBytes) + require.NoError(t, err) + assert.Equal(t, id, p2.CID()) + fmt.Println("read Cbor", p2) - require.JSONEq(t, string(delegationJson), string(readJson)) + readJson, err := p2.ToDagJson(privKey) + require.NoError(t, err) + fmt.Println("readJson length", len(readJson)) + fmt.Println("json: ", string(readJson)) + + assert.JSONEq(t, string(delegationJson), string(readJson)) + }) + + t.Run("via streaming", func(t *testing.T) { + t.Parallel() + + buf := bytes.NewBuffer(delegationJson) + + // format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson + // function: DecodeDagJson() Seal() Unseal() EncodeDagJson() + + p1, err := delegation.FromDagJsonReader(buf) + require.NoError(t, err) + + cborBytes := &bytes.Buffer{} + id, err := p1.SealWriter(cborBytes, privKey) + t.Log(len(id.Bytes()), id.Bytes()) + require.NoError(t, err) + assert.Equal(t, newCID, envelope.CIDToBase58BTC(id)) + + // buf = bytes.NewBuffer(cborBytes.Bytes()) + p2, err := delegation.UnsealReader(cborBytes) + require.NoError(t, err) + t.Log(len(p2.CID().Bytes()), p2.CID().Bytes()) + assert.Equal(t, envelope.CIDToBase58BTC(id), envelope.CIDToBase58BTC(p2.CID())) + + readJson := &bytes.Buffer{} + require.NoError(t, p2.ToDagJsonWriter(readJson, privKey)) + + assert.JSONEq(t, string(delegationJson), readJson.String()) + }) } func BenchmarkSchemaLoad(b *testing.B) { diff --git a/go.mod b/go.mod index 30dee27..02f05fc 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/libp2p/go-libp2p v0.36.3 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multicodec v0.9.0 + github.com/multiformats/go-multihash v0.2.3 github.com/multiformats/go-varint v0.0.7 github.com/stretchr/testify v1.9.0 gotest.tools/v3 v3.5.1 @@ -24,7 +25,6 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multihash v0.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect diff --git a/internal/envelope/cid.go b/internal/envelope/cid.go index 169d7c7..2f8c7cf 100644 --- a/internal/envelope/cid.go +++ b/internal/envelope/cid.go @@ -1,9 +1,11 @@ package envelope import ( + "crypto/sha256" + "hash" + "io" + "github.com/ipfs/go-cid" - "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/codec/dagcbor" "github.com/libp2p/go-libp2p/core/crypto" "github.com/multiformats/go-multibase" "github.com/multiformats/go-multicodec" @@ -38,12 +40,89 @@ func CIDFromBytes(b []byte) (cid.Cid, error) { }.Sum(b) } -// CIDFromIPLD returns the UCAN content identifier for an ipld.Node. -func CIDFromIPLD(node ipld.Node) (cid.Cid, error) { - data, err := ipld.Encode(node, dagcbor.Encode) - if err != nil { - return cid.Undef, nil +var _ io.Reader = (*CIDReader)(nil) + +// CIDReader wraps an io.Reader and includes a hash.Hash that is updated +// as data is read from the child io.Reader. +type CIDReader struct { + hash hash.Hash + r io.Reader + err error +} + +// NewCIDReader initializes a hash.Hash to calculate the CID's hash and +// and returns a wrapped io.Reader. +func NewCIDReader(r io.Reader) *CIDReader { + hash := sha256.New() + hash.Reset() + + return &CIDReader{ + hash: hash, + r: r, + } +} + +// CID returns the UCAN-formatted cid.Cid created from the hash calculated +// as bytes are read from the inner io.Reader. +func (r *CIDReader) CID() (cid.Cid, error) { + if r.err != nil { + return cid.Undef, r.err // TODO: Wrap to say it's an error during streaming? } - return CIDFromBytes(data) + return cidFromHash(r.hash) +} + +// Read implements io.Reader. +func (r *CIDReader) Read(p []byte) (n int, err error) { + n, err = r.r.Read(p) + if err != nil && err != io.EOF { + r.err = err + + return + } + + _, _ = r.hash.Write(p[:n]) + + return +} + +var _ io.Writer = (*CIDWriter)(nil) + +type CIDWriter struct { + hash hash.Hash + w io.Writer + err error +} + +func NewCIDWriter(w io.Writer) *CIDWriter { + hash := sha256.New() + hash.Reset() + + return &CIDWriter{ + hash: hash, + w: w, + } +} + +func (w *CIDWriter) CID() (cid.Cid, error) { + return cidFromHash(w.hash) +} + +func (w *CIDWriter) Write(p []byte) (n int, err error) { + if _, err = w.hash.Write(p); err != nil { + w.err = err + + return + } + + return w.w.Write(p) +} + +func cidFromHash(hash hash.Hash) (cid.Cid, error) { + mh, err := multihash.Encode(hash.Sum(nil), multihash.SHA2_256) + if err != nil { + return cid.Undef, err + } + + return cid.NewCidV1(uint64(multicodec.DagCbor), mh), nil } diff --git a/internal/envelope/cid_test.go b/internal/envelope/cid_test.go index 3ff9d13..449477d 100644 --- a/internal/envelope/cid_test.go +++ b/internal/envelope/cid_test.go @@ -1,8 +1,10 @@ package envelope_test import ( + "io" "testing" + "github.com/ipfs/go-cid" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" "github.com/stretchr/testify/assert" @@ -23,3 +25,58 @@ func TestCid(t *testing.T) { assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) assert.Equal(t, expHash, id.Hash()) } + +func TestStreaming(t *testing.T) { + t.Parallel() + + expData := []byte("this is a test") + + expCID, err := cid.V1Builder{ + Codec: uint64(multicodec.DagCbor), + MhType: multihash.SHA2_256, + MhLength: 0, + }.Sum(expData) + require.NoError(t, err) + + t.Run("CIDReader()", func(t *testing.T) { + t.Parallel() + + r, w := io.Pipe() //nolint:varnamelen + cidReader := envelope.NewCIDReader(r) + + go func() { + _, err := w.Write(expData) + assert.NoError(t, err) + assert.NoError(t, w.Close()) + }() + + actData, err := io.ReadAll(cidReader) + require.NoError(t, err) + assert.Equal(t, expData, actData) + + actCID, err := cidReader.CID() + require.NoError(t, err) + assert.Equal(t, expCID, actCID) + }) + + t.Run("CIDWriter", func(t *testing.T) { + t.Parallel() + + r, w := io.Pipe() //nolint:varnamelen + cidWriter := envelope.NewCIDWriter(w) + + go func() { + _, err := cidWriter.Write(expData) + assert.NoError(t, err) + assert.NoError(t, w.Close()) + }() + + actData, err := io.ReadAll(r) + require.NoError(t, err) + assert.Equal(t, expData, actData) + + actCID, err := cidWriter.CID() + require.NoError(t, err) + assert.Equal(t, expCID, actCID) + }) +} diff --git a/internal/envelope/ipld.go b/internal/envelope/ipld.go index 6aa3a2d..8514174 100644 --- a/internal/envelope/ipld.go +++ b/internal/envelope/ipld.go @@ -8,8 +8,7 @@ // // Decoding functions in this package likewise perform the signature // verification using a public key extracted from the TokenPayload as -// described by requirement two below. Additionally, the decode functions -// also return the CID for the verified Envelope. +// described by requirement two below. // // Types that wish to be marshaled and unmarshaled from the using // is package have two requirements. @@ -30,7 +29,6 @@ import ( "errors" "io" - "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/codec" "github.com/ipld/go-ipld-prime/codec/dagcbor" @@ -66,20 +64,20 @@ type Tokener interface { // // An error is returned if the conversion fails, or if the resulting // Tokener is invalid. -func Decode[T Tokener](b []byte, decFn codec.Decoder) (T, cid.Cid, error) { +func Decode[T Tokener](b []byte, decFn codec.Decoder) (T, error) { node, err := ipld.Decode(b, decFn) if err != nil { - return *new(T), cid.Undef, err + return *new(T), err } return FromIPLD[T](node) } // DecodeReader is the same as Decode, but accept an io.Reader. -func DecodeReader[T Tokener](r io.Reader, decFn codec.Decoder) (T, cid.Cid, error) { +func DecodeReader[T Tokener](r io.Reader, decFn codec.Decoder) (T, error) { node, err := ipld.DecodeStreaming(r, decFn) if err != nil { - return *new(T), cid.Undef, err + return *new(T), err } return FromIPLD[T](node) @@ -89,29 +87,24 @@ func DecodeReader[T Tokener](r io.Reader, decFn codec.Decoder) (T, cid.Cid, erro // // An error is returned if the conversion fails, or if the resulting // Tokener is invalid. -func FromDagCbor[T Tokener](b []byte) (T, cid.Cid, error) { +func FromDagCbor[T Tokener](b []byte) (T, error) { undef := *new(T) node, err := ipld.Decode(b, dagcbor.Decode) if err != nil { - return undef, cid.Undef, err - } - - id, err := CIDFromBytes(b) - if err != nil { - return undef, cid.Undef, err + return undef, err } tkn, err := fromIPLD[T](node) if err != nil { - return undef, cid.Undef, err + return undef, err } - return tkn, id, nil + return tkn, nil } // FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. -func FromDagCborReader[T Tokener](r io.Reader) (T, cid.Cid, error) { +func FromDagCborReader[T Tokener](r io.Reader) (T, error) { return DecodeReader[T](r, dagcbor.Decode) } @@ -119,12 +112,12 @@ func FromDagCborReader[T Tokener](r io.Reader) (T, cid.Cid, error) { // // An error is returned if the conversion fails, or if the resulting // Tokener is invalid. -func FromDagJson[T Tokener](b []byte) (T, cid.Cid, error) { +func FromDagJson[T Tokener](b []byte) (T, error) { return Decode[T](b, dagjson.Decode) } // FromDagJsonReader is the same as FromDagJson, but accept an io.Reader. -func FromDagJsonReader[T Tokener](r io.Reader) (T, cid.Cid, error) { +func FromDagJsonReader[T Tokener](r io.Reader) (T, error) { return DecodeReader[T](r, dagjson.Decode) } @@ -132,20 +125,15 @@ func FromDagJsonReader[T Tokener](r io.Reader) (T, cid.Cid, error) { // // An error is returned if the conversion fails, or if the resulting // Tokener is invalid. -func FromIPLD[T Tokener](node datamodel.Node) (T, cid.Cid, error) { +func FromIPLD[T Tokener](node datamodel.Node) (T, error) { undef := *new(T) - id, err := CIDFromIPLD(node) - if err != nil { - return undef, cid.Undef, err - } - tkn, err := fromIPLD[T](node) if err != nil { - return undef, cid.Undef, err + return undef, err } - return tkn, id, nil + return tkn, nil } func fromIPLD[T Tokener](node datamodel.Node) (T, error) { diff --git a/internal/envelope/ipld_test.go b/internal/envelope/ipld_test.go index 24001a8..12d7d73 100644 --- a/internal/envelope/ipld_test.go +++ b/internal/envelope/ipld_test.go @@ -2,6 +2,7 @@ package envelope_test import ( "bytes" + "crypto/sha256" "testing" "github.com/stretchr/testify/assert" @@ -18,7 +19,7 @@ func TestDecode(t *testing.T) { data := golden.Get(t, "example.dagcbor") - tkn, _, err := envelope.FromDagCbor[*Example](data) + tkn, err := envelope.FromDagCbor[*Example](data) require.NoError(t, err) assert.Equal(t, exampleGreeting, tkn.Hello) assert.Equal(t, exampleDID, tkn.Issuer) @@ -29,7 +30,7 @@ func TestDecode(t *testing.T) { data := golden.Get(t, "example.dagjson") - tkn, _, err := envelope.FromDagJson[*Example](data) + tkn, err := envelope.FromDagJson[*Example](data) require.NoError(t, err) assert.Equal(t, exampleGreeting, tkn.Hello) assert.Equal(t, exampleDID, tkn.Issuer) @@ -64,11 +65,10 @@ func TestRoundtrip(t *testing.T) { dataIn := golden.Get(t, exampleDAGCBORFilename) - tkn, id, err := envelope.FromDagCbor[*Example](dataIn) + tkn, err := envelope.FromDagCbor[*Example](dataIn) require.NoError(t, err) assert.Equal(t, exampleGreeting, tkn.Hello) assert.Equal(t, exampleDID, tkn.Issuer) - assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) dataOut, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t)) require.NoError(t, err) @@ -80,11 +80,10 @@ func TestRoundtrip(t *testing.T) { data := golden.Get(t, exampleDAGCBORFilename) - tkn, id, err := envelope.FromDagCborReader[*Example](bytes.NewReader(data)) + tkn, err := envelope.FromDagCborReader[*Example](bytes.NewReader(data)) require.NoError(t, err) assert.Equal(t, exampleGreeting, tkn.Hello) assert.Equal(t, exampleDID, tkn.Issuer) - assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) w := &bytes.Buffer{} require.NoError(t, envelope.ToDagCborWriter(w, examplePrivKey(t), newExample(t))) @@ -96,11 +95,10 @@ func TestRoundtrip(t *testing.T) { dataIn := golden.Get(t, exampleDAGJSONFilename) - tkn, id, err := envelope.FromDagJson[*Example](dataIn) + tkn, err := envelope.FromDagJson[*Example](dataIn) require.NoError(t, err) assert.Equal(t, exampleGreeting, tkn.Hello) assert.Equal(t, exampleDID, tkn.Issuer) - assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) dataOut, err := envelope.ToDagJson(examplePrivKey(t), newExample(t)) require.NoError(t, err) @@ -112,11 +110,10 @@ func TestRoundtrip(t *testing.T) { data := golden.Get(t, exampleDAGJSONFilename) - tkn, id, err := envelope.FromDagJsonReader[*Example](bytes.NewReader(data)) + tkn, err := envelope.FromDagJsonReader[*Example](bytes.NewReader(data)) require.NoError(t, err) assert.Equal(t, exampleGreeting, tkn.Hello) assert.Equal(t, exampleDID, tkn.Issuer) - assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) w := &bytes.Buffer{} require.NoError(t, envelope.ToDagJsonWriter(w, examplePrivKey(t), newExample(t))) @@ -128,7 +125,27 @@ func TestFromIPLD_with_invalid_signature(t *testing.T) { t.Parallel() node := invalidNodeFromGolden(t) - tkn, _, err := envelope.FromIPLD[*Example](node) + tkn, err := envelope.FromIPLD[*Example](node) assert.Nil(t, tkn) require.EqualError(t, err, "failed to verify the token's signature") } + +func TestHash(t *testing.T) { + t.Parallel() + + msg := []byte("this is a test") + + hash1 := sha256.Sum256(msg) + + hasher := sha256.New() + + for _, b := range msg { + hasher.Write([]byte{b}) + } + + hash2 := hasher.Sum(nil) + hash3 := hasher.Sum(nil) + + require.Equal(t, hash1[:], hash2) + require.Equal(t, hash1[:], hash3) +} From 371bf3b9f58062d2d3c1a6f54bb55551137fb8c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 24 Sep 2024 13:03:35 +0200 Subject: [PATCH 3/8] delegation/envelope: small cleanups --- delegation/delegation.go | 16 +++------------- internal/envelope/cid.go | 13 +++++++------ internal/envelope/cid_test.go | 3 ++- internal/envelope/ipld.go | 8 ++------ 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/delegation/delegation.go b/delegation/delegation.go index e0bbefa..fad627a 100644 --- a/delegation/delegation.go +++ b/delegation/delegation.go @@ -12,7 +12,6 @@ import ( "github.com/ucan-wg/go-ucan/capability/command" "github.com/ucan-wg/go-ucan/capability/policy" "github.com/ucan-wg/go-ucan/did" - "github.com/ucan-wg/go-ucan/internal/envelope" "github.com/ucan-wg/go-ucan/pkg/meta" ) @@ -54,6 +53,7 @@ func New(privKey crypto.PrivKey, aud did.DID, cmd command.Command, pol policy.Po policy: pol, meta: meta.NewMeta(), nonce: nil, + cid: cid.Undef, } for _, opt := range opts { @@ -73,18 +73,6 @@ func New(privKey crypto.PrivKey, aud did.DID, cmd command.Command, pol policy.Po return nil, err } - cbor, err := tkn.ToDagCbor(privKey) - if err != nil { - return nil, err - } - - id, err := envelope.CIDFromBytes(cbor) - if err != nil { - return nil, err - } - - tkn.cid = id - return tkn, nil } @@ -150,6 +138,7 @@ func (t *Token) Expiration() *time.Time { // CID returns the content identifier of the Token model when enclosed // in an Envelope and encoded to DAG-CBOR. +// Returns cid.Undef if the token has not been serialized or deserialized yet. func (t *Token) CID() cid.Cid { return t.cid } @@ -299,6 +288,7 @@ func tokenFromModel(m tokenPayloadModel) (*Token, error) { } // generateNonce creates a 12-byte random nonce. +// TODO: some crypto scheme require more, is that our case? func generateNonce() ([]byte, error) { res := make([]byte, 12) _, err := rand.Read(res) diff --git a/internal/envelope/cid.go b/internal/envelope/cid.go index 2f8c7cf..f127459 100644 --- a/internal/envelope/cid.go +++ b/internal/envelope/cid.go @@ -21,6 +21,7 @@ func CIDToBase58BTC(id cid.Cid) string { } // CID returns the UCAN content identifier a Tokener. +// TODO: remove? func CID(privKey crypto.PrivKey, token Tokener) (cid.Cid, error) { data, err := ToDagCbor(privKey, token) if err != nil { @@ -53,11 +54,11 @@ type CIDReader struct { // NewCIDReader initializes a hash.Hash to calculate the CID's hash and // and returns a wrapped io.Reader. func NewCIDReader(r io.Reader) *CIDReader { - hash := sha256.New() - hash.Reset() + h := sha256.New() + h.Reset() return &CIDReader{ - hash: hash, + hash: h, r: r, } } @@ -95,11 +96,11 @@ type CIDWriter struct { } func NewCIDWriter(w io.Writer) *CIDWriter { - hash := sha256.New() - hash.Reset() + h := sha256.New() + h.Reset() return &CIDWriter{ - hash: hash, + hash: h, w: w, } } diff --git a/internal/envelope/cid_test.go b/internal/envelope/cid_test.go index 449477d..290ddd4 100644 --- a/internal/envelope/cid_test.go +++ b/internal/envelope/cid_test.go @@ -9,8 +9,9 @@ import ( "github.com/multiformats/go-multihash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/ucan-wg/go-ucan/internal/envelope" "gotest.tools/v3/golden" + + "github.com/ucan-wg/go-ucan/internal/envelope" ) func TestCid(t *testing.T) { diff --git a/internal/envelope/ipld.go b/internal/envelope/ipld.go index 8514174..553bb30 100644 --- a/internal/envelope/ipld.go +++ b/internal/envelope/ipld.go @@ -39,6 +39,7 @@ import ( "github.com/ipld/go-ipld-prime/node/bindnode" "github.com/ipld/go-ipld-prime/schema" "github.com/libp2p/go-libp2p/core/crypto" + "github.com/ucan-wg/go-ucan/did" "github.com/ucan-wg/go-ucan/internal/varsig" ) @@ -164,19 +165,17 @@ func fromIPLD[T Tokener](node datamodel.Node) (T, error) { return undef, err } - // This needs to be done before converting this node to it's schema + // This needs to be done before converting this node to its schema // representation (afterwards, the field might be renamed os it's safer // to use the wire name). issuerNode, err := tokenPayloadNode.LookupByString("iss") if err != nil { return undef, err } - // ^^^ // Replaces the datamodel.Node in tokenPayloadNode with a // schema.TypedNode so that we can cast it to a *token.Token after // unwrapping it. - // vvv nb := undef.Prototype().Representation().NewBuilder() err = nb.AssignNode(tokenPayloadNode) @@ -185,7 +184,6 @@ func fromIPLD[T Tokener](node datamodel.Node) (T, error) { } tokenPayloadNode = nb.Build() - // ^^^ tokenPayload := bindnode.Unwrap(tokenPayloadNode) if tokenPayload == nil { @@ -199,7 +197,6 @@ func fromIPLD[T Tokener](node datamodel.Node) (T, error) { // Check that the issuer's DID contains a public key with a type that // matches the VarsigHeader and then verify the SigPayload. - // vvv issuer, err := issuerNode.AsString() if err != nil { return undef, err @@ -238,7 +235,6 @@ func fromIPLD[T Tokener](node datamodel.Node) (T, error) { if err != nil || !ok { return undef, errors.New("failed to verify the token's signature") } - // ^^^ return tkn, nil } From f4ad97679ceae3cf7a42925d9666a73f2d0eb876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 24 Sep 2024 14:09:36 +0200 Subject: [PATCH 4/8] delegation: add bench for the round-trip steps --- delegation/delegation_test.go | 4 +- delegation/schema_test.go | 89 ++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/delegation/delegation_test.go b/delegation/delegation_test.go index 9a2a6ad..b0de668 100644 --- a/delegation/delegation_test.go +++ b/delegation/delegation_test.go @@ -130,9 +130,7 @@ func TestConstructors(t *testing.T) { }) } -func privKey(t *testing.T, privKeyCfg string) crypto.PrivKey { - t.Helper() - +func privKey(t require.TestingT, privKeyCfg string) crypto.PrivKey { privKeyMar, err := crypto.ConfigDecodeKey(privKeyCfg) require.NoError(t, err) diff --git a/delegation/schema_test.go b/delegation/schema_test.go index 62274c5..6fbd158 100644 --- a/delegation/schema_test.go +++ b/delegation/schema_test.go @@ -30,7 +30,7 @@ func TestSchemaRoundTrip(t *testing.T) { // format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson // function: DecodeDagJson() Seal() Unseal() EncodeDagJson() - p1, err := delegation.FromDagJson([]byte(delegationJson)) + p1, err := delegation.FromDagJson(delegationJson) require.NoError(t, err) cborBytes, id, err := p1.Seal(privKey) @@ -88,3 +88,90 @@ func BenchmarkSchemaLoad(b *testing.B) { _, _ = ipld.LoadSchemaBytes(schemaBytes) } } + +func BenchmarkRoundTrip(b *testing.B) { + delegationJson := golden.Get(b, "new.dagjson") + privKey := privKey(b, issuerPrivKeyCfg) + + b.Run("via buffers", func(b *testing.B) { + p1, _ := delegation.FromDagJson(delegationJson) + cborBytes, _, _ := p1.Seal(privKey) + p2, _ := delegation.Unseal(cborBytes) + + b.ResetTimer() + + b.Run("FromDagJson", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _ = delegation.FromDagJson(delegationJson) + } + }) + + b.Run("Seal", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _, _ = p1.Seal(privKey) + } + }) + + b.Run("Unseal", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _ = delegation.Unseal(cborBytes) + } + }) + + b.Run("ToDagJson", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _ = p2.ToDagJson(privKey) + } + }) + }) + + b.Run("via streaming", func(b *testing.B) { + p1, _ := delegation.FromDagJsonReader(bytes.NewReader(delegationJson)) + cborBuf := &bytes.Buffer{} + _, _ = p1.SealWriter(cborBuf, privKey) + cborBytes := cborBuf.Bytes() + p2, _ := delegation.UnsealReader(bytes.NewReader(cborBytes)) + + b.ResetTimer() + + b.Run("FromDagJsonReader", func(b *testing.B) { + b.ReportAllocs() + reader := bytes.NewReader(delegationJson) + for i := 0; i < b.N; i++ { + _, _ = reader.Seek(0, 0) + _, _ = delegation.FromDagJsonReader(reader) + } + }) + + b.Run("SealWriter", func(b *testing.B) { + b.ReportAllocs() + buf := &bytes.Buffer{} + for i := 0; i < b.N; i++ { + buf.Reset() + _, _ = p1.SealWriter(buf, privKey) + } + }) + + b.Run("UnsealReader", func(b *testing.B) { + b.ReportAllocs() + reader := bytes.NewReader(cborBytes) + for i := 0; i < b.N; i++ { + _, _ = reader.Seek(0, 0) + _, _ = delegation.UnsealReader(reader) + } + }) + + b.Run("ToDagJsonReader", func(b *testing.B) { + b.ReportAllocs() + buf := &bytes.Buffer{} + for i := 0; i < b.N; i++ { + buf.Reset() + _ = p2.ToDagJsonWriter(buf, privKey) + } + }) + }) +} From 5509cce51373324888833ff5fca2cebe7eaed937 Mon Sep 17 00:00:00 2001 From: Steve Moyer Date: Tue, 24 Sep 2024 08:20:42 -0400 Subject: [PATCH 5/8] docs: finish Go docs for CID and delegation.Token --- delegation/delegation.go | 13 +++++++++++++ delegation/delegation_test.go | 33 ++++----------------------------- internal/envelope/cid.go | 15 +++++++++++---- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/delegation/delegation.go b/delegation/delegation.go index fad627a..df45491 100644 --- a/delegation/delegation.go +++ b/delegation/delegation.go @@ -1,5 +1,14 @@ +// Package delegation implements the UCAN [delegation] specification with +// an immutable Token type as well as methods to convert the Token to and +// from the [envelope]-enclosed, signed and DAG-CBOR-encoded form that +// should most commonly be used for transport and storage. +// +// [delegation]: https://github.com/ucan-wg/delegation/tree/v1_ipld +// [envelope]: https://github.com/ucan-wg/spec#envelope package delegation +// TODO: change the "delegation" link above when the specification is merged + import ( "crypto/rand" "errors" @@ -15,6 +24,7 @@ import ( "github.com/ucan-wg/go-ucan/pkg/meta" ) +// Token is an immutable type that holds the fields of a UCAN delegation. type Token struct { // Issuer DID (sender) issuer did.DID @@ -162,6 +172,8 @@ func (t *Token) validate() error { return errs } +// Option is a type that allows optional fields to be set during the +// creation of a Token. type Option func(*Token) error // WithExpiration set's the Token's optional "expiration" field to the @@ -179,6 +191,7 @@ func WithExpiration(exp time.Time) Option { } // WithMeta adds a key/value pair in the "meta" field. +// // WithMeta can be used multiple times in the same call. // Accepted types for the value are: bool, string, int, int32, int64, []byte, // and ipld.Node. diff --git a/delegation/delegation_test.go b/delegation/delegation_test.go index 9a2a6ad..7d05f10 100644 --- a/delegation/delegation_test.go +++ b/delegation/delegation_test.go @@ -1,12 +1,10 @@ package delegation_test import ( - "crypto/rand" "testing" "time" "github.com/libp2p/go-libp2p/core/crypto" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gotest.tools/v3/golden" @@ -14,7 +12,6 @@ import ( "github.com/ucan-wg/go-ucan/capability/policy" "github.com/ucan-wg/go-ucan/delegation" "github.com/ucan-wg/go-ucan/did" - "github.com/ucan-wg/go-ucan/internal/envelope" ) const ( @@ -91,7 +88,7 @@ func TestConstructors(t *testing.T) { require.NoError(t, err) t.Run("New", func(t *testing.T) { - dlg, err := delegation.New(privKey, aud, cmd, pol, + tkn, err := delegation.New(privKey, aud, cmd, pol, delegation.WithNonce([]byte(nonce)), delegation.WithSubject(sub), delegation.WithExpiration(exp), @@ -100,19 +97,18 @@ func TestConstructors(t *testing.T) { ) require.NoError(t, err) - data, err := dlg.ToDagJson(privKey) + data, err := tkn.ToDagJson(privKey) require.NoError(t, err) t.Log(string(data)) golden.Assert(t, string(data), "new.dagjson") - assert.Equal(t, newCID, envelope.CIDToBase58BTC(dlg.CID())) }) t.Run("Root", func(t *testing.T) { t.Parallel() - dlg, err := delegation.Root(privKey, aud, cmd, pol, + tkn, err := delegation.Root(privKey, aud, cmd, pol, delegation.WithNonce([]byte(nonce)), delegation.WithExpiration(exp), delegation.WithMeta("foo", "fooo"), @@ -120,13 +116,12 @@ func TestConstructors(t *testing.T) { ) require.NoError(t, err) - data, err := dlg.ToDagJson(privKey) + data, err := tkn.ToDagJson(privKey) require.NoError(t, err) t.Log(string(data)) golden.Assert(t, string(data), "root.dagjson") - assert.Equal(t, rootCID, envelope.CIDToBase58BTC(dlg.CID())) }) } @@ -141,23 +136,3 @@ func privKey(t *testing.T, privKeyCfg string) crypto.PrivKey { return privKey } - -func TestKey(t *testing.T) { - // TODO: why is this broken? - t.Skip("TODO: why is this broken?") - - priv, _, err := crypto.GenerateEd25519Key(rand.Reader) - require.NoError(t, err) - - privMar, err := crypto.MarshalPrivateKey(priv) - require.NoError(t, err) - - privCfg := crypto.ConfigEncodeKey(privMar) - t.Log(privCfg) - - id, err := did.FromPubKey(priv.GetPublic()) - require.NoError(t, err) - t.Log(id) - - t.Fail() -} diff --git a/internal/envelope/cid.go b/internal/envelope/cid.go index f127459..74ac583 100644 --- a/internal/envelope/cid.go +++ b/internal/envelope/cid.go @@ -43,8 +43,8 @@ func CIDFromBytes(b []byte) (cid.Cid, error) { var _ io.Reader = (*CIDReader)(nil) -// CIDReader wraps an io.Reader and includes a hash.Hash that is updated -// as data is read from the child io.Reader. +// CIDReader wraps an io.Reader and includes a hash.Hash that is +// incrementally updated as data is read from the child io.Reader. type CIDReader struct { hash hash.Hash r io.Reader @@ -52,7 +52,7 @@ type CIDReader struct { } // NewCIDReader initializes a hash.Hash to calculate the CID's hash and -// and returns a wrapped io.Reader. +// returns the wrapped io.Reader. func NewCIDReader(r io.Reader) *CIDReader { h := sha256.New() h.Reset() @@ -64,7 +64,7 @@ func NewCIDReader(r io.Reader) *CIDReader { } // CID returns the UCAN-formatted cid.Cid created from the hash calculated -// as bytes are read from the inner io.Reader. +// as bytes were read from the inner io.Reader. func (r *CIDReader) CID() (cid.Cid, error) { if r.err != nil { return cid.Undef, r.err // TODO: Wrap to say it's an error during streaming? @@ -89,12 +89,16 @@ func (r *CIDReader) Read(p []byte) (n int, err error) { var _ io.Writer = (*CIDWriter)(nil) +// CIDWriter wraps an io.Writer and includes a hash.Hash that is +// incrementally updated as data is written to the child io.Writer. type CIDWriter struct { hash hash.Hash w io.Writer err error } +// NewCIDWriter initializes a hash.Hash to calculate the CID's hash and +// returns the wrapped io.Writer. func NewCIDWriter(w io.Writer) *CIDWriter { h := sha256.New() h.Reset() @@ -105,10 +109,13 @@ func NewCIDWriter(w io.Writer) *CIDWriter { } } +// CID returns the UCAN-formatted cid.Cid created from the hash calculated +// as bytes were written from the inner io.Reader. func (w *CIDWriter) CID() (cid.Cid, error) { return cidFromHash(w.hash) } +// Write implements io.Writer. func (w *CIDWriter) Write(p []byte) (n int, err error) { if _, err = w.hash.Write(p); err != nil { w.err = err From 0d9955b7b039ece4f1737a33d74c5d2bd2ed74e6 Mon Sep 17 00:00:00 2001 From: Steve Moyer Date: Tue, 24 Sep 2024 08:49:31 -0400 Subject: [PATCH 6/8] refactor(delegation): align Seal/Unseal names to rest of encode/decode names --- delegation/ipld.go | 20 ++++++++++---------- delegation/schema_test.go | 24 ++++++++++++------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/delegation/ipld.go b/delegation/ipld.go index 8f4bdf6..540f4c2 100644 --- a/delegation/ipld.go +++ b/delegation/ipld.go @@ -15,10 +15,10 @@ import ( "github.com/ucan-wg/go-ucan/internal/envelope" ) -// Seal wraps the delegation token in an envelope, generates the signature, -// encodes the result to DAG-CBOR and calculates the CID of the resulting -// binary data. -func (t *Token) Seal(privKey crypto.PrivKey) ([]byte, cid.Cid, error) { +// ToSealed wraps the delegation token in an envelope, generates the +// signature, encodes the result to DAG-CBOR and calculates the CID of +// the resulting binary data. +func (t *Token) ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error) { data, err := t.ToDagCbor(privKey) if err != nil { return nil, cid.Undef, err @@ -32,8 +32,8 @@ func (t *Token) Seal(privKey crypto.PrivKey) ([]byte, cid.Cid, error) { return data, id, nil } -// SealWriter is the same as Seal but accepts an io.Writer. -func (t *Token) SealWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error) { +// ToSealedWriter is the same as Seal but accepts an io.Writer. +func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error) { cidWriter := envelope.NewCIDWriter(w) if err := t.ToDagCborWriter(cidWriter, privKey); err != nil { @@ -43,11 +43,11 @@ func (t *Token) SealWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error) return cidWriter.CID() } -// Unseal decodes the provided binary data from the DAG-CBOR format, +// FromSealed decodes the provided binary data from the DAG-CBOR format, // verifies that the envelope's signature is correct based on the public // key taken from the issuer (iss) field and calculates the CID of the // incoming data. -func Unseal(data []byte) (*Token, error) { +func FromSealed(data []byte) (*Token, error) { tkn, err := FromDagCbor(data) if err != nil { return nil, err @@ -63,8 +63,8 @@ func Unseal(data []byte) (*Token, error) { return tkn, nil } -// UnsealReader is the same as Unseal but accepts an io.Reader. -func UnsealReader(r io.Reader) (*Token, error) { +// FromSealedReader is the same as Unseal but accepts an io.Reader. +func FromSealedReader(r io.Reader) (*Token, error) { cidReader := envelope.NewCIDReader(r) tkn, err := FromDagCborReader(cidReader) diff --git a/delegation/schema_test.go b/delegation/schema_test.go index 6fbd158..73a68d8 100644 --- a/delegation/schema_test.go +++ b/delegation/schema_test.go @@ -33,13 +33,13 @@ func TestSchemaRoundTrip(t *testing.T) { p1, err := delegation.FromDagJson(delegationJson) require.NoError(t, err) - cborBytes, id, err := p1.Seal(privKey) + cborBytes, id, err := p1.ToSealed(privKey) require.NoError(t, err) assert.Equal(t, newCID, envelope.CIDToBase58BTC(id)) fmt.Println("cborBytes length", len(cborBytes)) fmt.Println("cbor", string(cborBytes)) - p2, err := delegation.Unseal(cborBytes) + p2, err := delegation.FromSealed(cborBytes) require.NoError(t, err) assert.Equal(t, id, p2.CID()) fmt.Println("read Cbor", p2) @@ -64,13 +64,13 @@ func TestSchemaRoundTrip(t *testing.T) { require.NoError(t, err) cborBytes := &bytes.Buffer{} - id, err := p1.SealWriter(cborBytes, privKey) + id, err := p1.ToSealedWriter(cborBytes, privKey) t.Log(len(id.Bytes()), id.Bytes()) require.NoError(t, err) assert.Equal(t, newCID, envelope.CIDToBase58BTC(id)) // buf = bytes.NewBuffer(cborBytes.Bytes()) - p2, err := delegation.UnsealReader(cborBytes) + p2, err := delegation.FromSealedReader(cborBytes) require.NoError(t, err) t.Log(len(p2.CID().Bytes()), p2.CID().Bytes()) assert.Equal(t, envelope.CIDToBase58BTC(id), envelope.CIDToBase58BTC(p2.CID())) @@ -95,8 +95,8 @@ func BenchmarkRoundTrip(b *testing.B) { b.Run("via buffers", func(b *testing.B) { p1, _ := delegation.FromDagJson(delegationJson) - cborBytes, _, _ := p1.Seal(privKey) - p2, _ := delegation.Unseal(cborBytes) + cborBytes, _, _ := p1.ToSealed(privKey) + p2, _ := delegation.FromSealed(cborBytes) b.ResetTimer() @@ -110,14 +110,14 @@ func BenchmarkRoundTrip(b *testing.B) { b.Run("Seal", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - _, _, _ = p1.Seal(privKey) + _, _, _ = p1.ToSealed(privKey) } }) b.Run("Unseal", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - _, _ = delegation.Unseal(cborBytes) + _, _ = delegation.FromSealed(cborBytes) } }) @@ -132,9 +132,9 @@ func BenchmarkRoundTrip(b *testing.B) { b.Run("via streaming", func(b *testing.B) { p1, _ := delegation.FromDagJsonReader(bytes.NewReader(delegationJson)) cborBuf := &bytes.Buffer{} - _, _ = p1.SealWriter(cborBuf, privKey) + _, _ = p1.ToSealedWriter(cborBuf, privKey) cborBytes := cborBuf.Bytes() - p2, _ := delegation.UnsealReader(bytes.NewReader(cborBytes)) + p2, _ := delegation.FromSealedReader(bytes.NewReader(cborBytes)) b.ResetTimer() @@ -152,7 +152,7 @@ func BenchmarkRoundTrip(b *testing.B) { buf := &bytes.Buffer{} for i := 0; i < b.N; i++ { buf.Reset() - _, _ = p1.SealWriter(buf, privKey) + _, _ = p1.ToSealedWriter(buf, privKey) } }) @@ -161,7 +161,7 @@ func BenchmarkRoundTrip(b *testing.B) { reader := bytes.NewReader(cborBytes) for i := 0; i < b.N; i++ { _, _ = reader.Seek(0, 0) - _, _ = delegation.UnsealReader(reader) + _, _ = delegation.FromSealedReader(reader) } }) From 4974fed93195d21a353f8e12d196b94b4cf2643c Mon Sep 17 00:00:00 2001 From: Steve Moyer Date: Tue, 24 Sep 2024 09:00:59 -0400 Subject: [PATCH 7/8] docs(delegation): add Go doc for Tag --- delegation/schema.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/delegation/schema.go b/delegation/schema.go index 33b8f46..c442caf 100644 --- a/delegation/schema.go +++ b/delegation/schema.go @@ -14,8 +14,14 @@ import ( "github.com/ucan-wg/go-ucan/pkg/meta" ) +// [Tag] is the string used as a key within the SigPayload that identifies +// that the TokenPayload is a delegation. +// +// [Tag]: https://github.com/ucan-wg/delegation/tree/v1_ipld#type-tag const Tag = "ucan/dlg@1.0.0-rc.1" +// TODO: update the above Tag URL once the delegation specification is merged. + //go:embed delegation.ipldsch var schemaBytes []byte From f7794771188ca7b664cf4b81a7225573be61fe8e Mon Sep 17 00:00:00 2001 From: Steve Moyer Date: Tue, 24 Sep 2024 09:08:07 -0400 Subject: [PATCH 8/8] chore(envelope): clean up TODOs --- internal/envelope/cid.go | 12 ------------ internal/envelope/cid_test.go | 7 +++++-- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/internal/envelope/cid.go b/internal/envelope/cid.go index 74ac583..410acbb 100644 --- a/internal/envelope/cid.go +++ b/internal/envelope/cid.go @@ -6,7 +6,6 @@ import ( "io" "github.com/ipfs/go-cid" - "github.com/libp2p/go-libp2p/core/crypto" "github.com/multiformats/go-multibase" "github.com/multiformats/go-multicodec" "github.com/multiformats/go-multihash" @@ -20,17 +19,6 @@ func CIDToBase58BTC(id cid.Cid) string { return id.Encode(b58BTCEnc) } -// CID returns the UCAN content identifier a Tokener. -// TODO: remove? -func CID(privKey crypto.PrivKey, token Tokener) (cid.Cid, error) { - data, err := ToDagCbor(privKey, token) - if err != nil { - return cid.Undef, err - } - - return CIDFromBytes(data) -} - // CIDFromBytes returns the UCAN content identifier for an arbitrary slice // of bytes. func CIDFromBytes(b []byte) (cid.Cid, error) { diff --git a/internal/envelope/cid_test.go b/internal/envelope/cid_test.go index 290ddd4..46f62d0 100644 --- a/internal/envelope/cid_test.go +++ b/internal/envelope/cid_test.go @@ -14,14 +14,17 @@ import ( "github.com/ucan-wg/go-ucan/internal/envelope" ) -func TestCid(t *testing.T) { +func TestCidFromBytes(t *testing.T) { t.Parallel() expData := golden.Get(t, "example.dagcbor") expHash, err := multihash.Sum(expData, uint64(multicodec.Sha2_256), -1) require.NoError(t, err) - id, err := envelope.CID(examplePrivKey(t), newExample(t)) + data, err := envelope.ToDagCbor(examplePrivKey(t), newExample(t)) + require.NoError(t, err) + + id, err := envelope.CIDFromBytes(data) require.NoError(t, err) assert.Equal(t, exampleCID, envelope.CIDToBase58BTC(id)) assert.Equal(t, expHash, id.Hash())