lightning-terminal/subservers/taproot-assets.go

198 lines
5.9 KiB
Go

package subservers
import (
"context"
"strings"
"github.com/btcsuite/btcd/chaincfg"
restProxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/lightninglabs/lndclient"
tap "github.com/lightninglabs/taproot-assets"
"github.com/lightninglabs/taproot-assets/address"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/tapcfg"
"github.com/lightninglabs/taproot-assets/taprpc"
"github.com/lightningnetwork/lnd/lnrpc"
"google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
)
// taprootAssetsSubServer implements the SubServer interface.
type taprootAssetsSubServer struct {
*tap.Server
remote bool
lndRemote bool
cfg *tapcfg.Config
remoteCfg *RemoteDaemonConfig
errChan chan error
}
// A compile-time check to ensure that taprootAssetsSubServer implements
// SubServer.
var _ SubServer = (*taprootAssetsSubServer)(nil)
// NewTaprootAssetsSubServer returns a new tap implementation of the SubServer
// interface.
func NewTaprootAssetsSubServer(network string, cfg *tapcfg.Config,
remoteCfg *RemoteDaemonConfig, remote, lndRemote bool) SubServer {
// Overwrite the tap daemon's user agent name, so it sends "litd"
// instead of "tapd".
tap.SetAgentName("litd")
// A quirk of the address package is that it requires the exact testnet
// name, not the generic one...
if network == "testnet" {
network = chaincfg.TestNet3Params.Name
}
chainCfg := address.ParamsForChain(network)
return &taprootAssetsSubServer{
Server: tap.NewServer(&chainCfg),
cfg: cfg,
remoteCfg: remoteCfg,
remote: remote,
lndRemote: lndRemote,
errChan: make(chan error, 1),
}
}
// Name returns the name of the sub-server.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) Name() string {
return TAP
}
// Remote returns true if the sub-server is running remotely and so should be
// connected to instead of spinning up an integrated server.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) Remote() bool {
return t.remote
}
// RemoteConfig returns the config required to connect to the sub-server if it
// is running in remote mode.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) RemoteConfig() *RemoteDaemonConfig {
return t.remoteCfg
}
// Start starts the sub-server in integrated mode.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) Start(_ lnrpc.LightningClient,
lndGrpc *lndclient.GrpcLndServices, withMacaroonService bool) error {
t.cfg.RpcConf.NoMacaroons = !withMacaroonService
var err error
t.cfg, err = tapcfg.ValidateConfig(*t.cfg, log)
if err != nil {
return err
}
// If we're being called here, it means tapd is running in integrated
// mode. But we can only offer Taproot Asset channel functionality if
// lnd is also running in integrated mode.
enableChannelFeatures := !t.lndRemote
err = tapcfg.ConfigureSubServer(
t.Server, t.cfg, log, &lndGrpc.LndServices,
enableChannelFeatures, t.errChan,
)
if err != nil {
return err
}
return t.StartAsSubserver(lndGrpc)
}
// RegisterGrpcService must register the sub-server's GRPC server with the given
// registrar.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) RegisterGrpcService(
registrar grpc.ServiceRegistrar) {
t.Server.RegisterGrpcService(registrar)
}
// RegisterRestService registers the sub-server's REST handlers with the given
// endpoint.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) RegisterRestService(ctx context.Context,
mux *restProxy.ServeMux, endpoint string,
dialOpts []grpc.DialOption) error {
err := t.Server.RegisterRestService(ctx, mux, endpoint, dialOpts)
if err != nil {
return err
}
return nil
}
// ServerErrChan returns an error channel that should be listened on after
// starting the sub-server to listen for any runtime errors. It is optional and
// may be set to nil. This only applies in integrated mode.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) ServerErrChan() chan error {
return t.errChan
}
// MacPath returns the path to the sub-server's macaroon if it is not running in
// remote mode.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) MacPath() string {
return t.cfg.RpcConf.MacaroonPath
}
// Permissions returns a map of all RPC methods and their required macaroon
// permissions to access the sub-server.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) Permissions() map[string][]bakery.Op {
return taprpc.RequiredPermissions
}
// WhiteListedURLs returns a map of all the sub-server's URLs that can be
// accessed without a macaroon.
//
// NOTE: this is part of the SubServer interface.
func (t *taprootAssetsSubServer) WhiteListedURLs() map[string]struct{} {
// If the taproot-asset daemon is running in integrated mode, then we
// use cfg.RpcConf.AllowPublicUniProofCourier to determine if universe
// proof courier RPC endpoints should be included in the whitelist, as
// well as cfg.RpcConf.AllowPublicStats for the public stats endpoints.
// If it is running in remote mode however, we just allow the request
// through since the remote daemon will handle blocking the call if it
// is not whitelisted there.
publicUniRead := strings.Contains(t.cfg.Universe.PublicAccess, "r")
publicUniWrite := strings.Contains(t.cfg.Universe.PublicAccess, "w")
return taprpc.MacaroonWhitelist(
publicUniRead || t.remote, publicUniWrite || t.remote,
t.cfg.RpcConf.AllowPublicUniProofCourier || t.remote,
t.cfg.RpcConf.AllowPublicStats || t.remote,
)
}
// Impl returns the actual implementation of the sub-server. This might not be
// set if the sub-server is running in remote mode.
func (t *taprootAssetsSubServer) Impl() fn.Option[any] {
if t.Server == nil {
return fn.None[any]()
}
return fn.Some[any](t.Server)
}