Update project structure and build process

This commit is contained in:
Juan Pablo Civile
2025-05-13 11:10:08 -03:00
parent 124e9fa1bc
commit d9f3e925a4
277 changed files with 15321 additions and 930 deletions

116
libwallet/swaps/swaps.go Normal file
View File

@@ -0,0 +1,116 @@
package swaps
import (
"crypto/sha256"
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/muun/libwallet/addresses"
"github.com/muun/libwallet/hdpath"
hash "golang.org/x/crypto/ripemd160" //lint:ignore SA1019 using deprecated hash function for compatibility
)
type SubmarineSwap struct {
Invoice string
Receiver SubmarineSwapReceiver
FundingOutput SubmarineSwapFundingOutput
PreimageInHex string
}
type SubmarineSwapFundingOutput struct {
ScriptVersion int64
OutputAddress string
OutputAmount int64
ConfirmationsNeeded int
ServerPaymentHashInHex string
ServerPublicKeyInHex string
UserLockTime int64 // TODO: not checked in v2?
// v1 only
UserRefundAddress *addresses.WalletAddress
// v2 only
ExpirationInBlocks int64
UserPublicKey *hdkeychain.ExtendedKey
MuunPublicKey *hdkeychain.ExtendedKey
KeyPath string
}
type SubmarineSwapReceiver struct {
Alias string
PublicKey string
}
type KeyDescriptor struct {
Key *hdkeychain.ExtendedKey
Path string
}
func (d *KeyDescriptor) DeriveTo(path string) (*hdkeychain.ExtendedKey, error) {
key := d.Key
currentPath, err := hdpath.Parse(d.Path)
if err != nil {
return nil, fmt.Errorf("invalid current key path: %w", err)
}
targetPath, err := hdpath.Parse(path)
if err != nil {
return nil, fmt.Errorf("invalid target key path: %w", err)
}
indexes := targetPath.IndexesFrom(currentPath)
for _, index := range indexes {
var err error
var modifier uint32
if index.Hardened {
modifier = hdkeychain.HardenedKeyStart
}
key, err = key.Child(index.Index | modifier)
if err != nil {
return nil, err
}
}
return key, nil
}
func (swap *SubmarineSwap) Validate(
rawInvoice string,
userPublicKey *KeyDescriptor,
muunPublicKey *KeyDescriptor,
originalExpirationInBlocks int64,
network *chaincfg.Params,
) error {
version := swap.FundingOutput.ScriptVersion
switch version {
case addresses.SubmarineSwapV1:
return swap.validateV1(rawInvoice, userPublicKey, muunPublicKey, network)
case addresses.SubmarineSwapV2:
return swap.validateV2(rawInvoice, userPublicKey, muunPublicKey, originalExpirationInBlocks, network)
default:
return fmt.Errorf("unknown swap version %v", version)
}
}
func createNonNativeSegwitRedeemScript(witnessScript []byte) ([]byte, error) {
witnessScriptHash := sha256.Sum256(witnessScript)
builder := txscript.NewScriptBuilder()
builder.AddInt64(0)
builder.AddData(witnessScriptHash[:])
return builder.Script()
}
func ripemd160(data []byte) []byte {
hasher := hash.New()
_, err := hasher.Write(data)
if err != nil {
panic("failed to hash")
}
return hasher.Sum([]byte{})
}

152
libwallet/swaps/v1.go Normal file
View File

