Skip to content

Commit 3d1a126

Browse files
committed
multi: add, initialize and test mailbox RPC server
We now plug everything together and make sure that the auth mailbox server works by adding a new integration test.
1 parent 7bd0e6b commit 3d1a126

File tree

10 files changed

+253
-3
lines changed

10 files changed

+253
-3
lines changed

cmd/commands/conn.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/lightninglabs/taproot-assets/taprpc"
2121
"github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc"
2222
wrpc "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc"
23+
"github.com/lightninglabs/taproot-assets/taprpc/authmailboxrpc"
2324
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
2425
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
2526
tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
@@ -71,6 +72,7 @@ type RpcClientsBundle interface {
7172
tchrpc.TaprootAssetChannelsClient
7273
universerpc.UniverseClient
7374
tapdevrpc.TapDevClient
75+
authmailboxrpc.MailboxClient
7476
}
7577

7678
// getRpcClientBundle returns a bundle of all the RPC clients.
@@ -88,6 +90,7 @@ func getRpcClientBundle(ctx *cli.Context) (RpcClientsBundle, func()) {
8890
channelsClient := tchrpc.NewTaprootAssetChannelsClient(conn)
8991
universeClient := universerpc.NewUniverseClient(conn)
9092
devClient := tapdevrpc.NewTapDevClient(conn)
93+
authMailboxClient := authmailboxrpc.NewMailboxClient(conn)
9194

9295
// Use an inline struct to eliminate the need for defining an additional
9396
// struct, reducing potential confusion with RpcClientsBundle.
@@ -99,6 +102,7 @@ func getRpcClientBundle(ctx *cli.Context) (RpcClientsBundle, func()) {
99102
tchrpc.TaprootAssetChannelsClient
100103
universerpc.UniverseClient
101104
tapdevrpc.TapDevClient
105+
authmailboxrpc.MailboxClient
102106
}{
103107
TaprootAssetsClient: tapClient,
104108
AssetWalletClient: assetWalletClient,
@@ -107,6 +111,7 @@ func getRpcClientBundle(ctx *cli.Context) (RpcClientsBundle, func()) {
107111
TaprootAssetChannelsClient: channelsClient,
108112
UniverseClient: universeClient,
109113
TapDevClient: devClient,
114+
MailboxClient: authMailboxClient,
110115
}, cleanUp
111116
}
112117

config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/lightninglabs/lndclient"
1010
"github.com/lightninglabs/taproot-assets/address"
11+
"github.com/lightninglabs/taproot-assets/authmailbox"
1112
"github.com/lightninglabs/taproot-assets/monitoring"
1213
"github.com/lightninglabs/taproot-assets/proof"
1314
"github.com/lightninglabs/taproot-assets/rfq"
@@ -158,6 +159,8 @@ type Config struct {
158159

159160
SignalInterceptor signal.Interceptor
160161

162+
MboxServerConfig authmailbox.ServerConfig
163+
161164
ReOrgWatcher *tapgarden.ReOrgWatcher
162165

163166
AssetMinter tapgarden.Planter

itest/authmailbox_test.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package itest
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/btcsuite/btcd/txscript"
8+
"github.com/btcsuite/btcd/wire"
9+
"github.com/lightninglabs/taproot-assets/asset"
10+
"github.com/lightninglabs/taproot-assets/authmailbox"
11+
"github.com/lightninglabs/taproot-assets/internal/test"
12+
"github.com/lightninglabs/taproot-assets/proof"
13+
"github.com/lightningnetwork/lnd/lntest/wait"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
// testAuthMailboxStoreAndFetchMessage tests that we can store and fetch auth
18+
// mailbox messages using the auth mailbox server.
19+
func testAuthMailboxStoreAndFetchMessage(t *harnessTest) {
20+
ctx := context.Background()
21+
22+
// We first need to create two on-chain outputs that we can use to
23+
// send messages to the auth mailbox server.
24+
internalKey := test.RandPubKey(t.t)
25+
merkleRoot := test.RandBytes(32)
26+
27+
pkScriptBip86, err := txscript.PayToTaprootScript(
28+
txscript.ComputeTaprootKeyNoScript(internalKey),
29+
)
30+
require.NoError(t.t, err)
31+
32+
pkScriptTapscript, err := txscript.PayToTaprootScript(
33+
txscript.ComputeTaprootOutputKey(
34+
internalKey, merkleRoot,
35+
),
36+
)
37+
require.NoError(t.t, err)
38+
39+
txHash, err := t.lndHarness.Miner().SendOutputs([]*wire.TxOut{
40+
{
41+
Value: 100000,
42+
PkScript: pkScriptBip86,
43+
},
44+
{
45+
Value: 100000,
46+
PkScript: pkScriptTapscript,
47+
},
48+
}, 10)
49+
require.NoError(t.t, err)
50+
51+
blockHash := t.lndHarness.Miner().GenerateBlocks(1)[0]
52+
bestBlockHash, blockHeight := t.lndHarness.Miner().GetBestBlock()
53+
require.Equal(t.t, blockHash, bestBlockHash)
54+
55+
block := t.lndHarness.Miner().GetBlock(blockHash)
56+
57+
require.Len(t.t, block.Transactions, 2)
58+
tx := block.Transactions[1]
59+
require.Equal(t.t, tx.TxHash(), *txHash)
60+
61+
txMerkleProof, err := proof.NewTxMerkleProof(block.Transactions, 1)
62+
require.NoError(t.t, err)
63+
64+
txProof1 := proof.TxProof{
65+
MsgTx: *tx,
66+
BlockHeader: block.Header,
67+
BlockHeight: uint32(blockHeight),
68+
MerkleProof: *txMerkleProof,
69+
ClaimedOutPoint: wire.OutPoint{
70+
Hash: *txHash,
71+
Index: 0,
72+
},
73+
InternalKey: *internalKey,
74+
}
75+
txProof2 := proof.TxProof{
76+
MsgTx: *tx,
77+
BlockHeader: block.Header,
78+
BlockHeight: uint32(blockHeight),
79+
MerkleProof: *txMerkleProof,
80+
ClaimedOutPoint: wire.OutPoint{
81+
Hash: *txHash,
82+
Index: 1,
83+
},
84+
InternalKey: *internalKey,
85+
MerkleRoot: merkleRoot,
86+
}
87+
88+
// Before we store any message, we set up a listener to make sure we
89+
// can receive messages.
90+
lndClient, err := t.newLndClient(t.tapd.cfg.LndNode)
91+
require.NoError(t.t, err)
92+
receiverKey, err := lndClient.WalletKit.DeriveNextKey(
93+
ctx, int32(asset.TaprootAssetsKeyFamily),
94+
)
95+
require.NoError(t.t, err)
96+
97+
mboxClient := authmailbox.NewClient(&authmailbox.ClientConfig{
98+
ServerAddress: t.tapd.rpcHost(),
99+
SkipTlsVerify: true,
100+
Signer: lndClient.Signer,
101+
MinBackoff: time.Second,
102+
MaxBackoff: time.Second,
103+
})
104+
require.NoError(t.t, mboxClient.Start())
105+
106+
inboundChan := make(chan *authmailbox.ReceivedMessages, 10)
107+
subscription, err := mboxClient.StartAccountSubscription(
108+
ctx, inboundChan, *receiverKey, authmailbox.MessageFilter{},
109+
)
110+
require.NoError(t.t, err)
111+
112+
require.Eventually(t.t, func() bool {
113+
return subscription.IsSubscribed()
114+
}, defaultTimeout, wait.PollInterval)
115+
116+
t.t.Cleanup(func() {
117+
require.NoError(t.t, subscription.Stop())
118+
require.NoError(t.t, mboxClient.Stop())
119+
})
120+
121+
// Now we can start sending messages to the mailbox server.
122+
id, err := mboxClient.SendMessage(
123+
ctx, *receiverKey.PubKey, []byte("message 1"), txProof1, 1234,
124+
)
125+
require.NoError(t.t, err)
126+
require.Greater(t.t, id, uint64(0))
127+
id2, err := mboxClient.SendMessage(
128+
ctx, *receiverKey.PubKey, []byte("message 2"), txProof2, 2345,
129+
)
130+
require.NoError(t.t, err)
131+
require.Greater(t.t, id2, uint64(0))
132+
133+
// We check that we can't use the same tx proof again.
134+
_, err = mboxClient.SendMessage(
135+
ctx, *receiverKey.PubKey, []byte("message 3"), txProof1, 3456,
136+
)
137+
require.ErrorContains(t.t, err, proof.ErrTxMerkleProofExists.Error())
138+
139+
// We also make sure that the TX proof is properly validated.
140+
txProof1.MerkleRoot = test.RandBytes(32)
141+
_, err = mboxClient.SendMessage(
142+
ctx, *receiverKey.PubKey, []byte("message 3"), txProof1, 3456,
143+
)
144+
require.ErrorContains(
145+
t.t, err, "validating proof: claimed output pk script doesn't "+
146+
"match constructed Taproot output key pk script",
147+
)
148+
149+
// And now we should have two messages in the mailbox.
150+
select {
151+
case msgs := <-inboundChan:
152+
require.Len(t.t, msgs.Messages, 1)
153+
msg := msgs.Messages[0]
154+
require.Equal(t.t, id, msg.MessageId)
155+
require.Equal(t.t, []byte("message 1"), msg.EncryptedPayload)
156+
157+
case <-time.After(defaultTimeout):
158+
require.Fail(t.t, "timed out waiting for message 1")
159+
}
160+
161+
select {
162+
case msgs := <-inboundChan:
163+
require.Len(t.t, msgs.Messages, 1)
164+
msg := msgs.Messages[0]
165+
require.Equal(t.t, id2, msg.MessageId)
166+
require.Equal(t.t, []byte("message 2"), msg.EncryptedPayload)
167+
168+
case <-time.After(defaultTimeout):
169+
require.Fail(t.t, "timed out waiting for message 2")
170+
}
171+
}

itest/tapd_harness.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/lightninglabs/taproot-assets/tapdb"
2323
"github.com/lightninglabs/taproot-assets/taprpc"
2424
"github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc"
25+
"github.com/lightninglabs/taproot-assets/taprpc/authmailboxrpc"
2526
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
2627
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
2728
tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
@@ -102,6 +103,7 @@ type tapdHarness struct {
102103
tchrpc.TaprootAssetChannelsClient
103104
universerpc.UniverseClient
104105
tapdevrpc.TapDevClient
106+
authmailboxrpc.MailboxClient
105107
}
106108

107109
// tapdConfig holds all configuration items that are required to start a tapd
@@ -442,6 +444,7 @@ func (hs *tapdHarness) start(expectErrExit bool) error {
442444
)
443445
hs.UniverseClient = universerpc.NewUniverseClient(rpcConn)
444446
hs.TapDevClient = tapdevrpc.NewTapDevClient(rpcConn)
447+
hs.MailboxClient = authmailboxrpc.NewMailboxClient(rpcConn)
445448

446449
return nil
447450
}

itest/test_list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,10 @@ var testCases = []*testCase{
347347
name: "pre commit output",
348348
test: testPreCommitOutput,
349349
},
350+
{
351+
name: "auth mailbox message store and fetch",
352+
test: testAuthMailboxStoreAndFetchMessage,
353+
},
350354
}
351355

