mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-03 18:01:39 +00:00
191 lines
6.6 KiB
Go
191 lines
6.6 KiB
Go
package plugin
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"log/slog"
|
||
|
|
|
||
|
|
"github.com/asynkron/protoactor-go/actor"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Actor represents the UCAN actor that handles MPC-based cryptographic operations.
|
||
|
|
// It maintains a WebAssembly plugin for secure UCAN token operations and
|
||
|
|
// uses behavioral state management to handle different actor lifecycle phases.
|
||
|
|
type Actor struct {
|
||
|
|
ctx context.Context // Context for managing plugin lifecycle
|
||
|
|
behavior actor.Behavior // Behavioral state manager for handling different phases
|
||
|
|
enclave Plugin // MPC enclave plugin instance
|
||
|
|
config *EnclaveConfig // Configuration for enclave initialization (optional)
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewActor creates a new UCAN actor instance.
|
||
|
|
// The actor starts in an uninitialized state and transitions to initialized
|
||
|
|
// once the MPC enclave plugin is successfully loaded.
|
||
|
|
func NewActor() actor.Actor {
|
||
|
|
return &Actor{
|
||
|
|
ctx: context.Background(),
|
||
|
|
behavior: actor.NewBehavior(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Props returns the actor properties configuration for creating new UCAN actors.
|
||
|
|
// It uses NewActor as the producer function to create UCAN actor instances.
|
||
|
|
func Props() *actor.Props {
|
||
|
|
return actor.PropsFromProducer(NewActor)
|
||
|
|
}
|
||
|
|
|
||
|
|
// PropsWithConfig returns actor properties configured with specific enclave data.
|
||
|
|
// This allows creating actors with pre-configured enclave data for testing or specific use cases.
|
||
|
|
func PropsWithConfig(config *EnclaveConfig) *actor.Props {
|
||
|
|
return actor.PropsFromProducer(func() actor.Actor {
|
||
|
|
return &Actor{
|
||
|
|
ctx: context.Background(),
|
||
|
|
behavior: actor.NewBehavior(),
|
||
|
|
config: config, // Store config for initialization
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Receive is the main message handler for the UCAN actor.
|
||
|
|
// It handles actor lifecycle messages and delegates other messages to the current behavior.
|
||
|
|
// The actor uses behavioral patterns to handle different states (uninitialized vs initialized).
|
||
|
|
func (a *Actor) Receive(c actor.Context) {
|
||
|
|
switch c.Message().(type) {
|
||
|
|
case *actor.Started:
|
||
|
|
a.handleStarted(c)
|
||
|
|
case *actor.Stopping:
|
||
|
|
a.handleStopping(c)
|
||
|
|
default:
|
||
|
|
a.behavior.Receive(c)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialized handles UCAN operation messages once the actor is fully initialized.
|
||
|
|
// This method is set as the behavior after the MPC enclave plugin is successfully loaded.
|
||
|
|
// It processes NewOriginToken, NewAttenuatedToken, SignData, VerifyData, and GetIssuerDID requests.
|
||
|
|
func (a *Actor) Initialized(c actor.Context) {
|
||
|
|
switch msg := c.Message().(type) {
|
||
|
|
case *NewOriginTokenRequest:
|
||
|
|
a.handleNewOriginToken(c, msg)
|
||
|
|
case *NewAttenuatedTokenRequest:
|
||
|
|
a.handleNewAttenuatedToken(c, msg)
|
||
|
|
case *SignDataRequest:
|
||
|
|
a.handleSignData(c, msg)
|
||
|
|
case *VerifyDataRequest:
|
||
|
|
a.handleVerifyData(c, msg)
|
||
|
|
case *GetIssuerDIDResponse: // Used as request for DID retrieval
|
||
|
|
a.handleGetIssuerDID(c)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleStarted initializes the MPC enclave plugin when the actor starts.
|
||
|
|
// It loads the WebAssembly plugin and transitions the actor to the initialized state.
|
||
|
|
// If plugin loading fails, the actor remains in an uninitialized state.
|
||
|
|
func (a *Actor) handleStarted(c actor.Context) {
|
||
|
|
a.ctx = context.Background()
|
||
|
|
|
||
|
|
// Use provided config if available, otherwise use default
|
||
|
|
var config *EnclaveConfig
|
||
|
|
if a.config != nil {
|
||
|
|
config = a.config
|
||
|
|
} else {
|
||
|
|
config = DefaultEnclaveConfig()
|
||
|
|
}
|
||
|
|
|
||
|
|
// For testing, we need to provide mock enclave data
|
||
|
|
// In production, this would be provided by the caller
|
||
|
|
if config.EnclaveData == nil {
|
||
|
|
c.Logger().Warn("No enclave data provided, actor will not initialize",
|
||
|
|
slog.String("config", fmt.Sprintf("%+v", config)),
|
||
|
|
)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.Logger().Info("Attempting to load MPC enclave plugin",
|
||
|
|
slog.String("config", fmt.Sprintf("%+v", config)),
|
||
|
|
)
|
||
|
|
|
||
|
|
e, err := LoadPluginWithManager(a.ctx, config)
|
||
|
|
if err != nil {
|
||
|
|
c.Logger().Error("Failed to create MPC enclave host",
|
||
|
|
slog.String("error", err.Error()),
|
||
|
|
slog.String("config", fmt.Sprintf("%+v", config)),
|
||
|
|
)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
a.enclave = e
|
||
|
|
c.Logger().Info("MPC enclave actor started successfully",
|
||
|
|
slog.String("config", fmt.Sprintf("%+v", config)),
|
||
|
|
)
|
||
|
|
a.behavior.Become(a.Initialized)
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleStopping performs cleanup when the actor is stopping.
|
||
|
|
// It releases the MPC enclave plugin resources and cleans up the context.
|
||
|
|
func (a *Actor) handleStopping(c actor.Context) {
|
||
|
|
a.ctx.Done()
|
||
|
|
a.enclave = nil
|
||
|
|
c.Logger().Info("MPC enclave plugin done")
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleNewOriginToken processes UCAN origin token creation requests by delegating to the MPC enclave plugin.
|
||
|
|
// It validates the request and responds with the generated UCAN token or an error.
|
||
|
|
func (a *Actor) handleNewOriginToken(context actor.Context, msg *NewOriginTokenRequest) {
|
||
|
|
resp, err := a.enclave.NewOriginToken(msg)
|
||
|
|
if err != nil {
|
||
|
|
context.Logger().Error("failed to create origin token", slog.String("error", err.Error()))
|
||
|
|
context.Respond(err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
context.Respond(resp)
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleNewAttenuatedToken processes UCAN attenuated token creation requests by delegating to the MPC enclave plugin.
|
||
|
|
// It creates a delegated token with reduced permissions and responds with the token or an error.
|
||
|
|
func (a *Actor) handleNewAttenuatedToken(context actor.Context, msg *NewAttenuatedTokenRequest) {
|
||
|
|
resp, err := a.enclave.NewAttenuatedToken(msg)
|
||
|
|
if err != nil {
|
||
|
|
context.Logger().
|
||
|
|
Error("failed to create attenuated token", slog.String("error", err.Error()))
|
||
|
|
context.Respond(err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
context.Respond(resp)
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleSignData processes data signing requests by delegating to the MPC enclave plugin.
|
||
|
|
// It creates a cryptographic signature using MPC and responds with the signature or an error.
|
||
|
|
func (a *Actor) handleSignData(context actor.Context, msg *SignDataRequest) {
|
||
|
|
resp, err := a.enclave.SignData(msg)
|
||
|
|
if err != nil {
|
||
|
|
context.Logger().Error("failed to sign data", slog.String("error", err.Error()))
|
||
|
|
context.Respond(err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
context.Respond(resp)
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleVerifyData processes signature verification requests by delegating to the MPC enclave plugin.
|
||
|
|
// It validates the signature against the data and responds with the verification result or an error.
|
||
|
|
func (a *Actor) handleVerifyData(context actor.Context, msg *VerifyDataRequest) {
|
||
|
|
resp, err := a.enclave.VerifyData(msg)
|
||
|
|
if err != nil {
|
||
|
|
context.Logger().Error("failed to verify data", slog.String("error", err.Error()))
|
||
|
|
context.Respond(err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
context.Respond(resp)
|
||
|
|
}
|
||
|
|
|
||
|
|
// handleGetIssuerDID processes DID retrieval requests by delegating to the MPC enclave plugin.
|
||
|
|
// It retrieves the issuer DID, address, and chain code from the enclave.
|
||
|
|
func (a *Actor) handleGetIssuerDID(context actor.Context) {
|
||
|
|
resp, err := a.enclave.GetIssuerDID()
|
||
|
|
if err != nil {
|
||
|
|
context.Logger().Error("failed to get issuer DID", slog.String("error", err.Error()))
|
||
|
|
context.Respond(err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
context.Respond(resp)
|
||
|
|
}
|