Skip to content

Commit

Permalink
multi: add vpsbt validator
Browse files Browse the repository at this point in the history
  • Loading branch information
GeorgeTsagk committed Feb 12, 2024
1 parent a6415f8 commit 45012e3
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 103 deletions.
17 changes: 9 additions & 8 deletions tapcfg/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,15 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
virtualTxSigner := tap.NewLndRpcVirtualTxSigner(lndServices)
coinSelect := tapfreighter.NewCoinSelect(assetStore)
assetWallet := tapfreighter.NewAssetWallet(&tapfreighter.WalletConfig{
CoinSelector: coinSelect,
AssetProofs: proofArchive,
AddrBook: tapdbAddrBook,
KeyRing: keyRing,
Signer: virtualTxSigner,
TxValidator: &tap.ValidatorV0{},
Wallet: walletAnchor,
ChainParams: &tapChainParams,
CoinSelector: coinSelect,
AssetProofs: proofArchive,
AddrBook: tapdbAddrBook,
KeyRing: keyRing,
Signer: virtualTxSigner,
TxValidator: &tap.ValidatorV0{},
VPsbtValidator: &tap.VPsbtValidatorV0{},
Wallet: walletAnchor,
ChainParams: &tapChainParams,
})

// Addresses can have different proof couriers configured, but both
Expand Down
7 changes: 5 additions & 2 deletions tapfreighter/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ type WalletConfig struct {
// transaction we create.
TxValidator tapscript.TxValidator

// VPsbtValidator allows us to validate each vPSBT we create.
VPsbtValidator tapscript.VPsbtValidator

// Wallet is used to fund+sign PSBTs for the transfer transaction.
Wallet WalletAnchor

Expand Down Expand Up @@ -1103,7 +1106,7 @@ func (f *AssetWallet) SignVirtualPacket(vPkt *tappsbt.VPacket,
// Asset leaves. The witness data for each input will be assigned for
// us.
err := tapscript.SignVirtualTransaction(
vPkt, f.cfg.Signer, f.cfg.TxValidator,
vPkt, f.cfg.Signer, f.cfg.VPsbtValidator,
)
if err != nil {
return nil, fmt.Errorf("unable to generate Taproot Asset "+
Expand Down Expand Up @@ -1505,7 +1508,7 @@ func (f *AssetWallet) SignOwnershipProof(
ownedAsset.Copy(), f.cfg.ChainParams,
)
err := tapscript.SignVirtualTransaction(
vPkt, f.cfg.Signer, f.cfg.TxValidator,
vPkt, f.cfg.Signer, f.cfg.VPsbtValidator,
)
if err != nil {
return nil, fmt.Errorf("unable to generate Taproot Asset "+
Expand Down
9 changes: 9 additions & 0 deletions tapscript/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ type TxValidator interface {
prevAssets commitment.InputSet) error
}

// VPsbtValidator is the interface used to validate a vPSBT. It is less strict
// compared to the TxValidator, as it focused on witness validation. This method
// may be used in partially constructed transfers.
type VPsbtValidator interface {
ValidateVPsbt(newAsset *asset.Asset,
splitAssets []*commitment.SplitAsset,
prevAssets commitment.InputSet) error
}

// Signer is the interface used to compute the witness for a Taproot Asset
// virtual TX.
type Signer interface {
Expand Down
93 changes: 45 additions & 48 deletions tapscript/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ func PrepareOutputAssets(ctx context.Context, vPkt *tappsbt.VPacket) error {
// signature over the asset transfer, verifying the transfer with the Taproot
// Asset VM, and attaching that signature to the new Asset.
func SignVirtualTransaction(vPkt *tappsbt.VPacket, signer Signer,
validator TxValidator) error {
validator VPsbtValidator) error {

inputs := vPkt.Inputs
outputs := vPkt.Outputs
Expand All @@ -632,13 +632,35 @@ func SignVirtualTransaction(vPkt *tappsbt.VPacket, signer Signer,
// Identify new output asset. For splits, the new asset that receives
// the signature is the one with the split root set to true.
newAsset := outputs[0].Asset
var splitAssets []*commitment.SplitAsset
if isSplit {
splitOut, err := vPkt.SplitRootOutput()
if err != nil {
return fmt.Errorf("no split root output found for "+
"split transaction: %w", err)
}
newAsset = splitOut.Asset

// If the transfer includes an asset split, we have to validate
// each split asset to ensure that our new Asset is committing
// to a valid SplitCommitment.
splitAssets = make([]*commitment.SplitAsset, len(outputs))
for idx := range outputs {
splitAssets[idx] = &commitment.SplitAsset{
Asset: *outputs[idx].Asset,
OutputIndex: outputs[idx].AnchorOutputIndex,
}

// The output that houses the root asset in case of a
// split has a special field for the split asset, which
// actually contains the split commitment proof. We need
// to use that one for the validation, as the root asset
// is already validated as the newAsset.
if outputs[idx].Type.IsSplitRoot() {
splitAssets[idx].Asset =
*outputs[idx].SplitAsset
}
}
}

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

// Create an instance of the Taproot Asset VM and validate the transfer.
verifySpend := func(splitAssets []*commitment.SplitAsset) error {
newAssetCopy := newAsset.Copy()
return validator.Execute(newAssetCopy, splitAssets, prevAssets)
}

// If the transfer contains no asset splits, we only need to validate
// the new asset with its witness attached, then we can exit early.
if !isSplit {
return verifySpend(nil)
}

// If the transfer includes an asset split, we have to validate each
// split asset to ensure that our new Asset is committing to a valid
// SplitCommitment.
splitAssets := make([]*commitment.SplitAsset, len(outputs))
for idx := range outputs {
splitAssets[idx] = &commitment.SplitAsset{
Asset: *outputs[idx].Asset,
OutputIndex: outputs[idx].AnchorOutputIndex,
}

// The output that houses the root asset in case of a split has
// a special field for the split asset, which actually contains
// the split commitment proof. We need to use that one for the
// validation, as the root asset is already validated as the
// newAsset.
if outputs[idx].Type.IsSplitRoot() {
splitAssets[idx].Asset = *outputs[idx].SplitAsset
}
}
if err := verifySpend(splitAssets); err != nil {
err = validator.ValidateVPsbt(newAsset, splitAssets, prevAssets)
if err != nil {
return err
}

// Update each split asset to store the root asset with the witness
// attached, so the receiver can verify inclusion of the root asset.
for idx := range outputs {
splitAsset := outputs[idx].Asset

// The output that houses the root asset in case of a split has
// a special field for the split asset. That asset is no longer
// needed (and isn't committed to anywhere), but in order for it
// to be validated externally, we still want to include it and
// therefore also want to update it with the signed root asset.
if outputs[idx].Type.IsSplitRoot() {
splitAsset = outputs[idx].SplitAsset
}
if isSplit {
// Update each split asset to store the root asset with the
// witness attached, so the receiver can verify inclusion of the
// root asset.
for idx := range outputs {
splitAsset := outputs[idx].Asset

// The output that houses the root asset in case of a
// split has a special field for the split asset. That
// asset is no longer needed (and isn't committed to
// anywhere), but in order for it to be validated
// externally, we still want to include it and therefore
// also want to update it with the signed root asset.
if outputs[idx].Type.IsSplitRoot() {
splitAsset = outputs[idx].SplitAsset
}

splitCommitment := splitAsset.PrevWitnesses[0].SplitCommitment
splitCommitment.RootAsset = *newAsset.Copy()
splitCommitment :=
splitAsset.PrevWitnesses[0].SplitCommitment
splitCommitment.RootAsset = *newAsset.Copy()
}
}

return nil
Expand Down
Loading

0 comments on commit 45012e3

Please sign in to comment.