352356
var optionalTestCases = []*testCase{

log.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package taprootassets
33
import (
44
"github.com/btcsuite/btclog/v2"
55
"github.com/lightninglabs/taproot-assets/address"
6+
"github.com/lightninglabs/taproot-assets/authmailbox"
67
"github.com/lightninglabs/taproot-assets/commitment"
78
"github.com/lightninglabs/taproot-assets/monitoring"
89
"github.com/lightninglabs/taproot-assets/proof"
@@ -118,6 +119,9 @@ func SetupLoggers(root *build.SubLoggerManager,
118119
AddSubLogger(
119120
root, tapchannel.Subsystem, interceptor, tapchannel.UseLogger,
120121
)
122+
AddSubLogger(
123+
root, authmailbox.Subsystem, interceptor, authmailbox.UseLogger,
124+
)
121125
}
122126

123127
// AddSubLogger is a helper method to conveniently create and register the

server.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
1616
"github.com/lightninglabs/lndclient"
1717
"github.com/lightninglabs/taproot-assets/address"
18+
"github.com/lightninglabs/taproot-assets/authmailbox"
1819
"github.com/lightninglabs/taproot-assets/fn"
1920
"github.com/lightninglabs/taproot-assets/monitoring"
2021
"github.com/lightninglabs/taproot-assets/rfqmsg"
@@ -63,6 +64,7 @@ type Server struct {
6364
cfg *Config
6465

6566
*rpcServer
67+
mboxServer *authmailbox.Server
6668
macaroonService *lndclient.MacaroonService
6769

6870
quit chan struct{}
@@ -178,6 +180,8 @@ func (s *Server) initialize(interceptorChain *rpcperms.InterceptorChain) error {
178180
return fmt.Errorf("unable to create rpc server: %w", err)
179181
}
180182

183+
s.mboxServer = authmailbox.NewServer(&s.cfg.MboxServerConfig)
184+
181185
// First, we'll start the main batched asset minter.
182186
if err := s.cfg.AssetMinter.Start(); err != nil {
183187
return fmt.Errorf("unable to start asset minter: %w", err)
@@ -240,13 +244,20 @@ func (s *Server) initialize(interceptorChain *rpcperms.InterceptorChain) error {
240244
return fmt.Errorf("unable to start RPC server: %w", err)
241245
}
242246

247+
shutdownFuncs["rpcServer"] = s.rpcServer.Stop
248+
249+
if err := s.mboxServer.Start(); err != nil {
250+
return fmt.Errorf("unable to start auth mailbox server: %w",
251+
err)
252+
}
253+
243254
// This does have no effect if starting the rpc server is the last step
244255
// in this function, but its better to have it here in case we add more
245256
// steps in the future.
246257
//
247258
// NOTE: if this is not the last step in the function, feel free to
248259
// delete this comment.
249-
shutdownFuncs["rpcServer"] = s.rpcServer.Stop
260+
shutdownFuncs["mboxServer"] = s.mboxServer.Stop
250261

251262
shutdownFuncs = nil
252263

@@ -360,6 +371,11 @@ func (s *Server) RunUntilShutdown(mainErrChan <-chan error) error {
360371
return mkErr("error registering gRPC server: %v", err)
361372
}
362373

374+
err = s.mboxServer.RegisterWithGrpcServer(grpcServer)
375+
if err != nil {
376+
return mkErr("error registering auth mailbox server: %v", err)
377+
}
378+
363379
// All the necessary components have been registered, so we can
364380
// actually start listening for requests.
365381
err = startGrpcListen(s.cfg, grpcServer, grpcListeners)
@@ -371,7 +387,7 @@ func (s *Server) RunUntilShutdown(mainErrChan <-chan error) error {
371387
// direct tapd to connect to its loopback address rather than a
372388
// wildcard to prevent certificate issues when accessing the proxy
373389
// externally.
374-
stopProxy, err := startRestProxy(s.cfg, s.rpcServer)
390+
stopProxy, err := startRestProxy(s.cfg, s.rpcServer, s.mboxServer)
375391
if err != nil {
376392
return mkErr("error starting REST proxy: %v", err)
377393
}
@@ -511,7 +527,9 @@ func startGrpcListen(cfg *Config, grpcServer *grpc.Server,
511527

512528
// startRestProxy starts the given REST proxy on the listeners found in the
513529
// config.
514-
func startRestProxy(cfg *Config, rpcServer *rpcServer) (func(), error) {
530+
func startRestProxy(cfg *Config, rpcServer *rpcServer,
531+
mboxServer *authmailbox.Server) (func(), error) {
532+
515533
// We use the first RPC listener as the destination for our REST proxy.
516534
// If the listener is set to listen on all interfaces, we replace it
517535
// with localhost, as we cannot dial it directly.
@@ -579,6 +597,13 @@ func startRestProxy(cfg *Config, rpcServer *rpcServer) (func(), error) {
579597
return nil, err
580598
}
581599

600+
err = mboxServer.RegisterWithRestProxy(
601+
ctx, mux, cfg.RestDialOpts, restProxyDest,
602+
)
603+
if err != nil {
604+
return nil, err
605+
}
606+
582607
// Wrap the default grpc-gateway handler with the WebSocket handler.
583608
restHandler := lnrpc.NewWebSocketProxy(
584609
mux, rpcsLog, cfg.WSPingInterval, cfg.WSPongWait,

0 commit comments

Comments
 (0)