@@ -0,0 +1,152 @@
package swaps
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/txscript"
"github.com/muun/libwallet/btcsuitew/btcutilw"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/muun/libwallet/addresses"
)
func (swap *SubmarineSwap) validateV1(rawInvoice string, userPublicKey, muunPublicKey *KeyDescriptor, network *chaincfg.Params) error {
invoice, err := zpay32.Decode(rawInvoice, network)
if err != nil {
return fmt.Errorf("failed to decode invoice: %w", err)
}
// Check the payment hash matches
serverPaymentHash, err := hex.DecodeString(swap.FundingOutput.ServerPaymentHashInHex)
if err != nil {
return fmt.Errorf("server payment hash is not valid hex: %w", err)
}
if !bytes.Equal(invoice.PaymentHash[:], serverPaymentHash) {
return fmt.Errorf("payment hash doesn't match %v != %v", hex.EncodeToString(invoice.PaymentHash[:]), swap.FundingOutput.ServerPaymentHashInHex)
}
// TODO: check that timelock is acceptable
// Validate that the refund address is one we can derive
swapRefundAddress := swap.FundingOutput.UserRefundAddress
derivedUserKey, err := userPublicKey.DeriveTo(swapRefundAddress.DerivationPath())
if err != nil {
return fmt.Errorf("failed to derive user key: %w", err)
}
derivedMuunKey, err := muunPublicKey.DeriveTo(swapRefundAddress.DerivationPath())
if err != nil {
return fmt.Errorf("failed to derive muun key: %w", err)
}
refundAddress, err := addresses.Create(
swapRefundAddress.Version(),
derivedUserKey,
derivedMuunKey,
swapRefundAddress.DerivationPath(),
network,
)
if err != nil {
return fmt.Errorf("failed to generate refund address: %w", err)
}
if refundAddress.Address() != swapRefundAddress.Address() {
return fmt.Errorf("refund address doesn't match generated (%v != %v)", swapRefundAddress.Address(), refundAddress.Address())
}
// Check the swap's witness script is a valid swap script
serverPubKey, err := hex.DecodeString(swap.FundingOutput.ServerPublicKeyInHex)
if err != nil {
return fmt.Errorf("server pub key is not hex: %w", err)
}
witnessScript, err := CreateWitnessScriptSubmarineSwapV1(
swapRefundAddress.Address(),
serverPaymentHash,
serverPubKey,
swap.FundingOutput.UserLockTime,
network)
if err != nil {
return fmt.Errorf("failed to compute witness script: %w", err)
}
redeemScript, err := createNonNativeSegwitRedeemScript(witnessScript)
if err != nil {
return fmt.Errorf("failed to build redeem script: %w", err)
}
address, err := btcutil.NewAddressScriptHash(redeemScript, network)
if err != nil {
return fmt.Errorf("failed to build address for swap script: %w", err)
}
if address.EncodeAddress() != swap.FundingOutput.OutputAddress {
return fmt.Errorf("address for swap script mismatch (%v != %v)", address.EncodeAddress(), swap.FundingOutput.OutputAddress)
}
if len(swap.PreimageInHex) > 0 {
preimage, err := hex.DecodeString(swap.PreimageInHex)
if err != nil {
return fmt.Errorf("preimagehex is not actually hex: %w", err)
}
calculatedPaymentHash := sha256.Sum256(preimage)
if !bytes.Equal(invoice.PaymentHash[:], calculatedPaymentHash[:]) {
return fmt.Errorf("payment hash doesn't match preimage (%v != hash(%v)", invoice.PaymentHash, swap.PreimageInHex)
}
}
return nil
}
func CreateWitnessScriptSubmarineSwapV1(refundAddress string, paymentHash []byte, swapServerPubKey []byte, lockTime int64, network *chaincfg.Params) ([]byte, error) {
// It turns out that the payment hash present in an invoice is just the SHA256 of the
// payment preimage, so we still have to do a pass of RIPEMD160 before pushing it to the
// script
paymentHash160 := ripemd160(paymentHash)
decodedRefundAddress, err := btcutilw.DecodeAddress(refundAddress, network)
if err != nil {
return nil, fmt.Errorf("refund address is invalid: %w", err)
}
refundAddressHash := decodedRefundAddress.ScriptAddress()
builder := txscript.NewScriptBuilder()
builder.AddOp(txscript.OP_DUP)
// Condition to decide which branch to follow:
builder.AddOp(txscript.OP_HASH160).
AddData(paymentHash160).
AddOp(txscript.OP_EQUAL)
// SubmarineSwap service spending script, for successful LN payments:
builder.AddOp(txscript.OP_IF).
AddOp(txscript.OP_DROP).
AddData(swapServerPubKey)
// User spending script, for failed LN payments:
builder.AddOp(txscript.OP_ELSE).
AddInt64(lockTime).
AddOp(txscript.OP_CHECKLOCKTIMEVERIFY).
AddOp(txscript.OP_DROP).
AddOp(txscript.OP_DUP).
AddOp(txscript.OP_HASH160).
AddData(refundAddressHash).
AddOp(txscript.OP_EQUALVERIFY)
// Final verification for both branches:
builder.AddOp(txscript.OP_ENDIF).
AddOp(txscript.OP_CHECKSIG)
return builder.Script()
}

75
libwallet/swaps/v1_test.go Executable file
View File

