From 974e0f2df5f6239fc39c6fd8f05ec6b75319adcd Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Tue, 22 Jan 2019 20:45:32 -0800 Subject: [PATCH] cnct: make original htlc amt available for contract resolvers Previously, contract resolvers that needed to publish a second level tx, did not have access to the original htlc amount. This commit reconstructs this amount from data that is already persisted in arbitrator log. Co-authored-by: Joost Jager --- contractcourt/channel_arbitrator.go | 101 ++++++++++++++++++++++++- contractcourt/htlc_success_resolver.go | 5 ++ contractcourt/htlc_timeout_resolver.go | 4 + 3 files changed, 109 insertions(+), 1 deletion(-) diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index 517221d0e..154821cb1 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -358,14 +358,109 @@ func (c *ChannelArbitrator) relaunchResolvers() error { return err } + // Retrieve the commitment tx hash from the log. + contractResolutions, err := c.log.FetchContractResolutions() + if err != nil { + log.Errorf("unable to fetch contract resolutions: %v", + err) + return err + } + commitHash := contractResolutions.CommitHash + + // Reconstruct the htlc outpoints and data from the chain action log. + // The purpose of the constructed htlc map is to supplement to resolvers + // restored from database with extra data. Ideally this data is stored + // as part of the resolver in the log. This is a workaround to prevent a + // db migration. + htlcMap := make(map[wire.OutPoint]*channeldb.HTLC) + chainActions, err := c.log.FetchChainActions() + if err != nil { + log.Errorf("unable to fetch chain actions: %v", err) + return err + } + for _, htlcs := range chainActions { + for _, htlc := range htlcs { + outpoint := wire.OutPoint{ + Hash: commitHash, + Index: uint32(htlc.OutputIndex), + } + htlcMap[outpoint] = &htlc + } + } + log.Infof("ChannelArbitrator(%v): relaunching %v contract "+ "resolvers", c.cfg.ChanPoint, len(unresolvedContracts)) + for _, resolver := range unresolvedContracts { + supplementResolver(resolver, htlcMap) + } + c.launchResolvers(unresolvedContracts) return nil } +// supplementResolver takes a resolver as it is restored from the log and fills +// in missing data from the htlcMap. +func supplementResolver(resolver ContractResolver, + htlcMap map[wire.OutPoint]*channeldb.HTLC) error { + + switch r := resolver.(type) { + + case *htlcSuccessResolver: + return supplementSuccessResolver(r, htlcMap) + + case *htlcIncomingContestResolver: + return supplementSuccessResolver( + &r.htlcSuccessResolver, htlcMap, + ) + + case *htlcTimeoutResolver: + return supplementTimeoutResolver(r, htlcMap) + + case *htlcOutgoingContestResolver: + return supplementTimeoutResolver( + &r.htlcTimeoutResolver, htlcMap, + ) + } + + return nil +} + +// supplementSuccessResolver takes a htlcSuccessResolver as it is restored from +// the log and fills in missing data from the htlcMap. +func supplementSuccessResolver(r *htlcSuccessResolver, + htlcMap map[wire.OutPoint]*channeldb.HTLC) error { + + res := r.htlcResolution + htlcPoint := res.HtlcPoint() + htlc, ok := htlcMap[htlcPoint] + if !ok { + return errors.New( + "htlc for success resolver unavailable", + ) + } + r.htlcAmt = htlc.Amt + return nil +} + +// supplementTimeoutResolver takes a htlcSuccessResolver as it is restored from +// the log and fills in missing data from the htlcMap. +func supplementTimeoutResolver(r *htlcTimeoutResolver, + htlcMap map[wire.OutPoint]*channeldb.HTLC) error { + + res := r.htlcResolution + htlcPoint := res.HtlcPoint() + htlc, ok := htlcMap[htlcPoint] + if !ok { + return errors.New( + "htlc for timeout resolver unavailable", + ) + } + r.htlcAmt = htlc.Amt + return nil +} + // Stop signals the ChannelArbitrator for a graceful shutdown. func (c *ChannelArbitrator) Stop() error { if !atomic.CompareAndSwapInt32(&c.stopped, 0, 1) { @@ -1146,6 +1241,7 @@ func (c *ChannelArbitrator) prepContractResolutions(htlcActions ChainActionMap, htlcResolution: resolution, broadcastHeight: height, payHash: htlc.RHash, + htlcAmt: htlc.Amt, ResolverKit: resKit, } htlcResolvers = append(htlcResolvers, resolver) @@ -1173,6 +1269,7 @@ func (c *ChannelArbitrator) prepContractResolutions(htlcActions ChainActionMap, htlcResolution: resolution, broadcastHeight: height, htlcIndex: htlc.HtlcIndex, + htlcAmt: htlc.Amt, ResolverKit: resKit, } htlcResolvers = append(htlcResolvers, resolver) @@ -1206,6 +1303,7 @@ func (c *ChannelArbitrator) prepContractResolutions(htlcActions ChainActionMap, htlcResolution: resolution, broadcastHeight: height, payHash: htlc.RHash, + htlcAmt: htlc.Amt, ResolverKit: resKit, }, } @@ -1232,10 +1330,11 @@ func (c *ChannelArbitrator) prepContractResolutions(htlcActions ChainActionMap, resKit.Quit = make(chan struct{}) resolver := &htlcOutgoingContestResolver{ - htlcTimeoutResolver{ + htlcTimeoutResolver: htlcTimeoutResolver{ htlcResolution: resolution, broadcastHeight: height, htlcIndex: htlc.HtlcIndex, + htlcAmt: htlc.Amt, ResolverKit: resKit, }, } diff --git a/contractcourt/htlc_success_resolver.go b/contractcourt/htlc_success_resolver.go index 56337c9e1..f41e4331e 100644 --- a/contractcourt/htlc_success_resolver.go +++ b/contractcourt/htlc_success_resolver.go @@ -3,6 +3,7 @@ package contractcourt import ( "encoding/binary" "fmt" + "github.com/lightningnetwork/lnd/lnwire" "io" "github.com/btcsuite/btcd/wire" @@ -46,6 +47,10 @@ type htlcSuccessResolver struct { // TODO(roasbeef): send off to utxobundler sweepTx *wire.MsgTx + // htlcAmt is the original amount of the htlc, not taking into + // account any fees that may have to be paid if it goes on chain. + htlcAmt lnwire.MilliSatoshi + ResolverKit } diff --git a/contractcourt/htlc_timeout_resolver.go b/contractcourt/htlc_timeout_resolver.go index 3b4adf1fd..55e9cc00d 100644 --- a/contractcourt/htlc_timeout_resolver.go +++ b/contractcourt/htlc_timeout_resolver.go @@ -40,6 +40,10 @@ type htlcTimeoutResolver struct { // additional commitment state machine. htlcIndex uint64 + // htlcAmt is the original amount of the htlc, not taking into + // account any fees that may have to be paid if it goes on chain. + htlcAmt lnwire.MilliSatoshi + ResolverKit }