Skip to content

Commit 2abc29d

Browse files
authored
Merge pull request #106 from lightninglabs/sweepremoteclosed-taproot-channels
sweepremoteclosed: add support for simple taproot channels
2 parents fd18186 + 801f881 commit 2abc29d

File tree

2 files changed

+128
-44
lines changed

2 files changed

+128
-44
lines changed

cmd/chantools/sweepremoteclosed.go

Lines changed: 112 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ funds can be swept after the force-close transaction was confirmed.
5353
Supported remote force-closed channel types are:
5454
- STATIC_REMOTE_KEY (a.k.a. tweakless channels)
5555
- ANCHOR (a.k.a. anchor output channels)
56+
- SIMPLE_TAPROOT (a.k.a. simple taproot channels)
5657
`,
5758
Example: `chantools sweepremoteclosed \
5859
--recoverywindow 300 \
@@ -113,12 +114,13 @@ func (c *sweepRemoteClosedCommand) Execute(_ *cobra.Command, _ []string) error {
113114
}
114115

115116
type targetAddr struct {
116-
addr btcutil.Address
117-
pubKey *btcec.PublicKey
118-
path string
119-
keyDesc *keychain.KeyDescriptor
120-
vouts []*btc.Vout
121-
script []byte
117+
addr btcutil.Address
118+
pubKey *btcec.PublicKey
119+
path string
120+
keyDesc *keychain.KeyDescriptor
121+
vouts []*btc.Vout
122+
script []byte
123+
scriptTree *input.CommitScriptTree
122124
}
123125

124126
func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
@@ -196,18 +198,6 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
196198
err)
197199
}
198200

199-
sequence := wire.MaxTxInSequenceNum
200-
switch target.addr.(type) {
201-
case *btcutil.AddressWitnessPubKeyHash:
202-
estimator.AddP2WKHInput()
203-
204-
case *btcutil.AddressWitnessScriptHash:
205-
estimator.AddWitnessInput(
206-
input.ToRemoteConfirmedWitnessSize,
207-
)
208-
sequence = 1
209-
}
210-
211201
prevOutPoint := wire.OutPoint{
212202
Hash: *txHash,
213203
Index: uint32(vout.Outspend.Vin),
@@ -217,18 +207,76 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
217207
Value: int64(vout.Value),
218208
}
219209
prevOutFetcher.AddPrevOut(prevOutPoint, prevTxOut)
220-
sweepTx.TxIn = append(sweepTx.TxIn, &wire.TxIn{
210+
txIn := &wire.TxIn{
221211
PreviousOutPoint: prevOutPoint,
222-
Sequence: sequence,
223-
})
212+
Sequence: wire.MaxTxInSequenceNum,
213+
}
214+
sweepTx.TxIn = append(sweepTx.TxIn, txIn)
215+
inputIndex := len(sweepTx.TxIn) - 1
224216

225-
signDescs = append(signDescs, &input.SignDescriptor{
226-
KeyDesc: *target.keyDesc,
227-
WitnessScript: target.script,
228-
Output: prevTxOut,
229-
HashType: txscript.SigHashAll,
230-
PrevOutputFetcher: prevOutFetcher,
231-
})
217+
var signDesc *input.SignDescriptor
218+
switch target.addr.(type) {
219+
case *btcutil.AddressWitnessPubKeyHash:
220+
estimator.AddP2WKHInput()
221+
222+
signDesc = &input.SignDescriptor{
223+
KeyDesc: *target.keyDesc,
224+
WitnessScript: target.script,
225+
Output: prevTxOut,
226+
HashType: txscript.SigHashAll,
227+
PrevOutputFetcher: prevOutFetcher,
228+
InputIndex: inputIndex,
229+
}
230+
231+
case *btcutil.AddressWitnessScriptHash:
232+
estimator.AddWitnessInput(
233+
input.ToRemoteConfirmedWitnessSize,
234+
)
235+
txIn.Sequence = 1
236+
237+
signDesc = &input.SignDescriptor{
238+
KeyDesc: *target.keyDesc,
239+
WitnessScript: target.script,
240+
Output: prevTxOut,
241+
HashType: txscript.SigHashAll,
242+
PrevOutputFetcher: prevOutFetcher,
243+
InputIndex: inputIndex,
244+
}
245+
246+
case *btcutil.AddressTaproot:
247+
estimator.AddWitnessInput(
248+
input.TaprootToRemoteWitnessSize,
249+
)
250+
txIn.Sequence = 1
251+
252+
tree := target.scriptTree
253+
controlBlock, err := tree.CtrlBlockForPath(
254+
input.ScriptPathSuccess,
255+
)
256+
if err != nil {
257+
return err
258+
}
259+
controlBlockBytes, err := controlBlock.ToBytes()
260+
if err != nil {
261+
return err
262+
}
263+
264+
script := tree.SettleLeaf.Script
265+
signMethod := input.TaprootScriptSpendSignMethod
266+
signDesc = &input.SignDescriptor{
267+
KeyDesc: *target.keyDesc,
268+
WitnessScript: script,
269+
Output: prevTxOut,
270+
HashType: txscript.SigHashDefault,
271+
PrevOutputFetcher: prevOutFetcher,
272+
ControlBlock: controlBlockBytes,
273+
InputIndex: inputIndex,
274+
SignMethod: signMethod,
275+
TapTweak: tree.TapscriptRoot,
276+
}
277+
}
278+
279+
signDescs = append(signDescs, signDesc)
232280
}
233281
}
234282

