Merge pull request #106 from lightninglabs/sweepremoteclosed-taproot-channels

sweepremoteclosed: add support for simple taproot channels
pull/109/head
Oliver Gugger 5 months ago committed by GitHub
commit 2abc29d01d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -53,6 +53,7 @@ funds can be swept after the force-close transaction was confirmed.
Supported remote force-closed channel types are: Supported remote force-closed channel types are:
- STATIC_REMOTE_KEY (a.k.a. tweakless channels) - STATIC_REMOTE_KEY (a.k.a. tweakless channels)
- ANCHOR (a.k.a. anchor output channels) - ANCHOR (a.k.a. anchor output channels)
- SIMPLE_TAPROOT (a.k.a. simple taproot channels)
`, `,
Example: `chantools sweepremoteclosed \ Example: `chantools sweepremoteclosed \
--recoverywindow 300 \ --recoverywindow 300 \
@ -113,12 +114,13 @@ func (c *sweepRemoteClosedCommand) Execute(_ *cobra.Command, _ []string) error {
} }
type targetAddr struct { type targetAddr struct {
addr btcutil.Address addr btcutil.Address
pubKey *btcec.PublicKey pubKey *btcec.PublicKey
path string path string
keyDesc *keychain.KeyDescriptor keyDesc *keychain.KeyDescriptor
vouts []*btc.Vout vouts []*btc.Vout
script []byte script []byte
scriptTree *input.CommitScriptTree
} }
func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL, func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
@ -196,18 +198,6 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
err) err)
} }
sequence := wire.MaxTxInSequenceNum
switch target.addr.(type) {
case *btcutil.AddressWitnessPubKeyHash:
estimator.AddP2WKHInput()
case *btcutil.AddressWitnessScriptHash:
estimator.AddWitnessInput(
input.ToRemoteConfirmedWitnessSize,
)
sequence = 1
}
prevOutPoint := wire.OutPoint{ prevOutPoint := wire.OutPoint{
Hash: *txHash, Hash: *txHash,
Index: uint32(vout.Outspend.Vin), Index: uint32(vout.Outspend.Vin),
@ -217,18 +207,76 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
Value: int64(vout.Value), Value: int64(vout.Value),
} }
prevOutFetcher.AddPrevOut(prevOutPoint, prevTxOut) prevOutFetcher.AddPrevOut(prevOutPoint, prevTxOut)
sweepTx.TxIn = append(sweepTx.TxIn, &wire.TxIn{ txIn := &wire.TxIn{
PreviousOutPoint: prevOutPoint, PreviousOutPoint: prevOutPoint,
Sequence: sequence, Sequence: wire.MaxTxInSequenceNum,
}) }
sweepTx.TxIn = append(sweepTx.TxIn, txIn)
inputIndex := len(sweepTx.TxIn) - 1
signDescs = append(signDescs, &input.SignDescriptor{ var signDesc *input.SignDescriptor
KeyDesc: *target.keyDesc, switch target.addr.(type) {
WitnessScript: target.script, case *btcutil.AddressWitnessPubKeyHash:
Output: prevTxOut, estimator.AddP2WKHInput()
HashType: txscript.SigHashAll,
PrevOutputFetcher: prevOutFetcher, signDesc = &input.SignDescriptor{
}) KeyDesc: *target.keyDesc,
WitnessScript: target.script,
Output: prevTxOut,
HashType: txscript.SigHashAll,
PrevOutputFetcher: prevOutFetcher,
InputIndex: inputIndex,
}
case *btcutil.AddressWitnessScriptHash:
estimator.AddWitnessInput(
input.ToRemoteConfirmedWitnessSize,
)
txIn.Sequence = 1
signDesc = &input.SignDescriptor{
KeyDesc: *target.keyDesc,
WitnessScript: target.script,
Output: prevTxOut,
HashType: txscript.SigHashAll,
PrevOutputFetcher: prevOutFetcher,
InputIndex: inputIndex,
}
case *btcutil.AddressTaproot:
estimator.AddWitnessInput(
input.TaprootToRemoteWitnessSize,
)
txIn.Sequence = 1
tree := target.scriptTree
controlBlock, err := tree.CtrlBlockForPath(
input.ScriptPathSuccess,
)
if err != nil {
return err
}
controlBlockBytes, err := controlBlock.ToBytes()
if err != nil {
return err
}
script := tree.SettleLeaf.Script
signMethod := input.TaprootScriptSpendSignMethod
signDesc = &input.SignDescriptor{
KeyDesc: *target.keyDesc,
WitnessScript: script,
Output: prevTxOut,
HashType: txscript.SigHashDefault,
PrevOutputFetcher: prevOutFetcher,
ControlBlock: controlBlockBytes,
InputIndex: inputIndex,
SignMethod: signMethod,
TapTweak: tree.TapscriptRoot,
}
}
signDescs = append(signDescs, signDesc)
} }
} }
@ -270,7 +318,19 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
desc.SigHashes = sigHashes desc.SigHashes = sigHashes
desc.InputIndex = idx desc.InputIndex = idx
if len(desc.WitnessScript) > 0 { switch {
// Simple Taproot Channels.
case desc.SignMethod == input.TaprootScriptSpendSignMethod:
witness, err := input.TaprootCommitSpendSuccess(
signer, desc, sweepTx, nil,
)
if err != nil {
return err
}
sweepTx.TxIn[idx].Witness = witness
// Anchor Channels.
case len(desc.WitnessScript) > 0:
witness, err := input.CommitSpendToRemoteConfirmed( witness, err := input.CommitSpendToRemoteConfirmed(
signer, desc, sweepTx, signer, desc, sweepTx,
) )
@ -278,7 +338,9 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
return err return err
} }
sweepTx.TxIn[idx].Witness = witness sweepTx.TxIn[idx].Witness = witness
} else {
// Static Remote Key Channels.
default:
// The txscript library expects the witness script of a // The txscript library expects the witness script of a
// P2WKH descriptor to be set to the pkScript of the // P2WKH descriptor to be set to the pkScript of the
// output... // output...
@ -320,7 +382,9 @@ func queryAddressBalances(pubKey *btcec.PublicKey, path string,
error) { error) {
var targets []*targetAddr var targets []*targetAddr
queryAddr := func(address btcutil.Address, script []byte) error { queryAddr := func(address btcutil.Address, script []byte,
scriptTree *input.CommitScriptTree) error {
unspent, err := api.Unspent(address.EncodeAddress()) unspent, err := api.Unspent(address.EncodeAddress())
if err != nil { if err != nil {
return fmt.Errorf("could not query unspent: %w", err) return fmt.Errorf("could not query unspent: %w", err)
@ -330,12 +394,13 @@ func queryAddressBalances(pubKey *btcec.PublicKey, path string,
log.Infof("Found %d unspent outputs for address %v", log.Infof("Found %d unspent outputs for address %v",
len(unspent), address.EncodeAddress()) len(unspent), address.EncodeAddress())
targets = append(targets, &targetAddr{ targets = append(targets, &targetAddr{
addr: address, addr: address,
pubKey: pubKey, pubKey: pubKey,
path: path, path: path,
keyDesc: keyDesc, keyDesc: keyDesc,
vouts: unspent, vouts: unspent,
script: script, script: script,
scriptTree: scriptTree,
}) })
} }
@ -346,7 +411,7 @@ func queryAddressBalances(pubKey *btcec.PublicKey, path string,
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := queryAddr(p2wkh, nil); err != nil { if err := queryAddr(p2wkh, nil, nil); err != nil {
return nil, err return nil, err
} }
@ -354,7 +419,15 @@ func queryAddressBalances(pubKey *btcec.PublicKey, path string,
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := queryAddr(p2anchor, script); err != nil { if err := queryAddr(p2anchor, script, nil); err != nil {
return nil, err
}
p2tr, scriptTree, err := lnd.P2TaprootStaticRemove(pubKey, chainParams)
if err != nil {
return nil, err
}
if err := queryAddr(p2tr, nil, scriptTree); err != nil {
return nil, err return nil, err
} }

@ -276,11 +276,7 @@ func GetWitnessAddrScript(addr btcutil.Address,
chainParams.Name) chainParams.Name)
} }
builder := txscript.NewScriptBuilder() return txscript.PayToAddrScript(addr)
builder.AddOp(txscript.OP_0)
builder.AddData(addr.ScriptAddress())
return builder.Script()
} }
// GetP2WPKHScript creates a P2WKH output script from an address. If the address // GetP2WPKHScript creates a P2WKH output script from an address. If the address
@ -387,6 +383,21 @@ func P2AnchorStaticRemote(pubKey *btcec.PublicKey,
return p2wsh, commitScript, err return p2wsh, commitScript, err
} }
func P2TaprootStaticRemove(pubKey *btcec.PublicKey,
params *chaincfg.Params) (*btcutil.AddressTaproot,
*input.CommitScriptTree, error) {
scriptTree, err := input.NewRemoteCommitScriptTree(pubKey)
if err != nil {
return nil, nil, fmt.Errorf("could not create script: %w", err)
}
addr, err := btcutil.NewAddressTaproot(
schnorr.SerializePubKey(scriptTree.TaprootKey), params,
)
return addr, scriptTree, err
}
type HDKeyRing struct { type HDKeyRing struct {
ExtendedKey *hdkeychain.ExtendedKey ExtendedKey *hdkeychain.ExtendedKey
ChainParams *chaincfg.Params ChainParams *chaincfg.Params

Loading…
Cancel
Save