@@ -0,0 +1,75 @@
package swaps
import (
"testing"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/muun/libwallet/addresses"
)
func TestValidateSubmarineSwapV1(t *testing.T) {
type args struct {
rawInvoice string
userPublicKey *KeyDescriptor
muunPublicKey *KeyDescriptor
swap *SubmarineSwap
network *chaincfg.Params
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "successful",
args: args{
rawInvoice: "lnbcrt1p033394pp5sfcfh0ukkjfcvcg2vwk2hudue9d48lawqkacdan4msxne66w4krqdqqcqzpgsp5jelulm6a7q38j6jffa9qet3scz4qvcs08x6hfsyn0lfg34p2584q9qy9qsqjcq059jh8qeslj7qwl69ln69znalrxykhaj4kl0g0kfstwsa3warsyxx2d0rqs24tx896lz895wffqaj7l82zs896ec7r5arnw0cwtqpvzt8yd",
userPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4Y3iy9soFSA9zoYbpyhUFu3eAH1sDWyERxH2yJVZUhPUX5QsxD6bZfMWRKzxw28ohD5n6AZWmvZbDpZzgxSVxUnMevqzTXQk"),
Path: "m",
},
muunPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4XbhomyY2axxKe3KB1FK2Wq2z7XYyDF3T4QCuEDZFBUyGfjfHChvEbsbP9RpaYA8cwxkZpQjEcNdaPfuj3cKGqCiHC5YeRTo"),
Path: "m",
},
swap: &SubmarineSwap{
FundingOutput: SubmarineSwapFundingOutput{
OutputAddress: "2MvW8nGkzFXnLWUca6ZGUh3yqEq5MKEyAxb",
ExpirationInBlocks: 10,
ServerPaymentHashInHex: "82709bbf96b49386610a63acabf1bcc95b53ffae05bb86f675dc0d3ceb4ead86",
UserRefundAddress: addresses.New(addresses.V4, "m", "bcrt1q553urspdhwr49xavd67fvl35pzacz4853l4u09vntr8z06djnw7s95fgat"),
UserPublicKey: decodeKey("tpubD6NzVbkrYhZ4Y3iy9soFSA9zoYbpyhUFu3eAH1sDWyERxH2yJVZUhPUX5QsxD6bZfMWRKzxw28ohD5n6AZWmvZbDpZzgxSVxUnMevqzTXQk"),
MuunPublicKey: decodeKey("tpubD6NzVbkrYhZ4XbhomyY2axxKe3KB1FK2Wq2z7XYyDF3T4QCuEDZFBUyGfjfHChvEbsbP9RpaYA8cwxkZpQjEcNdaPfuj3cKGqCiHC5YeRTo"),
},
Receiver: SubmarineSwapReceiver{
PublicKey: "02c9a35bdbeab0b93ee9542d85c38beab7d1e72ea1d9639e5b00b1d5feb64bcfdd",
},
},
network: &chaincfg.RegressionNetParams,
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.args.swap.validateV1(
tt.args.rawInvoice,
tt.args.userPublicKey,
tt.args.muunPublicKey,
tt.args.network,
)
if (err != nil) != tt.wantErr {
t.Errorf("validateV1() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func decodeKey(s string) *hdkeychain.ExtendedKey {
key, err := hdkeychain.NewKeyFromString(s)
if err != nil {
panic(err)
}
return key
}

204
libwallet/swaps/v2.go Normal file
View File

@@ -0,0 +1,204 @@
package swaps
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/lightningnetwork/lnd/zpay32"
)
func (swap *SubmarineSwap) validateV2(rawInvoice string, userPublicKey, muunPublicKey *KeyDescriptor, originalExpirationInBlocks int64, network *chaincfg.Params) error {
fundingOutput := swap.FundingOutput
invoice, err := zpay32.Decode(rawInvoice, network)
if err != nil {
return fmt.Errorf("failed to decode invoice: %w", err)
}
// Check the payment hash matches
serverPaymentHash, err := hex.DecodeString(fundingOutput.ServerPaymentHashInHex)
if err != nil {
return fmt.Errorf("server payment hash is not valid hex: %w", err)
}
if !bytes.Equal(invoice.PaymentHash[:], serverPaymentHash) {
return fmt.Errorf("payment hash doesn't match %v != %v", hex.EncodeToString(invoice.PaymentHash[:]), fundingOutput.ServerPaymentHashInHex)
}
destination, err := hex.DecodeString(swap.Receiver.PublicKey)
if err != nil {
return fmt.Errorf("destination is not valid hex: %w", err)
}
if !bytes.Equal(invoice.Destination.SerializeCompressed(), destination) {
return fmt.Errorf("destination doesnt match %v != %v", invoice.Destination.SerializeCompressed(), swap.Receiver.PublicKey)
}
if fundingOutput.ExpirationInBlocks != originalExpirationInBlocks {
return fmt.Errorf("expiration in blocks doesnt match %v != %v", originalExpirationInBlocks, fundingOutput.ExpirationInBlocks)
}
// Validate that we can derive the addresses involved
derivationPath := fundingOutput.KeyPath
derivedUserKey, err := userPublicKey.DeriveTo(derivationPath)
if err != nil {
return fmt.Errorf("failed to derive user key: %w", err)
}
if derivedUserKey.String() != fundingOutput.UserPublicKey.String() {
return fmt.Errorf("user pub keys dont match %v != %v", derivedUserKey.String(), fundingOutput.UserPublicKey.String())
}
derivedMuunKey, err := muunPublicKey.DeriveTo(derivationPath)
if err != nil {
return fmt.Errorf("failed to derive muun key: %w", err)
}
if derivedMuunKey.String() != fundingOutput.MuunPublicKey.String() {
return fmt.Errorf("muun pub keys dont match %v != %v", derivedMuunKey.String(), fundingOutput.MuunPublicKey.String())
}
// Check the swap's witness script is a valid swap script
serverPubKey, err := hex.DecodeString(swap.FundingOutput.ServerPublicKeyInHex)
if err != nil {
return fmt.Errorf("server pub key is not hex: %w", err)
}
witnessScript, err := CreateWitnessScriptSubmarineSwapV2(
serverPaymentHash,
encodeRaw(derivedUserKey),
encodeRaw(derivedMuunKey),
serverPubKey,
swap.FundingOutput.ExpirationInBlocks)
if err != nil {
return fmt.Errorf("failed to compute witness script: %w", err)
}
witnessScriptHash := sha256.Sum256(witnessScript)
address, err := btcutil.NewAddressWitnessScriptHash(witnessScriptHash[:], network)
if err != nil {
return fmt.Errorf("failed to build address for swap script: %w", err)
}
if address.EncodeAddress() != swap.FundingOutput.OutputAddress {
return fmt.Errorf("address for swap script mismatch (%v != %v)", address.EncodeAddress(), swap.FundingOutput.OutputAddress)
}
if len(swap.PreimageInHex) > 0 {
preimage, err := hex.DecodeString(swap.PreimageInHex)
if err != nil {
return fmt.Errorf("preimageInHex is not valid hex: %w", err)
}
calculatedPaymentHash := sha256.Sum256(preimage)
if !bytes.Equal(invoice.PaymentHash[:], calculatedPaymentHash[:]) {
return fmt.Errorf("payment hash doesn't match preimage (%v != hash(%v)", invoice.PaymentHash, swap.PreimageInHex)
}
}
return nil
}
func CreateWitnessScriptSubmarineSwapV2(paymentHash, userPubKey, muunPubKey, swapServerPubKey []byte, blocksForExpiration int64) ([]byte, error) {
// It turns out that the payment hash present in an invoice is just the SHA256 of the
// payment preimage, so we still have to do a pass of RIPEMD160 before pushing it to the
// script
paymentHash160 := ripemd160(paymentHash)
muunPublicKeyHash160 := btcutil.Hash160(muunPubKey)
// Equivalent miniscript (http://bitcoin.sipa.be/miniscript/):
// or(
// and(pk(userPublicKey), pk(swapServerPublicKey)),
// or(
// and(pk(swapServerPublicKey), hash160(swapPaymentHash160)),
// and(pk(userPublicKey), and(pk(muunPublicKey), older(numBlocksForExpiration)))
// )
// )
//
// However, we differ in that the size of the script was heavily optimized for spending the
// first two branches (the collaborative close and the unilateral close by swapper), which
// are the most probable to be used.
builder := txscript.NewScriptBuilder().
// Push the user public key to the second position of the stack
AddData(userPubKey).
AddOp(txscript.OP_SWAP).
// Check whether the first stack item was a valid swap server signature
AddData(swapServerPubKey).
AddOp(txscript.OP_CHECKSIG).
// If the swap server signature was correct
AddOp(txscript.OP_IF).
AddOp(txscript.OP_SWAP).
// Check whether the second stack item was the payment preimage
AddOp(txscript.OP_DUP).
AddOp(txscript.OP_HASH160).
AddData(paymentHash160).
AddOp(txscript.OP_EQUAL).
// If the preimage was correct
AddOp(txscript.OP_IF).
// We are done, leave just one true-ish item in the stack (there're 2
// remaining items)
AddOp(txscript.OP_DROP).
// If the second stack item wasn't a valid payment preimage
AddOp(txscript.OP_ELSE).
// Validate that the second stack item was a valid user signature
AddOp(txscript.OP_SWAP).
AddOp(txscript.OP_CHECKSIG).
AddOp(txscript.OP_ENDIF).
// If the first stack item wasn't a valid server signature
AddOp(txscript.OP_ELSE).
// Validate that the blockchain height is big enough
AddInt64(blocksForExpiration).
AddOp(txscript.OP_CHECKSEQUENCEVERIFY).
AddOp(txscript.OP_DROP).
// Validate that the second stack item was a valid user signature
AddOp(txscript.OP_CHECKSIGVERIFY).
// Validate that the third stack item was the muun public key
AddOp(txscript.OP_DUP).
AddOp(txscript.OP_HASH160).
AddData(muunPublicKeyHash160).
AddOp(txscript.OP_EQUALVERIFY).
// Notice that instead of directly pushing the public key here and checking the
// signature P2PK-style, we pushed the hash of the public key, and require an
// extra stack item with the actual public key, verifying the signature and
// public key P2PKH-style.
//
// This trick reduces the on-chain footprint of the muun key from 33 bytes to
// 20 bytes for the collaborative, and swap server's non-collaborative branches,
// which are the most frequent ones.
// Validate that the fourth stack item was a valid server signature
AddOp(txscript.OP_CHECKSIG).
AddOp(txscript.OP_ENDIF)
return builder.Script()
}
func encodeRaw(key *hdkeychain.ExtendedKey) []byte {
publicKey, err := key.ECPubKey()
if err != nil {
panic(err)
}
return publicKey.SerializeCompressed()
}

133
libwallet/swaps/v2_test.go Normal file
View File

@@ -0,0 +1,133 @@
package swaps
import (
"testing"
"github.com/btcsuite/btcd/chaincfg"
)
func TestValidateSubmarineSwapV2(t *testing.T) {
type args struct {
rawInvoice string
userPublicKey *KeyDescriptor
muunPublicKey *KeyDescriptor
swap *SubmarineSwap
originalExpirationInBlocks int64
network *chaincfg.Params
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "invalid invoice",
args: args{
swap: &SubmarineSwap{
FundingOutput: SubmarineSwapFundingOutput{},
},
rawInvoice: "invalid",
userPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4Y3iy9soFSA9zoYbpyhUFu3eAH1sDWyERxH2yJVZUhPUX5QsxD6bZfMWRKzxw28ohD5n6AZWmvZbDpZzgxSVxUnMevqzTXQk"),
Path: "m",
},
muunPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4XbhomyY2axxKe3KB1FK2Wq2z7XYyDF3T4QCuEDZFBUyGfjfHChvEbsbP9RpaYA8cwxkZpQjEcNdaPfuj3cKGqCiHC5YeRTo"),
Path: "m",
},
originalExpirationInBlocks: 0,
network: &chaincfg.RegressionNetParams,
},
wantErr: true,
},
{
name: "payment hash from server is invalid hex",
args: args{
swap: &SubmarineSwap{
FundingOutput: SubmarineSwapFundingOutput{
ServerPaymentHashInHex: "invalid hex",
},
},
rawInvoice: "lnbcrt1p033394pp5sfcfh0ukkjfcvcg2vwk2hudue9d48lawqkacdan4msxne66w4krqdqqcqzpgsp5jelulm6a7q38j6jffa9qet3scz4qvcs08x6hfsyn0lfg34p2584q9qy9qsqjcq059jh8qeslj7qwl69ln69znalrxykhaj4kl0g0kfstwsa3warsyxx2d0rqs24tx896lz895wffqaj7l82zs896ec7r5arnw0cwtqpvzt8yd",
userPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4Y3iy9soFSA9zoYbpyhUFu3eAH1sDWyERxH2yJVZUhPUX5QsxD6bZfMWRKzxw28ohD5n6AZWmvZbDpZzgxSVxUnMevqzTXQk"),
Path: "m",
},
muunPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4XbhomyY2axxKe3KB1FK2Wq2z7XYyDF3T4QCuEDZFBUyGfjfHChvEbsbP9RpaYA8cwxkZpQjEcNdaPfuj3cKGqCiHC5YeRTo"),
Path: "m",
},
originalExpirationInBlocks: 0,
network: &chaincfg.RegressionNetParams,
},
wantErr: true,
},
{
name: "payment hash from server does not match invoice",
args: args{
swap: &SubmarineSwap{
FundingOutput: SubmarineSwapFundingOutput{
ServerPaymentHashInHex: "112233445566778899",
},
},
rawInvoice: "lnbcrt1p033394pp5sfcfh0ukkjfcvcg2vwk2hudue9d48lawqkacdan4msxne66w4krqdqqcqzpgsp5jelulm6a7q38j6jffa9qet3scz4qvcs08x6hfsyn0lfg34p2584q9qy9qsqjcq059jh8qeslj7qwl69ln69znalrxykhaj4kl0g0kfstwsa3warsyxx2d0rqs24tx896lz895wffqaj7l82zs896ec7r5arnw0cwtqpvzt8yd",
userPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4Y3iy9soFSA9zoYbpyhUFu3eAH1sDWyERxH2yJVZUhPUX5QsxD6bZfMWRKzxw28ohD5n6AZWmvZbDpZzgxSVxUnMevqzTXQk"),
Path: "m",
},
muunPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4XbhomyY2axxKe3KB1FK2Wq2z7XYyDF3T4QCuEDZFBUyGfjfHChvEbsbP9RpaYA8cwxkZpQjEcNdaPfuj3cKGqCiHC5YeRTo"),
Path: "m",
},
originalExpirationInBlocks: 0,
network: &chaincfg.RegressionNetParams,
},
wantErr: true,
},
// TODO: add more test cases for the different error conditions
{
name: "successful",
args: args{
swap: &SubmarineSwap{
FundingOutput: SubmarineSwapFundingOutput{
OutputAddress: "bcrt1qk956axjf2pzmf6esd4jfrppkhmegn8eez2gl2zdkzje0w8lt2tmqvqvrut",
ExpirationInBlocks: 10,
ServerPaymentHashInHex: "82709bbf96b49386610a63acabf1bcc95b53ffae05bb86f675dc0d3ceb4ead86",
UserPublicKey: decodeKey("tpubD6NzVbkrYhZ4Y3iy9soFSA9zoYbpyhUFu3eAH1sDWyERxH2yJVZUhPUX5QsxD6bZfMWRKzxw28ohD5n6AZWmvZbDpZzgxSVxUnMevqzTXQk"),
MuunPublicKey: decodeKey("tpubD6NzVbkrYhZ4XbhomyY2axxKe3KB1FK2Wq2z7XYyDF3T4QCuEDZFBUyGfjfHChvEbsbP9RpaYA8cwxkZpQjEcNdaPfuj3cKGqCiHC5YeRTo"),
KeyPath: "m",
},
Receiver: SubmarineSwapReceiver{
PublicKey: "02c9a35bdbeab0b93ee9542d85c38beab7d1e72ea1d9639e5b00b1d5feb64bcfdd",
},
},
rawInvoice: "lnbcrt1p033394pp5sfcfh0ukkjfcvcg2vwk2hudue9d48lawqkacdan4msxne66w4krqdqqcqzpgsp5jelulm6a7q38j6jffa9qet3scz4qvcs08x6hfsyn0lfg34p2584q9qy9qsqjcq059jh8qeslj7qwl69ln69znalrxykhaj4kl0g0kfstwsa3warsyxx2d0rqs24tx896lz895wffqaj7l82zs896ec7r5arnw0cwtqpvzt8yd",
userPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4Y3iy9soFSA9zoYbpyhUFu3eAH1sDWyERxH2yJVZUhPUX5QsxD6bZfMWRKzxw28ohD5n6AZWmvZbDpZzgxSVxUnMevqzTXQk"),
Path: "m",
},
muunPublicKey: &KeyDescriptor{
Key: decodeKey("tpubD6NzVbkrYhZ4XbhomyY2axxKe3KB1FK2Wq2z7XYyDF3T4QCuEDZFBUyGfjfHChvEbsbP9RpaYA8cwxkZpQjEcNdaPfuj3cKGqCiHC5YeRTo"),
Path: "m",
},
originalExpirationInBlocks: 10,
network: &chaincfg.RegressionNetParams,
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.args.swap.validateV2(
tt.args.rawInvoice,
tt.args.userPublicKey,
tt.args.muunPublicKey,
tt.args.originalExpirationInBlocks,
tt.args.network,
)
if (err != nil) != tt.wantErr {
t.Errorf("validateV2() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}