diff --git a/proof/courier.go b/proof/courier.go index 1f8c43ebf..e2b616b87 100644 --- a/proof/courier.go +++ b/proof/courier.go @@ -39,41 +39,6 @@ const ( ApertureCourier = "hashmail" ) -// NewCourierType returns the CourierType that corresponds to the given string. -func NewCourierType(scheme string) (CourierType, error) { - switch scheme { - case ApertureCourier: - return ApertureCourier, nil - } - - return DisabledCourier, fmt.Errorf("unknown courier address "+ - "protocol: %v", scheme) -} - -func ParseCourierAddr(addr string) (*url.URL, error) { - // Parse URI. - urlAddr, err := url.ParseRequestURI(addr) - if err != nil { - return nil, fmt.Errorf("invalid proof courier URI address: %w", - err) - } - - // Validate port number. - if urlAddr.Port() == "" { - return nil, fmt.Errorf("proof courier URI address port "+ - "unspecified: %w", err) - } - - // Validate protocol supported. - _, err = NewCourierType(urlAddr.Scheme) - if err != nil { - return nil, fmt.Errorf("invalid proof courier protocol: %w", - err) - } - - return urlAddr, nil -} - // CourierHarness interface is an integration testing harness for a proof // courier service. type CourierHarness interface { @@ -90,20 +55,123 @@ type CourierHarness interface { // receiver can use this to fetch a proof from the sender. // // TODO(roasbeef): FileSystemCourier, RpcCourier -type Courier[Addr any] interface { +type Courier interface { // DeliverProof attempts to delivery a proof to the receiver, using the // information in the Addr type. - DeliverProof(context.Context, Addr, *AnnotatedProof) error + DeliverProof(context.Context, Recipient, *AnnotatedProof) error // ReceiveProof attempts to obtain a proof as identified by the passed // locator from the source encapsulated within the specified address. - ReceiveProof(context.Context, Addr, Locator) (*AnnotatedProof, error) + ReceiveProof(context.Context, Recipient, Locator) (*AnnotatedProof, + error) // SetSubscribers sets the set of subscribers that will be notified // of proof courier related events. SetSubscribers(map[uint64]*fn.EventReceiver[fn.Event]) } +// CourierAddr is a fully validated courier address (including protocol specific +// validation). +type CourierAddr interface { + // Url returns the url.URL representation of the courier address. + Url() *url.URL + + // NewCourier generates a new courier service handle. + NewCourier(ctx context.Context, cfg *CourierCfg) (Courier, error) +} + +// ParseCourierAddrString parses a proof courier address string and returns a +// protocol specific courier address instance. +func ParseCourierAddrString(addr string) (CourierAddr, error) { + // Parse URI. + urlAddr, err := url.ParseRequestURI(addr) + if err != nil { + return nil, fmt.Errorf("invalid proof courier URI address: %w", + err) + } + + return ParseCourierAddrUrl(*urlAddr) +} + +// ParseCourierAddrUrl parses a proof courier address url.URL and returns a +// protocol specific courier address instance. +func ParseCourierAddrUrl(addr url.URL) (CourierAddr, error) { + // Create new courier addr based on URL scheme. + switch addr.Scheme { + case ApertureCourier: + return NewHashMailCourierAddr(addr) + } + + return nil, fmt.Errorf("unknown courier address protocol: %v", + addr.Scheme) +} + +// HashMailCourierAddr is a hashmail protocol specific implementation of the +// CourierAddr interface. +type HashMailCourierAddr struct { + addr url.URL +} + +// Url returns the url.URL representation of the hashmail courier address. +func (h *HashMailCourierAddr) Url() *url.URL { + return &h.addr +} + +// NewCourier generates a new courier service handle. +func (h *HashMailCourierAddr) NewCourier(_ context.Context, + cfg *CourierCfg) (Courier, error) { + + hashMailBox, err := NewHashMailBox(&h.addr, cfg.HashMailCfg.TlsCertPath) + if err != nil { + return nil, fmt.Errorf("unable to make mailbox: %v", + err) + } + + subscribers := make( + map[uint64]*fn.EventReceiver[fn.Event], + ) + return &HashMailCourier{ + cfg: cfg.HashMailCfg, + mailbox: hashMailBox, + deliveryLog: cfg.DeliveryLog, + subscribers: subscribers, + }, nil +} + +// NewHashMailCourierAddr generates a new hashmail courier address from a given +// URL. This function also performs hashmail protocol specific address +// validation. +func NewHashMailCourierAddr(addr url.URL) (*HashMailCourierAddr, error) { + // We expect the port number to be specified for a hashmail service. + if addr.Port() == "" { + return nil, fmt.Errorf("hashmail proof courier URI address " + + "port unspecified") + } + + return &HashMailCourierAddr{ + addr, + }, nil +} + +// NewCourier instantiates a new courier service handle given a service URL +// address. +func NewCourier(ctx context.Context, addr url.URL, cfg *CourierCfg) (Courier, + error) { + + courierAddr, err := ParseCourierAddrUrl(addr) + if err != nil { + return nil, err + } + + return courierAddr.NewCourier(ctx, cfg) +} + +type CourierCfg struct { + HashMailCfg *HashMailCourierCfg + + DeliveryLog DeliveryLog +} + // ProofMailbox represents an abstract store-and-forward mailbox that can be // used to send/receive proofs. type ProofMailbox interface { @@ -403,7 +471,8 @@ type BackoffCfg struct { MaxBackoff time.Duration `long:"maxbackoff" description:"The maximum backoff time to wait before retrying to deliver the proof to the receiver."` } -// HashMailCourier is an implementation of the Courier interfaces that +// HashMailCourier is a hashmail proof courier service handle. It implements the +// Courier interface. type HashMailCourier struct { // cfg contains the courier's configuration parameters. cfg *HashMailCourierCfg @@ -423,23 +492,6 @@ type HashMailCourier struct { subscriberMtx sync.Mutex } -// NewHashMailCourier implements the Courier interface using the specified -// ProofMailbox. This instance of the Courier relies on the Taproot Asset -// address itself as the parametrized address type. -func NewHashMailCourier(cfg *HashMailCourierCfg, mailbox ProofMailbox, - deliveryLog DeliveryLog) (*HashMailCourier, error) { - - subscribers := make( - map[uint64]*fn.EventReceiver[fn.Event], - ) - return &HashMailCourier{ - cfg: cfg, - mailbox: mailbox, - deliveryLog: deliveryLog, - subscribers: subscribers, - }, nil -} - // DeliverProof attempts to delivery a proof to the receiver, using the // information in the Addr type. // @@ -790,7 +842,7 @@ func (h *HashMailCourier) SetSubscribers( // A compile-time assertion to ensure the HashMailCourier meets the // proof.Courier interface. -var _ Courier[Recipient] = (*HashMailCourier)(nil) +var _ Courier = (*HashMailCourier)(nil) // DeliveryLog is an interface that allows the courier to log the (attempted) // delivery of a proof. diff --git a/rpcserver.go b/rpcserver.go index 542783256..c56f46b5b 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -991,13 +991,19 @@ func (r *rpcServer) NewAddr(ctx context.Context, // the default specified in the config. courierAddr := r.cfg.DefaultProofCourierAddr if in.ProofCourierAddr != "" { - courierAddr, err = proof.ParseCourierAddr( + addr, err := proof.ParseCourierAddrString( in.ProofCourierAddr, ) if err != nil { return nil, fmt.Errorf("invalid proof courier "+ "address: %w", err) } + + // At this point, we do not intend on creating a proof courier + // service instance. We are only interested in parsing and + // validating the address. We therefore convert the address into + // an url.URL type for storage in the address book. + courierAddr = addr.Url() } // Check that the proof courier address is set. This should never diff --git a/tapcfg/server.go b/tapcfg/server.go index 02c52701d..6f909b4e4 100644 --- a/tapcfg/server.go +++ b/tapcfg/server.go @@ -161,7 +161,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, fallbackHashmailCourierAddr := fmt.Sprintf( "%s://%s", proof.ApertureCourier, fallbackHashMailAddr, ) - proofCourierAddr, err := proof.ParseCourierAddr( + proofCourierAddr, err := proof.ParseCourierAddrString( fallbackHashmailCourierAddr, ) if err != nil { @@ -171,7 +171,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, // If default proof courier address is set, use it as the default. if cfg.DefaultProofCourierAddr != "" { - proofCourierAddr, err = proof.ParseCourierAddr( + proofCourierAddr, err = proof.ParseCourierAddrString( cfg.DefaultProofCourierAddr, ) if err != nil { @@ -180,12 +180,12 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, } } - var hashMailCourier proof.Courier[proof.Recipient] + var hashMailCourier proof.Courier if cfg.HashMailCourier != nil && - proofCourierAddr.Scheme == proof.ApertureCourier { + proofCourierAddr.Type() == proof.ApertureCourier { hashMailBox, err := proof.NewHashMailBox( - proofCourierAddr, cfg.HashMailCourier.TlsCertPath, + proofCourierAddr.Url(), cfg.HashMailCourier.TlsCertPath, ) if err != nil { return nil, fmt.Errorf("unable to make mailbox: %v", @@ -201,6 +201,11 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, } } + proofCourierCfg := proof.CourierCfg{ + HashMailCfg: cfg.HashMailCourier, + DeliveryLog: assetStore, + } + reOrgWatcher := tapgarden.NewReOrgWatcher(&tapgarden.ReOrgWatcherConfig{ ChainBridge: chainBridge, ProofArchive: proofArchive, @@ -303,20 +308,20 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, }), AssetCustodian: tapgarden.NewCustodian( &tapgarden.CustodianConfig{ - ChainParams: &tapChainParams, - WalletAnchor: walletAnchor, - ChainBridge: chainBridge, - AddrBook: addrBook, - ProofArchive: proofArchive, - ProofNotifier: assetStore, - ErrChan: mainErrChan, - ProofCourier: hashMailCourier, - ProofWatcher: reOrgWatcher, + ChainParams: &tapChainParams, + WalletAnchor: walletAnchor, + ChainBridge: chainBridge, + AddrBook: addrBook, + ProofArchive: proofArchive, + ProofNotifier: assetStore, + ErrChan: mainErrChan, + ProofCourierCfg: &proofCourierCfg, + ProofWatcher: reOrgWatcher, }, ), ChainBridge: chainBridge, AddrBook: addrBook, - DefaultProofCourierAddr: proofCourierAddr, + DefaultProofCourierAddr: proofCourierAddr.Url(), ProofArchive: proofArchive, AssetWallet: assetWallet, CoinSelect: coinSelect, diff --git a/tapfreighter/chain_porter.go b/tapfreighter/chain_porter.go index 0b76f82e3..76079b09b 100644 --- a/tapfreighter/chain_porter.go +++ b/tapfreighter/chain_porter.go @@ -57,7 +57,7 @@ type ChainPorterConfig struct { // ProofCourier is used to optionally deliver the final proof to the // user using an asynchronous transport mechanism. - ProofCourier proof.Courier[proof.Recipient] + ProofCourier proof.Courier // ProofWatcher is used to watch new proofs for their anchor transaction // to be confirmed safely with a minimum number of confirmations. diff --git a/tapgarden/custodian.go b/tapgarden/custodian.go index ac4d49d0b..7fc71ad8f 100644 --- a/tapgarden/custodian.go +++ b/tapgarden/custodian.go @@ -46,9 +46,9 @@ type CustodianConfig struct { // being available in the relational database). ProofNotifier proof.NotifyArchiver - // ProofCourier is used to optionally deliver the final proof to the - // user using an asynchronous transport mechanism. - ProofCourier proof.Courier[proof.Recipient] + // ProofCourierCfg is a general config applicable to all proof courier + // service handles. + ProofCourierCfg *proof.CourierCfg // ProofWatcher is used to watch new proofs for their anchor transaction // to be confirmed safely with a minimum number of confirmations. @@ -337,7 +337,13 @@ func (c *Custodian) inspectWalletTx(walletTx *lndclient.Transaction) error { return err } - if c.cfg.ProofCourier == nil || addr == nil { + // TODO(ffranr): This proof courier disabled check should be + // removed. It was implemented because some integration test do + // not setup and use a proof courier. + proofCourierDisabled := c.cfg.ProofCourierCfg == nil || + c.cfg.ProofCourierCfg.HashMailCfg == nil || + addr == nil + if proofCourierDisabled { continue } @@ -351,16 +357,30 @@ func (c *Custodian) inspectWalletTx(walletTx *lndclient.Transaction) error { ctx, cancel := c.WithCtxQuitNoTimeout() defer cancel() + assetID := addr.AssetID + log.Debugf("Waiting to receive proof for script key %x", addr.ScriptKey.SerializeCompressed()) - assetID := addr.AssetID + // Initiate proof courier service handle from the proof + // courier address found in the Tap address. + courier, err := proof.NewCourier( + ctx, addr.ProofCourierAddr, + c.cfg.ProofCourierCfg, + ) + if err != nil { + log.Errorf("unable to initiate proof courier "+ + "service handle: %v", err) + return + } + + // Attempt to receive proof via proof courier service. recipient := proof.Recipient{ ScriptKey: &addr.ScriptKey, AssetID: assetID, Amount: addr.Amount, } - addrProof, err := c.cfg.ProofCourier.ReceiveProof( + addrProof, err := courier.ReceiveProof( ctx, recipient, proof.Locator{ AssetID: &assetID, ScriptKey: addr.ScriptKey,