diff --git a/contractcourt/briefcase.go b/contractcourt/briefcase.go index 22adcbdc..6a3bfa8b 100644 --- a/contractcourt/briefcase.go +++ b/contractcourt/briefcase.go @@ -29,6 +29,11 @@ type ContractResolutions struct { // HtlcResolutions contains all data required to fully resolve any // incoming+outgoing HTLC's present within the commitment transaction. HtlcResolutions lnwallet.HtlcResolutions + + // AnchorResolution contains the data required to sweep the anchor + // output. If the channel type doesn't include anchors, the value of + // this field will be nil. + AnchorResolution *lnwallet.AnchorResolution } // IsEmpty returns true if the set of resolutions is "empty". A resolution is @@ -37,7 +42,8 @@ type ContractResolutions struct { func (c *ContractResolutions) IsEmpty() bool { return c.CommitResolution == nil && len(c.HtlcResolutions.IncomingHTLCs) == 0 && - len(c.HtlcResolutions.OutgoingHTLCs) == 0 + len(c.HtlcResolutions.OutgoingHTLCs) == 0 && + c.AnchorResolution == nil } // ArbitratorLog is the primary source of persistent storage for the @@ -263,6 +269,10 @@ var ( // the full set of resolutions for a channel. resolutionsKey = []byte("resolutions") + // anchorResolutionKey is the key under the logScope that we'll use to + // store the anchor resolution, if any. + anchorResolutionKey = []byte("anchor-resolution") + // actionsBucketKey is the key under the logScope that we'll use to // store all chain actions once they're determined. actionsBucketKey = []byte("chain-actions") @@ -630,7 +640,26 @@ func (b *boltArbitratorLog) LogContractResolutions(c *ContractResolutions) error } } - return scopeBucket.Put(resolutionsKey, b.Bytes()) + err = scopeBucket.Put(resolutionsKey, b.Bytes()) + if err != nil { + return err + } + + // Write out the anchor resolution if present. + if c.AnchorResolution != nil { + var b bytes.Buffer + err := encodeAnchorResolution(&b, c.AnchorResolution) + if err != nil { + return err + } + + err = scopeBucket.Put(anchorResolutionKey, b.Bytes()) + if err != nil { + return err + } + } + + return nil }) } @@ -710,6 +739,18 @@ func (b *boltArbitratorLog) FetchContractResolutions() (*ContractResolutions, er } } + anchorResBytes := scopeBucket.Get(anchorResolutionKey) + if anchorResBytes != nil { + c.AnchorResolution = &lnwallet.AnchorResolution{} + resReader := bytes.NewReader(anchorResBytes) + err := decodeAnchorResolution( + resReader, c.AnchorResolution, + ) + if err != nil { + return err + } + } + return nil }) if err != nil { @@ -1045,6 +1086,35 @@ func decodeCommitResolution(r io.Reader, return binary.Read(r, endian, &c.MaturityDelay) } +func encodeAnchorResolution(w io.Writer, + a *lnwallet.AnchorResolution) error { + + if _, err := w.Write(a.CommitAnchor.Hash[:]); err != nil { + return err + } + err := binary.Write(w, endian, a.CommitAnchor.Index) + if err != nil { + return err + } + + return input.WriteSignDescriptor(w, &a.AnchorSignDescriptor) +} + +func decodeAnchorResolution(r io.Reader, + a *lnwallet.AnchorResolution) error { + + _, err := io.ReadFull(r, a.CommitAnchor.Hash[:]) + if err != nil { + return err + } + err = binary.Read(r, endian, &a.CommitAnchor.Index) + if err != nil { + return err + } + + return input.ReadSignDescriptor(r, &a.AnchorSignDescriptor) +} + func encodeHtlcSetKey(w io.Writer, h *HtlcSetKey) error { err := binary.Write(w, endian, h.IsRemote) if err != nil { diff --git a/contractcourt/briefcase_test.go b/contractcourt/briefcase_test.go index 0cccd5a6..3408b2c6 100644 --- a/contractcourt/briefcase_test.go +++ b/contractcourt/briefcase_test.go @@ -46,6 +46,15 @@ var ( Index: 2, } + testChanPoint3 = wire.OutPoint{ + Hash: chainhash.Hash{ + 0x48, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, + 0x51, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, + 0x2d, 0xe7, 0x93, 0xe4, + }, + Index: 3, + } + testPreimage = [32]byte{ 0x52, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, 0x48, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, @@ -540,6 +549,10 @@ func TestContractResolutionsStorage(t *testing.T) { }, }, }, + AnchorResolution: &lnwallet.AnchorResolution{ + CommitAnchor: testChanPoint3, + AnchorSignDescriptor: testSignDesc, + }, } // First make sure that fetching unlogged contract resolutions will diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index 8c6ba271..6b517daf 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -2090,6 +2090,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) { CommitHash: closeTx.TxHash(), CommitResolution: closeInfo.CommitResolution, HtlcResolutions: *closeInfo.HtlcResolutions, + AnchorResolution: closeInfo.AnchorResolution, } // When processing a unilateral close event, we'll @@ -2156,6 +2157,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) { CommitHash: *uniClosure.SpenderTxHash, CommitResolution: uniClosure.CommitResolution, HtlcResolutions: *uniClosure.HtlcResolutions, + AnchorResolution: uniClosure.AnchorResolution, } // When processing a unilateral close event, we'll