mirror of
https://github.com/lightninglabs/loop
synced 2024-11-11 13:11:12 +00:00
1eb8ed3da5
We introduce a new package: `lsat`, which aims to provide utilities that will serve useful in the context of LSAT creation and verification for LSAT-enabled services.
129 lines
3.2 KiB
Go
129 lines
3.2 KiB
Go
package lsat
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
// CondServices is the condition used for a services caveat.
|
|
CondServices = "services"
|
|
|
|
// CondCapabilitiesSuffix is the condition suffix used for a service's
|
|
// capabilities caveat. For example, the condition of a capabilities
|
|
// caveat for a service named `loop` would be `loop_capabilities`.
|
|
CondCapabilitiesSuffix = "_capabilities"
|
|
)
|
|
|
|
var (
|
|
// ErrNoServices is an error returned when we attempt to decode the
|
|
// services included in a caveat.
|
|
ErrNoServices = errors.New("no services found")
|
|
|
|
// ErrInvalidService is an error returned when we attempt to decode a
|
|
// service with an invalid format.
|
|
ErrInvalidService = errors.New("service must be of the form " +
|
|
"\"name:tier\"")
|
|
)
|
|
|
|
// ServiceTier represents the different possible tiers of an LSAT-enabled
|
|
// service.
|
|
type ServiceTier uint8
|
|
|
|
const (
|
|
// BaseTier is the base tier of an LSAT-enabled service. This tier
|
|
// should be used for any new LSATs that are not part of a service tier
|
|
// upgrade.
|
|
BaseTier ServiceTier = iota
|
|
)
|
|
|
|
// Service contains the details of an LSAT-enabled service.
|
|
type Service struct {
|
|
// Name is the name of the LSAT-enabled service.
|
|
Name string
|
|
|
|
// Tier is the tier of the LSAT-enabled service.
|
|
Tier ServiceTier
|
|
}
|
|
|
|
// NewServicesCaveat creates a new services caveat with the provided caveats.
|
|
func NewServicesCaveat(services ...Service) (Caveat, error) {
|
|
value, err := encodeServicesCaveatValue(services...)
|
|
if err != nil {
|
|
return Caveat{}, err
|
|
}
|
|
return Caveat{
|
|
Condition: CondServices,
|
|
Value: value,
|
|
}, nil
|
|
}
|
|
|
|
// encodeServicesCaveatValue encodes a list of services into the expected format
|
|
// of a services caveat's value.
|
|
func encodeServicesCaveatValue(services ...Service) (string, error) {
|
|
if len(services) == 0 {
|
|
return "", ErrNoServices
|
|
}
|
|
|
|
var s strings.Builder
|
|
for i, service := range services {
|
|
if service.Name == "" {
|
|
return "", errors.New("missing service name")
|
|
}
|
|
|
|
fmtStr := "%v:%v"
|
|
if i < len(services)-1 {
|
|
fmtStr += ","
|
|
}
|
|
|
|
fmt.Fprintf(&s, fmtStr, service.Name, uint8(service.Tier))
|
|
}
|
|
|
|
return s.String(), nil
|
|
}
|
|
|
|
// decodeServicesCaveatValue decodes a list of services from the expected format
|
|
// of a services caveat's value.
|
|
func decodeServicesCaveatValue(s string) ([]Service, error) {
|
|
if s == "" {
|
|
return nil, ErrNoServices
|
|
}
|
|
|
|
rawServices := strings.Split(s, ",")
|
|
services := make([]Service, 0, len(rawServices))
|
|
for _, rawService := range rawServices {
|
|
serviceInfo := strings.Split(rawService, ":")
|
|
if len(serviceInfo) != 2 {
|
|
return nil, ErrInvalidService
|
|
}
|
|
|
|
name, tierStr := serviceInfo[0], serviceInfo[1]
|
|
if name == "" {
|
|
return nil, fmt.Errorf("%w: %v", ErrInvalidService,
|
|
"empty name")
|
|
}
|
|
tier, err := strconv.Atoi(tierStr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrInvalidService, err)
|
|
}
|
|
|
|
services = append(services, Service{
|
|
Name: name,
|
|
Tier: ServiceTier(tier),
|
|
})
|
|
}
|
|
|
|
return services, nil
|
|
}
|
|
|
|
// NewCapabilitiesCaveat creates a new capabilities caveat for the given
|
|
// service.
|
|
func NewCapabilitiesCaveat(serviceName string, capabilities string) Caveat {
|
|
return Caveat{
|
|
Condition: serviceName + CondCapabilitiesSuffix,
|
|
Value: capabilities,
|
|
}
|
|
}
|