Files
ark/client/unilateral_redeem.go
Pietralberto Mazza dc00d60585 Rename folders (#97)
* Rename arkd folder & drop cli

* Rename ark cli folder & update docs

* Update readme

* Fix

* scripts: add build-all

* Add target to build cli for all platforms

* Update build scripts

---------

Co-authored-by: tiero <3596602+tiero@users.noreply.github.com>
2024-02-09 19:32:58 +01:00

186 lines
4.1 KiB
Go

package main
import (
"fmt"
"github.com/ark-network/ark/common/tree"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/urfave/cli/v2"
"github.com/vulpemventures/go-elements/psetv2"
"github.com/vulpemventures/go-elements/taproot"
)
type RedeemBranch interface {
// UpdatePath checks for transactions of the branch onchain and updates the branch accordingly
UpdatePath() error
// Redeem will sign the branch of the tree and return the associated signed pset + the vtxo input
RedeemPath() ([]string, error)
// AddInput adds the vtxo input created by the branch
AddVtxoInput(updater *psetv2.Updater) error
}
type redeemBranch struct {
vtxo *vtxo
branch []*psetv2.Pset
internalKey *secp256k1.PublicKey
sweepClosure *taproot.TapElementsLeaf
}
func newRedeemBranch(ctx *cli.Context, congestionTree tree.CongestionTree, vtxo vtxo) (RedeemBranch, error) {
sweepClosure, _, err := findSweepClosure(congestionTree)
if err != nil {
return nil, err
}
nodes, err := congestionTree.Branch(vtxo.txid)
if err != nil {
return nil, err
}
branch := make([]*psetv2.Pset, 0, len(nodes))
for _, node := range nodes {
pset, err := psetv2.NewPsetFromBase64(node.Tx)
if err != nil {
return nil, err
}
branch = append(branch, pset)
}
xOnlyKey := branch[0].Inputs[0].TapInternalKey
internalKey, err := schnorr.ParsePubKey(xOnlyKey)
if err != nil {
return nil, err
}
return &redeemBranch{
vtxo: &vtxo,
branch: branch,
internalKey: internalKey,
sweepClosure: sweepClosure,
}, nil
}
// UpdatePath checks for transactions of the branch onchain and updates the branch accordingly
func (r *redeemBranch) UpdatePath() error {
for i := len(r.branch) - 1; i >= 0; i-- {
pset := r.branch[i]
unsignedTx, err := pset.UnsignedTx()
if err != nil {
return err
}
txHash := unsignedTx.TxHash().String()
_, err = getTxHex(txHash)
if err != nil {
continue
}
// if no error, the tx exists onchain, so we can remove it (+ the parents) from the branch
if i == len(r.branch)-1 {
r.branch = []*psetv2.Pset{}
} else {
r.branch = r.branch[i+1:]
}
break
}
return nil
}
// RedeemPath returns the list of transactions to broadcast in order to access the vtxo output
func (r *redeemBranch) RedeemPath() ([]string, error) {
transactions := make([]string, 0, len(r.branch))
for _, pset := range r.branch {
for i, input := range pset.Inputs {
if len(input.TapLeafScript) == 0 {
return nil, fmt.Errorf("tap leaf script not found on input #%d", i)
}
for _, leaf := range input.TapLeafScript {
isSweep, _, _, err := tree.DecodeSweepScript(leaf.Script)
if err != nil {
return nil, err
}
if isSweep {
continue
}
controlBlock, err := leaf.ControlBlock.ToBytes()
if err != nil {
return nil, err
}
unsignedTx, err := pset.UnsignedTx()
if err != nil {
return nil, err
}
unsignedTx.Inputs[i].Witness = [][]byte{
leaf.Script,
controlBlock[:],
}
hex, err := unsignedTx.ToHex()
if err != nil {
return nil, err
}
transactions = append(transactions, hex)
break
}
}
}
return transactions, nil
}
// AddVtxoInput is a wrapper around psetv2.Updater adding a taproot input letting to spend the vtxo output
func (r *redeemBranch) AddVtxoInput(updater *psetv2.Updater) error {
walletPubkey, err := getWalletPublicKey()
if err != nil {
return err
}
nextInputIndex := len(updater.Pset.Inputs)
if err := updater.AddInputs([]psetv2.InputArgs{
{
Txid: r.vtxo.txid,
TxIndex: r.vtxo.vout,
},
}); err != nil {
return err
}
// add taproot tree letting to spend the vtxo
checksigLeaf, err := tree.VtxoScript(walletPubkey)
if err != nil {
return nil
}
vtxoTaprootTree := taproot.AssembleTaprootScriptTree(
*checksigLeaf,
*r.sweepClosure,
)
proofIndex := vtxoTaprootTree.LeafProofIndex[checksigLeaf.TapHash()]
if err := updater.AddInTapLeafScript(
nextInputIndex,
psetv2.NewTapLeafScript(
vtxoTaprootTree.LeafMerkleProofs[proofIndex],
r.internalKey,
),
); err != nil {
return err
}
return nil
}