Skip to content

Commit 7604d89

Browse files
committed
multi: add vpsbt validator
1 parent a6415f8 commit 7604d89

File tree

7 files changed

+201
-102
lines changed

7 files changed

+201
-102
lines changed

tapcfg/server.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,15 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
298298
virtualTxSigner := tap.NewLndRpcVirtualTxSigner(lndServices)
299299
coinSelect := tapfreighter.NewCoinSelect(assetStore)
300300
assetWallet := tapfreighter.NewAssetWallet(&tapfreighter.WalletConfig{
301-
CoinSelector: coinSelect,
302-
AssetProofs: proofArchive,
303-
AddrBook: tapdbAddrBook,
304-
KeyRing: keyRing,
305-
Signer: virtualTxSigner,
306-
TxValidator: &tap.ValidatorV0{},
307-
Wallet: walletAnchor,
308-
ChainParams: &tapChainParams,
301+
CoinSelector: coinSelect,
302+
AssetProofs: proofArchive,
303+
AddrBook: tapdbAddrBook,
304+
KeyRing: keyRing,
305+
Signer: virtualTxSigner,
306+
TxValidator: &tap.ValidatorV0{},
307+
WitnessValidator: &tap.WitnessValidatorV0{},
308+
Wallet: walletAnchor,
309+
ChainParams: &tapChainParams,
309310
})
310311

311312
// Addresses can have different proof couriers configured, but both

