Skip to content

Commit 3dda93e

Browse files
authored
Merge pull request #3821 from halseth/pluggable-anchors-lnwallet
[anchor] pluggable anchor commitments
2 parents f29169e + b7885db commit 3dda93e

37 files changed

+1854
-610
lines changed

breacharbiter.go

+14
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,13 @@ func (bo *breachedOutput) CraftInputScript(signer input.Signer, txn *wire.MsgTx,
896896
// must be built on top of the confirmation height before the output can be
897897
// spent.
898898
func (bo *breachedOutput) BlocksToMaturity() uint32 {
899+
// If the output is a to_remote output we can claim, and it's of the
900+
// confirmed type, we must wait one block before claiming it.
901+
if bo.witnessType == input.CommitmentToRemoteConfirmed {
902+
return 1
903+
}
904+
905+
// All other breached outputs have no CSV delay.
899906
return 0
900907
}
901908

@@ -952,6 +959,12 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
952959
witnessType = input.CommitSpendNoDelayTweakless
953960
}
954961

962+
// If the local delay is non-zero, it means this output is of
963+
// the confirmed to_remote type.
964+
if breachInfo.LocalDelay != 0 {
965+
witnessType = input.CommitmentToRemoteConfirmed
966+
}
967+
955968
localOutput := makeBreachedOutput(
956969
&breachInfo.LocalOutpoint,
957970
witnessType,
@@ -1117,6 +1130,7 @@ func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight int64,
11171130
for _, input := range inputs {
11181131
txn.AddTxIn(&wire.TxIn{
11191132
PreviousOutPoint: *input.OutPoint(),
1133+
Sequence: input.BlocksToMaturity(),
11201134
})
11211135
}
11221136

chanbackup/backup.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ func FetchBackupForChan(chanPoint wire.OutPoint,
6262
return nil, fmt.Errorf("unable to find target channel")
6363
}
6464

65+
// TODO(halseth): support chan backups for anchor types.
66+
if targetChan.ChanType.HasAnchors() {
67+
return nil, fmt.Errorf("channel type does not support " +
68+
"backups yet")
69+
}
70+
6571
// Once we have the target channel, we can assemble the backup using
6672
// the source to obtain any extra information that we may need.
6773
staticChanBackup, err := assembleChanBackup(chanSource, targetChan)
@@ -85,14 +91,19 @@ func FetchStaticChanBackups(chanSource LiveChannelSource) ([]Single, error) {
8591
// Now that we have all the channels, we'll use the chanSource to
8692
// obtain any auxiliary information we need to craft a backup for each
8793
// channel.
88-
staticChanBackups := make([]Single, len(openChans))
89-
for i, openChan := range openChans {
94+
staticChanBackups := make([]Single, 0, len(openChans))
95+
for _, openChan := range openChans {
96+
// TODO(halseth): support chan backups for anchor types.
97+
if openChan.ChanType.HasAnchors() {
98+
continue
99+
}
100+
90101
chanBackup, err := assembleChanBackup(chanSource, openChan)
91102
if err != nil {
92103
return nil, err
93104
}
94105

95-
staticChanBackups[i] = *chanBackup
106+
staticChanBackups = append(staticChanBackups, *chanBackup)
96107
}
97108

98109
return staticChanBackups, nil

chanbackup/pubsub.go

+6
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,12 @@ func (s *SubSwapper) backupUpdater() {
213213
// For all new open channels, we'll create a new SCB
214214
// given the required information.
215215
for _, newChan := range chanUpdate.NewChans {
216+
// TODO(halseth): support chan backups for
217+
// anchor types.
218+
if newChan.ChanType.HasAnchors() {
219+
continue
220+
}
221+
216222
log.Debugf("Adding channel %v to backup state",
217223
newChan.FundingOutpoint)
218224

channeldb/channel.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ const (
176176
// disk. This bit may be on if the funding transaction was crafted by a
177177
// wallet external to the primary daemon.
178178
NoFundingTxBit ChannelType = 1 << 2
179+
180+
// AnchorOutputsBit indicates that the channel makes use of anchor
181+
// outputs to bump the commitment transaction's effective feerate. This
182+
// channel type also uses a delayed to_remote output script. If bit is
183+
// set, we'll find the size of the anchor outputs in the database.
184+
AnchorOutputsBit ChannelType = 1 << 3
179185
)
180186

181187
// IsSingleFunder returns true if the channel type if one of the known single
@@ -201,6 +207,12 @@ func (c ChannelType) HasFundingTx() bool {
201207
return c&NoFundingTxBit == 0
202208
}
203209

210+
// HasAnchors returns true if this channel type has anchor ouputs on its
211+
// commitment.
212+
func (c ChannelType) HasAnchors() bool {
213+
return c&AnchorOutputsBit == AnchorOutputsBit
214+
}
215+
204216
// ChannelConstraints represents a set of constraints meant to allow a node to
205217
// limit their exposure, enact flow control and ensure that all HTLCs are
206218
// economically relevant. This struct will be mirrored for both sides of the
@@ -326,13 +338,15 @@ type ChannelCommitment struct {
326338
// LocalBalance is the current available settled balance within the
327339
// channel directly spendable by us.
328340
//
329-
// NOTE: This is the balance *after* subtracting any commitment fee.
341+
// NOTE: This is the balance *after* subtracting any commitment fee,
342+
// AND anchor output values.
330343
LocalBalance lnwire.MilliSatoshi
331344

332345
// RemoteBalance is the current available settled balance within the
333346
// channel directly spendable by the remote node.
334347
//
335-
// NOTE: This is the balance *after* subtracting any commitment fee.
348+
// NOTE: This is the balance *after* subtracting any commitment fee,
349+
// AND anchor output values.
336350
RemoteBalance lnwire.MilliSatoshi
337351

338352
// CommitFee is the amount calculated to be paid in fees for the

config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ type config struct {
345345

346346
Watchtower *lncfg.Watchtower `group:"watchtower" namespace:"watchtower"`
347347

348-
LegacyProtocol *lncfg.LegacyProtocol `group:"legacyprotocol" namespace:"legacyprotocol"`
348+
ProtocolOptions *lncfg.ProtocolOptions `group:"protocol" namespace:"protocol"`
349349

350350
AllowCircularRoute bool `long:"allow-circular-route" description:"If true, our node will allow htlc forwards that arrive and depart on the same channel."`
351351
}

contractcourt/chain_watcher.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -351,9 +351,8 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
351351

352352
// With the keys derived, we'll construct the remote script that'll be
353353
// present if they have a non-dust balance on the commitment.
354-
remoteDelay := uint32(remoteChanCfg.CsvDelay)
355-
remoteScript, err := lnwallet.CommitScriptToRemote(
356-
chanType, remoteDelay, commitKeyRing.ToRemoteKey,
354+
remoteScript, _, err := lnwallet.CommitScriptToRemote(
355+
chanType, commitKeyRing.ToRemoteKey,
357356
)
358357
if err != nil {
359358
return false, err

contractcourt/commit_sweep_resolver.go

+30-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"sync"
88

9+
"github.com/btcsuite/btcd/txscript"
910
"github.com/btcsuite/btcd/wire"
1011
"github.com/btcsuite/btcutil"
1112
"github.com/lightningnetwork/lnd/input"
@@ -172,24 +173,45 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
172173
}
173174
}
174175

175-
// We're dealing with our commitment transaction if the delay on the
176-
// resolution isn't zero.
177-
isLocalCommitTx := c.commitResolution.MaturityDelay != 0
178-
179-
// There're two types of commitments, those that have tweaks
180-
// for the remote key (us in this case), and those that don't.
181-
// We'll rely on the presence of the commitment tweak to to
182-
// discern which type of commitment this is.
176+
// The output is on our local commitment if the script starts with
177+
// OP_IF for the revocation clause. On the remote commitment it will
178+
// either be a regular P2WKH or a simple sig spend with a CSV delay.
179+
isLocalCommitTx := c.commitResolution.SelfOutputSignDesc.WitnessScript[0] == txscript.OP_IF
180+
isDelayedOutput := c.commitResolution.MaturityDelay != 0
181+
182+
c.log.Debugf("isDelayedOutput=%v, isLocalCommitTx=%v", isDelayedOutput,
183+
isLocalCommitTx)
184+
185+
// There're three types of commitments, those that have tweaks
186+
// for the remote key (us in this case), those that don't, and a third
187+
// where there is no tweak and the output is delayed. On the local
188+
// commitment our output will always be delayed. We'll rely on the
189+
// presence of the commitment tweak to to discern which type of
190+
// commitment this is.
183191
var witnessType input.WitnessType
184192
switch {
193+
194+
// Delayed output to us on our local commitment.
185195
case isLocalCommitTx:
186196
witnessType = input.CommitmentTimeLock
197+
198+
// A confirmed output to us on the remote commitment.
199+
case isDelayedOutput:
200+
witnessType = input.CommitmentToRemoteConfirmed
201+
202+
// A non-delayed output on the remote commitment where the key is
203+
// tweakless.
187204
case c.commitResolution.SelfOutputSignDesc.SingleTweak == nil:
188205
witnessType = input.CommitSpendNoDelayTweakless
206+
207+
// A non-delayed output on the remote commitment where the key is
208+
// tweaked.
189209
default:
190210
witnessType = input.CommitmentNoDelay
191211
}
192212

213+
c.log.Infof("Sweeping with witness type: %v", witnessType)
214+
193215
// We'll craft an input with all the information required for
194216
// the sweeper to create a fully valid sweeping transaction to
195217
// recover these coins.

contractcourt/commit_sweep_resolver_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ func TestCommitSweepResolverNoDelay(t *testing.T) {
133133
Output: &wire.TxOut{
134134
Value: 100,
135135
},
136+
WitnessScript: []byte{0},
136137
},
137138
}
138139

@@ -162,6 +163,7 @@ func TestCommitSweepResolverDelay(t *testing.T) {
162163
Output: &wire.TxOut{
163164
Value: amt,
164165
},
166+
WitnessScript: []byte{0},
165167
},
166168
MaturityDelay: 3,
167169
SelfOutPoint: outpoint,

contractcourt/htlc_success_resolver.go

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
118118
&h.htlcResolution.SweepSignDesc,
119119
h.htlcResolution.Preimage[:],
120120
h.broadcastHeight,
121+
h.htlcResolution.CsvDelay,
121122
)
122123

123124
// With the input created, we can now generate the full

contractcourt/htlc_timeout_resolver_test.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"testing"
88
"time"
99

10+
"github.com/btcsuite/btcd/txscript"
1011
"github.com/btcsuite/btcd/wire"
1112
"github.com/lightningnetwork/lnd/chainntnfs"
1213
"github.com/lightningnetwork/lnd/input"
@@ -144,7 +145,8 @@ func TestHtlcTimeoutResolver(t *testing.T) {
144145
timeout: true,
145146
txToBroadcast: func() (*wire.MsgTx, error) {
146147
witness, err := input.SenderHtlcSpendTimeout(
147-
nil, signer, fakeSignDesc, sweepTx,
148+
nil, txscript.SigHashAll, signer,
149+
fakeSignDesc, sweepTx,
148150
)
149151
if err != nil {
150152
return nil, err
@@ -163,7 +165,8 @@ func TestHtlcTimeoutResolver(t *testing.T) {
163165
timeout: false,
164166
txToBroadcast: func() (*wire.MsgTx, error) {
165167
witness, err := input.ReceiverHtlcSpendRedeem(
166-
nil, fakePreimageBytes, signer,
168+
nil, txscript.SigHashAll,
169+
fakePreimageBytes, signer,
167170
fakeSignDesc, sweepTx,
168171
)
169172
if err != nil {

feature/default_sets.go

+4
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,8 @@ var defaultSetDesc = setDesc{
4343
SetNodeAnn: {}, // N
4444
SetInvoice: {}, // 9
4545
},
46+
lnwire.AnchorsOptional: {
47+
SetInit: {}, // I
48+
SetNodeAnn: {}, // N
49+
},
4650
}

feature/deps.go

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ var deps = depDesc{
5555
lnwire.MPPOptional: {
5656
lnwire.PaymentAddrOptional: {},
5757
},
58+
lnwire.AnchorsOptional: {
59+
lnwire.StaticRemoteKeyOptional: {},
60+
},
5861
}
5962

6063
// ValidateDeps asserts that a feature vector sets all features and their

feature/manager.go

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ type Config struct {
1717
// NoStaticRemoteKey unsets any optional or required StaticRemoteKey
1818
// bits from all feature sets.
1919
NoStaticRemoteKey bool
20+
21+
// NoAnchors unsets any bits signaling support for anchor outputs.
22+
NoAnchors bool
2023
}
2124

2225
// Manager is responsible for generating feature vectors for different requested
@@ -76,6 +79,10 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
7679
raw.Unset(lnwire.StaticRemoteKeyOptional)
7780
raw.Unset(lnwire.StaticRemoteKeyRequired)
7881
}
82+
if cfg.NoAnchors {
83+
raw.Unset(lnwire.AnchorsOptional)
84+
raw.Unset(lnwire.AnchorsRequired)
85+
}
7986

8087
// Ensure that all of our feature sets properly set any
8188
// dependent features.

0 commit comments

Comments
 (0)