@@ -16,6 +16,7 @@ import (
1616 "github.com/lightninglabs/taproot-assets/proof"
1717 "github.com/lightninglabs/taproot-assets/tapdb/sqlc"
1818 "github.com/lightninglabs/taproot-assets/universe"
19+ "github.com/lightninglabs/taproot-assets/universe/supplyverifier"
1920 lfn "github.com/lightningnetwork/lnd/fn/v2"
2021 "github.com/lightningnetwork/lnd/keychain"
2122)
@@ -577,6 +578,146 @@ func upsertAssetGen(ctx context.Context, db UpsertAssetStore,
577578 return genAssetID , nil
578579}
579580
581+ // shouldInsertPreCommit determines whether a supply pre-commitment
582+ // output should be inserted for a given issuance proof.
583+ func shouldInsertPreCommit (proofType universe.ProofType ,
584+ issuanceProof proof.Proof , metaReveal * proof.MetaReveal ) bool {
585+
586+ // Only issuance proofs can carry supply pre-commitment outputs.
587+ if proofType != universe .ProofTypeIssuance {
588+ return false
589+ }
590+
591+ // Supply pre-commitment outputs apply only to asset groups.
592+ if issuanceProof .Asset .GroupKey == nil {
593+ return false
594+ }
595+
596+ // Without metadata, we can't determine pre-commitment support.
597+ if metaReveal == nil {
598+ return false
599+ }
600+
601+ // If the metadata indicates no supply commitment support, stop here.
602+ if ! metaReveal .UniverseCommitments {
603+ return false
604+ }
605+
606+ // A delegation key is mandatory for supply pre-commitment outputs.
607+ if metaReveal .DelegationKey .IsNone () {
608+ return false
609+ }
610+
611+ return true
612+ }
613+
614+ // maybeUpsertSupplyPreCommit inserts a supply pre-commitment output if the
615+ // asset group supports supply commitments and this is an issuance proof.
616+ func maybeUpsertSupplyPreCommit (ctx context.Context , dbTx UpsertAssetStore ,
617+ proofType universe.ProofType , issuanceProof proof.Proof ,
618+ metaReveal * proof.MetaReveal ) error {
619+
620+ if ! shouldInsertPreCommit (proofType , issuanceProof , metaReveal ) {
621+ return nil
622+ }
623+
624+ delegationKey , err := metaReveal .DelegationKey .UnwrapOrErr (
625+ errors .New ("missing delegation key" ),
626+ )
627+ if err != nil {
628+ return err
629+ }
630+
631+ preCommitOutput , err := supplyverifier .ExtractPreCommitOutput (
632+ issuanceProof , delegationKey ,
633+ )
634+ if err != nil {
635+ return fmt .Errorf ("unable to extract pre-commit " +
636+ "output: %w" , err )
637+ }
638+
639+ outPointBytes , err := encodeOutpoint (preCommitOutput .OutPoint ())
640+ if err != nil {
641+ return fmt .Errorf ("unable to encode supply pre-commit " +
642+ "outpoint: %w" , err )
643+ }
644+
645+ // Upsert the supply pre-commitment output.
646+ //
647+ // Encode the group key and taproot internal key.
648+ groupKeyBytes := schnorr .SerializePubKey (
649+ & issuanceProof .Asset .GroupKey .GroupPubKey ,
650+ )
651+ taprootInternalKeyBytes :=
652+ preCommitOutput .InternalKey .PubKey .SerializeCompressed ()
653+
654+ // Try to fetch an existing chain tx row from the database. We fetch
655+ // first instead of blindly upserting to avoid overwriting existing data
656+ // with null values.
657+ var chainTxDbID fn.Option [int64 ]
658+
659+ txIDBytes := fn .ByteSlice (issuanceProof .AnchorTx .TxHash ())
660+ chainTxn , err := dbTx .FetchChainTx (ctx , txIDBytes )
661+ switch {
662+ case errors .Is (err , sql .ErrNoRows ):
663+ // No existing chain tx, we'll insert a new one below.
664+
665+ case err != nil :
666+ return fmt .Errorf ("unable to fetch chain tx: %w" , err )
667+
668+ default :
669+ // We found an existing chain tx. If it has a valid block
670+ // height, then we'll use it. Otherwise, we'll insert a new one
671+ // below.
672+ if chainTxn .BlockHeight .Valid {
673+ chainTxDbID = fn .Some (chainTxn .TxnID )
674+ }
675+ }
676+
677+ // If we didn't find an existing chain tx, then we'll insert a new
678+ // one now.
679+ if chainTxDbID .IsNone () {
680+ blockHash := issuanceProof .BlockHeader .BlockHash ()
681+ txBytes , err := fn .Serialize (& issuanceProof .AnchorTx )
682+ if err != nil {
683+ return fmt .Errorf ("failed to serialize tx: %w" , err )
684+ }
685+
686+ txDbID , err := dbTx .UpsertChainTx (ctx , ChainTxParams {
687+ Txid : txIDBytes ,
688+ RawTx : txBytes ,
689+ BlockHeight : sqlInt32 (issuanceProof .BlockHeight ),
690+ BlockHash : blockHash .CloneBytes (),
691+ })
692+ if err != nil {
693+ return fmt .Errorf ("unable to upsert chain tx: %w" , err )
694+ }
695+
696+ chainTxDbID = fn .Some (txDbID )
697+ }
698+
699+ txDbID , err := chainTxDbID .UnwrapOrErr (
700+ errors .New ("missing chain tx db id" ),
701+ )
702+ if err != nil {
703+ return err
704+ }
705+
706+ _ , err = dbTx .UpsertSupplyPreCommit (
707+ ctx , sqlc.UpsertSupplyPreCommitParams {
708+ TaprootInternalKey : taprootInternalKeyBytes ,
709+ GroupKey : groupKeyBytes ,
710+ Outpoint : outPointBytes ,
711+ ChainTxnDbID : txDbID ,
712+ },
713+ )
714+ if err != nil {
715+ return fmt .Errorf ("unable to upsert supply pre-commit: %w" , err )
716+ }
717+
718+ return nil
719+ }
720+
580721// UpsertProofLeaf inserts or updates a proof leaf within the universe tree,
581722// stored at the base key. The metaReveal type is purely optional, and should be
582723// specified if the genesis proof committed to a non-zero meta hash.
@@ -795,13 +936,27 @@ func universeUpsertProofLeaf(ctx context.Context, dbTx BaseUniverseStore,
795936 return nil , fmt .Errorf ("unable to decode proof: %w" , err )
796937 }
797938
939+ // Upsert into the DB: the genesis point, asset genesis,
940+ // group key reveal, and the anchoring transaction for the issuance or
941+ // transfer.
798942 assetGenID , err := upsertAssetGen (
799943 ctx , dbTx , leaf .Genesis , leaf .GroupKey , & leafProof ,
800944 )
801945 if err != nil {
802946 return nil , err
803947 }
804948
949+ // If the asset group supports supply commitments and this is an
950+ // issuance proof, then we may need to log the supply pre-commitment
951+ // output.
952+ err = maybeUpsertSupplyPreCommit (
953+ ctx , dbTx , proofType , leafProof , metaReveal ,
954+ )
955+ if err != nil {
956+ return nil , fmt .Errorf ("unable to upsert supply " +
957+ "pre-commit: %w" , err )
958+ }
959+
805960 // If the block height isn't specified, then we'll attempt to extract it
806961 // from the proof itself.
807962 if blockHeight .IsNone () && leafProof .BlockHeight > 0 {
0 commit comments