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

Dyncomms [3/n]: Add channel epoch history tracker to handle breach scenarios #9158

Draft
wants to merge 28 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f3230a4
htlcswitch: define state machine for quiescence
ProofOfKeags Dec 8, 2023
9c4c65a
htlcswitch: implement stfu response
ProofOfKeags Dec 12, 2023
52ad0ab
lnwire: add IsChannelUpdate function to distinguish channel updates
ProofOfKeags Aug 9, 2024
2beb8eb
htlcswitch: bounce packets when quiescent
ProofOfKeags Dec 12, 2023
a3607ea
htlcswitch: drop connection if link updates after stfu
ProofOfKeags Dec 12, 2023
d8a11b1
lnwire: signal that we support quiescence
ProofOfKeags Dec 12, 2023
b930ec7
lnrpc add new RPC 'Quiesce' to protobuf definitions
ProofOfKeags Mar 12, 2024
998d39b
lnd: implement new Quiesce RPC with link operation stub
ProofOfKeags Mar 12, 2024
7fc6c2f
htlcswitch: add link operation for initiating quiescence
ProofOfKeags Mar 12, 2024
c097d20
lnd: finish Quiesce implementation using new link op
ProofOfKeags Mar 12, 2024
66ecd42
htlcswitch: implement InitStfu link operation
ProofOfKeags Mar 12, 2024
d20381b
itest+lntest: add itest for Quiesce RPC method
ProofOfKeags Mar 12, 2024
e63e310
htlcswitch: defer processRemoteAdds when quiescent
ProofOfKeags Apr 9, 2024
cb867aa
htlcswitch: add test for deferred processing remote adds when quiescent
ProofOfKeags Apr 10, 2024
42315d9
htlcswitch: implement noop quiescer
ProofOfKeags Aug 10, 2024
68ef311
htlcswitch+peer: allow the disabling of quiescence
ProofOfKeags Aug 10, 2024
a98b790
lnwire: remove no longer used initiator field
ProofOfKeags Sep 13, 2024
a269a83
lnwire: add signature to DynAck
ProofOfKeags Sep 12, 2024
f89f44d
lnwire: add DynCommit message to match spec
ProofOfKeags Sep 13, 2024
58eb9c9
lnwire: add function to easily construct DynCommit
ProofOfKeags Sep 13, 2024
e072626
lnwire: add convenience functions for protocol validation
ProofOfKeags Sep 13, 2024
dd55c6e
lnwire: remove kickoff feerate from propose/commit
ProofOfKeags Oct 7, 2024
56a34cb
multi: pack LocalChanCfg/RemoteChanCfg into Dual
ProofOfKeags Aug 19, 2024
30dbe64
multi: pack ChannelCommitments into Dual in OpenChannel
ProofOfKeags Aug 21, 2024
8d34873
channeldb+contractcourt: tidy type signature for LatestCommitments
ProofOfKeags Aug 21, 2024
5d4020f
channeldb: add ChannelEpochs component for historical params
ProofOfKeags Aug 21, 2024
dc3aef4
channeldb+lnwallet: add epoch history to OpenChannel
ProofOfKeags Sep 4, 2024
6678f08
lnwallet: use CommitChainEpochHistory to determine CsvDelay during Br…
ProofOfKeags Sep 4, 2024
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
2 changes: 1 addition & 1 deletion chanbackup/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func buildCloseTxInputs(
log.Debugf("Crafting CloseTxInputs for ChannelPoint(%v)",
targetChan.FundingOutpoint)

localCommit := targetChan.LocalCommitment
localCommit := targetChan.Commitments.Local

if localCommit.CommitTx == nil {
log.Infof("CommitTx is nil for ChannelPoint(%v), "+
Expand Down
4 changes: 2 additions & 2 deletions chanbackup/single.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,8 @@ func NewSingle(channel *channeldb.OpenChannel,
RemoteNodePub: channel.IdentityPub,
Addresses: nodeAddrs,
Capacity: channel.Capacity,
LocalChanCfg: channel.LocalChanCfg,
RemoteChanCfg: channel.RemoteChanCfg,
LocalChanCfg: channel.ChanCfgs.Local,
RemoteChanCfg: channel.ChanCfgs.Remote,
ShaChainRootDesc: shaChainRootDesc,
}

Expand Down
11 changes: 8 additions & 3 deletions chanbackup/single_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnencrypt"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -241,11 +242,15 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) {
),
ThawHeight: rand.Uint32(),
IdentityPub: pub,
LocalChanCfg: localCfg,
RemoteChanCfg: remoteCfg,
LocalCommitment: localCommit,
Commitments: lntypes.Dual[channeldb.ChannelCommitment]{
Local: localCommit,
},
RevocationProducer: shaChainProducer,
TapscriptRoot: tapscriptRootOption,
ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{
Local: localCfg,
Remote: remoteCfg,
},
}, nil
}

