Skip to content

Commit

Permalink
tapchannel: assert proof courier connection
Browse files Browse the repository at this point in the history
To make sure the universe proof courier address configured isn't only
formally valid but can also be connected to, we do a quick connection
check before requesting or accepting a channel funding action.
  • Loading branch information
guggero committed Feb 26, 2025
1 parent 57abc2e commit dbac40c
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 10 deletions.
32 changes: 29 additions & 3 deletions tapchannel/aux_funding_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ const (
// level ACK from the remote party before timing out.
ackTimeout = time.Second * 30

// proofCourierCheckTimeout is the amount of time we'll wait before we
// time out an attempt to connect to a proof courier when checking the
// configured address.
proofCourierCheckTimeout = time.Second * 5

// maxNumAssetIDs is the maximum number of fungible asset pieces (asset
// IDs) that can be committed to a single channel. The number needs to
// be limited to prevent the number of required HTLC signatures to be
Expand Down Expand Up @@ -1359,7 +1364,7 @@ func (f *FundingController) processFundingMsg(ctx context.Context,
// can't deal with the OP_TRUE funding output script key, as that's the
// same for asset channels out there. So the single mailbox would always
// be occupied.
if err := f.validateLocalProofCourier(); err != nil {
if err := f.validateLocalProofCourier(ctx); err != nil {
return tempPID, fmt.Errorf("unable to accept channel funding "+
"request, local proof courier is unsupported: %w", err)
}
Expand Down Expand Up @@ -1529,7 +1534,7 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
// with the OP_TRUE funding output script key, as that's the same for
// asset channels out there. So the single mailbox would always be
// occupied.
if err := f.validateLocalProofCourier(); err != nil {
if err := f.validateLocalProofCourier(fundReq.ctx); err != nil {
return fmt.Errorf("unable to fund channel, local proof "+
"courier is unsupported: %w", err)
}
Expand Down Expand Up @@ -2050,7 +2055,9 @@ func (f *FundingController) validateWitness(outAsset asset.Asset,
// validateLocalProofCourier checks if the local proof courier is supported by
// the funding controller. This is necessary to ensure that we can accept
// incoming asset channel funding requests.
func (f *FundingController) validateLocalProofCourier() error {
func (f *FundingController) validateLocalProofCourier(
ctx context.Context) error {

courierURL := f.cfg.DefaultCourierAddr

flagHelp := "please set a universe based (universerpc://) proof " +
Expand All @@ -2070,6 +2077,25 @@ func (f *FundingController) validateLocalProofCourier() error {
courierURL.Scheme, flagHelp)
}

// We now also make a quick test connection.
ctxt, cancel := context.WithTimeout(ctx, proofCourierCheckTimeout)
defer cancel()
courier, err := proof.NewUniverseRpcCourier(
ctxt, &proof.UniverseRpcCourierCfg{}, nil, nil, courierURL,
false,
)
if err != nil {
return fmt.Errorf("unable to test connection proof courier "+
"'%v': %v", courierURL.String(), err)
}

err = courier.Close()
if err != nil {
// We only log any disconnect errors, as they're not critical.
log.Warnf("Unable to disconnect from proof courier '%v': %v",
courierURL.String(), err)
}

return nil
}

Expand Down
64 changes: 57 additions & 7 deletions tapchannel/aux_funding_controller_test.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,76 @@
package tapchannel

import (
"context"
"fmt"
"net/url"
"testing"

"github.com/lightninglabs/taproot-assets/internal/test"
"github.com/lightninglabs/taproot-assets/proof"
"github.com/lightninglabs/taproot-assets/taprpc/universerpc"
"github.com/lightningnetwork/lnd/lntest/port"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)

func dummyURL(t *testing.T, protocol string) *url.URL {
urlString := fmt.Sprintf("%s://localhost:1234", protocol)
type mockUniverseServer struct {
universerpc.UnimplementedUniverseServer
}

func dummyURL(t *testing.T, protocol, addr string) *url.URL {
urlString := fmt.Sprintf("%s://%s", protocol, addr)
proofCourierAddr, err := proof.ParseCourierAddress(urlString)
require.NoError(t, err)

return proofCourierAddr
}

func TestValidateLocalProofCourier(t *testing.T) {
serverOpts := []grpc.ServerOption{
grpc.Creds(insecure.NewCredentials()),
}
grpcServer := grpc.NewServer(serverOpts...)

server := mockUniverseServer{}
universerpc.RegisterUniverseServer(grpcServer, &server)

// We also grab a port that is free to listen on for our negative test.
// Since we know the port is free, and we don't listen on it, we expect
// the connection to fail.
noConnectPort := port.NextAvailablePort()
noConnectAddr := fmt.Sprintf(test.ListenAddrTemplate, noConnectPort)

mockServerAddr, cleanup, err := test.StartMockGRPCServer(
t, grpcServer, true,
)
require.NoError(t, err)
t.Cleanup(cleanup)

tests := []struct {
name string
courierAddr *url.URL
expectErr string
}{
{
name: "valid universe rpc courier",
courierAddr: dummyURL(t, proof.UniverseRpcCourierType),
name: "valid universe rpc courier",
courierAddr: dummyURL(
t, proof.UniverseRpcCourierType, mockServerAddr,
),
},
{
name: "invalid courier type",
courierAddr: dummyURL(t, proof.HashmailCourierType),
name: "valid universe rpc courier, but can't connect",
courierAddr: dummyURL(
t, proof.UniverseRpcCourierType, noConnectAddr,
),
expectErr: "unable to connect to courier service",
},
{
name: "invalid courier type",
courierAddr: dummyURL(
t, proof.HashmailCourierType, mockServerAddr,
),
expectErr: "unsupported proof courier type " +
"'hashmail'",
},
Expand All @@ -52,7 +93,16 @@ func TestValidateLocalProofCourier(t *testing.T) {
},
}

err := fc.validateLocalProofCourier()
// We use a short timeout here, since we don't want to
// wait for the full default timeout of the funding
// controller
ctxb := context.Background()
ctxb, cancel := context.WithTimeout(
ctxb, test.StartupWaitTime*2,
)
defer cancel()

err := fc.validateLocalProofCourier(ctxb)
if tt.expectErr != "" {
require.ErrorContains(t, err, tt.expectErr)

Expand Down

0 comments on commit dbac40c

Please sign in to comment.