55 "context"
66 "errors"
77 "fmt"
8+ "slices"
89 "sync"
910 "time"
1011
@@ -724,10 +725,150 @@ func freezeMintingBatch(ctx context.Context, batchStore MintingStore,
724725 )
725726}
726727
728+ // filterFinalizedBatches separates a set of batches into two sets based on
729+ // their batch state.
730+ func filterFinalizedBatches (batches []* MintingBatch ) ([]* MintingBatch ,
731+ []* MintingBatch ) {
732+
733+ finalized := []* MintingBatch {}
734+ nonFinalized := []* MintingBatch {}
735+
736+ fn .ForEach (batches , func (batch * MintingBatch ) {
737+ switch batch .State () {
738+ case BatchStateFinalized :
739+ finalized = append (finalized , batch )
740+ default :
741+ nonFinalized = append (nonFinalized , batch )
742+ }
743+ })
744+
745+ return finalized , nonFinalized
746+ }
747+
748+ // fetchFinalizedBatch fetches the assets of a batch in their genesis state,
749+ // given a batch populated with seedlings.
750+ func fetchFinalizedBatch (ctx context.Context , batchStore MintingStore ,
751+ archiver proof.Archiver , batch * MintingBatch ) (* MintingBatch , error ) {
752+
753+ // Collect genesis TX information from the batch to build the proof
754+ // locators.
755+ anchorOutputIndex := extractAnchorOutputIndex (batch .GenesisPacket )
756+ signedTx , err := psbt .Extract (batch .GenesisPacket .Pkt )
757+ if err != nil {
758+ return nil , err
759+ }
760+
761+ genOutpoint := extractGenesisOutpoint (signedTx )
762+ genScript := signedTx .TxOut [anchorOutputIndex ].PkScript
763+ anchorOutpoint := wire.OutPoint {
764+ Hash : signedTx .TxHash (),
765+ Index : anchorOutputIndex ,
766+ }
767+
768+ batchAssets := make ([]* asset.Asset , 0 , len (batch .Seedlings ))
769+ assetMetas := make (AssetMetas )
770+ for _ , seedling := range batch .Seedlings {
771+ gen := seedling .Genesis (genOutpoint , anchorOutputIndex )
772+ issuanceProof , err := archiver .FetchIssuanceProof (
773+ ctx , gen .ID (), anchorOutpoint ,
774+ )
775+ if err != nil {
776+ return nil , err
777+ }
778+
779+ proofFile , err := issuanceProof .AsFile ()
780+ if err != nil {
781+ return nil , err
782+ }
783+
784+ if proofFile .NumProofs () != 1 {
785+ return nil , fmt .Errorf ("expected single proof for " +
786+ "issuance proof" )
787+ }
788+
789+ rawProof , err := proofFile .RawLastProof ()
790+ if err != nil {
791+ return nil , err
792+ }
793+
794+ // Decode the sprouted asset from the issuance proof.
795+ var sproutedAsset asset.Asset
796+ assetRecord := proof .AssetLeafRecord (& sproutedAsset )
797+ err = proof .SparseDecode (bytes .NewReader (rawProof ), assetRecord )
798+ if err != nil {
799+ return nil , fmt .Errorf ("unable to decode issuance " +
800+ "proof: %w" , err )
801+ }
802+
803+ if ! sproutedAsset .IsGenesisAsset () {
804+ return nil , fmt .Errorf ("decoded asset is not a " +
805+ "genesis asset" )
806+ }
807+
808+ // Populate the key info for the script key and group key.
809+ if sproutedAsset .ScriptKey .PubKey == nil {
810+ return nil , fmt .Errorf ("decoded asset is missing " +
811+ "script key" )
812+ }
813+
814+ tweakedScriptKey , err := batchStore .FetchScriptKeyByTweakedKey (
815+ ctx , sproutedAsset .ScriptKey .PubKey ,
816+ )
817+ if err != nil {
818+ return nil , err
819+ }
820+
821+ sproutedAsset .ScriptKey .TweakedScriptKey = tweakedScriptKey
822+ if sproutedAsset .GroupKey != nil {
823+ assetGroup , err := batchStore .FetchGroupByGroupKey (
824+ ctx , & sproutedAsset .GroupKey .GroupPubKey ,
825+ )
826+ if err != nil {
827+ return nil , err
828+ }
829+
830+ sproutedAsset .GroupKey = assetGroup .GroupKey
831+ }
832+
833+ batchAssets = append (batchAssets , & sproutedAsset )
834+ scriptKey := asset .ToSerialized (sproutedAsset .ScriptKey .PubKey )
835+ assetMetas [scriptKey ] = seedling .Meta
836+ }
837+
838+ // Verify that we can reconstruct the genesis output script used in the
839+ // anchor TX.
840+ batchSibling := batch .TapSibling ()
841+ var tapSibling * chainhash.Hash
842+ if len (batchSibling ) != 0 {
843+ var err error
844+ tapSibling , err = chainhash .NewHash (batchSibling )
845+ if err != nil {
846+ return nil , err
847+ }
848+ }
849+
850+ tapCommitment , err := VerifyOutputScript (
851+ batch .BatchKey .PubKey , tapSibling , genScript , batchAssets ,
852+ )
853+
854+ if err != nil {
855+ return nil , err
856+ }
857+
858+ // With the batch assets validated, construct the populated finalized
859+ // batch.
860+ batch .Seedlings = nil
861+ finalizedBatch := batch .Copy ()
862+ finalizedBatch .RootAssetCommitment = tapCommitment
863+ finalizedBatch .AssetMetas = assetMetas
864+
865+ return finalizedBatch , nil
866+ }
867+
727868// ListBatches returns the single batch specified by the batch key, or the set
728869// of batches not yet finalized on disk.
729870func listBatches (ctx context.Context , batchStore MintingStore ,
730- genBuilder asset.GenesisTxBuilder ,
871+ archiver proof. Archiver , genBuilder asset.GenesisTxBuilder ,
731872 params ListBatchesParams ) ([]* VerboseBatch , error ) {
732873
733874 var (
@@ -747,12 +888,54 @@ func listBatches(ctx context.Context, batchStore MintingStore,
747888 return nil , err
748889 }
749890
750- verboseBatches := fn .Map (batches , func (b * MintingBatch ) * VerboseBatch {
751- return & VerboseBatch {
752- MintingBatch : b ,
753- UnsealedSeedlings : nil ,
891+ var (
892+ finalBatches , nonFinalBatches = filterFinalizedBatches (batches )
893+ verboseBatches []* VerboseBatch
894+ )
895+
896+ switch {
897+ case len (finalBatches ) == 0 :
898+ verboseBatches = fn .Map (batches ,
899+ func (b * MintingBatch ) * VerboseBatch {
900+ return & VerboseBatch {
901+ MintingBatch : b ,
902+ UnsealedSeedlings : nil ,
903+ }
904+ },
905+ )
906+
907+ // For finalized batches, we need to fetch the assets from the proof
908+ // archiver, not the DB.
909+ default :
910+ finalizedBatches := make ([]* MintingBatch , 0 , len (finalBatches ))
911+ for _ , batch := range finalBatches {
912+ finalizedBatch , err := fetchFinalizedBatch (
913+ ctx , batchStore , archiver , batch ,
914+ )
915+ if err != nil {
916+ return nil , err
917+ }
918+
919+ finalizedBatches = append (
920+ finalizedBatches , finalizedBatch ,
921+ )
754922 }
755- })
923+
924+ // Re-sort the batches by creation time for consistent display.
925+ allBatches := append (nonFinalBatches , finalizedBatches ... )
926+ slices .SortFunc (allBatches , func (a , b * MintingBatch ) int {
927+ return a .CreationTime .Compare (b .CreationTime )
928+ })
929+
930+ verboseBatches = fn .Map (allBatches ,
931+ func (b * MintingBatch ) * VerboseBatch {
932+ return & VerboseBatch {
933+ MintingBatch : b ,
934+ UnsealedSeedlings : nil ,
935+ }
936+ },
937+ )
938+ }
756939
757940 // Return the batches without any extra asset group info.
758941 if ! params .Verbose {
@@ -787,11 +970,9 @@ func listBatches(ctx context.Context, batchStore MintingStore,
787970 // Before we can build the group key requests for each seedling,
788971 // we must fetch the genesis point and anchor index for the
789972 // batch.
790- anchorOutputIndex := uint32 (0 )
791- if currentBatch .GenesisPacket .ChangeOutputIndex == 0 {
792- anchorOutputIndex = 1
793- }
794-
973+ anchorOutputIndex := extractAnchorOutputIndex (
974+ currentBatch .GenesisPacket ,
975+ )
795976 genesisPoint := extractGenesisOutpoint (
796977 currentBatch .GenesisPacket .Pkt .UnsignedTx ,
797978 )
@@ -1030,8 +1211,8 @@ func (c *ChainPlanter) gardener() {
10301211
10311212 ctx , cancel := c .WithCtxQuit ()
10321213 batches , err := listBatches (
1033- ctx , c .cfg .Log , c .cfg .GenTxBuilder ,
1034- * listBatchesParams ,
1214+ ctx , c .cfg .Log , c .cfg .ProofFiles ,
1215+ c . cfg . GenTxBuilder , * listBatchesParams ,
10351216 )
10361217 cancel ()
10371218 if err != nil {
@@ -1311,11 +1492,9 @@ func (c *ChainPlanter) sealBatch(ctx context.Context, params SealParams,
13111492
13121493 // Before we can build the group key requests for each seedling, we must
13131494 // fetch the genesis point and anchor index for the batch.
1314- anchorOutputIndex := uint32 (0 )
1315- if workingBatch .GenesisPacket .ChangeOutputIndex == 0 {
1316- anchorOutputIndex = 1
1317- }
1318-
1495+ anchorOutputIndex := extractAnchorOutputIndex (
1496+ workingBatch .GenesisPacket ,
1497+ )
13191498 genesisPoint := extractGenesisOutpoint (
13201499 workingBatch .GenesisPacket .Pkt .UnsignedTx ,
13211500 )
0 commit comments