closepoolaccount: add compatibility for p2tr accounts

pull/63/head
Oliver Gugger 1 year ago
parent 905df7e966
commit 8f80d0e8b4
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -6,11 +6,13 @@ import (
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/guggero/chantools/btc"
"github.com/guggero/chantools/lnd"
"github.com/lightninglabs/pool/account"
"github.com/lightninglabs/pool/poolscript"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
@ -23,14 +25,12 @@ const (
defaultMaxNumBlocks = 200000
defaultMaxNumAccounts = 20
defaultMaxNumBatchKeys = 500
oddByte = input.PubKeyFormatCompressedOdd
)
var (
initialBatchKeyBytes, _ = hex.DecodeString(
"02824d0cbac65e01712124c50ff2cc74ce22851d7b444c1bf2ae66afefb8" +
"eaf27f",
)
initialBatchKey, _ = btcec.ParsePubKey(initialBatchKeyBytes)
initialBatchKeyBytes, _ = hex.DecodeString(account.InitialBatchKey)
initialBatchKey, _ = btcec.ParsePubKey(initialBatchKeyBytes)
mainnetAuctioneerKeyHex = "028e87bdd134238f8347f845d9ecc827b843d0d1e2" +
"7cdcb46da704d916613f4fce"
@ -186,6 +186,11 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
log.Debugf("Brute forcing pk script %x for outpoint %v", pkScript,
outpoint)
script, err := txscript.ParsePkScript(pkScript)
if err != nil {
return fmt.Errorf("error parsing pk script: %w", err)
}
// Let's derive the account key family's extended key first.
path := []uint32{
lnd.HardenedKeyStart + uint32(keychain.BIP0043Purpose),
@ -199,7 +204,22 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
}
// Try our luck.
acct, err := bruteForceAccountScript(
var (
acct *poolAccount
accountVersion account.Version
)
switch script.Class() {
case txscript.WitnessV0ScriptHashTy:
accountVersion = account.VersionInitialNoVersion
case txscript.WitnessV1TaprootTy:
accountVersion = account.VersionTaprootEnabled
default:
return fmt.Errorf("unsupported script class %v", script.Class())
}
acct, err = bruteForceAccountScript(
accountBaseKey, auctioneerKey, minExpiry, maxNumBlocks,
maxNumAccounts, maxNumBatchKeys, pkScript,
)
@ -220,8 +240,40 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
// Calculate the fee based on the given fee rate and our weight
// estimation.
var estimator input.TxWeightEstimator
estimator.AddWitnessInput(input.ToLocalTimeoutWitnessSize)
var (
estimator input.TxWeightEstimator
signDesc = &input.SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: poolscript.AccountKeyFamily,
Index: acct.keyIndex,
},
},
SingleTweak: acct.keyTweak,
WitnessScript: acct.witnessScript,
Output: &wire.TxOut{
PkScript: pkScript,
Value: sweepValue,
},
InputIndex: 0,
PrevOutputFetcher: txscript.NewCannedPrevOutputFetcher(
pkScript, sweepValue,
),
}
)
switch accountVersion {
case account.VersionInitialNoVersion:
estimator.AddWitnessInput(poolscript.ExpiryWitnessSize)
signDesc.HashType = txscript.SigHashAll
signDesc.SignMethod = input.WitnessV0SignMethod
signDesc.SigHashes = input.NewTxSigHashesV0Only(sweepTx)
case account.VersionTaprootEnabled:
estimator.AddWitnessInput(poolscript.TaprootExpiryWitnessSize)
signDesc.HashType = txscript.SigHashDefault
signDesc.SignMethod = input.TaprootScriptSpendSignMethod
}
estimator.AddP2WKHOutput()
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))
@ -240,32 +292,23 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
totalFee, sweepValue, estimator.Weight())
// Create the sign descriptor for the input then sign the transaction.
sigHashes := input.NewTxSigHashesV0Only(sweepTx)
signDesc := &input.SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: poolscript.AccountKeyFamily,
Index: acct.keyIndex,
},
},
SingleTweak: acct.keyTweak,
WitnessScript: acct.witnessScript,
Output: &wire.TxOut{
PkScript: pkScript,
Value: sweepValue,
},
InputIndex: 0,
SigHashes: sigHashes,
HashType: txscript.SigHashAll,
}
sig, err := signer.SignOutputRaw(sweepTx, signDesc)
if err != nil {
return fmt.Errorf("error signing sweep tx: %w", err)
}
ourSig := append(sig.Serialize(), byte(signDesc.HashType))
sweepTx.TxIn[0].Witness = poolscript.SpendExpiry(
acct.witnessScript, ourSig,
)
switch accountVersion {
case account.VersionInitialNoVersion:
ourSig := append(sig.Serialize(), byte(signDesc.HashType))
sweepTx.TxIn[0].Witness = poolscript.SpendExpiry(
acct.witnessScript, ourSig,
)
case account.VersionTaprootEnabled:
sweepTx.TxIn[0].Witness = poolscript.SpendExpiryTaproot(
acct.witnessScript, sig.Serialize(), acct.controlBlock,
)
}
var buf bytes.Buffer
err = sweepTx.Serialize(&buf)
@ -296,20 +339,22 @@ type poolAccount struct {
batchKey []byte
keyTweak []byte
witnessScript []byte
controlBlock []byte
version poolscript.Version
}
func (a *poolAccount) String() string {
return fmt.Sprintf("key_index=%d, expiry=%d, shared_key=%x, "+
"batch_key=%x, key_tweak=%x, witness_script=%x",
"batch_key=%x, key_tweak=%x, witness_script=%x, version=%d",
a.keyIndex, a.expiry, a.sharedKey[:], a.batchKey, a.keyTweak,
a.witnessScript)
a.witnessScript, a.version)
}
func bruteForceAccountScript(accountBaseKey *hdkeychain.ExtendedKey,
auctioneerKey *btcec.PublicKey, minExpiry, maxNumBlocks, maxNumAccounts,
auctioneerKey *btcec.PublicKey, minExpiry, maxExpiry, maxNumAccounts,
maxNumBatchKeys uint32, targetScript []byte) (*poolAccount, error) {
// The outer-most loop is over the possible accounts.
// The outermost loop is over the possible accounts.
for i := uint32(0); i < maxNumAccounts; i++ {
accountExtendedKey, err := accountBaseKey.DeriveNonStandard(i)
if err != nil {
@ -337,36 +382,31 @@ func bruteForceAccountScript(accountBaseKey *hdkeychain.ExtendedKey,
for batchKeyIndex < maxNumBatchKeys {
// And then finally the loop over the actual account
// expiry in blocks.
block, err := fastScript(
minExpiry, maxNumBlocks,
acct, err := fastScript(
i, minExpiry, maxExpiry,
accountPrivKey.PubKey(), auctioneerKey,
currentBatchKey, sharedKey, targetScript,
)
if err == nil {
witnessScript, err := poolscript.AccountWitnessScript(
block, accountPrivKey.PubKey(),
auctioneerKey, currentBatchKey,
sharedKey,
)
if err != nil {
return nil, fmt.Errorf("error "+
"deriving script: %w", err)
}
traderKeyTweak := poolscript.TraderKeyTweak(
currentBatchKey, sharedKey,
accountPrivKey.PubKey(),
)
batchKey := currentBatchKey.SerializeCompressed()
return &poolAccount{
keyIndex: i,
expiry: block,
sharedKey: sharedKey,
batchKey: batchKey,
keyTweak: traderKeyTweak,
witnessScript: witnessScript,
}, nil
return acct, nil
}
acct, err = fastScriptTaproot(
poolscript.VersionTaprootMuSig2, i, minExpiry,
maxExpiry, accountPrivKey.PubKey(),
auctioneerKey, currentBatchKey, sharedKey,
targetScript,
)
if err == nil {
return acct, nil
}
acct, err = fastScriptTaproot(
poolscript.VersionTaprootMuSig2V100RC2, i,
minExpiry, maxExpiry,
accountPrivKey.PubKey(), auctioneerKey,
currentBatchKey, sharedKey, targetScript,
)
if err == nil {
return acct, nil
}
currentBatchKey = poolscript.IncrementKey(
@ -381,13 +421,25 @@ func bruteForceAccountScript(accountBaseKey *hdkeychain.ExtendedKey,
return nil, fmt.Errorf("account script not derived")
}
func fastScript(expiryFrom, expiryTo uint32, traderKey, auctioneerKey,
func fastScript(keyIndex, expiryFrom, expiryTo uint32, traderKey, auctioneerKey,
batchKey *btcec.PublicKey, secret [32]byte,
targetScript []byte) (uint32, error) {
targetScript []byte) (*poolAccount, error) {
script, err := txscript.ParsePkScript(targetScript)
if err != nil {
return nil, err
}
if script.Class() != txscript.WitnessV0ScriptHashTy {
return nil, fmt.Errorf("incompatible script class")
}
traderKeyTweak := poolscript.TraderKeyTweak(batchKey, secret, traderKey)
tweakedTraderKey := input.TweakPubKeyWithTweak(traderKey, traderKeyTweak)
tweakedAuctioneerKey := input.TweakPubKey(auctioneerKey, tweakedTraderKey)
tweakedTraderKey := input.TweakPubKeyWithTweak(
traderKey, traderKeyTweak,
)
tweakedAuctioneerKey := input.TweakPubKey(
auctioneerKey, tweakedTraderKey,
)
for block := expiryFrom; block <= expiryTo; block++ {
builder := txscript.NewScriptBuilder()
@ -406,17 +458,136 @@ func fastScript(expiryFrom, expiryTo uint32, traderKey, auctioneerKey,
currentScript, err := builder.Script()
if err != nil {
return 0, fmt.Errorf("error building script: %w", err)
return nil, fmt.Errorf("error building script: %w", err)
}
currentPkScript, err := input.WitnessScriptHash(currentScript)
if err != nil {
return 0, fmt.Errorf("error hashing script: %w", err)
return nil, fmt.Errorf("error hashing script: %w", err)
}
if bytes.Equal(currentPkScript, targetScript) {
return block, nil
if !bytes.Equal(currentPkScript, targetScript) {
continue
}
return &poolAccount{
keyIndex: keyIndex,
expiry: block,
sharedKey: secret,
batchKey: batchKey.SerializeCompressed(),
keyTweak: traderKeyTweak,
witnessScript: currentScript,
version: poolscript.VersionWitnessScript,
}, nil
}
return 0, fmt.Errorf("account script not derived")
return nil, fmt.Errorf("account script not derived")
}
func fastScriptTaproot(scriptVersion poolscript.Version, keyIndex, expiryFrom,
expiryTo uint32, traderKey, auctioneerKey, batchKey *btcec.PublicKey,
secret [32]byte, targetScript []byte) (*poolAccount, error) {
parsedScript, err := txscript.ParsePkScript(targetScript)
if err != nil {
return nil, err
}
if parsedScript.Class() != txscript.WitnessV1TaprootTy {
return nil, fmt.Errorf("incompatible script class")
}
traderKeyTweak := poolscript.TraderKeyTweak(batchKey, secret, traderKey)
tweakedTraderKey := input.TweakPubKeyWithTweak(
traderKey, traderKeyTweak,
)
var muSig2Version input.MuSig2Version
switch scriptVersion {
// The v0.4.0 MuSig2 implementation requires the keys to be serialized
// using the Schnorr (32-byte x-only) serialization format.
case poolscript.VersionTaprootMuSig2:
muSig2Version = input.MuSig2Version040
var err error
auctioneerKey, err = schnorr.ParsePubKey(
schnorr.SerializePubKey(auctioneerKey),
)
if err != nil {
return nil, fmt.Errorf("error parsing auctioneer key: "+
"%w", err)
}
traderKey, err = schnorr.ParsePubKey(
schnorr.SerializePubKey(traderKey),
)
if err != nil {
return nil, fmt.Errorf("error parsing trader key: %w",
err)
}
// The v1.0.0-rc2 MuSig2 implementation works with the regular, 33-byte
// compressed keys, so we can just pass them in as they are.
case poolscript.VersionTaprootMuSig2V100RC2:
muSig2Version = input.MuSig2Version100RC2
default:
return nil, fmt.Errorf("invalid account version <%d>",
scriptVersion)
}
for block := expiryFrom; block <= expiryTo; block++ {
builder := txscript.NewScriptBuilder()
builder.AddData(schnorr.SerializePubKey(tweakedTraderKey))
builder.AddOp(txscript.OP_CHECKSIGVERIFY)
builder.AddInt64(int64(block))
builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
script, err := builder.Script()
if err != nil {
return nil, err
}
rootHash := txscript.NewBaseTapLeaf(script).TapHash()
aggregateKey, err := input.MuSig2CombineKeys(
muSig2Version, []*btcec.PublicKey{
auctioneerKey, traderKey,
}, true, &input.MuSig2Tweaks{
TaprootTweak: rootHash[:],
},
)
if err != nil {
return nil, fmt.Errorf("error combining keys: %w", err)
}
currentKey := schnorr.SerializePubKey(aggregateKey.FinalKey)
if !bytes.Equal(currentKey, targetScript[2:]) {
continue
}
odd := aggregateKey.FinalKey.SerializeCompressed()[0] == oddByte
controlBlock := txscript.ControlBlock{
InternalKey: aggregateKey.PreTweakedKey,
LeafVersion: txscript.BaseLeafVersion,
OutputKeyYIsOdd: odd,
}
blockBytes, err := controlBlock.ToBytes()
if err != nil {
return nil, fmt.Errorf("error serializing control "+
"block: %w", err)
}
return &poolAccount{
keyIndex: keyIndex,
expiry: block,
sharedKey: secret,
batchKey: batchKey.SerializeCompressed(),
keyTweak: traderKeyTweak,
witnessScript: script,
controlBlock: blockBytes,
version: scriptVersion,
}, nil
}
return nil, fmt.Errorf("account script not derived")
}

@ -0,0 +1,98 @@
package main
import (
"encoding/hex"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/guggero/chantools/lnd"
"github.com/lightninglabs/pool/poolscript"
"github.com/lightningnetwork/lnd/keychain"
"github.com/stretchr/testify/require"
)
type testAccount struct {
name string
rootKey string
pkScript string
minExpiry uint32
}
var (
auctioneerKeyBytes, _ = hex.DecodeString(
"0353c7c0d3258c4957331b86af335568232e9af8df61330cee3a7488b61c" +
"f6c298",
)
auctioneerKey, _ = btcec.ParsePubKey(auctioneerKeyBytes)
testAccounts = []testAccount{{
name: "regtest taproot (v1)",
rootKey: "tprv8ZgxMBicQKsPdkvdLKn7HG2hhZ9Ewsgze1Yj3KDEcvb6H5U" +
"519UtfoPPP3hYVgFTn7hXmvE41qaugbaYiZN8wM1HoQHhs3AzSwg" +
"xGYdD8gM",
pkScript: "512001e8d17b83358476534aae4eae2062ea9025dfd858cd81" +
"7bac5f439969da92a6",
minExpiry: 1600,
}, {
name: "regtest taproot (v2)",
rootKey: "tprv8ZgxMBicQKsPdkvdLKn7HG2hhZ9Ewsgze1Yj3KDEcvb6H5U" +
"519UtfoPPP3hYVgFTn7hXmvE41qaugbaYiZN8wM1HoQHhs3AzSwg" +
"xGYdD8gM",
pkScript: "51209dfee24b87f5c35d5a310496a64fab70641bd03d40d5cc" +
"3720f6061f7435778a",
minExpiry: 2060,
}, {
name: "regtest segwit (v0)",
rootKey: "tprv8ZgxMBicQKsPdkvdLKn7HG2hhZ9Ewsgze1Yj3KDEcvb6H5U" +
"519UtfoPPP3hYVgFTn7hXmvE41qaugbaYiZN8wM1HoQHhs3AzSwg" +
"xGYdD8gM",
pkScript: "00201acfd449370aca0f744141bc6fe1f9fe326aa57a9cd35f" +
"bc2f8f15af4c0f4597",
minExpiry: 1600,
}}
)
func TestClosePoolAccount(t *testing.T) {
t.Parallel()
path := []uint32{
lnd.HardenedKeyStart + uint32(keychain.BIP0043Purpose),
lnd.HardenedKeyStart + chaincfg.RegressionNetParams.HDCoinType,
lnd.HardenedKeyStart + uint32(poolscript.AccountKeyFamily),
0,
}
const (
maxBlocks = 50
maxAccounts = 5
maxBatchKeys = 10
)
for _, tc := range testAccounts {
tc := tc
t.Run(tc.name, func(tt *testing.T) {
tt.Parallel()
extendedKey, err := hdkeychain.NewKeyFromString(
tc.rootKey,
)
require.NoError(tt, err)
accountBaseKey, err := lnd.DeriveChildren(
extendedKey, path,
)
require.NoError(tt, err)
targetScriptBytes, err := hex.DecodeString(tc.pkScript)
require.NoError(tt, err)
acct, err := bruteForceAccountScript(
accountBaseKey, auctioneerKey, tc.minExpiry,
tc.minExpiry+maxBlocks, maxAccounts,
maxBatchKeys, targetScriptBytes,
)
require.NoError(tt, err)
t.Logf("Found account: %v", acct)
})
}
}

Binary file not shown.

@ -17,8 +17,8 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
github.com/gogo/protobuf v1.3.2
github.com/hasura/go-graphql-client v0.9.1
github.com/lightninglabs/pool v0.5.7-alpha.0.20220715160511-f7c1ef26af2b
github.com/lightningnetwork/lnd v0.16.0-beta.rc2
github.com/lightninglabs/pool v0.6.2-beta.0.20230329135228-c3bffb52df3a
github.com/lightningnetwork/lnd v0.16.0-beta
github.com/lightningnetwork/lnd/kvdb v1.4.1
github.com/lightningnetwork/lnd/queue v1.1.0
github.com/lightningnetwork/lnd/ticker v1.1.0
@ -56,8 +56,8 @@ require (
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gofrs/uuid v4.2.0+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect
@ -94,8 +94,10 @@ require (
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/lib/pq v1.10.3 // indirect
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect
github.com/lightninglabs/lndclient v0.16.0-10 // indirect
github.com/lightninglabs/neutrino v0.15.0 // indirect
github.com/lightninglabs/neutrino/cache v1.1.1 // indirect
github.com/lightninglabs/pool/auctioneerrpc v1.0.7 // indirect
github.com/lightningnetwork/lightning-onion v1.2.1-0.20221202012345-ca23184850a1 // indirect
github.com/lightningnetwork/lnd/clock v1.1.0 // indirect
github.com/lightningnetwork/lnd/healthcheck v1.2.2 // indirect

@ -223,7 +223,6 @@ github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/E
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@ -244,6 +243,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -464,16 +465,20 @@ github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
github.com/lightninglabs/lndclient v0.16.0-10 h1:cMBJNfssBQtpgYIu23QLP/qw0ijiT5SBZffnXz8zjJk=
github.com/lightninglabs/lndclient v0.16.0-10/go.mod h1:mqY0znSNa+M40HZowwKfno29RyZnmxoqo++BlYP82EY=
github.com/lightninglabs/neutrino v0.15.0 h1:yr3uz36fLAq8hyM0TRUVlef1TRNoWAqpmmNlVtKUDtI=
github.com/lightninglabs/neutrino v0.15.0/go.mod h1:pmjwElN/091TErtSE9Vd5W4hpxoG2/+xlb+HoPm9Gug=
github.com/lightninglabs/neutrino/cache v1.1.1 h1:TllWOSlkABhpgbWJfzsrdUaDH2fBy/54VSIB4vVqV8M=
github.com/lightninglabs/neutrino/cache v1.1.1/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo=
github.com/lightninglabs/pool v0.5.7-alpha.0.20220715160511-f7c1ef26af2b h1:rr0bxgJXScA9WehMv5OxKQrUd4G2EVXBub8my9N+TcA=
github.com/lightninglabs/pool v0.5.7-alpha.0.20220715160511-f7c1ef26af2b/go.mod h1:iQJIB6oPP++G0PfOu9wVNNxuTYV++gDetYyPmz7VUJU=
github.com/lightninglabs/pool v0.6.2-beta.0.20230329135228-c3bffb52df3a h1:beyzoh3UcnQNrFWNNomoueAj3nbuyMjiGQHMqRxspL8=
github.com/lightninglabs/pool v0.6.2-beta.0.20230329135228-c3bffb52df3a/go.mod h1:K84YAySAQak8Jxpno8Gj/7IAl5n8TtdCYH8V+J6VwVM=
github.com/lightninglabs/pool/auctioneerrpc v1.0.7 h1:+a1ynzXAHDlFT4pOeKeAfV38rlwRX3wVdKnGxggtDEQ=
github.com/lightninglabs/pool/auctioneerrpc v1.0.7/go.mod h1:F9uND5Kpj2eYeYe0RLi8IWQHsRjQ88FUp8itkYmX1Mo=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20221202012345-ca23184850a1 h1:Wm0g70gkcAu2pGpNZwfWPSVOY21j8IyYsNewwK4OkT4=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20221202012345-ca23184850a1/go.mod h1:7dDx73ApjEZA0kcknI799m2O5kkpfg4/gr7N092ojNo=
github.com/lightningnetwork/lnd v0.16.0-beta.rc2 h1:49vVlAzUBcE6kEBDl77ohKsFmlzJ5YOZ4dGHCzNoYb4=
github.com/lightningnetwork/lnd v0.16.0-beta.rc2/go.mod h1:MepaR6bfcPFLt6dGfz3Y8P2BFtvOLJuqT6aulf2eYhE=
github.com/lightningnetwork/lnd v0.16.0-beta h1:h1EIN501DuFrBboO7vPiZ6Z7c9PIOx7IjRw1JhB5hEE=
github.com/lightningnetwork/lnd v0.16.0-beta/go.mod h1:dpm/SAXH7CG00ax3Pks7T2hVvvxdX/cc5ACJNPw+vLo=
github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
github.com/lightningnetwork/lnd/clock v1.1.0 h1:/yfVAwtPmdx45aQBoXQImeY7sOIEr7IXlImRMBOZ7GQ=
github.com/lightningnetwork/lnd/clock v1.1.0/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
@ -655,8 +660,8 @@ github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4A
github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
@ -975,6 +980,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=

@ -6,6 +6,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/chaincfg"
@ -35,9 +36,65 @@ func (s *Signer) SignOutputRaw(tx *wire.MsgTx,
}
privKey = maybeTweakPrivKey(signDesc, privKey)
sigHashes := txscript.NewTxSigHashes(tx, signDesc.PrevOutputFetcher)
if txscript.IsPayToTaproot(signDesc.Output.PkScript) {
// Are we spending a script path or the key path? The API is
// slightly different, so we need to account for that to get the
// raw signature.
var rawSig []byte
switch signDesc.SignMethod {
case input.TaprootKeySpendBIP0086SignMethod,
input.TaprootKeySpendSignMethod:
// This function tweaks the private key using the tap
// root key supplied as the tweak.
rawSig, err = txscript.RawTxInTaprootSignature(
tx, sigHashes, signDesc.InputIndex,
signDesc.Output.Value, signDesc.Output.PkScript,
signDesc.TapTweak, signDesc.HashType,
privKey,
)
if err != nil {
return nil, err
}
case input.TaprootScriptSpendSignMethod:
leaf := txscript.TapLeaf{
LeafVersion: txscript.BaseLeafVersion,
Script: witnessScript,
}
rawSig, err = txscript.RawTxInTapscriptSignature(
tx, sigHashes, signDesc.InputIndex,
signDesc.Output.Value, signDesc.Output.PkScript,
leaf, signDesc.HashType, privKey,
)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unknown sign method: %v",
signDesc.SignMethod)
}
// The signature returned above might have a sighash flag
// attached if a non-default type was used. We'll slice this
// off if it exists to ensure we can properly parse the raw
// signature.
sig, err := schnorr.ParseSignature(
rawSig[:schnorr.SignatureSize],
)
if err != nil {
return nil, err
}
return sig, nil
}
amt := signDesc.Output.Value
sig, err := txscript.RawTxInWitnessSignature(
tx, signDesc.SigHashes, signDesc.InputIndex, amt,
tx, sigHashes, signDesc.InputIndex, amt,
witnessScript, signDesc.HashType, privKey,
)
if err != nil {

Loading…
Cancel
Save