github.com/decred/dcrlnd@v0.7.6/contractcourt/anchor_resolver.go (about) 1 package contractcourt 2 3 import ( 4 "errors" 5 "io" 6 "sync" 7 8 "github.com/decred/dcrd/chaincfg/chainhash" 9 "github.com/decred/dcrd/dcrutil/v4" 10 "github.com/decred/dcrd/wire" 11 "github.com/decred/dcrlnd/channeldb" 12 "github.com/decred/dcrlnd/input" 13 "github.com/decred/dcrlnd/sweep" 14 ) 15 16 // anchorResolver is a resolver that will attempt to sweep our anchor output. 17 type anchorResolver struct { 18 // anchorSignDescriptor contains the information that is required to 19 // sweep the anchor. 20 anchorSignDescriptor input.SignDescriptor 21 22 // anchor is the outpoint on the commitment transaction. 23 anchor wire.OutPoint 24 25 // resolved reflects if the contract has been fully resolved or not. 26 resolved bool 27 28 // broadcastHeight is the height that the original contract was 29 // broadcast to the main-chain at. We'll use this value to bound any 30 // historical queries to the chain for spends/confirmations. 31 broadcastHeight uint32 32 33 // chanPoint is the channel point of the original contract. 34 chanPoint wire.OutPoint 35 36 // currentReport stores the current state of the resolver for reporting 37 // over the rpc interface. 38 currentReport ContractReport 39 40 // reportLock prevents concurrent access to the resolver report. 41 reportLock sync.Mutex 42 43 contractResolverKit 44 } 45 46 // newAnchorResolver instantiates a new anchor resolver. 47 func newAnchorResolver(anchorSignDescriptor input.SignDescriptor, 48 anchor wire.OutPoint, broadcastHeight uint32, 49 chanPoint wire.OutPoint, resCfg ResolverConfig) *anchorResolver { 50 51 amt := dcrutil.Amount(anchorSignDescriptor.Output.Value) 52 53 report := ContractReport{ 54 Outpoint: anchor, 55 Type: ReportOutputAnchor, 56 Amount: amt, 57 LimboBalance: amt, 58 RecoveredBalance: 0, 59 } 60 61 r := &anchorResolver{ 62 contractResolverKit: *newContractResolverKit(resCfg), 63 anchorSignDescriptor: anchorSignDescriptor, 64 anchor: anchor, 65 broadcastHeight: broadcastHeight, 66 chanPoint: chanPoint, 67 currentReport: report, 68 } 69 70 r.initLogger(r) 71 72 return r 73 } 74 75 // ResolverKey returns an identifier which should be globally unique for this 76 // particular resolver within the chain the original contract resides within. 77 func (c *anchorResolver) ResolverKey() []byte { 78 // The anchor resolver is stateless and doesn't need a database key. 79 return nil 80 } 81 82 // Resolve offers the anchor output to the sweeper and waits for it to be swept. 83 func (c *anchorResolver) Resolve() (ContractResolver, error) { 84 // Attempt to update the sweep parameters to the post-confirmation 85 // situation. We don't want to force sweep anymore, because the anchor 86 // lost its special purpose to get the commitment confirmed. It is just 87 // an output that we want to sweep only if it is economical to do so. 88 // 89 // An exclusive group is not necessary anymore, because we know that 90 // this is the only anchor that can be swept. 91 // 92 // We also clear the parent tx information for cpfp, because the 93 // commitment tx is confirmed. 94 // 95 // After a restart or when the remote force closes, the sweeper is not 96 // yet aware of the anchor. In that case, it will be added as new input 97 // to the sweeper. 98 relayFeeRate := c.Sweeper.RelayFeePerKB() 99 100 anchorInput := input.MakeBaseInput( 101 &c.anchor, 102 input.CommitmentAnchor, 103 &c.anchorSignDescriptor, 104 c.broadcastHeight, 105 nil, 106 ) 107 108 resultChan, err := c.Sweeper.SweepInput( 109 &anchorInput, 110 sweep.Params{ 111 Fee: sweep.FeePreference{ 112 FeeRate: relayFeeRate, 113 }, 114 }, 115 ) 116 if err != nil { 117 return nil, err 118 } 119 120 var ( 121 outcome channeldb.ResolverOutcome 122 spendTx *chainhash.Hash 123 ) 124 125 select { 126 case sweepRes := <-resultChan: 127 switch sweepRes.Err { 128 129 // Anchor was swept successfully. 130 case nil: 131 sweepTxID := sweepRes.Tx.TxHash() 132 133 spendTx = &sweepTxID 134 outcome = channeldb.ResolverOutcomeClaimed 135 136 // Anchor was swept by someone else. This is possible after the 137 // 16 block csv lock. 138 case sweep.ErrRemoteSpend: 139 c.log.Warnf("our anchor spent by someone else") 140 outcome = channeldb.ResolverOutcomeUnclaimed 141 142 // The sweeper gave up on sweeping the anchor. This happens 143 // after the maximum number of sweep attempts has been reached. 144 // See sweep.DefaultMaxSweepAttempts. Sweep attempts are 145 // interspaced with random delays picked from a range that 146 // increases exponentially. 147 // 148 // We consider the anchor as being lost. 149 case sweep.ErrTooManyAttempts: 150 c.log.Warnf("anchor sweep abandoned") 151 outcome = channeldb.ResolverOutcomeUnclaimed 152 153 // An unexpected error occurred. 154 default: 155 c.log.Errorf("unable to sweep anchor: %v", sweepRes.Err) 156 157 return nil, sweepRes.Err 158 } 159 160 case <-c.quit: 161 return nil, errResolverShuttingDown 162 } 163 164 // Update report to reflect that funds are no longer in limbo. 165 c.reportLock.Lock() 166 if outcome == channeldb.ResolverOutcomeClaimed { 167 c.currentReport.RecoveredBalance = c.currentReport.LimboBalance 168 } 169 c.currentReport.LimboBalance = 0 170 report := c.currentReport.resolverReport( 171 spendTx, channeldb.ResolverTypeAnchor, outcome, 172 ) 173 c.reportLock.Unlock() 174 175 c.resolved = true 176 return nil, c.PutResolverReport(nil, report) 177 } 178 179 // Stop signals the resolver to cancel any current resolution processes, and 180 // suspend. 181 // 182 // NOTE: Part of the ContractResolver interface. 183 func (c *anchorResolver) Stop() { 184 close(c.quit) 185 } 186 187 // IsResolved returns true if the stored state in the resolve is fully 188 // resolved. In this case the target output can be forgotten. 189 // 190 // NOTE: Part of the ContractResolver interface. 191 func (c *anchorResolver) IsResolved() bool { 192 return c.resolved 193 } 194 195 // SupplementState allows the user of a ContractResolver to supplement it with 196 // state required for the proper resolution of a contract. 197 // 198 // NOTE: Part of the ContractResolver interface. 199 func (c *anchorResolver) SupplementState(_ *channeldb.OpenChannel) { 200 } 201 202 // report returns a report on the resolution state of the contract. 203 func (c *anchorResolver) report() *ContractReport { 204 c.reportLock.Lock() 205 defer c.reportLock.Unlock() 206 207 reportCopy := c.currentReport 208 return &reportCopy 209 } 210 211 func (c *anchorResolver) Encode(w io.Writer) error { 212 return errors.New("serialization not supported") 213 } 214 215 // A compile time assertion to ensure anchorResolver meets the 216 // ContractResolver interface. 217 var _ ContractResolver = (*anchorResolver)(nil)