Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[anchor] pluggable anchor commitments #3821

Merged
merged 31 commits into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
02dd8d2
input/size: remove unused constants
halseth Mar 6, 2020
865776c
lnwallet/channel: use genHtlcScript to create scripts
halseth Mar 6, 2020
b56c7e3
input/script_utils_test: extract script execution into assert method
halseth Mar 6, 2020
b228681
input/script_utils_test: refactor TestHTLCSenderSpendValidation
halseth Mar 6, 2020
6ecb379
lnwallet: thread chanType down into genHtlcScript, gen[..]SigJobs,
halseth Mar 6, 2020
b25f2fa
channeldb: define AnchorOutputsBit channel type
halseth Mar 6, 2020
8c0deb8
input+lnwallet: Add scripts for CSV delayed HTLC outputs
halseth Mar 6, 2020
dc271a8
input/script_utils test: add test cases for delayed HTLC sender script
halseth Mar 6, 2020
a309132
input/script_utils test: add test cases for delayed HTLC receiver scrpts
halseth Mar 6, 2020
990992c
input/script_utils: add delayed to_remote script + tests
halseth Mar 6, 2020
6deb913
input: add CommitmentToRemoteConfirmed witness type
halseth Mar 6, 2020
50199ae
input/script_utils: add anchor scripts + tests
halseth Mar 6, 2020
af68ff1
lnwallet: add anchor commitmenttype
halseth Mar 6, 2020
1f28bd8
contractcourt/commit_sweep_resolver: set sweep witness type based on …
halseth Mar 6, 2020
ea94dbb
input+lnwallet: use individual commit weight calculations for channel…
halseth Mar 6, 2020
21c5a95
lnwallet/channel: add feerate sanity check
halseth Mar 6, 2020
6810912
lnwallet: choose HTLC scripts based on channel type
halseth Mar 6, 2020
d1089fb
input/test_utils: make mockSigner use SigHashType from sign descriptor
halseth Mar 6, 2020
bddd3e1
lnwallet: make second level sigs using sighash single|anyonecanpay
halseth Mar 6, 2020
c5d58b4
lnwallet: set 2nd level sequence according to channel type
halseth Mar 6, 2020
92af234
lnwallet+nursery+input: set sequence=1 for direct HTLC spends
halseth Mar 6, 2020
ad8e9f3
lnwallet+breacharbiter: record local csv delay
halseth Mar 6, 2020
8741b93
lnwallet/reservation: add non-initiator balance check
halseth Mar 6, 2020
f95a82b
lnwallet+funding: create CommitmentType enum
halseth Mar 6, 2020
51c5352
itest: make commit type enum
halseth Mar 6, 2020
44756b5
cfg: rename legacyprotocol to protocol
halseth Mar 6, 2020
21126ab
multi: optionally enable and signal anchor support
halseth Mar 6, 2020
fd93c56
config+link: disable watchtower for anchors
halseth Mar 6, 2020
7adb1bc
chanbackup: disable channel backup for anchor types
halseth Mar 6, 2020
ea2a58e
fundingmanager+lnwallet: enable anchor commitments
halseth Mar 6, 2020
b7885db
lnwallet+size: select HTLC fees based on channel type
halseth Mar 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions breacharbiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,13 @@ func (bo *breachedOutput) CraftInputScript(signer input.Signer, txn *wire.MsgTx,
// must be built on top of the confirmation height before the output can be
// spent.
func (bo *breachedOutput) BlocksToMaturity() uint32 {
// If the output is a to_remote output we can claim, and it's of the
// confirmed type, we must wait one block before claiming it.
if bo.witnessType == input.CommitmentToRemoteConfirmed {
return 1
}

// All other breached outputs have no CSV delay.
return 0
}

Expand Down Expand Up @@ -952,6 +959,12 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
witnessType = input.CommitSpendNoDelayTweakless
}

// If the local delay is non-zero, it means this output is of
// the confirmed to_remote type.
if breachInfo.LocalDelay != 0 {
witnessType = input.CommitmentToRemoteConfirmed
}

