Files
sonr/client/modules/did/client.go
T
Prad NukalaandGitHub 13e6c3e84d Master (#1262)
* clear

* feat: Add everything

* fix: Commenht
2025-10-03 14:45:52 -04:00

607 lines
22 KiB
Go

// Package did provides a client interface for interacting with the Sonr DID module.
package did
import (
"context"
"fmt"
"strings"
"google.golang.org/grpc"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/sonr-io/sonr/client/config"
"github.com/sonr-io/sonr/client/errors"
didtypes "github.com/sonr-io/sonr/x/did/types"
)
// Client provides an interface for interacting with the DID module.
type Client interface {
// DID Operations
CreateDID(ctx context.Context, opts *CreateDIDOptions) (*DIDDocument, error)
ResolveDID(ctx context.Context, did string) (*DIDDocument, error)
UpdateDID(ctx context.Context, did string, opts *UpdateDIDOptions) (*DIDDocument, error)
DeactivateDID(ctx context.Context, did string) error
// DID Document Operations
AddVerificationMethod(ctx context.Context, did string, method *VerificationMethod) error
RemoveVerificationMethod(ctx context.Context, did string, methodID string) error
AddService(ctx context.Context, did string, service *Service) error
RemoveService(ctx context.Context, did string, serviceID string) error
// WebAuthn Operations
RegisterWebAuthn(ctx context.Context, opts *WebAuthnRegistrationOptions) (*WebAuthnCredential, error)
AuthenticateWebAuthn(ctx context.Context, opts *WebAuthnAuthenticationOptions) (*WebAuthnAssertion, error)
// Query Operations
ListDIDs(ctx context.Context, options *ListDIDsOptions) (*DIDListResponse, error)
GetDIDsByOwner(ctx context.Context, owner string) ([]*DIDDocument, error)
}
// DIDDocument represents a W3C DID Document.
type DIDDocument struct {
ID string `json:"id"`
Controller []string `json:"controller,omitempty"`
VerificationMethod []*VerificationMethod `json:"verificationMethod,omitempty"`
Authentication []string `json:"authentication,omitempty"`
AssertionMethod []string `json:"assertionMethod,omitempty"`
KeyAgreement []string `json:"keyAgreement,omitempty"`
CapabilityInvocation []string `json:"capabilityInvocation,omitempty"`
CapabilityDelegation []string `json:"capabilityDelegation,omitempty"`
Service []*Service `json:"service,omitempty"`
AlsoKnownAs []string `json:"alsoKnownAs,omitempty"`
Metadata *DIDMetadata `json:"metadata,omitempty"`
}
// VerificationMethod represents a verification method in a DID document.
type VerificationMethod struct {
ID string `json:"id"`
Type string `json:"type"`
Controller string `json:"controller"`
PublicKeyJwk map[string]any `json:"publicKeyJwk,omitempty"`
PublicKeyMultibase string `json:"publicKeyMultibase,omitempty"`
}
// Service represents a service endpoint in a DID document.
type Service struct {
ID string `json:"id"`
Type string `json:"type"`
ServiceEndpoint any `json:"serviceEndpoint"`
}
// DIDMetadata contains metadata about a DID.
type DIDMetadata struct {
Created string `json:"created"`
Updated string `json:"updated,omitempty"`
Deactivated bool `json:"deactivated,omitempty"`
VersionID string `json:"versionId,omitempty"`
NextUpdate string `json:"nextUpdate,omitempty"`
NextVersionID string `json:"nextVersionId,omitempty"`
}
// CreateDIDOptions configures DID creation.
type CreateDIDOptions struct {
Controller []string `json:"controller,omitempty"`
VerificationMethods []*VerificationMethod `json:"verificationMethods,omitempty"`
Services []*Service `json:"services,omitempty"`
AlsoKnownAs []string `json:"alsoKnownAs,omitempty"`
UseWebAuthn bool `json:"useWebAuthn,omitempty"`
}
// UpdateDIDOptions configures DID updates.
type UpdateDIDOptions struct {
AddVerificationMethods []*VerificationMethod `json:"addVerificationMethods,omitempty"`
RemoveVerificationMethods []string `json:"removeVerificationMethods,omitempty"`
AddServices []*Service `json:"addServices,omitempty"`
RemoveServices []string `json:"removeServices,omitempty"`
AddController []string `json:"addController,omitempty"`
RemoveController []string `json:"removeController,omitempty"`
}
// WebAuthnRegistrationOptions configures WebAuthn registration.
type WebAuthnRegistrationOptions struct {
Username string `json:"username"`
DisplayName string `json:"displayName"`
Challenge []byte `json:"challenge"`
Timeout int `json:"timeout,omitempty"`
Extensions map[string]any `json:"extensions,omitempty"`
}
// WebAuthnCredential represents a WebAuthn credential.
type WebAuthnCredential struct {
ID string `json:"id"`
RawID []byte `json:"rawId"`
Type string `json:"type"`
Response *AuthenticatorResponse `json:"response"`
ClientExtensions map[string]any `json:"clientExtensions,omitempty"`
}
// AuthenticatorResponse represents the authenticator response.
type AuthenticatorResponse struct {
ClientDataJSON []byte `json:"clientDataJSON"`
AttestationObject []byte `json:"attestationObject"`
}
// WebAuthnAuthenticationOptions configures WebAuthn authentication.
type WebAuthnAuthenticationOptions struct {
Challenge []byte `json:"challenge"`
Timeout int `json:"timeout,omitempty"`
AllowedCredentials []string `json:"allowedCredentials,omitempty"`
}
// WebAuthnAssertion represents a WebAuthn assertion.
type WebAuthnAssertion struct {
ID string `json:"id"`
RawID []byte `json:"rawId"`
Type string `json:"type"`
Response *AuthenticatorAssertionResponse `json:"response"`
}
// AuthenticatorAssertionResponse represents the assertion response.
type AuthenticatorAssertionResponse struct {
ClientDataJSON []byte `json:"clientDataJSON"`
AuthenticatorData []byte `json:"authenticatorData"`
Signature []byte `json:"signature"`
UserHandle []byte `json:"userHandle,omitempty"`
}
// ListDIDsOptions configures DID listing.
type ListDIDsOptions struct {
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset,omitempty"`
Owner string `json:"owner,omitempty"`
}
// DIDListResponse contains a list of DIDs with pagination.
type DIDListResponse struct {
DIDs []*DIDDocument `json:"dids"`
TotalCount uint64 `json:"totalCount"`
Limit uint64 `json:"limit"`
Offset uint64 `json:"offset"`
}
// client implements the DID Client interface.
type client struct {
grpcConn *grpc.ClientConn
config *config.NetworkConfig
// Service clients for DID module
queryClient didtypes.QueryClient
msgClient didtypes.MsgClient
txClient tx.ServiceClient
}
// NewClient creates a new DID module client.
func NewClient(grpcConn *grpc.ClientConn, cfg *config.NetworkConfig) Client {
return &client{
grpcConn: grpcConn,
config: cfg,
queryClient: didtypes.NewQueryClient(grpcConn),
msgClient: didtypes.NewMsgClient(grpcConn),
txClient: tx.NewServiceClient(grpcConn),
}
}
// CreateDID creates a new DID document on the Sonr blockchain.
func (c *client) CreateDID(ctx context.Context, opts *CreateDIDOptions) (*DIDDocument, error) {
if opts == nil {
return nil, errors.NewModuleError("did", "CreateDID",
fmt.Errorf("options cannot be nil"))
}
// Generate DID ID
// Note: In a real implementation, this would use proper key derivation
didID := GenerateDID(fmt.Sprintf("user_%d", len(opts.Controller)))
// Convert verification methods to protobuf format
var verificationMethods []*didtypes.VerificationMethod
for _, vm := range opts.VerificationMethods {
verificationMethods = append(verificationMethods, &didtypes.VerificationMethod{
Id: vm.ID,
VerificationMethodKind: vm.Type,
Controller: vm.Controller,
PublicKeyMultibase: vm.PublicKeyMultibase,
})
}
// Convert services to protobuf format
var services []*didtypes.Service
for _, svc := range opts.Services {
services = append(services, &didtypes.Service{
Id: svc.ID,
ServiceKind: svc.Type,
SingleEndpoint: fmt.Sprintf("%v", svc.ServiceEndpoint),
})
}
// Get primary controller (first one if multiple)
primaryController := ""
if len(opts.Controller) > 0 {
primaryController = opts.Controller[0]
}
// Create DID Document
didDocument := didtypes.DIDDocument{
Id: didID,
PrimaryController: primaryController,
AlsoKnownAs: opts.AlsoKnownAs,
VerificationMethod: verificationMethods,
Service: services,
}
// Create the MsgCreateDID message
msg := &didtypes.MsgCreateDID{
Controller: primaryController, // Will be set by the transaction builder
DidDocument: didDocument,
}
// In a real implementation, this would submit the transaction
// For now, store the message for later use
_ = msg
// Return a mock DID document
return &DIDDocument{
ID: didID,
Controller: opts.Controller,
VerificationMethod: opts.VerificationMethods,
Service: opts.Services,
AlsoKnownAs: opts.AlsoKnownAs,
Metadata: &DIDMetadata{
Created: "2024-01-01T00:00:00Z",
},
}, nil
}
// ResolveDID resolves a DID to its document.
func (c *client) ResolveDID(ctx context.Context, did string) (*DIDDocument, error) {
// TODO: Implement DID resolution using DID module query client
// Should validate DID format before querying chain
// Query chain state for DID document by ID
// Convert protobuf DIDDocument to client type
// Handle DID not found and deactivated DID cases
return nil, errors.NewModuleError("did", "ResolveDID",
fmt.Errorf("DID resolution not yet implemented"))
}
// UpdateDID updates an existing DID document.
func (c *client) UpdateDID(ctx context.Context, did string, opts *UpdateDIDOptions) (*DIDDocument, error) {
// TODO: Implement DID updates using DID module
// Should validate DID ownership and update permissions
// Build MsgUpdateDID with incremental changes
// Handle verification method and service updates
// Return updated DID document with new version
return nil, errors.NewModuleError("did", "UpdateDID",
fmt.Errorf("DID updates not yet implemented"))
}
// DeactivateDID deactivates a DID document.
func (c *client) DeactivateDID(ctx context.Context, did string) error {
// TODO: Implement DID deactivation using DID module
// Should validate DID ownership before deactivation
// Build MsgDeactivateDID and submit to chain
// Mark DID as deactivated in chain state
// Handle cascading effects on dependent services
return errors.NewModuleError("did", "DeactivateDID",
fmt.Errorf("DID deactivation not yet implemented"))
}
// AddVerificationMethod adds a verification method to a DID document.
func (c *client) AddVerificationMethod(ctx context.Context, did string, method *VerificationMethod) error {
// TODO: Implement verification method addition using DID module
// Should validate DID ownership and method format
// Build MsgAddVerificationMethod and submit to chain
// Validate public key format and cryptographic validity
// Update DID document with new verification method
return errors.NewModuleError("did", "AddVerificationMethod",
fmt.Errorf("verification method addition not yet implemented"))
}
// RemoveVerificationMethod removes a verification method from a DID document.
func (c *client) RemoveVerificationMethod(ctx context.Context, did string, methodID string) error {
// TODO: Implement verification method removal using DID module
// Should validate DID ownership and method existence
// Build MsgRemoveVerificationMethod and submit to chain
// Check if method is used in other DID relationships
// Prevent removal of last verification method
return errors.NewModuleError("did", "RemoveVerificationMethod",
fmt.Errorf("verification method removal not yet implemented"))
}
// AddService adds a service to a DID document.
func (c *client) AddService(ctx context.Context, did string, service *Service) error {
// TODO: Implement service addition using DID module
// Should validate DID ownership and service format
// Build MsgAddService and submit to chain
// Validate service endpoint URLs and accessibility
// Update DID document with new service entry
return errors.NewModuleError("did", "AddService",
fmt.Errorf("service addition not yet implemented"))
}
// RemoveService removes a service from a DID document.
func (c *client) RemoveService(ctx context.Context, did string, serviceID string) error {
// TODO: Implement service removal using DID module
// Should validate DID ownership and service existence
// Build MsgRemoveService and submit to chain
// Check for dependent systems using this service
// Update DID document removing service entry
return errors.NewModuleError("did", "RemoveService",
fmt.Errorf("service removal not yet implemented"))
}
// RegisterWebAuthn registers a WebAuthn credential with a DID.
func (c *client) RegisterWebAuthn(ctx context.Context, opts *WebAuthnRegistrationOptions) (*WebAuthnCredential, error) {
// TODO: Implement WebAuthn registration using DID module
// Should validate registration options and challenge
// Build MsgRegisterWebAuthnCredential and submit to chain
// Process authenticator attestation and public key
// Store credential ID and public key in DID document
// Support auto-vault creation if enabled
return nil, errors.NewModuleError("did", "RegisterWebAuthn",
fmt.Errorf("WebAuthn registration not yet implemented"))
}
// AuthenticateWebAuthn performs WebAuthn authentication.
func (c *client) AuthenticateWebAuthn(ctx context.Context, opts *WebAuthnAuthenticationOptions) (*WebAuthnAssertion, error) {
// TODO: Implement WebAuthn authentication using DID module
// Should validate authentication challenge and credentials
// Verify authenticator assertion against stored public key
// Check credential ID against allowed credentials list
// Return verified assertion with user handle and signature
return nil, errors.NewModuleError("did", "AuthenticateWebAuthn",
fmt.Errorf("WebAuthn authentication not yet implemented"))
}
// ListDIDs lists DIDs with optional filtering and pagination.
func (c *client) ListDIDs(ctx context.Context, options *ListDIDsOptions) (*DIDListResponse, error) {
// TODO: Implement DID listing using DID module query client
// Should support pagination with limit/offset
// Filter by owner address if specified
// Return DIDs with basic metadata and status
// Handle empty result sets gracefully
return nil, errors.NewModuleError("did", "ListDIDs",
fmt.Errorf("DID listing not yet implemented"))
}
// GetDIDsByOwner retrieves all DIDs owned by a specific address.
func (c *client) GetDIDsByOwner(ctx context.Context, owner string) ([]*DIDDocument, error) {
// TODO: Implement owner-based DID lookup using DID module
// Should validate owner address format
// Query chain state for DIDs controlled by owner
// Return complete DID documents for all owned DIDs
// Include active and deactivated DIDs with status
return nil, errors.NewModuleError("did", "GetDIDsByOwner",
fmt.Errorf("owner-based DID lookup not yet implemented"))
}
// Utility functions
// GenerateDID generates a new DID identifier for the Sonr network.
func GenerateDID(identifier string) string {
return fmt.Sprintf("did:sonr:%s", identifier)
}
// ValidateDID validates a DID format.
func ValidateDID(did string) error {
// Basic DID format validation
if len(did) == 0 {
return fmt.Errorf("DID cannot be empty")
}
if !strings.HasPrefix(did, "did:sonr:") {
return fmt.Errorf("DID must start with 'did:sonr:'")
}
return nil
}
// CreateDefaultVerificationMethod creates a default verification method.
func CreateDefaultVerificationMethod(did string, publicKey []byte) *VerificationMethod {
return &VerificationMethod{
ID: fmt.Sprintf("%s#key-1", did),
Type: "Ed25519VerificationKey2020",
Controller: did,
PublicKeyMultibase: fmt.Sprintf("z%x", publicKey), // Simplified multibase encoding
}
}
// CreateWebAuthnService creates a service entry for WebAuthn.
func CreateWebAuthnService(did string, endpoint string) *Service {
return &Service{
ID: fmt.Sprintf("%s#webauthn", did),
Type: "WebAuthnService",
ServiceEndpoint: endpoint,
}
}
// Message Builders - These create the actual transaction messages
// BuildMsgCreateDID builds a MsgCreateDID message.
func BuildMsgCreateDID(controller string, opts *CreateDIDOptions) (*didtypes.MsgCreateDID, error) {
if opts == nil {
return nil, fmt.Errorf("options cannot be nil")
}
// Generate DID ID
didID := GenerateDID(fmt.Sprintf("user_%s", controller[:8]))
// Convert verification methods
var verificationMethods []*didtypes.VerificationMethod
for _, vm := range opts.VerificationMethods {
verificationMethods = append(verificationMethods, &didtypes.VerificationMethod{
Id: vm.ID,
VerificationMethodKind: vm.Type,
Controller: vm.Controller,
PublicKeyMultibase: vm.PublicKeyMultibase,
})
}
// Convert services
var services []*didtypes.Service
for _, svc := range opts.Services {
services = append(services, &didtypes.Service{
Id: svc.ID,
ServiceKind: svc.Type,
SingleEndpoint: fmt.Sprintf("%v", svc.ServiceEndpoint),
})
}
// Create DID Document
didDocument := didtypes.DIDDocument{
Id: didID,
PrimaryController: controller,
AlsoKnownAs: opts.AlsoKnownAs,
VerificationMethod: verificationMethods,
Service: services,
}
return &didtypes.MsgCreateDID{
Controller: controller,
DidDocument: didDocument,
}, nil
}
// BuildMsgUpdateDID builds a MsgUpdateDID message.
func BuildMsgUpdateDID(controller, did string, opts *UpdateDIDOptions) (*didtypes.MsgUpdateDID, error) {
if opts == nil {
return nil, fmt.Errorf("options cannot be nil")
}
// Note: In a real implementation, we would need to query the existing DID document
// and apply the updates. For now, we create a minimal DID document with updates.
// Convert new verification methods
var verificationMethods []*didtypes.VerificationMethod
for _, vm := range opts.AddVerificationMethods {
verificationMethods = append(verificationMethods, &didtypes.VerificationMethod{
Id: vm.ID,
VerificationMethodKind: vm.Type,
Controller: vm.Controller,
PublicKeyMultibase: vm.PublicKeyMultibase,
})
}
// Convert new services
var services []*didtypes.Service
for _, svc := range opts.AddServices {
services = append(services, &didtypes.Service{
Id: svc.ID,
ServiceKind: svc.Type,
SingleEndpoint: fmt.Sprintf("%v", svc.ServiceEndpoint),
})
}
// Create updated DID Document
didDocument := didtypes.DIDDocument{
Id: did,
PrimaryController: controller,
VerificationMethod: verificationMethods,
Service: services,
}
return &didtypes.MsgUpdateDID{
Controller: controller,
Did: did,
DidDocument: didDocument,
}, nil
}
// BuildMsgDeactivateDID builds a MsgDeactivateDID message.
func BuildMsgDeactivateDID(controller, did string) *didtypes.MsgDeactivateDID {
return &didtypes.MsgDeactivateDID{
Controller: controller,
Did: did,
}
}
// BuildMsgAddVerificationMethod builds a MsgAddVerificationMethod message.
func BuildMsgAddVerificationMethod(controller, did string, method *VerificationMethod) (*didtypes.MsgAddVerificationMethod, error) {
if method == nil {
return nil, fmt.Errorf("verification method cannot be nil")
}
return &didtypes.MsgAddVerificationMethod{
Controller: controller,
Did: did,
VerificationMethod: didtypes.VerificationMethod{
Id: method.ID,
VerificationMethodKind: method.Type,
Controller: method.Controller,
PublicKeyMultibase: method.PublicKeyMultibase,
},
}, nil
}
// BuildMsgRemoveVerificationMethod builds a MsgRemoveVerificationMethod message.
func BuildMsgRemoveVerificationMethod(controller, did, methodID string) *didtypes.MsgRemoveVerificationMethod {
return &didtypes.MsgRemoveVerificationMethod{
Controller: controller,
Did: did,
VerificationMethodId: methodID,
}
}
// BuildMsgAddService builds a MsgAddService message.
func BuildMsgAddService(controller, did string, service *Service) (*didtypes.MsgAddService, error) {
if service == nil {
return nil, fmt.Errorf("service cannot be nil")
}
return &didtypes.MsgAddService{
Controller: controller,
Did: did,
Service: didtypes.Service{
Id: service.ID,
ServiceKind: service.Type,
SingleEndpoint: fmt.Sprintf("%v", service.ServiceEndpoint),
},
}, nil
}
// BuildMsgRemoveService builds a MsgRemoveService message.
func BuildMsgRemoveService(controller, did, serviceID string) *didtypes.MsgRemoveService {
return &didtypes.MsgRemoveService{
Controller: controller,
Did: did,
ServiceId: serviceID,
}
}
// BuildMsgRegisterWebAuthnCredential builds a MsgRegisterWebAuthnCredential message.
func BuildMsgRegisterWebAuthnCredential(controller, username string, credential *WebAuthnCredential, autoCreateVault bool) (*didtypes.MsgRegisterWebAuthnCredential, error) {
if credential == nil {
return nil, fmt.Errorf("credential cannot be nil")
}
// Convert our WebAuthnCredential to the protobuf type
webAuthnCred := didtypes.WebAuthnCredential{
CredentialId: credential.ID,
PublicKey: credential.RawID, // Using RawID as the public key bytes
// Algorithm would need to be determined from the credential
// AttestationType would need to be extracted from the attestation object
// Origin would need to be extracted from the client data
}
// Generate a verification method ID based on the username
verificationMethodID := fmt.Sprintf("did:sonr:%s#webauthn-1", username)
return &didtypes.MsgRegisterWebAuthnCredential{
Controller: controller,
Username: username,
WebauthnCredential: webAuthnCred,
VerificationMethodId: verificationMethodID,
AutoCreateVault: autoCreateVault,
}, nil
}