Files
ark/server/internal/infrastructure/wallet/btc-embedded/psbt.go
Louis Singer 3782793431 Add tests for adversarial scenarios (#300)
* fix and test cheating scenario (malicious double spend)

* test and fix async vtxo cheating cases

* add replace statement in go.mod

* Update server/internal/core/application/covenantless.go

Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
Signed-off-by: Louis Singer <41042567+louisinger@users.noreply.github.com>

* Update server/internal/infrastructure/wallet/btc-embedded/psbt.go

Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
Signed-off-by: Louis Singer <41042567+louisinger@users.noreply.github.com>

* Update server/test/e2e/covenant/e2e_test.go

Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
Signed-off-by: Louis Singer <41042567+louisinger@users.noreply.github.com>

* Update server/test/e2e/covenantless/e2e_test.go

Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
Signed-off-by: Louis Singer <41042567+louisinger@users.noreply.github.com>

* Update server/test/e2e/covenantless/e2e_test.go

Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
Signed-off-by: Louis Singer <41042567+louisinger@users.noreply.github.com>

* remove unused

* [btc-embedded] fix GetNotificationChannel

* [tx-builder] fix redeem transaction fee estimator

* close grpc client in tests

* [application] rework listentoscannerNotification

* [application][covenant] fix getConnectorAmount

* [tx-builder][covenant] get connector amount from wallet

* e2e test sleep time

* [liquid-standalone] ListConnectorUtxos: filter by script client side

* fix Makefile integrationtest

* do not use cache in integration tests

* use VtxoKey as argument of findForfeitTxBitcoin

* wrap adversarial test in t.Run

* increaste test timeout

* CI: setup go 1.23.1

* CI: revert go version

* add replace in server/go.mod

* Update server/internal/core/application/covenant.go

Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
Signed-off-by: Louis Singer <41042567+louisinger@users.noreply.github.com>

* remove replace

* readd replace statement

* fixes

* go work sync

* fix CI

---------

Signed-off-by: Louis Singer <41042567+louisinger@users.noreply.github.com>
Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
2024-09-16 17:03:43 +02:00

146 lines
3.6 KiB
Go

package btcwallet
import (
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcwallet/waddrmgr"
log "github.com/sirupsen/logrus"
)
func (s *service) signPsbt(packet *psbt.Packet, inputsToSign []int) ([]uint32, error) {
// iterates over the inputs and set the default sighash flags
updater, err := psbt.NewUpdater(packet)
if err != nil {
return nil, err
}
for idx, input := range packet.Inputs {
if input.WitnessUtxo == nil {
continue
}
script, err := txscript.ParsePkScript(input.WitnessUtxo.PkScript)
if err != nil {
return nil, fmt.Errorf("error detecting signing method, "+
"couldn't parse pkScript: %v", err)
}
sighashType := txscript.SigHashAll
if script.Class() == txscript.WitnessV1TaprootTy {
sighashType = txscript.SigHashDefault
}
if err := updater.AddInSighashType(sighashType, idx); err != nil {
return nil, err
}
}
tx := packet.UnsignedTx
signedInputs := make([]uint32, 0)
for idx := range tx.TxIn {
in := &packet.Inputs[idx]
// skip if the input does not have a witness utxo
if in.WitnessUtxo == nil {
continue
}
// skip if already signed
if len(in.FinalScriptWitness) > 0 {
continue
}
if len(inputsToSign) > 0 {
found := false
for _, i := range inputsToSign {
if i == idx {
found = true
break
}
}
if !found {
continue
}
}
var managedAddress waddrmgr.ManagedPubKeyAddress
var isTaproot bool
if len(in.TaprootLeafScript) > 0 && txscript.IsPayToTaproot(in.WitnessUtxo.PkScript) {
// segwit v1
isTaproot = true
managedAddress = s.aspTaprootAddr
} else {
// segwit v0
var err error
managedAddress, _, _, err = s.wallet.ScriptForOutput(in.WitnessUtxo)
if err != nil {
log.WithError(err).Debugf(
"failed to fetch address for input %d with script %s",
idx, hex.EncodeToString(in.WitnessUtxo.PkScript),
)
continue
}
}
signedInputs = append(signedInputs, uint32(idx))
bip32Infos := derivationPathForAddress(managedAddress)
packet.Inputs[idx].Bip32Derivation = []*psbt.Bip32Derivation{bip32Infos}
if isTaproot {
leafHashes := make([][]byte, 0, len(in.TaprootLeafScript))
for _, leafScript := range in.TaprootLeafScript {
leafHash := txscript.NewBaseTapLeaf(leafScript.Script).TapHash()
leafHashes = append(leafHashes, leafHash[:])
}
xonlypubkey := schnorr.SerializePubKey(managedAddress.PubKey())
packet.Inputs[idx].TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{
{
XOnlyPubKey: xonlypubkey,
MasterKeyFingerprint: bip32Infos.MasterKeyFingerprint,
Bip32Path: bip32Infos.Bip32Path,
LeafHashes: leafHashes,
},
}
}
}
ins, err := s.wallet.SignPsbt(packet)
if err != nil {
return nil, err
}
// delete derivation paths to avoid duplicate keys error
for idx := range signedInputs {
packet.Inputs[idx].Bip32Derivation = nil
packet.Inputs[idx].TaprootBip32Derivation = nil
}
return ins, nil
}
func derivationPathForAddress(addr waddrmgr.ManagedPubKeyAddress) *psbt.Bip32Derivation {
keyscope, derivationInfos, _ := addr.DerivationInfo()
return &psbt.Bip32Derivation{
PubKey: addr.PubKey().SerializeCompressed(),
MasterKeyFingerprint: derivationInfos.MasterKeyFingerprint,
Bip32Path: []uint32{
keyscope.Purpose + hdkeychain.HardenedKeyStart,
keyscope.Coin + hdkeychain.HardenedKeyStart,
derivationInfos.Account,
derivationInfos.Branch,
derivationInfos.Index,
},
}
}