Expand Down
118 changes: 78 additions & 40 deletions channeldb/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ type openChannelTlvData struct {
// customBlob is an optional TLV encoded blob of data representing
// custom channel funding information.
customBlob tlv.OptionalRecordT[tlv.TlvType7, tlv.Blob]

// commitChainEpochHistory is the optional TLV encoded blob of data
// representing the commit chain epoch history for the channel.
commitChainEpochHistory tlv.OptionalRecordT[tlv.TlvType8, CommitChainEpochHistory] //nolint:lll
}

// encode serializes the openChannelTlvData to the given io.Writer.
Expand All @@ -281,6 +285,11 @@ func (c *openChannelTlvData) encode(w io.Writer) error {
c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType7, tlv.Blob]) {
tlvRecords = append(tlvRecords, blob.Record())
})
c.commitChainEpochHistory.WhenSome(
func(hist tlv.RecordT[tlv.TlvType8, CommitChainEpochHistory]) {
tlvRecords = append(tlvRecords, hist.Record())
},
)

// Create the tlv stream.
tlvStream, err := tlv.NewStream(tlvRecords...)
Expand All @@ -296,6 +305,7 @@ func (c *openChannelTlvData) decode(r io.Reader) error {
memo := c.memo.Zero()
tapscriptRoot := c.tapscriptRoot.Zero()
blob := c.customBlob.Zero()
commitChainEpochHistory := c.commitChainEpochHistory.Zero()

// Create the tlv stream.
tlvStream, err := tlv.NewStream(
Expand All @@ -306,6 +316,7 @@ func (c *openChannelTlvData) decode(r io.Reader) error {
memo.Record(),
tapscriptRoot.Record(),
blob.Record(),
commitChainEpochHistory.Record(),
)
if err != nil {
return err
Expand All @@ -325,6 +336,10 @@ func (c *openChannelTlvData) decode(r io.Reader) error {
if _, ok := tlvs[c.customBlob.TlvType()]; ok {
c.customBlob = tlv.SomeRecordT(blob)
}
if _, ok := tlvs[c.commitChainEpochHistory.TlvType()]; ok {
c.commitChainEpochHistory =
tlv.SomeRecordT(commitChainEpochHistory)
}

return nil
}
Expand Down Expand Up @@ -938,23 +953,18 @@ type OpenChannel struct {
// opening.
InitialRemoteBalance lnwire.MilliSatoshi

// LocalChanCfg is the channel configuration for the local node.
LocalChanCfg ChannelConfig
// ChanCfgs is the channel configuration for the local and remote nodes.
ChanCfgs lntypes.Dual[ChannelConfig]

// RemoteChanCfg is the channel configuration for the remote node.
RemoteChanCfg ChannelConfig
// Commitments is the pair of ChannelCommitments for both the
// local and remote parties. They are stored distinctly as there are
// certain asymmetric parameters which affect the structure of each
// commitment.
Commitments lntypes.Dual[ChannelCommitment]

// LocalCommitment is the current local commitment state for the local
// party. This is stored distinct from the state of the remote party
// as there are certain asymmetric parameters which affect the
// structure of each commitment.
LocalCommitment ChannelCommitment

// RemoteCommitment is the current remote commitment state for the
// remote party. This is stored distinct from the state of the local
// party as there are certain asymmetric parameters which affect the
// structure of each commitment.
RemoteCommitment ChannelCommitment
// CommitChainEpochHistory is the history of the CommitmentParams for
// each side of the channel.
CommitChainEpochHistory CommitChainEpochHistory

// RemoteCurrentRevocation is the current revocation for their
// commitment transaction. However, since this the derived public key,
Expand Down Expand Up @@ -1051,13 +1061,13 @@ func (c *OpenChannel) String() string {
indexStr := "height=%v, local_htlc_index=%v, local_log_index=%v, " +
"remote_htlc_index=%v, remote_log_index=%v"

commit := c.LocalCommitment
commit := c.Commitments.Local
local := fmt.Sprintf(indexStr, commit.CommitHeight,
commit.LocalHtlcIndex, commit.LocalLogIndex,
commit.RemoteHtlcIndex, commit.RemoteLogIndex,
)

commit = c.RemoteCommitment
commit = c.Commitments.Remote
remote := fmt.Sprintf(indexStr, commit.CommitHeight,
commit.LocalHtlcIndex, commit.LocalLogIndex,
commit.RemoteHtlcIndex, commit.RemoteLogIndex,
Expand Down Expand Up @@ -1216,6 +1226,11 @@ func (c *OpenChannel) amendTlvData(auxData openChannelTlvData) {
auxData.customBlob.WhenSomeV(func(blob tlv.Blob) {
c.CustomBlob = fn.Some(blob)
})
auxData.commitChainEpochHistory.WhenSomeV(
func(history CommitChainEpochHistory) {
c.CommitChainEpochHistory = history
},
)
}

// extractTlvData creates a new openChannelTlvData from the given channel.
Expand All @@ -1233,6 +1248,11 @@ func (c *OpenChannel) extractTlvData() openChannelTlvData {
realScid: tlv.NewRecordT[tlv.TlvType4](
c.confirmedScid,
),
commitChainEpochHistory: tlv.SomeRecordT(
tlv.NewRecordT[tlv.TlvType8](
c.CommitChainEpochHistory,
),
),
}

if len(c.Memo) != 0 {
Expand Down Expand Up @@ -1763,13 +1783,13 @@ func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) {
// one. If the receiver thinks that our commitment height is actually
// *equal* to this value, then they'll re-send the last commitment that
// they sent but we never fully processed.
localHeight := c.LocalCommitment.CommitHeight
localHeight := c.Commitments.Local.CommitHeight
nextLocalCommitHeight := localHeight + 1

// The second value we'll send is the height of the remote commitment
// from our PoV. If the receiver thinks that their height is actually
// *one plus* this value, then they'll re-send their last revocation.
remoteChainTipHeight := c.RemoteCommitment.CommitHeight
remoteChainTipHeight := c.Commitments.Remote.CommitHeight

// If this channel has undergone a commitment update, then in order to
// prove to the remote party our knowledge of their prior commitment
Expand Down Expand Up @@ -1823,7 +1843,7 @@ func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) {
}

nextNonce, err := NewMusigVerificationNonce(
c.LocalChanCfg.MultiSigKey.PubKey,
c.ChanCfgs.Local.MultiSigKey.PubKey,
nextLocalCommitHeight, taprootRevProducer,
)
if err != nil {
Expand Down Expand Up @@ -2413,7 +2433,7 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *ChannelCommitment,
return nil, err
}

c.LocalCommitment = *newCommitment
c.Commitments.Local = *newCommitment

return finalHtlcs, nil
}
Expand Down Expand Up @@ -2474,7 +2494,7 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC {
// transactions. So we'll iterate through their set of HTLC's to note
// which ones are present on their commitment.
remoteHtlcs := make(map[[32]byte]struct{})
for _, htlc := range c.RemoteCommitment.Htlcs {
for _, htlc := range c.Commitments.Remote.Htlcs {
log.Tracef("RemoteCommitment has htlc: id=%v, update=%v "+
"incoming=%v", htlc.HtlcIndex, htlc.LogIndex,
htlc.Incoming)
Expand All @@ -2486,7 +2506,7 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC {
// Now that we know which HTLC's they have, we'll only mark the HTLC's
// as active if *we* know them as well.
activeHtlcs := make([]HTLC, 0, len(remoteHtlcs))
for _, htlc := range c.LocalCommitment.Htlcs {
for _, htlc := range c.Commitments.Local.Htlcs {
log.Tracef("LocalCommitment has htlc: id=%v, update=%v "+
"incoming=%v", htlc.HtlcIndex, htlc.LogIndex,
htlc.Incoming)
Expand Down Expand Up @@ -3330,7 +3350,7 @@ func (c *OpenChannel) AdvanceCommitChainTail(fwdPkg *FwdPkg,
// With the commitment pointer swapped, we can now add the
// revoked (prior) state to the revocation log.
err = putRevocationLog(
logBucket, &c.RemoteCommitment, ourOutputIndex,
logBucket, &c.Commitments.Remote, ourOutputIndex,
theirOutputIndex, c.Db.parent.noRevLogAmtData,
)
if err != nil {
Expand Down Expand Up @@ -3412,7 +3432,7 @@ func (c *OpenChannel) AdvanceCommitChainTail(fwdPkg *FwdPkg,
// With the db transaction complete, we'll swap over the in-memory
// pointer of the new remote commitment, which was previously the tip
// of the commit chain.
c.RemoteCommitment = *newRemoteCommit
c.Commitments.Remote = *newRemoteCommit

return nil
}
Expand Down Expand Up @@ -3467,7 +3487,7 @@ func (c *OpenChannel) NextLocalHtlcIndex() (uint64, error) {
}

// Otherwise, fallback to using the local htlc index of their commitment.
return c.RemoteCommitment.LocalHtlcIndex, nil
return c.Commitments.Remote.LocalHtlcIndex, nil
}

// LoadFwdPkgs scans the forwarding log for any packages that haven't been
Expand Down Expand Up @@ -3562,7 +3582,7 @@ func (c *OpenChannel) revocationLogTailCommitHeight() (uint64, error) {

// If we haven't created any state updates yet, then we'll exit early as
// there's nothing to be found on disk in the revocation bucket.
if c.RemoteCommitment.CommitHeight == 0 {
if c.Commitments.Remote.CommitHeight == 0 {
return height, nil
}

Expand Down Expand Up @@ -3984,7 +4004,7 @@ func (c *OpenChannel) Snapshot() *ChannelSnapshot {
c.RLock()
defer c.RUnlock()

localCommit := c.LocalCommitment
localCommit := c.Commitments.Local
snapshot := &ChannelSnapshot{
RemoteIdentity: *c.IdentityPub,
ChannelPoint: c.FundingOutpoint,
Expand Down Expand Up @@ -4021,7 +4041,9 @@ func (c *OpenChannel) Snapshot() *ChannelSnapshot {
// remote party. These commitments are read from disk to ensure that only the
// latest fully committed state is returned. The first commitment returned is
// the local commitment, and the second returned is the remote commitment.
func (c *OpenChannel) LatestCommitments() (*ChannelCommitment, *ChannelCommitment, error) {
//
//nolint:lll
func (c *OpenChannel) LatestCommitments() fn.Result[*lntypes.Dual[ChannelCommitment]] {
err := kvdb.View(c.Db.backend, func(tx kvdb.RTx) error {
chanBucket, err := fetchChanBucket(
tx, c.IdentityPub, &c.FundingOutpoint, c.ChainHash,
Expand All @@ -4033,10 +4055,10 @@ func (c *OpenChannel) LatestCommitments() (*ChannelCommitment, *ChannelCommitmen
return fetchChanCommitments(chanBucket, c)
}, func() {})
if err != nil {
return nil, nil, err
return fn.Err[*lntypes.Dual[ChannelCommitment]](err)
}

return &c.LocalCommitment, &c.RemoteCommitment, nil
return fn.Ok(&c.Commitments)
}

// RemoteRevocationStore returns the most up to date commitment version of the
Expand Down Expand Up @@ -4112,7 +4134,7 @@ func putChannelCloseSummary(tx kvdb.RwTx, chanID []byte,

summary.RemoteCurrentRevocation = lastChanState.RemoteCurrentRevocation
summary.RemoteNextRevocation = lastChanState.RemoteNextRevocation
summary.LocalChanConfig = lastChanState.LocalChanCfg
summary.LocalChanConfig = lastChanState.ChanCfgs.Local

var b bytes.Buffer
if err := serializeChannelCloseSummary(&b, summary); err != nil {
Expand Down Expand Up @@ -4302,10 +4324,10 @@ func putChanInfo(chanBucket kvdb.RwBucket, channel *OpenChannel) error {
}
}

if err := writeChanConfig(&w, &channel.LocalChanCfg); err != nil {
if err := writeChanConfig(&w, &channel.ChanCfgs.Local); err != nil {
return err
}
if err := writeChanConfig(&w, &channel.RemoteChanCfg); err != nil {
if err := writeChanConfig(&w, &channel.ChanCfgs.Remote); err != nil {
return err
}

Expand Down Expand Up @@ -4415,14 +4437,14 @@ func putChanCommitments(chanBucket kvdb.RwBucket, channel *OpenChannel) error {
}

err := putChanCommitment(
chanBucket, &channel.LocalCommitment, true,
chanBucket, &channel.Commitments.Local, true,
)
if err != nil {
return err
}

return putChanCommitment(
chanBucket, &channel.RemoteCommitment, false,
chanBucket, &channel.Commitments.Remote, false,
)
}

Expand Down Expand Up @@ -4484,10 +4506,10 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error {
}
}

if err := readChanConfig(r, &channel.LocalChanCfg); err != nil {
if err := readChanConfig(r, &channel.ChanCfgs.Local); err != nil {
return err
}
if err := readChanConfig(r, &channel.RemoteChanCfg); err != nil {
if err := readChanConfig(r, &channel.ChanCfgs.Remote); err != nil {
return err
}

Expand Down Expand Up @@ -4515,6 +4537,22 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error {
// open channel.
channel.amendTlvData(auxData)

// Now that we've extracted the aux data, we can initialize the
// CommitChainEpochHistory. If we don't find it in the aux data,
// then we initialize it with the original CommitmentParams from
// the ChannelConfig.
histVal := auxData.commitChainEpochHistory.ValOpt()
channel.CommitChainEpochHistory = histVal.UnwrapOr(
BeginChainEpochHistory(
lntypes.MapDual(
channel.ChanCfgs,
func(cfg ChannelConfig) CommitmentParams {
return cfg.CommitmentParams
},
),
),
)

channel.Packager = NewChannelPackager(channel.ShortChannelID)

// Finally, read the optional shutdown scripts.
Expand Down Expand Up @@ -4593,11 +4631,11 @@ func fetchChanCommitments(chanBucket kvdb.RBucket, channel *OpenChannel) error {
return nil
}

channel.LocalCommitment, err = fetchChanCommitment(chanBucket, true)
channel.Commitments.Local, err = fetchChanCommitment(chanBucket, true)
if err != nil {
return err
}
channel.RemoteCommitment, err = fetchChanCommitment(chanBucket, false)
channel.Commitments.Remote, err = fetchChanCommitment(chanBucket, false)
if err != nil {
return err
}
Expand Down
Loading
Loading