assets: make tapd macaroon path network-aware

Build the default tapd admin macaroon path from the configured Bitcoin
network while preserving explicit path overrides. Add regression tests for
tapd credential loading and network-specific path selection.
This commit is contained in:
Boris Nagaev 2026-07-17 16:28:18 -05:00
parent 74cbb651bc
commit 378825414b
No known key found for this signature in database
4 changed files with 128 additions and 4 deletions

View file

@ -49,13 +49,21 @@ type TapdConfig struct {
// DefaultTapdConfig returns a default configuration to connect to a taproot
// assets daemon.
func DefaultTapdConfig() *TapdConfig {
defaultTapdDir := btcutil.AppDataDir("tapd", false)
return DefaultTapdConfigForNetwork(
btcutil.AppDataDir("tapd", false), "mainnet",
)
}
// DefaultTapdConfigForNetwork returns the default tapd configuration rooted in
// defaultTapdDir for the given Bitcoin network. Passing the directory
// explicitly keeps path construction testable without accessing the user's
// real tapd data.
func DefaultTapdConfigForNetwork(defaultTapdDir, network string) *TapdConfig {
return &TapdConfig{
Activate: false,
Host: "localhost:10029",
MacaroonPath: filepath.Join(
defaultTapdDir, "mainnet", "admin.macaroon",
defaultTapdDir, "data", network, "admin.macaroon",
),
TLSPath: filepath.Join(defaultTapdDir, "tls.cert"),
RFQtimeout: defaultRfqTimeout,

View file

@ -1,6 +1,10 @@
package assets
import (
"encoding/pem"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
@ -8,6 +12,7 @@ import (
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
"gopkg.in/macaroon.v2"
)
// TestDefaultTapdConfig tests that the default tapd connection paths match
@ -16,10 +21,62 @@ func TestDefaultTapdConfig(t *testing.T) {
defaultTapdDir := btcutil.AppDataDir("tapd", false)
config := DefaultTapdConfig()
require.Equal(t, filepath.Join(
defaultTapdDir, "data", "mainnet", "admin.macaroon",
), config.MacaroonPath)
require.Equal(
t, filepath.Join(defaultTapdDir, "mainnet", "admin.macaroon"),
config.MacaroonPath,
t, filepath.Join(defaultTapdDir, "tls.cert"), config.TLSPath,
)
}
// TestTapdConfigClientConn tests that the default tapd file layout can be used
// to construct a client connection.
func TestTapdConfigClientConn(t *testing.T) {
// Use an isolated tapd root so the test never reads from or writes to a
// user's real tapd data directory.
defaultTapdDir := t.TempDir()
network := "regtest"
macaroonPath := filepath.Join(
defaultTapdDir, "data", network, "admin.macaroon",
)
require.NoError(t, os.MkdirAll(filepath.Dir(macaroonPath), 0o700))
// NewTapdClient parses the configured TLS certificate before creating
// its gRPC client. An httptest server provides a valid certificate
// without requiring a running tapd instance.
tlsServer := httptest.NewTLSServer(http.NotFoundHandler())
t.Cleanup(tlsServer.Close)
cert := tlsServer.Certificate()
certBytes := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE", Bytes: cert.Raw,
})
require.NoError(t, os.WriteFile(
filepath.Join(defaultTapdDir, "tls.cert"), certBytes, 0o600,
))
// Store a valid serialized macaroon at tapd's production path. This
// ensures connection setup tests the path itself rather than failing on
// malformed credentials.
mac, err := macaroon.New(
[]byte("root-key"), []byte("id"), "tapd",
macaroon.LatestVersion,
)
require.NoError(t, err)
macBytes, err := mac.MarshalBinary()
require.NoError(t, err)
require.NoError(t, os.WriteFile(macaroonPath, macBytes, 0o600))
// grpc.NewClient connects lazily, so constructing the tapd client verifies
// that both credentials can be loaded and parsed without needing a live
// tapd server.
config := DefaultTapdConfigForNetwork(defaultTapdDir, network)
client, err := NewTapdClient(config)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, client.cc.Close())
})
require.Equal(t, macaroonPath, config.MacaroonPath)
require.Equal(
t, filepath.Join(defaultTapdDir, "tls.cert"), config.TLSPath,
)

View file

@ -335,6 +335,14 @@ func Validate(cfg *Config) error {
)
}
// If the user doesn't specify Tapd.MacaroonPath, reassemble it with
// the configured Bitcoin network.
if cfg.Tapd.MacaroonPath == assets.DefaultTapdConfig().MacaroonPath {
cfg.Tapd.MacaroonPath = assets.DefaultTapdConfigForNetwork(
btcutil.AppDataDir("tapd", false), cfg.Network,
).MacaroonPath
}
// We'll also update the database file location as well, if it wasn't
// set.
if cfg.Sqlite.DatabaseFileName == defaultSqliteDatabasePath {

51
loopd/config_test.go Normal file
View file

@ -0,0 +1,51 @@
package loopd
import (
"path/filepath"
"testing"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/loop/assets"
"github.com/stretchr/testify/require"
)
// TestValidateTapdMacaroonPath tests that validation updates the default tapd
// macaroon path for the configured network without changing an explicit path.
func TestValidateTapdMacaroonPath(t *testing.T) {
customPath := filepath.Join(t.TempDir(), "custom.macaroon")
defaultConfig := assets.DefaultTapdConfig()
regtestConfig := assets.DefaultTapdConfigForNetwork(
btcutil.AppDataDir("tapd", false), "regtest",
)
tests := []struct {
name string
macaroonPath string
expectedPath string
}{
{
name: "default path",
macaroonPath: defaultConfig.MacaroonPath,
expectedPath: regtestConfig.MacaroonPath,
},
{
name: "explicit path",
macaroonPath: customPath,
expectedPath: customPath,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cfg := DefaultConfig()
cfg.Network = "regtest"
cfg.LoopDir = t.TempDir()
cfg.Tapd.MacaroonPath = test.macaroonPath
require.NoError(t, Validate(&cfg))
require.Equal(
t, test.expectedPath, cfg.Tapd.MacaroonPath,
)
})
}
}