localOutput := makeBreachedOutput(
&breachInfo.LocalOutpoint,
witnessType,
Expand Down Expand Up @@ -1117,6 +1130,7 @@ func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight int64,
for _, input := range inputs {
txn.AddTxIn(&wire.TxIn{
PreviousOutPoint: *input.OutPoint(),
Sequence: input.BlocksToMaturity(),
})
}

Expand Down
17 changes: 14 additions & 3 deletions chanbackup/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ func FetchBackupForChan(chanPoint wire.OutPoint,
return nil, fmt.Errorf("unable to find target channel")
}

// TODO(halseth): support chan backups for anchor types.
if targetChan.ChanType.HasAnchors() {
return nil, fmt.Errorf("channel type does not support " +
"backups yet")
}

// Once we have the target channel, we can assemble the backup using
// the source to obtain any extra information that we may need.
staticChanBackup, err := assembleChanBackup(chanSource, targetChan)
Expand All @@ -85,14 +91,19 @@ func FetchStaticChanBackups(chanSource LiveChannelSource) ([]Single, error) {
// Now that we have all the channels, we'll use the chanSource to
// obtain any auxiliary information we need to craft a backup for each
// channel.
staticChanBackups := make([]Single, len(openChans))
for i, openChan := range openChans {
staticChanBackups := make([]Single, 0, len(openChans))
for _, openChan := range openChans {
// TODO(halseth): support chan backups for anchor types.
if openChan.ChanType.HasAnchors() {
continue
}

chanBackup, err := assembleChanBackup(chanSource, openChan)
if err != nil {
return nil, err
}

staticChanBackups[i] = *chanBackup
staticChanBackups = append(staticChanBackups, *chanBackup)
}

return staticChanBackups, nil
Expand Down
6 changes: 6 additions & 0 deletions chanbackup/pubsub.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ func (s *SubSwapper) backupUpdater() {
// For all new open channels, we'll create a new SCB
// given the required information.
for _, newChan := range chanUpdate.NewChans {
// TODO(halseth): support chan backups for
// anchor types.
if newChan.ChanType.HasAnchors() {
continue
}

log.Debugf("Adding channel %v to backup state",
newChan.FundingOutpoint)

Expand Down
18 changes: 16 additions & 2 deletions channeldb/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ const (
// disk. This bit may be on if the funding transaction was crafted by a
// wallet external to the primary daemon.
NoFundingTxBit ChannelType = 1 << 2

// AnchorOutputsBit indicates that the channel makes use of anchor
// outputs to bump the commitment transaction's effective feerate. This
// channel type also uses a delayed to_remote output script. If bit is
// set, we'll find the size of the anchor outputs in the database.
AnchorOutputsBit ChannelType = 1 << 3
)

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

// HasAnchors returns true if this channel type has anchor ouputs on its
// commitment.
func (c ChannelType) HasAnchors() bool {
return c&AnchorOutputsBit == AnchorOutputsBit
}

// ChannelConstraints represents a set of constraints meant to allow a node to
// limit their exposure, enact flow control and ensure that all HTLCs are
// economically relevant. This struct will be mirrored for both sides of the
Expand Down Expand Up @@ -326,13 +338,15 @@ type ChannelCommitment struct {
// LocalBalance is the current available settled balance within the
// channel directly spendable by us.
//
// NOTE: This is the balance *after* subtracting any commitment fee.
// NOTE: This is the balance *after* subtracting any commitment fee,
// AND anchor output values.
LocalBalance lnwire.MilliSatoshi

// RemoteBalance is the current available settled balance within the
// channel directly spendable by the remote node.
//
// NOTE: This is the balance *after* subtracting any commitment fee.
// NOTE: This is the balance *after* subtracting any commitment fee,
// AND anchor output values.
RemoteBalance lnwire.MilliSatoshi

// CommitFee is the amount calculated to be paid in fees for the
Expand Down
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ type config struct {

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

LegacyProtocol *lncfg.LegacyProtocol `group:"legacyprotocol" namespace:"legacyprotocol"`
ProtocolOptions *lncfg.ProtocolOptions `group:"protocol" namespace:"protocol"`

AllowCircularRoute bool `long:"allow-circular-route" description:"If true, our node will allow htlc forwards that arrive and depart on the same channel."`
}
Expand Down
5 changes: 2 additions & 3 deletions contractcourt/chain_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,8 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,

// With the keys derived, we'll construct the remote script that'll be
// present if they have a non-dust balance on the commitment.
remoteDelay := uint32(remoteChanCfg.CsvDelay)
remoteScript, err := lnwallet.CommitScriptToRemote(
chanType, remoteDelay, commitKeyRing.ToRemoteKey,
remoteScript, _, err := lnwallet.CommitScriptToRemote(
chanType, commitKeyRing.ToRemoteKey,
)
if err != nil {
return false, err
Expand Down
38 changes: 30 additions & 8 deletions contractcourt/commit_sweep_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"sync"

"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/input"
Expand Down Expand Up @@ -172,24 +173,45 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
}
}

// We're dealing with our commitment transaction if the delay on the
// resolution isn't zero.
isLocalCommitTx := c.commitResolution.MaturityDelay != 0

// There're two types of commitments, those that have tweaks
// for the remote key (us in this case), and those that don't.
// We'll rely on the presence of the commitment tweak to to
// discern which type of commitment this is.
// The output is on our local commitment if the script starts with
// OP_IF for the revocation clause. On the remote commitment it will
// either be a regular P2WKH or a simple sig spend with a CSV delay.
isLocalCommitTx := c.commitResolution.SelfOutputSignDesc.WitnessScript[0] == txscript.OP_IF
isDelayedOutput := c.commitResolution.MaturityDelay != 0

c.log.Debugf("isDelayedOutput=%v, isLocalCommitTx=%v", isDelayedOutput,
isLocalCommitTx)

// There're three types of commitments, those that have tweaks
// for the remote key (us in this case), those that don't, and a third
// where there is no tweak and the output is delayed. On the local
// commitment our output will always be delayed. We'll rely on the
// presence of the commitment tweak to to discern which type of
// commitment this is.
var witnessType input.WitnessType
switch {

// Delayed output to us on our local commitment.
case isLocalCommitTx:
witnessType = input.CommitmentTimeLock

// A confirmed output to us on the remote commitment.
case isDelayedOutput:
witnessType = input.CommitmentToRemoteConfirmed

// A non-delayed output on the remote commitment where the key is
// tweakless.
case c.commitResolution.SelfOutputSignDesc.SingleTweak == nil:
witnessType = input.CommitSpendNoDelayTweakless

// A non-delayed output on the remote commitment where the key is
// tweaked.
default:
witnessType = input.CommitmentNoDelay
}

c.log.Infof("Sweeping with witness type: %v", witnessType)

// We'll craft an input with all the information required for
// the sweeper to create a fully valid sweeping transaction to
// recover these coins.
Expand Down
2 changes: 2 additions & 0 deletions contractcourt/commit_sweep_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ func TestCommitSweepResolverNoDelay(t *testing.T) {
Output: &wire.TxOut{
Value: 100,
},
WitnessScript: []byte{0},
},
}

Expand Down Expand Up @@ -162,6 +163,7 @@ func TestCommitSweepResolverDelay(t *testing.T) {
Output: &wire.TxOut{
Value: amt,
},
WitnessScript: []byte{0},
},
MaturityDelay: 3,
SelfOutPoint: outpoint,
Expand Down
1 change: 1 addition & 0 deletions contractcourt/htlc_success_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
&h.htlcResolution.SweepSignDesc,
h.htlcResolution.Preimage[:],
h.broadcastHeight,
h.htlcResolution.CsvDelay,
)

// With the input created, we can now generate the full
Expand Down
7 changes: 5 additions & 2 deletions contractcourt/htlc_timeout_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"
"time"

"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/input"
Expand Down Expand Up @@ -144,7 +145,8 @@ func TestHtlcTimeoutResolver(t *testing.T) {
timeout: true,
txToBroadcast: func() (*wire.MsgTx, error) {
witness, err := input.SenderHtlcSpendTimeout(
nil, signer, fakeSignDesc, sweepTx,
nil, txscript.SigHashAll, signer,
fakeSignDesc, sweepTx,
)
if err != nil {
return nil, err
Expand All @@ -163,7 +165,8 @@ func TestHtlcTimeoutResolver(t *testing.T) {
timeout: false,
txToBroadcast: func() (*wire.MsgTx, error) {
witness, err := input.ReceiverHtlcSpendRedeem(
nil, fakePreimageBytes, signer,
nil, txscript.SigHashAll,
fakePreimageBytes, signer,
fakeSignDesc, sweepTx,
)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions feature/default_sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@ var defaultSetDesc = setDesc{
SetNodeAnn: {}, // N
SetInvoice: {}, // 9
},
lnwire.AnchorsOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
}
3 changes: 3 additions & 0 deletions feature/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ var deps = depDesc{
lnwire.MPPOptional: {
lnwire.PaymentAddrOptional: {},
},
lnwire.AnchorsOptional: {
lnwire.StaticRemoteKeyOptional: {},
},
}

// ValidateDeps asserts that a feature vector sets all features and their
Expand Down
7 changes: 7 additions & 0 deletions feature/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type Config struct {
// NoStaticRemoteKey unsets any optional or required StaticRemoteKey
// bits from all feature sets.
NoStaticRemoteKey bool

// NoAnchors unsets any bits signaling support for anchor outputs.
NoAnchors bool
}

// Manager is responsible for generating feature vectors for different requested
Expand Down Expand Up @@ -76,6 +79,10 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.StaticRemoteKeyOptional)
raw.Unset(lnwire.StaticRemoteKeyRequired)
}
if cfg.NoAnchors {
raw.Unset(lnwire.AnchorsOptional)
raw.Unset(lnwire.AnchorsRequired)
}

// Ensure that all of our feature sets properly set any
// dependent features.
Expand Down
Loading