55 "context"
66 "database/sql"
77 "fmt"
8+ "io"
89 "math/rand"
910 "net/url"
1011 "testing"
@@ -23,6 +24,7 @@ import (
2324 "github.com/lightninglabs/taproot-assets/tapdb"
2425 "github.com/lightninglabs/taproot-assets/tapgarden"
2526 "github.com/lightninglabs/taproot-assets/tapscript"
27+ "github.com/lightninglabs/taproot-assets/universe"
2628 "github.com/lightningnetwork/lnd/clock"
2729 "github.com/lightningnetwork/lnd/lnrpc"
2830 "github.com/lightningnetwork/lnd/lntest/wait"
@@ -59,9 +61,46 @@ func newAddrBookForDB(db *tapdb.BaseDB, keyRing *tapgarden.MockKeyRing,
5961 return book , tapdbBook
6062}
6163
64+ type mockVerifier struct {
65+ t * testing.T
66+ }
67+
68+ func newMockVerifier (t * testing.T ) * mockVerifier {
69+ return & mockVerifier {
70+ t : t ,
71+ }
72+ }
73+
74+ func (m * mockVerifier ) Verify (_ context.Context , r io.Reader ,
75+ headerVerifier proof.HeaderVerifier ,
76+ groupVerifier proof.GroupVerifier ) (* proof.AssetSnapshot , error ) {
77+
78+ f := & proof.File {}
79+ err := f .Decode (r )
80+ require .NoError (m .t , err )
81+
82+ lastProof , err := f .LastProof ()
83+ require .NoError (m .t , err )
84+
85+ ac , err := commitment .NewAssetCommitment (& lastProof .Asset )
86+ require .NoError (m .t , err )
87+ tc , err := commitment .NewTapCommitment (ac )
88+ require .NoError (m .t , err )
89+
90+ return & proof.AssetSnapshot {
91+ Asset : & lastProof .Asset ,
92+ OutPoint : lastProof .OutPoint (),
93+ OutputIndex : lastProof .InclusionProof .OutputIndex ,
94+ AnchorBlockHash : lastProof .BlockHeader .BlockHash (),
95+ AnchorTx : & lastProof .AnchorTx ,
96+ InternalKey : lastProof .InclusionProof .InternalKey ,
97+ ScriptRoot : tc ,
98+ }, nil
99+ }
100+
62101// newProofArchive creates a new instance of the MultiArchiver.
63102func newProofArchiveForDB (t * testing.T , db * tapdb.BaseDB ) (* proof.MultiArchiver ,
64- * tapdb.AssetStore ) {
103+ * tapdb.AssetStore , * tapdb. MultiverseStore ) {
65104
66105 txCreator := func (tx * sql.Tx ) tapdb.ActiveAssetsStore {
67106 return db .WithTx (tx )
@@ -78,7 +117,14 @@ func newProofArchiveForDB(t *testing.T, db *tapdb.BaseDB) (*proof.MultiArchiver,
78117 assetStore ,
79118 )
80119
81- return proofArchive , assetStore
120+ multiverseDB := tapdb .NewTransactionExecutor (
121+ db , func (tx * sql.Tx ) tapdb.BaseMultiverseStore {
122+ return db .WithTx (tx )
123+ },
124+ )
125+ multiverse := tapdb .NewMultiverseStore (multiverseDB )
126+
127+ return proofArchive , assetStore , multiverse
82128}
83129
84130type custodianHarness struct {
@@ -93,6 +139,7 @@ type custodianHarness struct {
93139 addrBook * address.Book
94140 syncer * tapgarden.MockAssetSyncer
95141 assetDB * tapdb.AssetStore
142+ multiverse * tapdb.MultiverseStore
96143 courier * proof.MockProofCourier
97144}
98145
@@ -192,6 +239,48 @@ func (h *custodianHarness) assertAddrsRegistered(
192239 }
193240}
194241
242+ // addProofFileToMultiverse adds the given proof to the multiverse store.
243+ func (h * custodianHarness ) addProofFileToMultiverse (p * proof.AnnotatedProof ) {
244+ f := & proof.File {}
245+ err := f .Decode (bytes .NewReader (p .Blob ))
246+ require .NoError (h .t , err )
247+
248+ ctx := context .Background ()
249+ ctxt , cancel := context .WithTimeout (ctx , testTimeout )
250+ defer cancel ()
251+
252+ for i := uint32 (0 ); i < uint32 (f .NumProofs ()); i ++ {
253+ transition , err := f .ProofAt (i )
254+ require .NoError (h .t , err )
255+
256+ rawTransition , err := f .RawProofAt (i )
257+ require .NoError (h .t , err )
258+
259+ id := universe .NewUniIDFromAsset (transition .Asset )
260+ key := universe.LeafKey {
261+ OutPoint : transition .OutPoint (),
262+ ScriptKey : fn .Ptr (asset .NewScriptKey (
263+ transition .Asset .ScriptKey .PubKey ,
264+ )),
265+ }
266+ leaf := & universe.Leaf {
267+ GenesisWithGroup : universe.GenesisWithGroup {
268+ Genesis : transition .Asset .Genesis ,
269+ GroupKey : transition .Asset .GroupKey ,
270+ },
271+ RawProof : rawTransition ,
272+ Asset : & transition .Asset ,
273+ Amt : transition .Asset .Amount ,
274+ }
275+ h .t .Logf ("Importing proof with script key %x and outpoint %v " +
276+ "into multiverse" ,
277+ key .ScriptKey .PubKey .SerializeCompressed (),
278+ key .OutPoint )
279+ _ , err = h .multiverse .UpsertProofLeaf (ctxt , id , key , leaf , nil )
280+ require .NoError (h .t , err )
281+ }
282+ }
283+
195284func newHarness (t * testing.T ,
196285 initialAddrs []* address.AddrWithKeyInfo ) * custodianHarness {
197286
@@ -201,7 +290,10 @@ func newHarness(t *testing.T,
201290 syncer := tapgarden .NewMockAssetSyncer ()
202291 db := tapdb .NewTestDB (t )
203292 addrBook , tapdbBook := newAddrBookForDB (db .BaseDB , keyRing , syncer )
204- _ , assetDB := newProofArchiveForDB (t , db .BaseDB )
293+
294+ _ , assetDB , multiverse := newProofArchiveForDB (t , db .BaseDB )
295+ notifier := proof .NewMultiArchiveNotifier (assetDB , multiverse )
296+
205297 courier := proof .NewMockProofCourier ()
206298 courierDispatch := & proof.MockProofCourierDispatcher {
207299 Courier : courier ,
@@ -214,14 +306,18 @@ func newHarness(t *testing.T,
214306 require .NoError (t , err )
215307 }
216308
309+ archive := proof .NewMultiArchiver (
310+ newMockVerifier (t ), testTimeout , assetDB ,
311+ )
312+
217313 errChan := make (chan error , 1 )
218314 cfg := & tapgarden.CustodianConfig {
219315 ChainParams : chainParams ,
220316 ChainBridge : chainBridge ,
221317 WalletAnchor : walletAnchor ,
222318 AddrBook : addrBook ,
223- ProofArchive : assetDB ,
224- ProofNotifier : assetDB ,
319+ ProofArchive : archive ,
320+ ProofNotifier : notifier ,
225321 ProofCourierDispatcher : courierDispatch ,
226322 ProofWatcher : proofWatcher ,
227323 ErrChan : errChan ,
@@ -238,6 +334,7 @@ func newHarness(t *testing.T,
238334 addrBook : addrBook ,
239335 syncer : syncer ,
240336 assetDB : assetDB ,
337+ multiverse : multiverse ,
241338 courier : courier ,
242339 }
243340}
@@ -315,6 +412,11 @@ func randProof(t *testing.T, outputIndex int, tx *wire.MsgTx,
315412 Genesis : * genesis ,
316413 Amount : addr .Amount ,
317414 ScriptKey : asset .NewScriptKey (& addr .ScriptKey ),
415+ PrevWitnesses : []asset.Witness {
416+ {
417+ PrevID : & asset.PrevID {},
418+ },
419+ },
318420 }
319421 if addr .GroupKey != nil {
320422 a .GroupKey = & asset.GroupKey {
@@ -653,6 +755,49 @@ func mustMakeAddr(t *testing.T,
653755 return addr
654756}
655757
758+ // TestProofInMultiverseOnly tests that the custodian imports a proof correctly
759+ // into the local archive if it's only present in the multiverse.
760+ func TestProofInMultiverseOnly (t * testing.T ) {
761+ h := newHarness (t , nil )
762+
763+ // Before we start the custodian, we create a random address and a
764+ // corresponding wallet transaction.
765+ ctx := context .Background ()
766+
767+ addr , genesis := randAddr (h )
768+ err := h .tapdbBook .InsertAddrs (ctx , * addr )
769+ require .NoError (t , err )
770+
771+ // We now start the custodian and make sure it's started up correctly
772+ // and the pending event is registered.
773+ require .NoError (t , h .c .Start ())
774+ h .assertStartup ()
775+ h .assertAddrsRegistered (addr )
776+
777+ // Receiving a TX for it should create a pending event and cause the
778+ // proof courier to attempt to fetch it. But the courier won't find it.
779+ outputIdx , tx := randWalletTx (addr )
780+ h .walletAnchor .SubscribeTx <- * tx
781+ h .assertEventsPresent (1 , address .StatusTransactionDetected )
782+
783+ // We now stop the custodian again.
784+ require .NoError (t , h .c .Stop ())
785+
786+ // The proof is only in the multiverse, not in the local archive. And we
787+ // add the proof to the multiverse before starting the custodian, so the
788+ // notification for it doesn't trigger.
789+ mockProof := randProof (t , outputIdx , tx .Tx , genesis , addr )
790+ h .addProofFileToMultiverse (mockProof )
791+
792+ // And a new start should import the proof into the local archive.
793+ h .c = tapgarden .NewCustodian (h .cfg )
794+ require .NoError (t , h .c .Start ())
795+ t .Cleanup (func () {
796+ require .NoError (t , h .c .Stop ())
797+ })
798+ h .assertStartup ()
799+ }
800+
656801// TestAddrMatchesAsset tests that the AddrMatchesAsset function works
657802// correctly.
658803func TestAddrMatchesAsset (t * testing.T ) {
0 commit comments