@@ -248,7 +248,8 @@ func randIgnoreTupleGen(t *rapid.T,
248248 ScriptKey : asset .ToSerialized (scriptKey .PubKey ),
249249 OutPoint : op ,
250250 },
251- Amount : 100 ,
251+ Amount : 100 ,
252+ BlockHeight : rapid .Uint32Range (1 , 1000 ).Draw (t , "block_height" ),
252253 }
253254
254255 // Create a signature for the ignore tuple.
@@ -367,6 +368,101 @@ func setupSupplyTreeTestForProps(t *testing.T) (*SupplyTreeStore,
367368 return supplyStore , spec , eventGen
368369}
369370
371+ // createMintEventWithHeight creates a mint event with a specific block height.
372+ func createMintEventWithHeight (t * testing.T , groupKey * btcec.PublicKey ,
373+ height uint32 ) * supplycommit.NewMintEvent {
374+
375+ mintAsset := asset .RandAsset (t , asset .Normal )
376+ mintAsset .GroupKey = & asset.GroupKey {GroupPubKey : * groupKey }
377+ mintAsset .GroupKey .Witness = mintAsset .PrevWitnesses [0 ].TxWitness
378+
379+ mintProof := randProof (t , mintAsset )
380+ mintProof .BlockHeight = height
381+ mintProof .GroupKeyReveal = asset .NewGroupKeyRevealV0 (
382+ asset .ToSerialized (groupKey ), nil ,
383+ )
384+
385+ var proofBuf bytes.Buffer
386+ require .NoError (t , mintProof .Encode (& proofBuf ))
387+
388+ mintLeaf := universe.Leaf {
389+ GenesisWithGroup : universe.GenesisWithGroup {
390+ Genesis : mintAsset .Genesis ,
391+ GroupKey : mintAsset .GroupKey ,
392+ },
393+ Asset : & mintProof .Asset ,
394+ Amt : mintProof .Asset .Amount ,
395+ RawProof : proofBuf .Bytes (),
396+ }
397+
398+ mintKey := universe.AssetLeafKey {
399+ BaseLeafKey : universe.BaseLeafKey {
400+ OutPoint : mintProof .OutPoint (),
401+ ScriptKey : & mintProof .Asset .ScriptKey ,
402+ },
403+ AssetID : mintProof .Asset .ID (),
404+ }
405+
406+ return & supplycommit.NewMintEvent {
407+ LeafKey : mintKey ,
408+ IssuanceProof : mintLeaf ,
409+ }
410+ }
411+
412+ // createBurnEventWithHeight creates a burn event with a specific block height.
413+ func createBurnEventWithHeight (t * testing.T , baseGenesis asset.Genesis ,
414+ groupKey * asset.GroupKey , db BatchedUniverseTree ,
415+ height uint32 ) * supplycommit.NewBurnEvent {
416+
417+ burnAsset := createBurnAsset (t )
418+ burnAsset .Genesis = baseGenesis
419+ burnAsset .GroupKey = groupKey
420+
421+ burnProof := randProof (t , burnAsset )
422+ burnProof .BlockHeight = height
423+ burnProof .GenesisReveal = & baseGenesis
424+
425+ // Ensure genesis exists for this burn leaf in the DB.
426+ ctx := context .Background ()
427+ genesisPointID , err := upsertGenesisPoint (
428+ ctx , db , burnAsset .Genesis .FirstPrevOut ,
429+ )
430+ require .NoError (t , err )
431+ _ , err = upsertGenesis (
432+ ctx , db , genesisPointID , burnAsset .Genesis ,
433+ )
434+ require .NoError (t , err )
435+
436+ burnLeaf := & universe.BurnLeaf {
437+ UniverseKey : universe.AssetLeafKey {
438+ BaseLeafKey : universe.BaseLeafKey {
439+ OutPoint : burnProof .OutPoint (),
440+ ScriptKey : & burnProof .Asset .ScriptKey ,
441+ },
442+ AssetID : burnProof .Asset .ID (),
443+ },
444+ BurnProof : burnProof ,
445+ }
446+
447+ return & supplycommit.NewBurnEvent {
448+ BurnLeaf : * burnLeaf ,
449+ }
450+ }
451+
452+ // createIgnoreEventWithHeight creates an ignore event with a specific block
453+ // height.
454+ func createIgnoreEventWithHeight (t * testing.T , baseAssetID asset.ID ,
455+ db BatchedUniverseTree , height uint32 ) * supplycommit.NewIgnoreEvent {
456+
457+ signedTuple := randIgnoreTuple (t , db )
458+ signedTuple .IgnoreTuple .Val .ID = baseAssetID
459+ signedTuple .IgnoreTuple .Val .BlockHeight = height
460+
461+ return & supplycommit.NewIgnoreEvent {
462+ SignedIgnoreTuple : signedTuple ,
463+ }
464+ }
465+
370466// TestSupplyTreeStoreApplySupplyUpdates tests that the ApplySupplyUpdates meets
371467// a series of key invariant via property based testing.
372468func TestSupplyTreeStoreApplySupplyUpdates (t * testing.T ) {
@@ -538,3 +634,114 @@ func TestSupplyTreeStoreApplySupplyUpdates(t *testing.T) {
538634 )
539635 require .NoError (t , err )
540636}
637+
638+ // TestSupplyTreeStoreFetchSupplyLeavesByHeight tests the
639+ // FetchSupplyLeavesByHeight method.
640+ func TestSupplyTreeStoreFetchSupplyLeavesByHeight (t * testing.T ) {
641+ t .Parallel ()
642+
643+ supplyStore , spec , _ := setupSupplyTreeTestForProps (t )
644+ ctxb := context .Background ()
645+ dbTxer := supplyStore .db
646+
647+ groupKey , err := spec .UnwrapGroupKeyOrErr ()
648+ require .NoError (t , err )
649+ assetID := spec .UnwrapIdToPtr ()
650+
651+ fullGroupKey := & asset.GroupKey {
652+ GroupPubKey : * groupKey ,
653+ }
654+
655+ // Create events with specific block heights, we'll use these heights
656+ // below to ensure that the new leaf height is properly set/read all the
657+ // way down the call stack.
658+ mintEvent100 := createMintEventWithHeight (t , groupKey , 100 )
659+ burnEvent200 := createBurnEventWithHeight (
660+ t , asset .RandGenesis (t , asset .Normal ), fullGroupKey , dbTxer ,
661+ 200 ,
662+ )
663+ ignoreEvent300 := createIgnoreEventWithHeight (t , * assetID , dbTxer , 300 )
664+ mintEvent400 := createMintEventWithHeight (t , groupKey , 400 )
665+
666+ updates := []supplycommit.SupplyUpdateEvent {
667+ mintEvent100 , burnEvent200 , ignoreEvent300 , mintEvent400 ,
668+ }
669+
670+ // Apply updates.
671+ _ , err = supplyStore .ApplySupplyUpdates (ctxb , spec , updates )
672+ require .NoError (t , err )
673+
674+ testCases := []struct {
675+ name string
676+ startHeight uint32
677+ endHeight uint32
678+ expectedCount int
679+ expectedHeights []uint32
680+ }{
681+ {
682+ name : "range including first" ,
683+ startHeight : 0 ,
684+ endHeight : 150 ,
685+ expectedCount : 1 ,
686+ expectedHeights : []uint32 {100 },
687+ },
688+ {
689+ name : "range including second" ,
690+ startHeight : 150 ,
691+ endHeight : 250 ,
692+ expectedCount : 1 ,
693+ expectedHeights : []uint32 {200 },
694+ },
695+ {
696+ name : "range including all" ,
697+ startHeight : 0 ,
698+ endHeight : 500 ,
699+ expectedCount : 4 ,
700+ expectedHeights : []uint32 {100 , 200 , 300 , 400 },
701+ },
702+ {
703+ name : "exact range" ,
704+ startHeight : 100 ,
705+ endHeight : 400 ,
706+ expectedCount : 4 ,
707+ expectedHeights : []uint32 {100 , 200 , 300 , 400 },
708+ },
709+ {
710+ name : "inner range" ,
711+ startHeight : 101 ,
712+ endHeight : 399 ,
713+ expectedCount : 2 ,
714+ expectedHeights : []uint32 {200 , 300 },
715+ },
716+ {
717+ name : "range after all" ,
718+ startHeight : 501 ,
719+ endHeight : 1000 ,
720+ expectedCount : 0 ,
721+ expectedHeights : nil ,
722+ },
723+ {
724+ name : "range before all" ,
725+ startHeight : 0 ,
726+ endHeight : 99 ,
727+ expectedCount : 0 ,
728+ expectedHeights : nil ,
729+ },
730+ }
731+
732+ for _ , tc := range testCases {
733+ t .Run (tc .name , func (t * testing.T ) {
734+ leaves , err := supplyStore .FetchSupplyLeavesByHeight (
735+ ctxb , spec , tc .startHeight , tc .endHeight ,
736+ )
737+ require .NoError (t , err )
738+ require .Len (t , leaves , tc .expectedCount )
739+
740+ var heights []uint32
741+ for _ , leaf := range leaves {
742+ heights = append (heights , leaf .BlockHeight ())
743+ }
744+ require .ElementsMatch (t , tc .expectedHeights , heights )
745+ })
746+ }
747+ }
0 commit comments