diff --git a/chanbackup/backup.go b/chanbackup/backup.go index 406cc1bf..b956c6fa 100644 --- a/chanbackup/backup.go +++ b/chanbackup/backup.go @@ -62,12 +62,6 @@ 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) @@ -93,11 +87,6 @@ func FetchStaticChanBackups(chanSource LiveChannelSource) ([]Single, error) { // channel. 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 diff --git a/chanbackup/pubsub.go b/chanbackup/pubsub.go index 2bc74898..b9331820 100644 --- a/chanbackup/pubsub.go +++ b/chanbackup/pubsub.go @@ -213,12 +213,6 @@ 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) diff --git a/chanbackup/single.go b/chanbackup/single.go index c989bda9..490657b9 100644 --- a/chanbackup/single.go +++ b/chanbackup/single.go @@ -31,6 +31,11 @@ const ( // implicitly denotes that this channel uses the new tweakless commit // format. TweaklessCommitVersion = 1 + + // AnchorsCommitVersion is the third SCB version. This version + // implicitly denotes that this channel uses the new anchor commitment + // format. + AnchorsCommitVersion = 2 ) // Single is a static description of an existing channel that can be used for @@ -157,9 +162,14 @@ func NewSingle(channel *channeldb.OpenChannel, }, } - if channel.ChanType.IsTweakless() { + switch { + case channel.ChanType.HasAnchors(): + single.Version = AnchorsCommitVersion + + case channel.ChanType.IsTweakless(): single.Version = TweaklessCommitVersion - } else { + + default: single.Version = DefaultSingleVersion } @@ -174,6 +184,7 @@ func (s *Single) Serialize(w io.Writer) error { switch s.Version { case DefaultSingleVersion: case TweaklessCommitVersion: + case AnchorsCommitVersion: default: return fmt.Errorf("unable to serialize w/ unknown "+ "version: %v", s.Version) @@ -332,6 +343,7 @@ func (s *Single) Deserialize(r io.Reader) error { switch s.Version { case DefaultSingleVersion: case TweaklessCommitVersion: + case AnchorsCommitVersion: default: return fmt.Errorf("unable to de-serialize w/ unknown "+ "version: %v", s.Version) diff --git a/chanbackup/single_test.go b/chanbackup/single_test.go index 88e2f594..ee20d892 100644 --- a/chanbackup/single_test.go +++ b/chanbackup/single_test.go @@ -229,12 +229,20 @@ func TestSinglePackUnpack(t *testing.T) { valid: true, }, - // The new tweakless version, should pack/unpack with no problem. + // The new tweakless version, should pack/unpack with no + // problem. { version: TweaklessCommitVersion, valid: true, }, + // The new anchor version, should pack/unpack with no + // problem. + { + version: AnchorsCommitVersion, + valid: true, + }, + // A non-default version, atm this should result in a failure. { version: 99, diff --git a/chanrestore.go b/chanrestore.go index ccc1b34a..c42f0de3 100644 --- a/chanrestore.go +++ b/chanrestore.go @@ -106,10 +106,17 @@ func (c *chanDBRestorer) openChannelShell(backup chanbackup.Single) ( case chanbackup.TweaklessCommitVersion: chanType = channeldb.SingleFunderTweaklessBit + case chanbackup.AnchorsCommitVersion: + chanType = channeldb.AnchorOutputsBit + chanType |= channeldb.SingleFunderTweaklessBit + default: return nil, fmt.Errorf("unknown Single version: %v", err) } + ltndLog.Infof("SCB Recovery: created channel shell for ChannelPoint(%v), "+ + "chan_type=%v", backup.FundingOutpoint, chanType) + chanShell := channeldb.ChannelShell{ NodeAddrs: backup.Addresses, Chan: &channeldb.OpenChannel{ diff --git a/lncfg/protocol_legacy_on.go b/lncfg/protocol_legacy_on.go index 9be0d1d6..ac388f02 100644 --- a/lncfg/protocol_legacy_on.go +++ b/lncfg/protocol_legacy_on.go @@ -19,7 +19,7 @@ type ProtocolOptions struct { // Anchors should be set if we want to support opening or accepting // channels having the anchor commitment type. - Anchors bool `long:"anchors" description:"EXPERIMENTAL: enable experimental support for anchor commitments. Won't work with watchtowers or static channel backups"` + Anchors bool `long:"anchors" description:"EXPERIMENTAL: enable experimental support for anchor commitments, won't work with watchtowers"` } // LegacyOnion returns true if the old legacy onion format should be used when @@ -35,7 +35,7 @@ func (l *ProtocolOptions) NoStaticRemoteKey() bool { return l.CommitmentTweak } -// AnchorCommitments returns true if support for the the anchor commitment type +// AnchorCommitments returns true if support for the anchor commitment type // should be signaled. func (l *ProtocolOptions) AnchorCommitments() bool { return l.Anchors diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index 8ca1ee7c..cc344372 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -959,11 +959,15 @@ type commitType byte const ( // commitTypeLegacy is the old school commitment type. - commitTypeLegacy = iota + commitTypeLegacy commitType = iota // commiTypeTweakless is the commitment type where the remote key is // static (non-tweaked). commitTypeTweakless + + // commitTypeAnchors is the kind of commitment that has extra outputs + // used for anchoring down to commitment using CPFP. + commitTypeAnchors ) // String returns that name of the commitment type. @@ -973,6 +977,8 @@ func (c commitType) String() string { return "legacy" case commitTypeTweakless: return "tweakless" + case commitTypeAnchors: + return "anchors" default: return "invalid" } @@ -985,6 +991,8 @@ func (c commitType) Args() []string { return []string{"--protocol.committweak"} case commitTypeTweakless: return []string{} + case commitTypeAnchors: + return []string{"--protocol.anchors"} } return nil @@ -8888,6 +8896,7 @@ func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) { if err != nil { t.Fatalf("unable to suspend node: %v", err) } + return restart, chanPoint, balResp.ConfirmedBalance, nil } @@ -13986,6 +13995,10 @@ type chanRestoreTestCase struct { // confirmed or not. unconfirmed bool + // anchorCommit is true, then the new anchor commitment type will be + // used for the channels created in the test. + anchorCommit bool + // restoreMethod takes an old node, then returns a function // closure that'll return the same node, but with its state // restored via a custom method. We use this to abstract away @@ -14010,11 +14023,16 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness, ctxb := context.Background() + var nodeArgs []string + if testCase.anchorCommit { + nodeArgs = commitTypeAnchors.Args() + } + // First, we'll create a brand new node we'll use within the test. If // we have a custom backup file specified, then we'll also create that // for use. dave, mnemonic, err := net.NewNodeWithSeed( - "dave", nil, password, + "dave", nodeArgs, password, ) if err != nil { t.Fatalf("unable to create new node: %v", err) @@ -14024,7 +14042,7 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness, defer func() { shutdownAndAssert(net, t, dave) }() - carol, err := net.NewNode("carol", nil) + carol, err := net.NewNode("carol", nodeArgs) if err != nil { t.Fatalf("unable to make new node: %v", err) } @@ -14545,6 +14563,33 @@ func testChannelBackupRestore(net *lntest.NetworkHarness, t *harnessTest) { ) }, }, + + // Restore the backup from the on-disk file, using the RPC + // interface, for anchor commitment channels. + { + name: "restore from backup file anchors", + initiator: true, + private: false, + anchorCommit: true, + restoreMethod: func(oldNode *lntest.HarnessNode, + backupFilePath string, + mnemonic []string) (nodeRestorer, error) { + + // Read the entire Multi backup stored within + // this node's channels.backup file. + multi, err := ioutil.ReadFile(backupFilePath) + if err != nil { + return nil, err + } + + // Now that we have Dave's backup file, we'll + // create a new nodeRestorer that will restore + // using the on-disk channels.backup. + return chanRestoreViaRPC( + net, password, mnemonic, multi, + ) + }, + }, } // TODO(roasbeef): online vs offline close? diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 077b6da9..251f7b20 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -309,7 +309,7 @@ func CommitScriptAnchors(localChanCfg, return nil, nil, err } - // And the anchor spemdable by the remote node. + // And the anchor spendable by the remote node. remoteAnchor, err := anchorScript(remoteChanCfg.MultiSigKey.PubKey) if err != nil { return nil, nil, err