Skip to content

Commit

Permalink
WIP: proof: add multiverse RPC proof courier
Browse files Browse the repository at this point in the history
  • Loading branch information
ffranr committed Aug 29, 2023
1 parent b875569 commit 5e2fce0
Showing 1 changed file with 185 additions and 0 deletions.
185 changes: 185 additions & 0 deletions proof/courier.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"crypto/sha512"
"crypto/tls"
"encoding/hex"
"fmt"
"net/url"
"sync"
Expand All @@ -14,6 +15,8 @@ import (
"github.com/lightninglabs/lightning-node-connect/hashmailrpc"
"github.com/lightninglabs/taproot-assets/asset"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/taprpc"
unirpc "github.com/lightninglabs/taproot-assets/taprpc/universerpc"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
Expand All @@ -37,6 +40,10 @@ const (
// TODO(ffranr): Rename to HashmailCourier (use protocol name rather
// than service).
ApertureCourier = "hashmail"

// UniverseRpcCourier is a courier that uses the daemon universe RPC
// endpoints to deliver proofs.
UniverseRpcCourierType = "universerpc"
)

// CourierHarness interface is an integration testing harness for a proof
Expand Down Expand Up @@ -100,6 +107,8 @@ func ParseCourierAddrUrl(addr url.URL) (CourierAddr, error) {
switch addr.Scheme {
case ApertureCourier:
return NewHashMailCourierAddr(addr)
case UniverseRpcCourierType:
return NewUniverseRpcCourierAddr(addr)
}

return nil, fmt.Errorf("unknown courier address protocol: %v",
Expand Down Expand Up @@ -153,6 +162,70 @@ func NewHashMailCourierAddr(addr url.URL) (*HashMailCourierAddr, error) {
}, nil
}

// UniverseRpcCourierAddr is a universe RPC protocol specific implementation of
// the CourierAddr interface.
type UniverseRpcCourierAddr struct {
addr url.URL
}

// Url returns the url.URL representation of the courier address.
func (h *UniverseRpcCourierAddr) Url() *url.URL {
return &h.addr
}

// NewCourier generates a new courier service handle.
func (h *UniverseRpcCourierAddr) NewCourier(_ context.Context,
cfg *CourierCfg) (Courier, error) {

// Ensure that the courier address is a universe RPC address.
if h.addr.Scheme != UniverseRpcCourierType {
return nil, fmt.Errorf("unsupported courier protocol: %v",
h.addr.Scheme)
}

// Connect to the universe RPC server.
dialOpts, err := serverDialOpts(cfg.HashMailCfg.TlsCertPath)
if err != nil {
return nil, err
}

serverAddr := fmt.Sprintf(
"%s:%s", h.addr.Hostname(), h.addr.Port(),
)
conn, err := grpc.Dial(serverAddr, dialOpts...)
if err != nil {
return nil, err
}

client := unirpc.NewUniverseClient(conn)

// Instantiate the events subscribers map.
subscribers := make(
map[uint64]*fn.EventReceiver[fn.Event],
)

return &UniverseRpcCourier{
cfg: cfg.HashMailCfg,
client: client,
deliveryLog: cfg.DeliveryLog,
subscribers: subscribers,
}, nil
}

// NewUniverseRpcCourierAddr generates a new universe RPC courier address from a
// given URL. This function also performs protocol specific address validation.
func NewUniverseRpcCourierAddr(addr url.URL) (*UniverseRpcCourierAddr, error) {
// We expect the port number to be specified.
if addr.Port() == "" {
return nil, fmt.Errorf("proof courier URI address port " +
"unspecified")
}

return &UniverseRpcCourierAddr{
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,
Expand Down Expand Up @@ -844,6 +917,118 @@ func (h *HashMailCourier) SetSubscribers(
// proof.Courier interface.
var _ Courier = (*HashMailCourier)(nil)

// UniverseRpcCourier is a universe RPC proof courier service handle. It
// implements the Courier interface.
type UniverseRpcCourier struct {
// cfg contains the courier's configuration parameters.
cfg *HashMailCourierCfg

// client is the RPC client that the courier will use to interact with
// the universe RPC server.
client unirpc.UniverseClient

// deliveryLog is the log that the courier will use to record the
// attempted delivery of proofs to the receiver.
deliveryLog DeliveryLog

// subscribers is a map of components that want to be notified on new
// events, keyed by their subscription ID.
subscribers map[uint64]*fn.EventReceiver[fn.Event]

// subscriberMtx guards the subscribers map and access to the
// subscriptionID.
subscriberMtx sync.Mutex
}

// DeliverProof attempts to delivery a proof to the receiver.
func (c *UniverseRpcCourier) DeliverProof(ctx context.Context,
recipient Recipient, annotatedProof *AnnotatedProof) error {

// Construct universe key.
universeID := unirpc.ID{
Id: &unirpc.ID_AssetId{
AssetId: recipient.AssetID[:],
},
}

// Serialize the script key in compressed format. The RPC endpoint can
// handle a compressed script key.
scriptKeyBytes := recipient.ScriptKey.SerializeUncompressed()

outPoint := annotatedProof.OutPoint

assetKey := unirpc.AssetKey{
Outpoint: &unirpc.AssetKey_Op{
Op: &unirpc.Outpoint{
HashStr: outPoint.Hash.String(),
Index: int32(outPoint.Index),
},
},
ScriptKey: &unirpc.AssetKey_ScriptKeyStr{
ScriptKeyStr: hex.EncodeToString(scriptKeyBytes),
},
}

universeKey := unirpc.UniverseKey{
Id: &universeID,
LeafKey: &assetKey,
}

// Construct asset leaf.
rpcAsset, err := taprpc.MarshalAsset(
ctx, annotatedProof.Asset, true, true, nil,
)
if err != nil {
return err
}

// Extract the last proof from the annotated proof.
proofFile := &File{}
err = proofFile.Decode(bytes.NewReader(annotatedProof.Blob))
if err != nil {
return err
}
rawProof, err := proofFile.RawLastProof()
if err != nil {
return err
}

assetLeaf := unirpc.AssetLeaf{
Asset: rpcAsset,
IssuanceProof: rawProof,
}

// Submit proof to courier.
_, err = c.client.InsertProof(ctx, &unirpc.AssetProof{
Key: &universeKey,
AssetLeaf: &assetLeaf,
})
return err
}

// ReceiveProof attempts to obtain a proof from the courier service. The proof
// is identified by the given locator.
func (c *UniverseRpcCourier) ReceiveProof(ctx context.Context,
recipient Recipient, loc Locator) (*AnnotatedProof, error) {

return nil, nil
}

// SetSubscribers sets the subscribers for the courier. This method is
// thread-safe.
func (c *UniverseRpcCourier) SetSubscribers(
subscribers map[uint64]*fn.EventReceiver[fn.Event]) {

c.subscriberMtx.Lock()
defer c.subscriberMtx.Unlock()

c.subscribers = subscribers
}

// A compile-time assertion to ensure the UniverseRpcCourier meets the
// proof.Courier interface.
var _ Courier = (*UniverseRpcCourier)(nil)

// DeliveryLog is an interface that allows the courier to log the (attempted)
// delivery of a proof.
type DeliveryLog interface {
Expand Down

0 comments on commit 5e2fce0

Please sign in to comment.