@@ -20,6 +20,7 @@ import (
2020 "github.com/btcsuite/btcd/wire"
2121 "github.com/btcsuite/btcwallet/walletdb"
2222 "github.com/lightningnetwork/lnd/channeldb/models"
23+ "github.com/lightningnetwork/lnd/fn"
2324 "github.com/lightningnetwork/lnd/htlcswitch/hop"
2425 "github.com/lightningnetwork/lnd/input"
2526 "github.com/lightningnetwork/lnd/keychain"
@@ -121,6 +122,12 @@ var (
121122 // broadcasted when moving the channel to state CoopBroadcasted.
122123 coopCloseTxKey = []byte ("coop-closing-tx-key" )
123124
125+ // shutdownInfoKey points to the serialised shutdown info that has been
126+ // persisted for a channel. The existence of this info means that we
127+ // have sent the Shutdown message before and so should re-initiate the
128+ // shutdown on re-establish.
129+ shutdownInfoKey = []byte ("shutdown-info-key" )
130+
124131 // commitDiffKey stores the current pending commitment state we've
125132 // extended to the remote party (if any). Each time we propose a new
126133 // state, we store the information necessary to reconstruct this state
@@ -188,6 +195,10 @@ var (
188195 // in the state CommitBroadcasted.
189196 ErrNoCloseTx = fmt .Errorf ("no closing tx found" )
190197
198+ // ErrNoShutdownInfo is returned when no shutdown info has been
199+ // persisted for a channel.
200+ ErrNoShutdownInfo = errors .New ("no shutdown info" )
201+
191202 // ErrNoRestoredChannelMutation is returned when a caller attempts to
192203 // mutate a channel that's been recovered.
193204 ErrNoRestoredChannelMutation = fmt .Errorf ("cannot mutate restored " +
@@ -1575,6 +1586,79 @@ func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) {
15751586 }, nil
15761587}
15771588
1589+ // MarkShutdownSent serialises and persist the given ShutdownInfo for this
1590+ // channel. Persisting this info represents the fact that we have sent the
1591+ // Shutdown message to the remote side and hence that we should re-transmit the
1592+ // same Shutdown message on re-establish.
1593+ func (c * OpenChannel ) MarkShutdownSent (info * ShutdownInfo ) error {
1594+ c .Lock ()
1595+ defer c .Unlock ()
1596+
1597+ return c .storeShutdownInfo (info )
1598+ }
1599+
1600+ // storeShutdownInfo serialises the ShutdownInfo and persists it under the
1601+ // shutdownInfoKey.
1602+ func (c * OpenChannel ) storeShutdownInfo (info * ShutdownInfo ) error {
1603+ var b bytes.Buffer
1604+ err := info .encode (& b )
1605+ if err != nil {
1606+ return err
1607+ }
1608+
1609+ return kvdb .Update (c .Db .backend , func (tx kvdb.RwTx ) error {
1610+ chanBucket , err := fetchChanBucketRw (
1611+ tx , c .IdentityPub , & c .FundingOutpoint , c .ChainHash ,
1612+ )
1613+ if err != nil {
1614+ return err
1615+ }
1616+
1617+ return chanBucket .Put (shutdownInfoKey , b .Bytes ())
1618+ }, func () {})
1619+ }
1620+
1621+ // ShutdownInfo decodes the shutdown info stored for this channel and returns
1622+ // the result. If no shutdown info has been persisted for this channel then the
1623+ // ErrNoShutdownInfo error is returned.
1624+ func (c * OpenChannel ) ShutdownInfo () (fn.Option [ShutdownInfo ], error ) {
1625+ c .RLock ()
1626+ defer c .RUnlock ()
1627+
1628+ var shutdownInfo * ShutdownInfo
1629+ err := kvdb .View (c .Db .backend , func (tx kvdb.RTx ) error {
1630+ chanBucket , err := fetchChanBucket (
1631+ tx , c .IdentityPub , & c .FundingOutpoint , c .ChainHash ,
1632+ )
1633+ switch {
1634+ case err == nil :
1635+ case errors .Is (err , ErrNoChanDBExists ),
1636+ errors .Is (err , ErrNoActiveChannels ),
1637+ errors .Is (err , ErrChannelNotFound ):
1638+
1639+ return ErrNoShutdownInfo
1640+ default :
1641+ return err
1642+ }
1643+
1644+ shutdownInfoBytes := chanBucket .Get (shutdownInfoKey )
1645+ if shutdownInfoBytes == nil {
1646+ return ErrNoShutdownInfo
1647+ }
1648+
1649+ shutdownInfo , err = decodeShutdownInfo (shutdownInfoBytes )
1650+
1651+ return err
1652+ }, func () {
1653+ shutdownInfo = nil
1654+ })
1655+ if err != nil {
1656+ return fn .None [ShutdownInfo ](), err
1657+ }
1658+
1659+ return fn.Some [ShutdownInfo ](* shutdownInfo ), nil
1660+ }
1661+
15781662// isBorked returns true if the channel has been marked as borked in the
15791663// database. This requires an existing database transaction to already be
15801664// active.
@@ -4294,3 +4378,59 @@ func MakeScidRecord(typ tlv.Type, scid *lnwire.ShortChannelID) tlv.Record {
42944378 typ , scid , 8 , lnwire .EShortChannelID , lnwire .DShortChannelID ,
42954379 )
42964380}
4381+
4382+ // ShutdownInfo contains various info about the shutdown initiation of a
4383+ // channel.
4384+ type ShutdownInfo struct {
4385+ // DeliveryScript is the address that we have included in any previous
4386+ // Shutdown message for a particular channel and so should include in
4387+ // any future re-sends of the Shutdown message.
4388+ DeliveryScript tlv.RecordT [tlv.TlvType0 , lnwire.DeliveryAddress ]
4389+
4390+ // LocalInitiator is true if we sent a Shutdown message before ever
4391+ // receiving a Shutdown message from the remote peer.
4392+ LocalInitiator tlv.RecordT [tlv.TlvType1 , bool ]
4393+ }
4394+
4395+ // NewShutdownInfo constructs a new ShutdownInfo object.
4396+ func NewShutdownInfo (deliveryScript lnwire.DeliveryAddress ,
4397+ locallyInitiated bool ) * ShutdownInfo {
4398+
4399+ return & ShutdownInfo {
4400+ DeliveryScript : tlv.NewRecordT [tlv.TlvType0 ](deliveryScript ),
4401+ LocalInitiator : tlv.NewPrimitiveRecord [tlv.TlvType1 ](
4402+ locallyInitiated ,
4403+ ),
4404+ }
4405+ }
4406+
4407+ // encode serialises the ShutdownInfo to the given io.Writer.
4408+ func (s * ShutdownInfo ) encode (w io.Writer ) error {
4409+ records := []tlv.Record {
4410+ s .DeliveryScript .Record (),
4411+ s .LocalInitiator .Record (),
4412+ }
4413+
4414+ stream , err := tlv .NewStream (records ... )
4415+ if err != nil {
4416+ return err
4417+ }
4418+
4419+ return stream .Encode (w )
4420+ }
4421+
4422+ // decodeShutdownInfo constructs a ShutdownInfo struct by decoding the given
4423+ // byte slice.
4424+ func decodeShutdownInfo (b []byte ) (* ShutdownInfo , error ) {
4425+ tlvStream := lnwire .ExtraOpaqueData (b )
4426+
4427+ var info ShutdownInfo
4428+ records := []tlv.RecordProducer {
4429+ & info .DeliveryScript ,
4430+ & info .LocalInitiator ,
4431+ }
4432+
4433+ _ , err := tlvStream .ExtractRecords (records ... )
4434+
4435+ return & info , err
4436+ }
0 commit comments