github.com/decred/dcrlnd@v0.7.6/lnwallet/chancloser/chancloser.go (about) 1 package chancloser 2 3 import ( 4 "bytes" 5 "fmt" 6 7 "github.com/davecgh/go-spew/spew" 8 "github.com/decred/dcrd/chaincfg/v3" 9 "github.com/decred/dcrd/dcrutil/v4" 10 "github.com/decred/dcrd/txscript/v4/stdaddr" 11 "github.com/decred/dcrd/wire" 12 "github.com/decred/dcrlnd/htlcswitch" 13 "github.com/decred/dcrlnd/input" 14 "github.com/decred/dcrlnd/labels" 15 "github.com/decred/dcrlnd/lnwallet" 16 "github.com/decred/dcrlnd/lnwallet/chainfee" 17 "github.com/decred/dcrlnd/lnwire" 18 ) 19 20 var ( 21 // ErrChanAlreadyClosing is returned when a channel shutdown is attempted 22 // more than once. 23 ErrChanAlreadyClosing = fmt.Errorf("channel shutdown already initiated") 24 25 // ErrChanCloseNotFinished is returned when a caller attempts to access 26 // a field or function that is contingent on the channel closure negotiation 27 // already being completed. 28 ErrChanCloseNotFinished = fmt.Errorf("close negotiation not finished") 29 30 // ErrInvalidState is returned when the closing state machine receives a 31 // message while it is in an unknown state. 32 ErrInvalidState = fmt.Errorf("invalid state") 33 34 // ErrUpfrontShutdownScriptMismatch is returned when a peer or end user 35 // provides a cooperative close script which does not match the upfront 36 // shutdown script previously set for that party. 37 ErrUpfrontShutdownScriptMismatch = fmt.Errorf("shutdown script does not " + 38 "match upfront shutdown script") 39 ) 40 41 // closeState represents all the possible states the channel closer state 42 // machine can be in. Each message will either advance to the next state, or 43 // remain at the current state. Once the state machine reaches a state of 44 // closeFinished, then negotiation is over. 45 type closeState uint8 46 47 const ( 48 // closeIdle is the initial starting state. In this state, the state 49 // machine has been instantiated, but no state transitions have been 50 // attempted. If a state machine receives a message while in this state, 51 // then it is the responder to an initiated cooperative channel closure. 52 closeIdle closeState = iota 53 54 // closeShutdownInitiated is the state that's transitioned to once the 55 // initiator of a closing workflow sends the shutdown message. At this 56 // point, they're waiting for the remote party to respond with their own 57 // shutdown message. After which, they'll both enter the fee negotiation 58 // phase. 59 closeShutdownInitiated 60 61 // closeFeeNegotiation is the third, and most persistent state. Both 62 // parties enter this state after they've sent and received a shutdown 63 // message. During this phase, both sides will send monotonically 64 // increasing fee requests until one side accepts the last fee rate offered 65 // by the other party. In this case, the party will broadcast the closing 66 // transaction, and send the accepted fee to the remote party. This then 67 // causes a shift into the closeFinished state. 68 closeFeeNegotiation 69 70 // closeFinished is the final state of the state machine. In this state, a 71 // side has accepted a fee offer and has broadcast the valid closing 72 // transaction to the network. During this phase, the closing transaction 73 // becomes available for examination. 74 closeFinished 75 ) 76 77 // ChanCloseCfg holds all the items that a ChanCloser requires to carry out its 78 // duties. 79 type ChanCloseCfg struct { 80 // Channel is the channel that should be closed. 81 Channel *lnwallet.LightningChannel 82 83 // BroadcastTx broadcasts the passed transaction to the network. 84 BroadcastTx func(*wire.MsgTx, string) error 85 86 // DisableChannel disables a channel, resulting in it not being able to 87 // forward payments. 88 DisableChannel func(wire.OutPoint) error 89 90 // Disconnect will disconnect from the remote peer in this close. 91 Disconnect func() error 92 93 // Quit is a channel that should be sent upon in the occasion the state 94 // machine should cease all progress and shutdown. 95 Quit chan struct{} 96 } 97 98 // ChanCloser is a state machine that handles the cooperative channel closure 99 // procedure. This includes shutting down a channel, marking it ineligible for 100 // routing HTLC's, negotiating fees with the remote party, and finally 101 // broadcasting the fully signed closure transaction to the network. 102 type ChanCloser struct { 103 // state is the current state of the state machine. 104 state closeState 105 106 // cfg holds the configuration for this ChanCloser instance. 107 cfg ChanCloseCfg 108 109 // chanPoint is the full channel point of the target channel. 110 chanPoint wire.OutPoint 111 112 // cid is the full channel ID of the target channel. 113 cid lnwire.ChannelID 114 115 // negotiationHeight is the height that the fee negotiation begun at. 116 negotiationHeight uint32 117 118 // closingTx is the final, fully signed closing transaction. This will only 119 // be populated once the state machine shifts to the closeFinished state. 120 closingTx *wire.MsgTx 121 122 // idealFeeSat is the ideal fee that the state machine should initially 123 // offer when starting negotiation. This will be used as a baseline. 124 idealFeeSat dcrutil.Amount 125 126 // lastFeeProposal is the last fee that we proposed to the remote 127 // party. We'll use this as a pivot point to ratchet our next offer 128 // up, down, or simply accept the remote party's prior offer. 129 lastFeeProposal dcrutil.Amount 130 131 // priorFeeOffers is a map that keeps track of all the proposed fees that 132 // we've offered during the fee negotiation. We use this map to cut the 133 // negotiation early if the remote party ever sends an offer that we've 134 // sent in the past. Once negotiation terminates, we can extract the prior 135 // signature of our accepted offer from this map. 136 // 137 // TODO(roasbeef): need to ensure if they broadcast w/ any of our prior 138 // sigs, we are aware of 139 priorFeeOffers map[dcrutil.Amount]*lnwire.ClosingSigned 140 141 // closeReq is the initial closing request. This will only be populated if 142 // we're the initiator of this closing negotiation. 143 // 144 // TODO(roasbeef): abstract away 145 closeReq *htlcswitch.ChanClose 146 147 // localDeliveryScript is the script that we'll send our settled channel 148 // funds to. 149 localDeliveryScript []byte 150 151 // remoteDeliveryScript is the script that we'll send the remote party's 152 // settled channel funds to. 153 remoteDeliveryScript []byte 154 155 // locallyInitiated is true if we initiated the channel close. 156 locallyInitiated bool 157 } 158 159 // NewChanCloser creates a new instance of the channel closure given the passed 160 // configuration, and delivery+fee preference. The final argument should only 161 // be populated iff, we're the initiator of this closing request. 162 func NewChanCloser(cfg ChanCloseCfg, deliveryScript []byte, 163 idealFeePerKB chainfee.AtomPerKByte, negotiationHeight uint32, 164 closeReq *htlcswitch.ChanClose, locallyInitiated bool) *ChanCloser { 165 166 // Given the target fee-per-kw, we'll compute what our ideal _total_ fee 167 // will be starting at for this fee negotiation. 168 // 169 // TODO(roasbeef): should factor in minimal commit 170 idealFeeSat := cfg.Channel.CalcFee(idealFeePerKB) 171 172 // If this fee is greater than the fee currently present within the 173 // commitment transaction, then we'll clamp it down to be within the proper 174 // range. 175 // 176 // TODO(roasbeef): clamp fee func? 177 channelCommitFee := cfg.Channel.StateSnapshot().CommitFee 178 if idealFeeSat > channelCommitFee { 179 chancloserLog.Infof("Ideal starting fee of %v is greater than commit "+ 180 "fee of %v, clamping", int64(idealFeeSat), int64(channelCommitFee)) 181 182 idealFeeSat = channelCommitFee 183 } 184 185 chancloserLog.Infof("Ideal fee for closure of ChannelPoint(%v) is: %v sat", 186 cfg.Channel.ChannelPoint(), int64(idealFeeSat)) 187 188 cid := lnwire.NewChanIDFromOutPoint(cfg.Channel.ChannelPoint()) 189 return &ChanCloser{ 190 closeReq: closeReq, 191 state: closeIdle, 192 chanPoint: *cfg.Channel.ChannelPoint(), 193 cid: cid, 194 cfg: cfg, 195 negotiationHeight: negotiationHeight, 196 idealFeeSat: idealFeeSat, 197 localDeliveryScript: deliveryScript, 198 priorFeeOffers: make(map[dcrutil.Amount]*lnwire.ClosingSigned), 199 locallyInitiated: locallyInitiated, 200 } 201 } 202 203 // initChanShutdown begins the shutdown process by un-registering the channel, 204 // and creating a valid shutdown message to our target delivery address. 205 func (c *ChanCloser) initChanShutdown() (*lnwire.Shutdown, error) { 206 // With both items constructed we'll now send the shutdown message for this 207 // particular channel, advertising a shutdown request to our desired 208 // closing script. 209 shutdown := lnwire.NewShutdown(c.cid, c.localDeliveryScript) 210 211 // Before closing, we'll attempt to send a disable update for the channel. 212 // We do so before closing the channel as otherwise the current edge policy 213 // won't be retrievable from the graph. 214 if err := c.cfg.DisableChannel(c.chanPoint); err != nil { 215 chancloserLog.Warnf("Unable to disable channel %v on close: %v", 216 c.chanPoint, err) 217 } 218 219 // Before continuing, mark the channel as cooperatively closed with a nil 220 // txn. Even though we haven't negotiated the final txn, this guarantees 221 // that our listchannels rpc will be externally consistent, and reflect 222 // that the channel is being shutdown by the time the closing request 223 // returns. 224 err := c.cfg.Channel.MarkCoopBroadcasted(nil, c.locallyInitiated) 225 if err != nil { 226 return nil, err 227 } 228 229 chancloserLog.Infof("ChannelPoint(%v): sending shutdown message", 230 c.chanPoint) 231 232 return shutdown, nil 233 } 234 235 // ShutdownChan is the first method that's to be called by the initiator of the 236 // cooperative channel closure. This message returns the shutdown message to 237 // send to the remote party. Upon completion, we enter the 238 // closeShutdownInitiated phase as we await a response. 239 func (c *ChanCloser) ShutdownChan() (*lnwire.Shutdown, error) { 240 // If we attempt to shutdown the channel for the first time, and we're not 241 // in the closeIdle state, then the caller made an error. 242 if c.state != closeIdle { 243 return nil, ErrChanAlreadyClosing 244 } 245 246 chancloserLog.Infof("ChannelPoint(%v): initiating shutdown", c.chanPoint) 247 248 shutdownMsg, err := c.initChanShutdown() 249 if err != nil { 250 return nil, err 251 } 252 253 // With the opening steps complete, we'll transition into the 254 // closeShutdownInitiated state. In this state, we'll wait until the other 255 // party sends their version of the shutdown message. 256 c.state = closeShutdownInitiated 257 258 // Finally, we'll return the shutdown message to the caller so it can send 259 // it to the remote peer. 260 return shutdownMsg, nil 261 } 262 263 // ClosingTx returns the fully signed, final closing transaction. 264 // 265 // NOTE: This transaction is only available if the state machine is in the 266 // closeFinished state. 267 func (c *ChanCloser) ClosingTx() (*wire.MsgTx, error) { 268 // If the state machine hasn't finished closing the channel, then we'll 269 // return an error as we haven't yet computed the closing tx. 270 if c.state != closeFinished { 271 return nil, ErrChanCloseNotFinished 272 } 273 274 return c.closingTx, nil 275 } 276 277 // CloseRequest returns the original close request that prompted the creation 278 // of the state machine. 279 // 280 // NOTE: This will only return a non-nil pointer if we were the initiator of 281 // the cooperative closure workflow. 282 func (c *ChanCloser) CloseRequest() *htlcswitch.ChanClose { 283 return c.closeReq 284 } 285 286 // Channel returns the channel stored in the config. 287 func (c *ChanCloser) Channel() *lnwallet.LightningChannel { 288 return c.cfg.Channel 289 } 290 291 // NegotiationHeight returns the negotiation height. 292 func (c *ChanCloser) NegotiationHeight() uint32 { 293 return c.negotiationHeight 294 } 295 296 // maybeMatchScript attempts to match the script provided in our peer's 297 // shutdown message with the upfront shutdown script we have on record. If no 298 // upfront shutdown script was set, we do not need to enforce option upfront 299 // shutdown, so the function returns early. If an upfront script is set, we 300 // check whether it matches the script provided by our peer. If they do not 301 // match, we use the disconnect function provided to disconnect from the peer. 302 func maybeMatchScript(disconnect func() error, upfrontScript, 303 peerScript lnwire.DeliveryAddress) error { 304 305 // If no upfront shutdown script was set, return early because we do not 306 // need to enforce closure to a specific script. 307 if len(upfrontScript) == 0 { 308 return nil 309 } 310 311 // If an upfront shutdown script was provided, disconnect from the peer, as 312 // per BOLT 2, and return an error. 313 if !bytes.Equal(upfrontScript, peerScript) { 314 chancloserLog.Warnf("peer's script: %x does not match upfront "+ 315 "shutdown script: %x", peerScript, upfrontScript) 316 317 // Disconnect from the peer because they have violated option upfront 318 // shutdown. 319 if err := disconnect(); err != nil { 320 return err 321 } 322 323 return ErrUpfrontShutdownScriptMismatch 324 } 325 326 return nil 327 } 328 329 // ProcessCloseMsg attempts to process the next message in the closing series. 330 // This method will update the state accordingly and return two primary values: 331 // the next set of messages to be sent, and a bool indicating if the fee 332 // negotiation process has completed. If the second value is true, then this 333 // means the ChanCloser can be garbage collected. 334 func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, 335 bool, error) { 336 337 switch c.state { 338 339 // If we're in the close idle state, and we're receiving a channel closure 340 // related message, then this indicates that we're on the receiving side of 341 // an initiated channel closure. 342 case closeIdle: 343 // First, we'll assert that we have a channel shutdown message, 344 // as otherwise, this is an attempted invalid state transition. 345 shutdownMsg, ok := msg.(*lnwire.Shutdown) 346 if !ok { 347 return nil, false, fmt.Errorf("expected lnwire.Shutdown, instead "+ 348 "have %v", spew.Sdump(msg)) 349 } 350 351 // As we're the responder to this shutdown (the other party 352 // wants to close), we'll check if this is a frozen channel or 353 // not. If the channel is frozen and we were not also the 354 // initiator of the channel opening, then we'll deny their close 355 // attempt. 356 chanInitiator := c.cfg.Channel.IsInitiator() 357 chanState := c.cfg.Channel.State() 358 if !chanInitiator { 359 absoluteThawHeight, err := chanState.AbsoluteThawHeight() 360 if err != nil { 361 return nil, false, err 362 } 363 if c.negotiationHeight < absoluteThawHeight { 364 return nil, false, fmt.Errorf("initiator "+ 365 "attempting to co-op close frozen "+ 366 "ChannelPoint(%v) (current_height=%v, "+ 367 "thaw_height=%v)", c.chanPoint, 368 c.negotiationHeight, absoluteThawHeight) 369 } 370 } 371 372 // If the remote node opened the channel with option upfront shutdown 373 // script, check that the script they provided matches. 374 if err := maybeMatchScript( 375 c.cfg.Disconnect, c.cfg.Channel.RemoteUpfrontShutdownScript(), 376 shutdownMsg.Address, 377 ); err != nil { 378 return nil, false, err 379 } 380 381 // Once we have checked that the other party has not violated option 382 // upfront shutdown we set their preference for delivery address. We'll 383 // use this when we craft the closure transaction. 384 c.remoteDeliveryScript = shutdownMsg.Address 385 386 // We'll generate a shutdown message of our own to send across the 387 // wire. 388 localShutdown, err := c.initChanShutdown() 389 if err != nil { 390 return nil, false, err 391 } 392 393 chancloserLog.Infof("ChannelPoint(%v): responding to shutdown", 394 c.chanPoint) 395 396 msgsToSend := make([]lnwire.Message, 0, 2) 397 msgsToSend = append(msgsToSend, localShutdown) 398 399 // After the other party receives this message, we'll actually start 400 // the final stage of the closure process: fee negotiation. So we'll 401 // update our internal state to reflect this, so we can handle the next 402 // message sent. 403 c.state = closeFeeNegotiation 404 405 // We'll also craft our initial close proposal in order to keep the 406 // negotiation moving, but only if we're the negotiator. 407 if chanInitiator { 408 closeSigned, err := c.proposeCloseSigned(c.idealFeeSat) 409 if err != nil { 410 return nil, false, err 411 } 412 msgsToSend = append(msgsToSend, closeSigned) 413 } 414 415 // We'll return both sets of messages to send to the remote party to 416 // kick off the fee negotiation process. 417 return msgsToSend, false, nil 418 419 // If we just initiated a channel shutdown, and we receive a new message, 420 // then this indicates the other party is ready to shutdown as well. In 421 // this state we'll send our first signature. 422 case closeShutdownInitiated: 423 // First, we'll assert that we have a channel shutdown message. 424 // Otherwise, this is an attempted invalid state transition. 425 shutdownMsg, ok := msg.(*lnwire.Shutdown) 426 if !ok { 427 return nil, false, fmt.Errorf("expected lnwire.Shutdown, instead "+ 428 "have %v", spew.Sdump(msg)) 429 } 430 431 // If the remote node opened the channel with option upfront shutdown 432 // script, check that the script they provided matches. 433 if err := maybeMatchScript(c.cfg.Disconnect, 434 c.cfg.Channel.RemoteUpfrontShutdownScript(), shutdownMsg.Address, 435 ); err != nil { 436 return nil, false, err 437 } 438 439 // Now that we know this is a valid shutdown message and address, we'll 440 // record their preferred delivery closing script. 441 c.remoteDeliveryScript = shutdownMsg.Address 442 443 // At this point, we can now start the fee negotiation state, by 444 // constructing and sending our initial signature for what we think the 445 // closing transaction should look like. 446 c.state = closeFeeNegotiation 447 448 chancloserLog.Infof("ChannelPoint(%v): shutdown response received, "+ 449 "entering fee negotiation", c.chanPoint) 450 451 // Starting with our ideal fee rate, we'll create an initial closing 452 // proposal, but only if we're the initiator, as otherwise, the other 453 // party will send their initial proposal first. 454 if c.cfg.Channel.IsInitiator() { 455 closeSigned, err := c.proposeCloseSigned(c.idealFeeSat) 456 if err != nil { 457 return nil, false, err 458 } 459 460 return []lnwire.Message{closeSigned}, false, nil 461 } 462 463 return nil, false, nil 464 465 // If we're receiving a message while we're in the fee negotiation phase, 466 // then this indicates the remote party is responding to a close signed 467 // message we sent, or kicking off the process with their own. 468 case closeFeeNegotiation: 469 // First, we'll assert that we're actually getting a ClosingSigned 470 // message, otherwise an invalid state transition was attempted. 471 closeSignedMsg, ok := msg.(*lnwire.ClosingSigned) 472 if !ok { 473 return nil, false, fmt.Errorf("expected lnwire.ClosingSigned, "+ 474 "instead have %v", spew.Sdump(msg)) 475 } 476 // We'll compare the proposed total fee, to what we've proposed 477 // during the negotiations. If it doesn't match any of our 478 // prior offers, then we'll attempt to ratchet the fee closer 479 // to 480 remoteProposedFee := closeSignedMsg.FeeAtoms 481 if _, ok := c.priorFeeOffers[remoteProposedFee]; !ok { 482 // We'll now attempt to ratchet towards a fee deemed acceptable by 483 // both parties, factoring in our ideal fee rate, and the last 484 // proposed fee by both sides. 485 feeProposal := calcCompromiseFee(c.chanPoint, c.idealFeeSat, 486 c.lastFeeProposal, remoteProposedFee, 487 ) 488 489 // With our new fee proposal calculated, we'll craft a new close 490 // signed signature to send to the other party so we can continue 491 // the fee negotiation process. 492 closeSigned, err := c.proposeCloseSigned(feeProposal) 493 if err != nil { 494 return nil, false, err 495 } 496 497 // If the compromise fee doesn't match what the peer proposed, then 498 // we'll return this latest close signed message so we can continue 499 // negotiation. 500 if feeProposal != remoteProposedFee { 501 chancloserLog.Debugf("ChannelPoint(%v): close tx fee "+ 502 "disagreement, continuing negotiation", c.chanPoint) 503 return []lnwire.Message{closeSigned}, false, nil 504 } 505 } 506 507 chancloserLog.Infof("ChannelPoint(%v) fee of %v accepted, ending "+ 508 "negotiation", c.chanPoint, remoteProposedFee) 509 510 // Otherwise, we've agreed on a fee for the closing transaction! We'll 511 // craft the final closing transaction so we can broadcast it to the 512 // network. 513 matchingSig := c.priorFeeOffers[remoteProposedFee].Signature 514 localSig, err := matchingSig.ToSignature() 515 if err != nil { 516 return nil, false, err 517 } 518 519 remoteSig, err := closeSignedMsg.Signature.ToSignature() 520 if err != nil { 521 return nil, false, err 522 } 523 524 closeTx, _, err := c.cfg.Channel.CompleteCooperativeClose( 525 localSig, remoteSig, c.localDeliveryScript, c.remoteDeliveryScript, 526 remoteProposedFee, 527 ) 528 if err != nil { 529 return nil, false, err 530 } 531 c.closingTx = closeTx 532 533 // Before publishing the closing tx, we persist it to the database, 534 // such that it can be republished if something goes wrong. 535 err = c.cfg.Channel.MarkCoopBroadcasted(closeTx, c.locallyInitiated) 536 if err != nil { 537 return nil, false, err 538 } 539 540 // With the closing transaction crafted, we'll now broadcast it to the 541 // network. 542 chancloserLog.Infof("Broadcasting cooperative close tx: %v", 543 newLogClosure(func() string { 544 return spew.Sdump(closeTx) 545 }), 546 ) 547 548 // Create a close channel label. 549 chanID := c.cfg.Channel.ShortChanID() 550 closeLabel := labels.MakeLabel( 551 labels.LabelTypeChannelClose, &chanID, 552 ) 553 554 if err := c.cfg.BroadcastTx(closeTx, closeLabel); err != nil { 555 return nil, false, err 556 } 557 558 // Finally, we'll transition to the closeFinished state, and also 559 // return the final close signed message we sent. Additionally, we 560 // return true for the second argument to indicate we're finished with 561 // the channel closing negotiation. 562 c.state = closeFinished 563 matchingOffer := c.priorFeeOffers[remoteProposedFee] 564 return []lnwire.Message{matchingOffer}, true, nil 565 566 // If we received a message while in the closeFinished state, then this 567 // should only be the remote party echoing the last ClosingSigned message 568 // that we agreed on. 569 case closeFinished: 570 if _, ok := msg.(*lnwire.ClosingSigned); !ok { 571 return nil, false, fmt.Errorf("expected lnwire.ClosingSigned, "+ 572 "instead have %v", spew.Sdump(msg)) 573 } 574 575 // There's no more to do as both sides should have already broadcast 576 // the closing transaction at this state. 577 return nil, true, nil 578 579 // Otherwise, we're in an unknown state, and can't proceed. 580 default: 581 return nil, false, ErrInvalidState 582 } 583 } 584 585 // proposeCloseSigned attempts to propose a new signature for the closing 586 // transaction for a channel based on the prior fee negotiations and our 587 // current compromise fee. 588 func (c *ChanCloser) proposeCloseSigned(fee dcrutil.Amount) (*lnwire.ClosingSigned, error) { 589 rawSig, _, _, err := c.cfg.Channel.CreateCloseProposal( 590 fee, c.localDeliveryScript, c.remoteDeliveryScript, 591 ) 592 if err != nil { 593 return nil, err 594 } 595 596 // We'll note our last signature and proposed fee so when the remote party 597 // responds we'll be able to decide if we've agreed on fees or not. 598 c.lastFeeProposal = fee 599 parsedSig, err := lnwire.NewSigFromSignature(rawSig) 600 if err != nil { 601 return nil, err 602 } 603 604 chancloserLog.Infof("ChannelPoint(%v): proposing fee of %v atoms to close "+ 605 "chan", c.chanPoint, int64(fee)) 606 607 // We'll assemble a ClosingSigned message using this information and return 608 // it to the caller so we can kick off the final stage of the channel 609 // closure process. 610 closeSignedMsg := lnwire.NewClosingSigned(c.cid, fee, parsedSig) 611 612 // We'll also save this close signed, in the case that the remote party 613 // accepts our offer. This way, we don't have to re-sign. 614 c.priorFeeOffers[fee] = closeSignedMsg 615 616 return closeSignedMsg, nil 617 } 618 619 // feeInAcceptableRange returns true if the passed remote fee is deemed to be 620 // in an "acceptable" range to our local fee. This is an attempt at a 621 // compromise and to ensure that the fee negotiation has a stopping point. We 622 // consider their fee acceptable if it's within 30% of our fee. 623 func feeInAcceptableRange(localFee, remoteFee dcrutil.Amount) bool { 624 // If our offer is lower than theirs, then we'll accept their offer if it's 625 // no more than 30% *greater* than our current offer. 626 if localFee < remoteFee { 627 acceptableRange := localFee + ((localFee * 3) / 10) 628 return remoteFee <= acceptableRange 629 } 630 631 // If our offer is greater than theirs, then we'll accept their offer if 632 // it's no more than 30% *less* than our current offer. 633 acceptableRange := localFee - ((localFee * 3) / 10) 634 return remoteFee >= acceptableRange 635 } 636 637 // ratchetFee is our step function used to inch our fee closer to something 638 // that both sides can agree on. If up is true, then we'll attempt to increase 639 // our offered fee. Otherwise, if up is false, then we'll attempt to decrease 640 // our offered fee. 641 func ratchetFee(fee dcrutil.Amount, up bool) dcrutil.Amount { 642 // If we need to ratchet up, then we'll increase our fee by 10%. 643 if up { 644 return fee + ((fee * 1) / 10) 645 } 646 647 // Otherwise, we'll *decrease* our fee by 10%. 648 return fee - ((fee * 1) / 10) 649 } 650 651 // calcCompromiseFee performs the current fee negotiation algorithm, taking 652 // into consideration our ideal fee based on current fee environment, the fee 653 // we last proposed (if any), and the fee proposed by the peer. 654 func calcCompromiseFee(chanPoint wire.OutPoint, ourIdealFee, lastSentFee, 655 remoteFee dcrutil.Amount) dcrutil.Amount { 656 657 // TODO(roasbeef): take in number of rounds as well? 658 659 chancloserLog.Infof("ChannelPoint(%v): computing fee compromise, ideal="+ 660 "%v, last_sent=%v, remote_offer=%v", chanPoint, int64(ourIdealFee), 661 int64(lastSentFee), int64(remoteFee)) 662 663 // Otherwise, we'll need to attempt to make a fee compromise if this is the 664 // second round, and neither side has agreed on fees. 665 switch { 666 667 // If their proposed fee is identical to our ideal fee, then we'll go with 668 // that as we can short circuit the fee negotiation. Similarly, if we 669 // haven't sent an offer yet, we'll default to our ideal fee. 670 case ourIdealFee == remoteFee || lastSentFee == 0: 671 return ourIdealFee 672 673 // If the last fee we sent, is equal to the fee the remote party is 674 // offering, then we can simply return this fee as the negotiation is over. 675 case remoteFee == lastSentFee: 676 return lastSentFee 677 678 // If the fee the remote party is offering is less than the last one we 679 // sent, then we'll need to ratchet down in order to move our offer closer 680 // to theirs. 681 case remoteFee < lastSentFee: 682 // If the fee is lower, but still acceptable, then we'll just return 683 // this fee and end the negotiation. 684 if feeInAcceptableRange(lastSentFee, remoteFee) { 685 chancloserLog.Infof("ChannelPoint(%v): proposed remote fee is "+ 686 "close enough, capitulating", chanPoint) 687 return remoteFee 688 } 689 690 // Otherwise, we'll ratchet the fee *down* using our current algorithm. 691 return ratchetFee(lastSentFee, false) 692 693 // If the fee the remote party is offering is greater than the last one we 694 // sent, then we'll ratchet up in order to ensure we terminate eventually. 695 case remoteFee > lastSentFee: 696 // If the fee is greater, but still acceptable, then we'll just return 697 // this fee in order to put an end to the negotiation. 698 if feeInAcceptableRange(lastSentFee, remoteFee) { 699 chancloserLog.Infof("ChannelPoint(%v): proposed remote fee is "+ 700 "close enough, capitulating", chanPoint) 701 return remoteFee 702 } 703 704 // Otherwise, we'll ratchet the fee up using our current algorithm. 705 return ratchetFee(lastSentFee, true) 706 707 default: 708 // TODO(roasbeef): fail if their fee isn't in expected range 709 return remoteFee 710 } 711 } 712 713 // ParseUpfrontShutdownAddress attempts to parse an upfront shutdown address. 714 // If the address is empty, it returns nil. If it successfully decoded the 715 // address, it returns a script that pays out to the address. 716 func ParseUpfrontShutdownAddress(address string, 717 params *chaincfg.Params) (lnwire.DeliveryAddress, error) { 718 719 if len(address) == 0 { 720 return nil, nil 721 } 722 723 addr, err := stdaddr.DecodeAddress( 724 address, params, 725 ) 726 if err != nil { 727 return nil, fmt.Errorf("invalid address: %v", err) 728 } 729 730 return input.PayToAddrScript(addr) 731 }