From af03c8cb46b0c566bbe1fdbf91f3a959ace3cd81 Mon Sep 17 00:00:00 2001 From: eugene Date: Thu, 13 Jan 2022 14:52:58 -0500 Subject: [PATCH] contractcourt: handle writing BreachResolution in arbitrator log --- contractcourt/briefcase.go | 68 ++++++++++++++++++++++++++++++++-- contractcourt/chain_watcher.go | 5 +++ 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/contractcourt/briefcase.go b/contractcourt/briefcase.go index 7c80b922a..61f2d2482 100644 --- a/contractcourt/briefcase.go +++ b/contractcourt/briefcase.go @@ -36,16 +36,21 @@ type ContractResolutions struct { // output. If the channel type doesn't include anchors, the value of // this field will be nil. AnchorResolution *lnwallet.AnchorResolution + + // BreachResolution contains the data required to manage the lifecycle + // of a breach in the ChannelArbitrator. + BreachResolution *BreachResolution } // IsEmpty returns true if the set of resolutions is "empty". A resolution is -// empty if: our commitment output has been trimmed, and we don't have any -// incoming or outgoing HTLC's active. +// empty if: our commitment output has been trimmed, we don't have any +// incoming or outgoing HTLC's active, there is no anchor output to sweep, or +// there are no breached outputs to resolve. func (c *ContractResolutions) IsEmpty() bool { return c.CommitResolution == nil && len(c.HtlcResolutions.IncomingHTLCs) == 0 && len(c.HtlcResolutions.OutgoingHTLCs) == 0 && - c.AnchorResolution == nil + c.AnchorResolution == nil && c.BreachResolution == nil } // ArbitratorLog is the primary source of persistent storage for the @@ -269,6 +274,10 @@ const ( // sweeping out direct commitment output form the remote party's // commitment transaction. resolverUnilateralSweep resolverType = 4 + + // resolverBreach is the type of resolver that manages a contract + // breach on-chain. + resolverBreach resolverType = 5 ) // resolverIDLen is the size of the resolver ID key. This is 36 bytes as we get @@ -341,6 +350,11 @@ var ( // store the anchor resolution, if any. anchorResolutionKey = []byte("anchor-resolution") + // breachResolutionKey is the key under the logScope that we'll use to + // store the breach resolution, if any. This is used rather than the + // resolutionsKey. + breachResolutionKey = []byte("breach-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") @@ -464,6 +478,8 @@ func (b *boltArbitratorLog) writeResolver(contractBucket kvdb.RwBucket, rType = resolverIncomingContest case *commitSweepResolver: rType = resolverUnilateralSweep + case *breachResolver: + rType = resolverBreach } if _, err := buf.Write([]byte{byte(rType)}); err != nil { return err @@ -593,6 +609,11 @@ func (b *boltArbitratorLog) FetchUnresolvedContracts() ([]ContractResolver, erro resReader, resolverCfg, ) + case resolverBreach: + res, err = newBreachResolverFromReader( + resReader, resolverCfg, + ) + default: return fmt.Errorf("unknown resolver type: %v", resType) } @@ -785,6 +806,20 @@ func (b *boltArbitratorLog) LogContractResolutions(c *ContractResolutions) error } } + // Write out the breach resolution if present. + if c.BreachResolution != nil { + var b bytes.Buffer + err := encodeBreachResolution(&b, c.BreachResolution) + if err != nil { + return err + } + + err = scopeBucket.Put(breachResolutionKey, b.Bytes()) + if err != nil { + return err + } + } + return nil }) } @@ -904,6 +939,18 @@ func (b *boltArbitratorLog) FetchContractResolutions() (*ContractResolutions, er } } + breachResBytes := scopeBucket.Get(breachResolutionKey) + if breachResBytes != nil { + c.BreachResolution = &BreachResolution{} + resReader := bytes.NewReader(breachResBytes) + err := decodeBreachResolution( + resReader, c.BreachResolution, + ) + if err != nil { + return err + } + } + return nil }, func() { c = &ContractResolutions{} @@ -1372,6 +1419,21 @@ func decodeAnchorResolution(r io.Reader, return input.ReadSignDescriptor(r, &a.AnchorSignDescriptor) } +func encodeBreachResolution(w io.Writer, b *BreachResolution) error { + if _, err := w.Write(b.FundingOutPoint.Hash[:]); err != nil { + return err + } + return binary.Write(w, endian, b.FundingOutPoint.Index) +} + +func decodeBreachResolution(r io.Reader, b *BreachResolution) error { + _, err := io.ReadFull(r, b.FundingOutPoint.Hash[:]) + if err != nil { + return err + } + return binary.Read(r, endian, &b.FundingOutPoint.Index) +} + func encodeHtlcSetKey(w io.Writer, h *HtlcSetKey) error { err := binary.Write(w, endian, h.IsRemote) if err != nil { diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 973a0ca73..2d8dcce89 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -57,6 +57,11 @@ type RemoteUnilateralCloseInfo struct { CommitSet CommitSet } +// BreachResolution wraps the outpoint of the breached channel. +type BreachResolution struct { + FundingOutPoint wire.OutPoint +} + // CommitSet is a collection of the set of known valid commitments at a given // instant. If ConfCommitKey is set, then the commitment identified by the // HtlcSetKey has hit the chain. This struct will be used to examine all live