Files
ark/client/signer.go
Louis Singer b8e0914ba9 [Client] Add vtxo expiration details to balance & Fix coin selection
* add expiry details in balance command

* coin selection: sort vtxos by olderFirst

* rename type

* balance: add next expiration

* add next expiration in offchain_balance json

* print duration in nextExpiration

* fix dust coin selection

* refactor sort
2024-02-20 17:10:18 +01:00

192 lines
4.0 KiB
Go

package main
import (
"bytes"
"fmt"
"github.com/ark-network/ark/common/tree"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/vulpemventures/go-elements/address"
"github.com/vulpemventures/go-elements/payment"
"github.com/vulpemventures/go-elements/psetv2"
"github.com/vulpemventures/go-elements/transaction"
)
func signPset(
pset *psetv2.Pset,
explorer Explorer,
prvKey *secp256k1.PrivateKey,
) error {
updater, err := psetv2.NewUpdater(pset)
if err != nil {
return err
}
for i, input := range pset.Inputs {
if input.WitnessUtxo != nil {
continue
}
prevoutTxHex, err := explorer.GetTxHex(chainhash.Hash(input.PreviousTxid).String())
if err != nil {
return err
}
prevoutTx, err := transaction.NewTxFromHex(prevoutTxHex)
if err != nil {
return err
}
utxo := prevoutTx.Outputs[input.PreviousTxIndex]
if utxo == nil {
return fmt.Errorf("witness utxo not found")
}
if err := updater.AddInWitnessUtxo(i, utxo); err != nil {
return err
}
sighashType := txscript.SigHashAll
if utxo.Script[0] == txscript.OP_1 {
sighashType = txscript.SigHashDefault
}
if err := updater.AddInSighashType(i, sighashType); err != nil {
return err
}
}
signer, err := psetv2.NewSigner(updater.Pset)
if err != nil {
return err
}
_, onchainAddr, err := getAddress()
if err != nil {
return err
}
onchainWalletScript, err := address.ToOutputScript(onchainAddr)
if err != nil {
return err
}
utx, err := pset.UnsignedTx()
if err != nil {
return err
}
_, liquidNet := getNetwork()
prevoutsScripts := make([][]byte, 0)
prevoutsValues := make([][]byte, 0)
prevoutsAssets := make([][]byte, 0)
for _, input := range pset.Inputs {
prevoutsScripts = append(prevoutsScripts, input.WitnessUtxo.Script)
prevoutsValues = append(prevoutsValues, input.WitnessUtxo.Value)
prevoutsAssets = append(prevoutsAssets, input.WitnessUtxo.Asset)
}
for i, input := range pset.Inputs {
if bytes.Equal(input.WitnessUtxo.Script, onchainWalletScript) {
p, err := payment.FromScript(input.WitnessUtxo.Script, liquidNet, nil)
if err != nil {
return err
}
preimage := utx.HashForWitnessV0(
i,
p.Script,
input.WitnessUtxo.Value,
txscript.SigHashAll,
)
sig := ecdsa.Sign(
prvKey,
preimage[:],
)
signatureWithSighashType := append(sig.Serialize(), byte(txscript.SigHashAll))
err = signer.SignInput(i, signatureWithSighashType, prvKey.PubKey().SerializeCompressed(), nil, nil)
if err != nil {
fmt.Println("error signing input: ", err)
return err
}
continue
}
pubkey := prvKey.PubKey()
vtxoLeaf, err := tree.VtxoScript(pubkey)
if err != nil {
return err
}
if len(input.TapLeafScript) > 0 {
genesis, err := chainhash.NewHashFromStr(liquidNet.GenesisBlockHash)
if err != nil {
return err
}
for _, leaf := range input.TapLeafScript {
if bytes.Equal(leaf.Script, vtxoLeaf.Script) {
hash := leaf.TapHash()
preimage := utx.HashForWitnessV1(
i,
prevoutsScripts,
prevoutsAssets,
prevoutsValues,
txscript.SigHashDefault,
genesis,
&hash,
nil,
)
sig, err := schnorr.Sign(
prvKey,
preimage[:],
)
if err != nil {
return err
}
tapScriptSig := psetv2.TapScriptSig{
PartialSig: psetv2.PartialSig{
PubKey: schnorr.SerializePubKey(prvKey.PubKey()),
Signature: sig.Serialize(),
},
LeafHash: hash.CloneBytes(),
}
if err := signer.SignTaprootInputTapscriptSig(i, tapScriptSig); err != nil {
return err
}
}
}
}
}
for i, input := range pset.Inputs {
if len(input.PartialSigs) > 0 {
valid, err := pset.ValidateInputSignatures(i)
if err != nil {
return err
}
if !valid {
return fmt.Errorf("invalid signature for input %d", i)
}
}
}
return nil
}