tapfreighter/wallet.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,10 @@ type WalletConfig struct {
384384
// transaction we create.
385385
TxValidator tapscript.TxValidator
386386

387+
// WitnessValidator allows us to validate the witnesses of vPSBTs
388+
// we create.
389+
WitnessValidator tapscript.WitnessValidator
390+
387391
// Wallet is used to fund+sign PSBTs for the transfer transaction.
388392
Wallet WalletAnchor
389393

@@ -1103,7 +1107,7 @@ func (f *AssetWallet) SignVirtualPacket(vPkt *tappsbt.VPacket,
11031107
// Asset leaves. The witness data for each input will be assigned for
11041108
// us.
11051109
err := tapscript.SignVirtualTransaction(
1106-
vPkt, f.cfg.Signer, f.cfg.TxValidator,
1110+
vPkt, f.cfg.Signer, f.cfg.WitnessValidator,
11071111
)
11081112
if err != nil {
11091113
return nil, fmt.Errorf("unable to generate Taproot Asset "+
@@ -1505,7 +1509,7 @@ func (f *AssetWallet) SignOwnershipProof(
15051509
ownedAsset.Copy(), f.cfg.ChainParams,
15061510
)
15071511
err := tapscript.SignVirtualTransaction(
1508-
vPkt, f.cfg.Signer, f.cfg.TxValidator,
1512+
vPkt, f.cfg.Signer, f.cfg.WitnessValidator,
15091513
)
15101514
if err != nil {
15111515
return nil, fmt.Errorf("unable to generate Taproot Asset "+

tapscript/interface.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ type TxValidator interface {
1717
prevAssets commitment.InputSet) error
1818
}
1919

20+
// WitnessValidator is the interface used to validate the witnesses of an asset
21+
// transfer. This method may be used in partially constructed asset transfers
22+
// to only check signature validity.
23+
type WitnessValidator interface {
24+
// ValidateWitnesses validates the generated witnesses of an asset
25+
// transfer.
26+
ValidateWitnesses(newAsset *asset.Asset,
27+
splitAssets []*commitment.SplitAsset,
28+
prevAssets commitment.InputSet) error
29+
}
30+
2031
// Signer is the interface used to compute the witness for a Taproot Asset
2132
// virtual TX.
2233
type Signer interface {

tapscript/send.go

Lines changed: 45 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ func PrepareOutputAssets(ctx context.Context, vPkt *tappsbt.VPacket) error {
617617
// signature over the asset transfer, verifying the transfer with the Taproot
618618
// Asset VM, and attaching that signature to the new Asset.
619619
func SignVirtualTransaction(vPkt *tappsbt.VPacket, signer Signer,
620-
validator TxValidator) error {
620+
validator WitnessValidator) error {
621621

622622
inputs := vPkt.Inputs
623623
outputs := vPkt.Outputs
@@ -632,13 +632,35 @@ func SignVirtualTransaction(vPkt *tappsbt.VPacket, signer Signer,
632632
// Identify new output asset. For splits, the new asset that receives
633633
// the signature is the one with the split root set to true.
634634
newAsset := outputs[0].Asset
635+
var splitAssets []*commitment.SplitAsset
635636
if isSplit {
636637
splitOut, err := vPkt.SplitRootOutput()
637638
if err != nil {
638639
return fmt.Errorf("no split root output found for "+
639640
"split transaction: %w", err)
640641
}
641642
newAsset = splitOut.Asset
643+
644+
// If the transfer includes an asset split, we have to validate
645+
// each split asset to ensure that our new Asset is committing
646+
// to a valid SplitCommitment.
647+
splitAssets = make([]*commitment.SplitAsset, len(outputs))
648+
for idx := range outputs {
649+
splitAssets[idx] = &commitment.SplitAsset{
650+
Asset: *outputs[idx].Asset,
651+
OutputIndex: outputs[idx].AnchorOutputIndex,
652+
}
653+
654+
// The output that houses the root asset in case of a
655+
// split has a special field for the split asset, which
656+
// actually contains the split commitment proof. We need
657+
// to use that one for the validation, as the root asset
658+
// is already validated as the newAsset.
659+
if outputs[idx].Type.IsSplitRoot() {
660+
splitAssets[idx].Asset =
661+
*outputs[idx].SplitAsset
662+
}
663+
}
642664
}
643665

644666
// Construct input set from all input assets.
@@ -679,57 +701,32 @@ func SignVirtualTransaction(vPkt *tappsbt.VPacket, signer Signer,
679701
newAsset.PrevWitnesses[idx].TxWitness = newWitness
680702
}
681703

682-
// Create an instance of the Taproot Asset VM and validate the transfer.
683-
verifySpend := func(splitAssets []*commitment.SplitAsset) error {
684-
newAssetCopy := newAsset.Copy()
685-
return validator.Execute(newAssetCopy, splitAssets, prevAssets)
686-
}
687-
688-
// If the transfer contains no asset splits, we only need to validate
689-
// the new asset with its witness attached, then we can exit early.
690-
if !isSplit {
691-
return verifySpend(nil)
692-
}
693-
694-
// If the transfer includes an asset split, we have to validate each
695-
// split asset to ensure that our new Asset is committing to a valid
696-
// SplitCommitment.
697-
splitAssets := make([]*commitment.SplitAsset, len(outputs))
698-
for idx := range outputs {
699-
splitAssets[idx] = &commitment.SplitAsset{
700-
Asset: *outputs[idx].Asset,
701-
OutputIndex: outputs[idx].AnchorOutputIndex,
702-
}
703-
704-
// The output that houses the root asset in case of a split has
705-
// a special field for the split asset, which actually contains
706-
// the split commitment proof. We need to use that one for the
707-
// validation, as the root asset is already validated as the
708-
// newAsset.
709-
if outputs[idx].Type.IsSplitRoot() {
710-
splitAssets[idx].Asset = *outputs[idx].SplitAsset
711-
}
712-
}
713-
if err := verifySpend(splitAssets); err != nil {
704+
err = validator.ValidateWitnesses(newAsset, splitAssets, prevAssets)
705+
if err != nil {
714706
return err
715707
}
716708

717-
// Update each split asset to store the root asset with the witness
718-
// attached, so the receiver can verify inclusion of the root asset.
719-
for idx := range outputs {
720-
splitAsset := outputs[idx].Asset
721-
722-
// The output that houses the root asset in case of a split has
723-
// a special field for the split asset. That asset is no longer
724-
// needed (and isn't committed to anywhere), but in order for it
725-
// to be validated externally, we still want to include it and
726-
// therefore also want to update it with the signed root asset.
727-
if outputs[idx].Type.IsSplitRoot() {
728-
splitAsset = outputs[idx].SplitAsset
729-
}
709+
if isSplit {
710+
// Update each split asset to store the root asset with the
711+
// witness attached, so the receiver can verify inclusion of the
712+
// root asset.
713+
for idx := range outputs {
714+
splitAsset := outputs[idx].Asset
715+
716+
// The output that houses the root asset in case of a
717+
// split has a special field for the split asset. That
718+
// asset is no longer needed (and isn't committed to
719+
// anywhere), but in order for it to be validated
720+
// externally, we still want to include it and therefore
721+
// also want to update it with the signed root asset.
722+
if outputs[idx].Type.IsSplitRoot() {
723+
splitAsset = outputs[idx].SplitAsset
724+
}
730725

731-
splitCommitment := splitAsset.PrevWitnesses[0].SplitCommitment
732-
splitCommitment.RootAsset = *newAsset.Copy()
726+
splitCommitment :=
727+
splitAsset.PrevWitnesses[0].SplitCommitment
728+
splitCommitment.RootAsset = *newAsset.Copy()
729+
}
733730
}
734731

735732
return nil

0 commit comments

Comments
 (0)