@@ -7953,7 +7953,7 @@ func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,
79537953 AssetId : assetID [:],
79547954 }
79557955
7956- // If this asset ID belongs to an asset group, then we'll display thiat
7956+ // If this asset ID belongs to an asset group, then we'll display that
79577957 // information as well.
79587958 //
79597959 // nolint:lll
@@ -8009,3 +8009,162 @@ func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,
80098009
80108010 return & resp , nil
80118011}
8012+
8013+ // RegisterTransfer informs the daemon about a new inbound transfer that has
8014+ // happened. This is used for interactive transfers where no TAP address is
8015+ // involved and the recipient is aware of the transfer through an out-of-band
8016+ // protocol but the daemon hasn't been informed about the completion of the
8017+ // transfer. For this to work, the proof must already be in the recipient's
8018+ // local universe (e.g. through the use of the universerpc.ImportProof RPC or
8019+ // the universe proof courier and universe sync mechanisms) and this call
8020+ // simply instructs the daemon to detect the transfer as an asset it owns.
8021+ func (r * rpcServer ) RegisterTransfer (ctx context.Context ,
8022+ req * taprpc.RegisterTransferRequest ) (* taprpc.RegisterTransferResponse ,
8023+ error ) {
8024+
8025+ // First, we'll perform some basic input validation.
8026+ switch {
8027+ case len (req .AssetId ) == 0 :
8028+ return nil , fmt .Errorf ("asset ID must be specified" )
8029+
8030+ case len (req .AssetId ) != 32 :
8031+ return nil , fmt .Errorf ("asset ID must be 32 bytes, was %d" ,
8032+ len (req .AssetId ))
8033+
8034+ case len (req .GroupKey ) > 0 && len (req .GroupKey ) != 33 :
8035+ return nil , fmt .Errorf ("group key must be 33 bytes, was %d" ,
8036+ len (req .GroupKey ))
8037+
8038+ case len (req .ScriptKey ) == 0 :
8039+ return nil , fmt .Errorf ("script key must be specified" )
8040+
8041+ case len (req .ScriptKey ) != 33 :
8042+ return nil , fmt .Errorf ("script key must be 33 bytes, was %d" ,
8043+ len (req .ScriptKey ))
8044+
8045+ case req .Outpoint == nil :
8046+ return nil , fmt .Errorf ("outpoint must be specified" )
8047+ }
8048+
8049+ // We'll query our local universe for the full proof. Since we're
8050+ // talking about a transfer here, we'll only look at transfer proofs.
8051+ var (
8052+ locator = proof.Locator {
8053+ AssetID : & asset.ID {},
8054+ }
8055+ err error
8056+ )
8057+ copy (locator .AssetID [:], req .AssetId )
8058+
8059+ if len (req .GroupKey ) > 0 {
8060+ locator .GroupKey , err = btcec .ParsePubKey (req .GroupKey )
8061+ if err != nil {
8062+ return nil , fmt .Errorf ("error parsing group key: %w" ,
8063+ err )
8064+ }
8065+ }
8066+
8067+ scriptPubKey , err := btcec .ParsePubKey (req .ScriptKey )
8068+ if err != nil {
8069+ return nil , fmt .Errorf ("error parsing script key: %w" , err )
8070+ }
8071+ locator .ScriptKey = * scriptPubKey
8072+
8073+ hash , err := chainhash .NewHash (req .Outpoint .Txid )
8074+ if err != nil {
8075+ return nil , err
8076+ }
8077+ locator .OutPoint = & wire.OutPoint {
8078+ Hash : * hash ,
8079+ Index : req .Outpoint .OutputIndex ,
8080+ }
8081+
8082+ // Before we query for the proof, we want to make sure the script key is
8083+ // already known to us. In an interactive transfer, we'd expect a script
8084+ // key to be derived on the recipient node, so it should already be
8085+ // registered. This is mainly to prevent the user from importing a proof
8086+ // for an asset that they won't be able to spend, because it doesn't
8087+ // belong to this node (which is the main issue with the old
8088+ // tapdevrpc.ImportProof RPC).
8089+ _ , err = r .cfg .DatabaseConfig .TapAddrBook .FetchScriptKey (
8090+ ctx , scriptPubKey ,
8091+ )
8092+ if err != nil {
8093+ return nil , fmt .Errorf ("error fetching script key, consider " +
8094+ "declaring it with DeclareScriptKey if it does " +
8095+ "belong to this node: %w" , err )
8096+ }
8097+
8098+ // Next, we make sure we don't already have this proof in the local
8099+ // archive (only in the universe). If we have, it means the user already
8100+ // imported it, and we don't want to overwrite it.
8101+ haveProof , err := r .cfg .ProofArchive .HasProof (ctx , locator )
8102+ if err != nil {
8103+ return nil , fmt .Errorf ("error checking if proof is available: " +
8104+ "%w" , err )
8105+ }
8106+ if haveProof {
8107+ return nil , fmt .Errorf ("proof already exists for this transfer" )
8108+ }
8109+
8110+ // We now fetch the full proof file from the local multiverse store,
8111+ // making sure we have the full proof chain for this transfer.
8112+ fullProvenance , err := r .cfg .Multiverse .FetchProof (ctx , locator )
8113+ if err != nil {
8114+ return nil , fmt .Errorf ("error fetching full proof: %w" , err )
8115+ }
8116+
8117+ // Let's make sure we can parse it as a file.
8118+ proofFile , err := fullProvenance .AsFile ()
8119+ if err != nil {
8120+ return nil , fmt .Errorf ("error converting proof to file: %w" ,
8121+ err )
8122+ }
8123+
8124+ // All seems well, we can now import the proof into our local proof
8125+ // archive, which will also materialize an asset in the asset database.
8126+ headerVerifier := tapgarden .GenHeaderVerifier (ctx , r .cfg .ChainBridge )
8127+ groupVerifier := tapgarden .GenGroupVerifier (ctx , r .cfg .MintingStore )
8128+ err = r .cfg .ProofArchive .ImportProofs (
8129+ ctx , headerVerifier , proof .DefaultMerkleVerifier , groupVerifier ,
8130+ r .cfg .ChainBridge , false , & proof.AnnotatedProof {
8131+ Locator : locator ,
8132+ Blob : fullProvenance ,
8133+ },
8134+ )
8135+ if err != nil {
8136+ return nil , fmt .Errorf ("error importing proof: %w" , err )
8137+ }
8138+
8139+ // In case this proof hasn't been buried sufficiently, let's also hand
8140+ // it to the re-org watcher.
8141+ err = r .cfg .ReOrgWatcher .MaybeWatch (
8142+ proofFile , r .cfg .ReOrgWatcher .DefaultUpdateCallback (),
8143+ )
8144+ if err != nil {
8145+ return nil , fmt .Errorf ("error watching received proof: %w" , err )
8146+ }
8147+
8148+ lastProof , err := proofFile .LastProof ()
8149+ if err != nil {
8150+ return nil , fmt .Errorf ("error getting last proof: %w" , err )
8151+ }
8152+
8153+ chainAsset , err := lastProof .ToChainAsset ()
8154+ if err != nil {
8155+ return nil , fmt .Errorf ("unable to convert proof to chain " +
8156+ "asset: %w" , err )
8157+ }
8158+
8159+ rpcAsset , err := r .MarshalChainAsset (
8160+ ctx , chainAsset , lastProof .MetaReveal , false , r .cfg .AddrBook ,
8161+ )
8162+ if err != nil {
8163+ return nil , fmt .Errorf ("unable to marshal chain asset: %w" ,
8164+ err )
8165+ }
8166+
8167+ return & taprpc.RegisterTransferResponse {
8168+ RegisteredAsset : rpcAsset ,
8169+ }, nil
8170+ }
0 commit comments