Skip to content
Draft
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
2 changes: 0 additions & 2 deletions pkg/principalresolver/httpresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,6 @@ func NewHTTPResolver(webKeys []did.DID, opts ...Option) (*HTTPResolver, error) {
return resolver, nil
}

// TODO(forrest): the interface this implements in go-ucanto should probably accept a context
// since means of resolution here are open ended, and may go to network or disk.
func (r *HTTPResolver) ResolveDIDKey(ctx context.Context, input did.DID) (did.DID, validator.UnresolvedDID) {
endpoint, ok := r.webKeys[input]
if !ok {
Expand Down
68 changes: 68 additions & 0 deletions pkg/service/publisher/publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"slices"
"strings"
"sync"

"github.com/ipfs/go-cid"
Expand All @@ -26,8 +27,14 @@ import (
"github.com/storacha/go-ucanto/core/receipt"
"github.com/storacha/go-ucanto/core/result"
"github.com/storacha/go-ucanto/core/result/ok"
"github.com/storacha/go-ucanto/did"
"github.com/storacha/go-ucanto/principal"
edverifier "github.com/storacha/go-ucanto/principal/ed25519/verifier"
"github.com/storacha/go-ucanto/principal/verifier"
"github.com/storacha/go-ucanto/ucan"
"github.com/storacha/go-ucanto/validator"
"github.com/storacha/piri/lib"
"github.com/storacha/piri/pkg/principalresolver"

"github.com/storacha/go-libstoracha/advertisement"
)
Expand Down Expand Up @@ -286,6 +293,67 @@ func New(
}, nil
}

func validateClaimCacheDelegation(ctx context.Context, id ucan.Signer, serviceID ucan.Principal, proofs []delegation.Proof, options ...principalresolver.Option) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between a principal.Signer and ucan.Signer? It looks like principal.Signer is a ucan.Signer, plus some other methods? Would it be possible to align on just a single interface here and everywhere else?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only need something that can sign a message here, we don't need the other methods that signers that are principals also have (like accessing the DID or encoding the key to bytes etc.).

var authority principal.Verifier
var principalResolver validator.PrincipalResolverFunc
if strings.HasPrefix(serviceID.DID().String(), "did:web:") {
reslv, err := principalresolver.NewHTTPResolver([]did.DID{serviceID.DID()}, options...)
if err != nil {
return fmt.Errorf("creating HTTP resolver: %w", err)
}
didkey, err := reslv.ResolveDIDKey(ctx, serviceID.DID())
if err != nil {
return fmt.Errorf("resolving indexing service DID: %w", err)
}
v, err := edverifier.Decode(didkey.Bytes())
if err != nil {
return fmt.Errorf("decoding indexing service DID: %w", err)
}
authority, err = verifier.Wrap(v, serviceID.DID())
if err != nil {
return fmt.Errorf("wrapping indexing service DID: %w", err)
}
principalResolver = reslv.ResolveDIDKey
} else {
v, err := edverifier.Decode(serviceID.DID().Bytes())
if err != nil {
return fmt.Errorf("decoding indexing service DID: %w", err)
}
authority = v
principalResolver = validator.FailDIDKeyResolution
}

vctx := validator.NewValidationContext(
authority,
claim.Cache,
validator.IsSelfIssued,
// TODO: check revocation status when revocation API is implemented.
func(ctx context.Context, auth validator.Authorization[any]) validator.Revoked {
return nil
},
validator.ProofUnavailable,
edverifier.Parse,
principalResolver,
)

placeholder, _ := cid.Parse("bafkqaaa")
inv, err := claim.Cache.Invoke(
id,
serviceID,
serviceID.DID().String(),
claim.CacheCaveats{
Claim: cidlink.Link{Cid: placeholder},
},
delegation.WithProof(proofs...),
)
if err != nil {
return fmt.Errorf("creating claim cache invocation: %w", err)
}

_, err = validator.Access(ctx, inv, vctx)
return err
}

func providerInfo(peerID peer.ID, publicAddr multiaddr.Multiaddr, blobAddr multiaddr.Multiaddr) (peer.AddrInfo, error) {
provider := peer.AddrInfo{ID: peerID}
if blobAddr == nil {
Expand Down
53 changes: 53 additions & 0 deletions pkg/service/publisher/publisher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package publisher

import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"

"github.com/ipfs/go-datastore"
Expand All @@ -24,10 +28,13 @@ import (
"github.com/storacha/go-ucanto/core/result"
"github.com/storacha/go-ucanto/core/result/failure"
"github.com/storacha/go-ucanto/core/result/ok"
"github.com/storacha/go-ucanto/did"
"github.com/storacha/go-ucanto/principal"
"github.com/storacha/go-ucanto/principal/signer"
"github.com/storacha/go-ucanto/server"
"github.com/storacha/go-ucanto/ucan"
"github.com/storacha/piri/pkg/internal/digestutil"
"github.com/storacha/piri/pkg/principalresolver"
"github.com/storacha/piri/pkg/service/publisher/advertisement"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -214,3 +221,49 @@ func mockIndexingService(t *testing.T, id principal.Signer, handler server.Handl
),
)(t)
}

func TestValidateClaimCacheDelegation(t *testing.T) {
var serviceID principal.Signer
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != principalresolver.WellKnownDIDPath {
w.WriteHeader(http.StatusNotFound)
return
}

doc := principalresolver.Document{
Context: principalresolver.FlexibleContext{"https://w3id.org/did/v1", "https://w3id.org/security/v1"},
ID: serviceID.DID().String(),
VerificationMethod: []principalresolver.VerificationMethod{
{
ID: serviceID.DID().String() + "#key1",
Type: "Ed25519VerificationKey2018",
Controller: serviceID.DID().String(),
PublicKeyMultibase: strings.TrimPrefix(testutil.Service.DID().String(), "did:key:"),
},
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(doc)
}))
defer server.Close()

didWeb, err := did.Parse("did:web:" + testutil.Must(url.Parse(server.URL))(t).Host)
require.NoError(t, err)

serviceID, err = signer.Wrap(testutil.Service, didWeb)
require.NoError(t, err)

dlg, err := delegation.Delegate(
serviceID,
testutil.Alice,
[]ucan.Capability[ucan.NoCaveats]{
ucan.NewCapability(claim.CacheAbility, serviceID.DID().String(), ucan.NoCaveats{}),
},
)
require.NoError(t, err)

proofs := []delegation.Proof{delegation.FromDelegation(dlg)}

err = validateClaimCacheDelegation(t.Context(), testutil.Alice, serviceID, proofs, principalresolver.InsecureResolution())
require.NoError(t, err)
}
Loading