cnct+lnrpc: report anchor resolution

This commit is contained in:
Joost Jager 2020-03-10 13:39:01 +01:00
parent ea397c9d6e
commit ab451f634e
No known key found for this signature in database
GPG key ID: A61B9D4C393C59C7
6 changed files with 826 additions and 708 deletions

View file

@ -3,8 +3,10 @@ package contractcourt
import (
"errors"
"io"
"sync"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/sweep"
@ -30,6 +32,13 @@ type anchorResolver struct {
// chanPoint is the channel point of the original contract.
chanPoint wire.OutPoint
// currentReport stores the current state of the resolver for reporting
// over the rpc interface.
currentReport ContractReport
// reportLock prevents concurrent access to the resolver report.
reportLock sync.Mutex
contractResolverKit
}
@ -38,12 +47,23 @@ func newAnchorResolver(anchorSignDescriptor input.SignDescriptor,
anchor wire.OutPoint, broadcastHeight uint32,
chanPoint wire.OutPoint, resCfg ResolverConfig) *anchorResolver {
amt := btcutil.Amount(anchorSignDescriptor.Output.Value)
report := ContractReport{
Outpoint: anchor,
Type: ReportOutputAnchor,
Amount: amt,
LimboBalance: amt,
RecoveredBalance: 0,
}
r := &anchorResolver{
contractResolverKit: *newContractResolverKit(resCfg),
anchorSignDescriptor: anchorSignDescriptor,
anchor: anchor,
broadcastHeight: broadcastHeight,
chanPoint: chanPoint,
currentReport: report,
}
r.initLogger(r)
@ -101,6 +121,7 @@ func (c *anchorResolver) Resolve() (ContractResolver, error) {
}
}
var anchorRecovered bool
select {
case sweepRes := <-resultChan:
switch sweepRes.Err {
@ -110,6 +131,8 @@ func (c *anchorResolver) Resolve() (ContractResolver, error) {
c.log.Debugf("anchor swept by tx %v",
sweepRes.Tx.TxHash())
anchorRecovered = true
// Anchor was swept by someone else. This is possible after the
// 16 block csv lock.
case sweep.ErrRemoteSpend:
@ -136,6 +159,14 @@ func (c *anchorResolver) Resolve() (ContractResolver, error) {
return nil, errResolverShuttingDown
}
// Update report to reflect that funds are no longer in limbo.
c.reportLock.Lock()
if anchorRecovered {
c.currentReport.RecoveredBalance = c.currentReport.LimboBalance
}
c.currentReport.LimboBalance = 0
c.reportLock.Unlock()
c.resolved = true
return nil, nil
}
@ -156,6 +187,15 @@ func (c *anchorResolver) IsResolved() bool {
return c.resolved
}
// report returns a report on the resolution state of the contract.
func (c *anchorResolver) report() *ContractReport {
c.reportLock.Lock()
defer c.reportLock.Unlock()
reportCopy := c.currentReport
return &reportCopy
}
func (c *anchorResolver) Encode(w io.Writer) error {
return errors.New("serialization not supported")
}

View file

@ -162,6 +162,9 @@ const (
// ReportOutputUnencumbered is an uncontested output on the commitment
// transaction paying to us directly.
ReportOutputUnencumbered
// ReportOutputAnchor is an anchor output on the commitment tx.
ReportOutputAnchor
)
// ContractReport provides a summary of a commitment tx output.

File diff suppressed because it is too large Load diff

View file

@ -2016,6 +2016,14 @@ message PendingChannelsResponse {
int64 recovered_balance = 6;
repeated PendingHTLC pending_htlcs = 8;
enum AnchorState {
LIMBO = 0;
RECOVERED = 1;
LOST = 2;
}
AnchorState anchor = 9;
}
/// The balance in satoshis encumbered in pending channels

View file

@ -1534,6 +1534,15 @@
"default": "RESERVED",
"description": " - RESERVED: *\nThe numbers assigned in this enumeration match the failure codes as\ndefined in BOLT #4. Because protobuf 3 requires enums to start with 0,\na RESERVED value is added.\n - INTERNAL_FAILURE: *\nAn internal error occurred.\n - UNKNOWN_FAILURE: *\nThe error source is known, but the failure itself couldn't be decoded.\n - UNREADABLE_FAILURE: *\nAn unreadable failure result is returned if the received failure message\ncannot be decrypted. In that case the error source is unknown."
},
"ForceClosedChannelAnchorState": {
"type": "string",
"enum": [
"LIMBO",
"RECOVERED",
"LOST"
],
"default": "LIMBO"
},
"HTLCAttemptHTLCStatus": {
"type": "string",
"enum": [
@ -1630,6 +1639,9 @@
"items": {
"$ref": "#/definitions/lnrpcPendingHTLC"
}
},
"anchor": {
"$ref": "#/definitions/ForceClosedChannelAnchorState"
}
}
},

View file

@ -2839,6 +2839,22 @@ func (r *rpcServer) arbitratorPopulateForceCloseResp(chanPoint *wire.OutPoint,
forceClose.PendingHtlcs = append(forceClose.PendingHtlcs, htlc)
case contractcourt.ReportOutputAnchor:
// There are three resolution states for the anchor:
// limbo, lost and recovered. Derive the current state
// from the limbo and recovered balances.
switch {
case report.RecoveredBalance != 0:
forceClose.Anchor = lnrpc.PendingChannelsResponse_ForceClosedChannel_RECOVERED
case report.LimboBalance != 0:
forceClose.Anchor = lnrpc.PendingChannelsResponse_ForceClosedChannel_LIMBO
default:
forceClose.Anchor = lnrpc.PendingChannelsResponse_ForceClosedChannel_LOST
}
default:
return fmt.Errorf("unknown report output type: %v",
report.Type)
@ -2846,7 +2862,6 @@ func (r *rpcServer) arbitratorPopulateForceCloseResp(chanPoint *wire.OutPoint,
forceClose.LimboBalance += int64(report.LimboBalance)
forceClose.RecoveredBalance += int64(report.RecoveredBalance)
}
return nil