@@ -270,15 +318,29 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
270318
desc.SigHashes = sigHashes
271319
desc.InputIndex = idx
272320

273-
if len(desc.WitnessScript) > 0 {
321+
switch {
322+
// Simple Taproot Channels.
323+
case desc.SignMethod == input.TaprootScriptSpendSignMethod:
324+
witness, err := input.TaprootCommitSpendSuccess(
325+
signer, desc, sweepTx, nil,
326+
)
327+
if err != nil {
328+
return err
329+
}
330+
sweepTx.TxIn[idx].Witness = witness
331+
332+
// Anchor Channels.
333+
case len(desc.WitnessScript) > 0:
274334
witness, err := input.CommitSpendToRemoteConfirmed(
275335
signer, desc, sweepTx,
276336
)
277337
if err != nil {
278338
return err
279339
}
280340
sweepTx.TxIn[idx].Witness = witness
281-
} else {
341+
342+
// Static Remote Key Channels.
343+
default:
282344
// The txscript library expects the witness script of a
283345
// P2WKH descriptor to be set to the pkScript of the
284346
// output...
@@ -320,7 +382,9 @@ func queryAddressBalances(pubKey *btcec.PublicKey, path string,
320382
error) {
321383

322384
var targets []*targetAddr
323-
queryAddr := func(address btcutil.Address, script []byte) error {
385+
queryAddr := func(address btcutil.Address, script []byte,
386+
scriptTree *input.CommitScriptTree) error {
387+
324388
unspent, err := api.Unspent(address.EncodeAddress())
325389
if err != nil {
326390
return fmt.Errorf("could not query unspent: %w", err)
@@ -330,12 +394,13 @@ func queryAddressBalances(pubKey *btcec.PublicKey, path string,
330394
log.Infof("Found %d unspent outputs for address %v",
331395
len(unspent), address.EncodeAddress())
332396
targets = append(targets, &targetAddr{
333-
addr: address,
334-
pubKey: pubKey,
335-
path: path,
336-
keyDesc: keyDesc,
337-
vouts: unspent,
338-
script: script,
397+
addr: address,
398+
pubKey: pubKey,
399+
path: path,
400+
keyDesc: keyDesc,
401+
vouts: unspent,
402+
script: script,
403+
scriptTree: scriptTree,
339404
})
340405
}
341406

@@ -346,15 +411,23 @@ func queryAddressBalances(pubKey *btcec.PublicKey, path string,
346411
if err != nil {
347412
return nil, err
348413
}
349-
if err := queryAddr(p2wkh, nil); err != nil {
414+
if err := queryAddr(p2wkh, nil, nil); err != nil {
350415
return nil, err
351416
}
352417

353418
p2anchor, script, err := lnd.P2AnchorStaticRemote(pubKey, chainParams)
354419
if err != nil {
355420
return nil, err
356421
}
357-
if err := queryAddr(p2anchor, script); err != nil {
422+
if err := queryAddr(p2anchor, script, nil); err != nil {
423+
return nil, err
424+
}
425+
426+
p2tr, scriptTree, err := lnd.P2TaprootStaticRemove(pubKey, chainParams)
427+
if err != nil {
428+
return nil, err
429+
}
430+
if err := queryAddr(p2tr, nil, scriptTree); err != nil {
358431
return nil, err
359432
}
360433

lnd/hdkeychain.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,7 @@ func GetWitnessAddrScript(addr btcutil.Address,
276276
chainParams.Name)
277277
}
278278

279-
builder := txscript.NewScriptBuilder()
280-
builder.AddOp(txscript.OP_0)
281-
builder.AddData(addr.ScriptAddress())
282-
283-
return builder.Script()
279+
return txscript.PayToAddrScript(addr)
284280
}
285281

286282
// GetP2WPKHScript creates a P2WKH output script from an address. If the address
@@ -387,6 +383,21 @@ func P2AnchorStaticRemote(pubKey *btcec.PublicKey,
387383
return p2wsh, commitScript, err
388384
}
389385

386+
func P2TaprootStaticRemove(pubKey *btcec.PublicKey,
387+
params *chaincfg.Params) (*btcutil.AddressTaproot,
388+
*input.CommitScriptTree, error) {
389+
390+
scriptTree, err := input.NewRemoteCommitScriptTree(pubKey)
391+
if err != nil {
392+
return nil, nil, fmt.Errorf("could not create script: %w", err)
393+
}
394+
395+
addr, err := btcutil.NewAddressTaproot(
396+
schnorr.SerializePubKey(scriptTree.TaprootKey), params,
397+
)
398+
return addr, scriptTree, err
399+
}
400+
390401
type HDKeyRing struct {
391402
ExtendedKey *hdkeychain.ExtendedKey
392403
ChainParams *chaincfg.Params

0 commit comments

Comments
 (0)