diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 51bde351..6d1fb848 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -571,13 +571,21 @@ func compactLogs(ourLog, theirLog *updateLog, // // See the individual comments within the above methods for further details. type LightningChannel struct { - signer Signer + // signer is the main signer instances that will be responsible for + // signing any HTLC and commitment transaction generated by the state + // machine. + signer Signer + + // signDesc is the primary sign descriptor that is capable of signing + // the commitment transaction that spends the multi-sig output. signDesc *SignDescriptor channelEvents chainntnfs.ChainNotifier - sync.RWMutex - + // pendingACk denotes if we have an outstanding commitment transaction + // and are waiting for a revocation to be received. Until the + // revocation is received, we're unable to propose a new commitment + // state. pendingACK bool status channelState @@ -589,6 +597,10 @@ type LightningChannel struct { // Capcity is the total capacity of this channel. Capacity btcutil.Amount + // stateHintObsfucator is a 48-bit state hint that's used to obfsucate + // the current state number on the commitment transactions. + stateHintObsfucator [StateHintSize]byte + // currentHeight is the current height of our local commitment chain. // This is also the same as the number of updates to the channel we've // accepted. @@ -629,6 +641,10 @@ type LightningChannel struct { channelState *channeldb.OpenChannel + localChanCfg *channeldb.ChannelConfig + + remoteChanCfg *channeldb.ChannelConfig + // [local|remote]Log is a (mostly) append-only log storing all the HTLC // updates to this channel. The log is walked backwards as HTLC updates // are applied in order to re-construct a commitment transaction from a @@ -654,9 +670,7 @@ type LightningChannel struct { // way to lookup the original PaymentDescriptor. rHashMap map[PaymentHash][]*PaymentDescriptor - LocalDeliveryScript []byte RemoteDeliveryScript []byte - // FundingWitnessScript is the witness script for the 2-of-2 multi-sig // that opened the channel. FundingWitnessScript []byte @@ -697,6 +711,10 @@ type LightningChannel struct { // might be processed by this channel at the specific point of time. availableLocalBalance btcutil.Amount + sync.RWMutex + + wg sync.WaitGroup + shutdown int32 quit chan struct{} } @@ -709,28 +727,48 @@ type LightningChannel struct { func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier, fe FeeEstimator, state *channeldb.OpenChannel) (*LightningChannel, error) { + localKey := state.LocalChanCfg.MultiSigKey.SerializeCompressed() + remoteKey := state.RemoteChanCfg.MultiSigKey.SerializeCompressed() + multiSigScript, err := genMultiSigScript(localKey, remoteKey) + if err != nil { + return nil, err + } + + var stateHint [StateHintSize]byte + if state.IsInitiator { + stateHint = deriveStateHintObfuscator( + state.LocalChanCfg.PaymentBasePoint, + state.RemoteChanCfg.PaymentBasePoint, + ) + } else { + stateHint = deriveStateHintObfuscator( + state.RemoteChanCfg.PaymentBasePoint, + state.LocalChanCfg.PaymentBasePoint, + ) + } + lc := &LightningChannel{ signer: signer, channelEvents: events, feeEstimator: fe, + stateHintObsfucator: stateHint, currentHeight: state.NumUpdates, remoteCommitChain: newCommitmentChain(state.NumUpdates), localCommitChain: newCommitmentChain(state.NumUpdates), channelState: state, - revocationWindowEdge: state.NumUpdates, + localChanCfg: &state.LocalChanCfg, + remoteChanCfg: &state.RemoteChanCfg, localUpdateLog: newUpdateLog(), remoteUpdateLog: newUpdateLog(), rHashMap: make(map[PaymentHash][]*PaymentDescriptor), Capacity: state.Capacity, - LocalDeliveryScript: state.OurDeliveryScript, - RemoteDeliveryScript: state.TheirDeliveryScript, - FundingWitnessScript: state.FundingWitnessScript, + FundingWitnessScript: multiSigScript, ForceCloseSignal: make(chan struct{}), UnilateralClose: make(chan *UnilateralCloseSummary, 1), UnilateralCloseSignal: make(chan struct{}), ContractBreach: make(chan *BreachRetribution, 1), - LocalFundingKey: state.OurMultiSigKey, - RemoteFundingKey: state.TheirMultiSigKey, + LocalFundingKey: state.LocalChanCfg.MultiSigKey, + RemoteFundingKey: state.RemoteChanCfg.MultiSigKey, quit: make(chan struct{}), } @@ -738,15 +776,15 @@ func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier, // for each side. lc.localCommitChain.addCommitment(&commitment{ height: lc.currentHeight, - ourBalance: state.OurBalance, + ourBalance: state.LocalBalance, ourMessageIndex: 0, - theirBalance: state.TheirBalance, + theirBalance: state.RemoteBalance, theirMessageIndex: 0, fee: state.CommitFee, feePerKw: state.FeePerKw, }) walletLog.Debugf("ChannelPoint(%v), starting local commitment: %v", - state.ChanID, newLogClosure(func() string { + state.FundingOutpoint, newLogClosure(func() string { return spew.Sdump(lc.localCommitChain.tail()) }), ) @@ -760,9 +798,9 @@ func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier, return nil, err } remoteCommitment := &commitment{ - ourBalance: state.OurBalance, + ourBalance: state.LocalBalance, ourMessageIndex: 0, - theirBalance: state.TheirBalance, + theirBalance: state.RemoteBalance, theirMessageIndex: 0, fee: state.CommitFee, feePerKw: state.FeePerKw, @@ -774,7 +812,7 @@ func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier, } lc.remoteCommitChain.addCommitment(remoteCommitment) walletLog.Debugf("ChannelPoint(%v), starting remote commitment: %v", - state.ChanID, newLogClosure(func() string { + state.FundingOutpoint, newLogClosure(func() string { return spew.Sdump(lc.remoteCommitChain.tail()) }), ) @@ -788,15 +826,15 @@ func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier, // Create the sign descriptor which we'll be using very frequently to // request a signature for the 2-of-2 multi-sig from the signer in // order to complete channel state transitions. - fundingPkScript, err := witnessScriptHash(state.FundingWitnessScript) + fundingPkScript, err := witnessScriptHash(multiSigScript) if err != nil { return nil, err } - lc.fundingTxIn = wire.NewTxIn(state.FundingOutpoint, nil, nil) + lc.fundingTxIn = wire.NewTxIn(&state.FundingOutpoint, nil, nil) lc.fundingP2WSH = fundingPkScript lc.signDesc = &SignDescriptor{ - PubKey: lc.channelState.OurMultiSigKey, - WitnessScript: lc.channelState.FundingWitnessScript, + PubKey: lc.localChanCfg.MultiSigKey, + WitnessScript: multiSigScript, Output: &wire.TxOut{ PkScript: lc.fundingP2WSH, Value: int64(lc.channelState.Capacity), @@ -833,6 +871,7 @@ func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier, // Launch the close observer which will vigilantly watch the // network for any broadcasts the current or prior commitment // transactions, taking action accordingly. + lc.wg.Add(1) go lc.closeObserver(channelCloseNtfn) } @@ -989,8 +1028,10 @@ func newBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // // NOTE: This MUST be run as a goroutine. func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEvent) { + defer lc.wg.Done() + walletLog.Infof("Close observer for ChannelPoint(%v) active", - lc.channelState.ChanID) + lc.channelState.FundingOutpoint) var ( commitSpend *chainntnfs.SpendDetail @@ -1039,7 +1080,7 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven // If this is our commitment transaction, then we can exit here as we // don't have any further processing we need to do (we can't cheat // ourselves :p). - commitmentHash := lc.channelState.OurCommitTx.TxHash() + commitmentHash := lc.channelState.CommitTx.TxHash() isOurCommitment := commitSpend.SpenderTxHash.IsEqual(&commitmentHash) if isOurCommitment { return @@ -1047,7 +1088,7 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven // Decode the state hint encoded within the commitment transaction to // determine if this is a revoked state or not. - obsfucator := lc.channelState.StateHintObsfucator + obsfucator := lc.stateHintObsfucator broadcastStateNum := GetStateNumHint(commitTxBroadcast, obsfucator) currentStateNum := lc.currentHeight @@ -1067,23 +1108,23 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven // their signature to us. case broadcastStateNum >= currentStateNum: walletLog.Infof("Unilateral close of ChannelPoint(%v) "+ - "detected", lc.channelState.ChanID) + "detected", lc.channelState.FundingOutpoint) // As we've detected that the channel has been closed, // immediately delete the state from disk, creating a close // summary for future usage by related sub-systems. // TODO(roasbeef): include HTLC's - // * and time-locked balance - closeSummary := &channeldb.ChannelCloseSummary{ - ChanPoint: *lc.channelState.ChanID, + // * and time-locked balance, NEED TO??? + closeSummary := channeldb.ChannelCloseSummary{ + ChanPoint: lc.channelState.FundingOutpoint, ClosingTXID: *commitSpend.SpenderTxHash, RemotePub: lc.channelState.IdentityPub, Capacity: lc.Capacity, - SettledBalance: lc.channelState.OurBalance, + SettledBalance: lc.channelState.LocalBalance, CloseType: channeldb.ForceClose, IsPending: true, } - if err := lc.DeleteState(closeSummary); err != nil { + if err := lc.DeleteState(&closeSummary); err != nil { walletLog.Errorf("unable to delete channel state: %v", err) } @@ -1104,7 +1145,7 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven case broadcastStateNum < currentStateNum: walletLog.Warnf("Remote peer has breached the channel "+ "contract for ChannelPoint(%v). Revoked state #%v was "+ - "broadcast!!!", lc.channelState.ChanID, + "broadcast!!!", lc.channelState.FundingOutpoint, broadcastStateNum) // Create a new reach retribution struct which contains all the @@ -1455,7 +1496,7 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool, // Set the state hint of the commitment transaction to facilitate // quickly recovering the necessary penalty state in the case of an // uncooperative broadcast. - obsfucator := lc.channelState.StateHintObsfucator + obsfucator := lc.stateHintObsfucator stateNum := nextHeight if err := SetStateNumHint(commitTx, stateNum, obsfucator); err != nil { return nil, err @@ -1509,8 +1550,8 @@ func (lc *LightningChannel) evaluateHTLCView(view *htlcView, ourBalance, skipThem := make(map[uint64]struct{}) // First we run through non-add entries in both logs, populating the - // skip sets and mutating the current chain state (crediting balances, etc) to - // reflect the settle/timeout entry encountered. + // skip sets and mutating the current chain state (crediting balances, + // etc) to reflect the settle/timeout entry encountered. for _, entry := range view.ourUpdates { if entry.EntryType == Add { continue @@ -1552,8 +1593,8 @@ func (lc *LightningChannel) evaluateHTLCView(view *htlcView, ourBalance, } // Next we take a second pass through all the log entries, skipping any - // settled HTLCs, and debiting the chain state balance due to any - // newly added HTLCs. + // settled HTLCs, and debiting the chain state balance due to any newly + // added HTLCs. for _, entry := range view.ourUpdates { isAdd := entry.EntryType == Add if _, ok := skipUs[entry.Index]; !isAdd || ok { @@ -1719,13 +1760,16 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) { } walletLog.Tracef("ChannelPoint(%v): extending remote chain to height %v", - lc.channelState.ChanID, newCommitView.height) + lc.channelState.FundingOutpoint, newCommitView.height) + walletLog.Tracef("ChannelPoint(%v): remote chain: our_balance=%v, "+ - "their_balance=%v, commit_tx: %v", lc.channelState.ChanID, - newCommitView.ourBalance, newCommitView.theirBalance, + "their_balance=%v, commit_tx: %v", + lc.channelState.FundingOutpoint, newCommitView.ourBalance, + newCommitView.theirBalance, newLogClosure(func() string { return spew.Sdump(newCommitView.txn) - })) + }), + ) // Sign their version of the new commitment transaction. lc.signDesc.SigHashes = txscript.NewTxSigHashes(newCommitView.txn) @@ -1739,10 +1783,10 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) { lc.remoteCommitChain.addCommitment(newCommitView) // If we are the channel initiator then we would have signed any sent - // fee update at this point, so mark this update as pending ACK, and set - // pendingFeeUpdate to nil. We can do this since we know we won't sign - // any new commitment before receiving a revoke_and_ack, because of the - // revocation window of 1. + // fee update at this point, so mark this update as pending ACK, and + // set pendingFeeUpdate to nil. We can do this since we know we won't + // sign any new commitment before receiving a revoke_and_ack, because + // of the revocation window of 1. if lc.channelState.IsInitiator { lc.pendingAckFeeUpdate = lc.pendingFeeUpdate lc.pendingFeeUpdate = nil @@ -1879,10 +1923,10 @@ func (lc *LightningChannel) ReceiveNewCommitment(rawSig []byte) error { } walletLog.Tracef("ChannelPoint(%v): extending local chain to height %v", - lc.channelState.ChanID, localCommitmentView.height) + lc.channelState.FundingOutpoint, localCommitmentView.height) walletLog.Tracef("ChannelPoint(%v): local chain: our_balance=%v, "+ - "their_balance=%v, commit_tx: %v", lc.channelState.ChanID, + "their_balance=%v, commit_tx: %v", lc.channelState.FundingOutpoint, localCommitmentView.ourBalance, localCommitmentView.theirBalance, newLogClosure(func() string { return spew.Sdump(localCommitmentView.txn) @@ -1892,10 +1936,11 @@ func (lc *LightningChannel) ReceiveNewCommitment(rawSig []byte) error { // Construct the sighash of the commitment transaction corresponding to // this newly proposed state update. localCommitTx := localCommitmentView.txn - multiSigScript := lc.channelState.FundingWitnessScript + multiSigScript := lc.FundingWitnessScript hashCache := txscript.NewTxSigHashes(localCommitTx) sigHash, err := txscript.CalcWitnessSigHash(multiSigScript, hashCache, - txscript.SigHashAll, localCommitTx, 0, int64(lc.channelState.Capacity)) + txscript.SigHashAll, localCommitTx, 0, + int64(lc.channelState.Capacity)) if err != nil { // TODO(roasbeef): fetchview has already mutated the HTLCs... // * need to either roll-back, or make pure @@ -2011,8 +2056,9 @@ func (lc *LightningChannel) RevokeCurrentCommitment() (*lnwire.RevokeAndAck, err } walletLog.Tracef("ChannelPoint(%v): state transition accepted: "+ - "our_balance=%v, their_balance=%v", lc.channelState.ChanID, - tail.ourBalance, tail.theirBalance) + "our_balance=%v, their_balance=%v", + lc.channelState.FundingOutpoint, tail.ourBalance, + tail.theirBalance) // In the process of revoking our current commitment, we've also // implicitly ACK'd their set of pending changes that arrived before @@ -2020,12 +2066,15 @@ func (lc *LightningChannel) RevokeCurrentCommitment() (*lnwire.RevokeAndAck, err // ACK'd index within the log to right at this set of pending changes. lc.remoteUpdateLog.ackTransition() - revocationMsg.ChanID = lnwire.NewChanIDFromOutPoint(lc.channelState.ChanID) + revocationMsg.ChanID = lnwire.NewChanIDFromOutPoint( + &lc.channelState.FundingOutpoint, + ) + return revocationMsg, nil } // LocalAvailableBalance returns the amount of available money which might be -// procced by this channel at the specific point of time. +// proceed by this channel at the specific point of time. func (lc *LightningChannel) LocalAvailableBalance() btcutil.Amount { lc.Lock() defer lc.Unlock() @@ -2096,7 +2145,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ([]*P lc.revocationWindow = append(lc.revocationWindow, revMsg) walletLog.Tracef("ChannelPoint(%v): remote party accepted state transition, "+ - "revoked height %v, now at %v", lc.channelState.ChanID, + "revoked height %v, now at %v", lc.channelState.FundingOutpoint, lc.remoteCommitChain.tail().height, lc.remoteCommitChain.tail().height+1) @@ -2220,9 +2269,6 @@ func (lc *LightningChannel) NextRevocationkey() (*btcec.PublicKey, error) { // AddHTLC adds an HTLC to the state machine's local update log. This method // should be called when preparing to send an outgoing HTLC. -// -// TODO(roasbeef): check for duplicates below? edge case during restart w/ HTLC -// persistence func (lc *LightningChannel) AddHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, error) { lc.Lock() defer lc.Unlock() @@ -2415,7 +2461,7 @@ func (lc *LightningChannel) ChannelPoint() *wire.OutPoint { lc.RLock() defer lc.RUnlock() - return lc.channelState.ChanID + return &lc.channelState.FundingOutpoint } // ShortChanID returns the short channel ID for the channel. The short channel @@ -2519,13 +2565,13 @@ func (lc *LightningChannel) addHTLC(commitTx *wire.MsgTx, ourCommit bool, func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) { // Fetch the current commitment transaction, along with their signature // for the transaction. - commitTx := lc.channelState.OurCommitTx - theirSig := append(lc.channelState.OurCommitSig, byte(txscript.SigHashAll)) + commitTx := lc.channelState.CommitTx + theirSig := append(lc.channelState.CommitSig, byte(txscript.SigHashAll)) // With this, we then generate the full witness so the caller can // broadcast a fully signed transaction. - lc.signDesc.SigHashes = txscript.NewTxSigHashes(commitTx) - ourSigRaw, err := lc.signer.SignOutputRaw(commitTx, lc.signDesc) + lc.signDesc.SigHashes = txscript.NewTxSigHashes(&commitTx) + ourSigRaw, err := lc.signer.SignOutputRaw(&commitTx, lc.signDesc) if err != nil { return nil, err } @@ -2534,13 +2580,14 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) { // With the final signature generated, create the witness stack // required to spend from the multi-sig output. - ourKey := lc.channelState.OurMultiSigKey.SerializeCompressed() - theirKey := lc.channelState.TheirMultiSigKey.SerializeCompressed() + ourKey := lc.localChanCfg.MultiSigKey.SerializeCompressed() + theirKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed() commitTx.TxIn[0].Witness = SpendMultiSig(lc.FundingWitnessScript, ourKey, ourSig, theirKey, theirSig) - return commitTx, nil + return &commitTx, nil +} } // ForceCloseSummary describes the final commitment state before the channel is @@ -2656,7 +2703,7 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) { WitnessScript: selfScript, Output: &wire.TxOut{ PkScript: delayScript, - Value: int64(lc.channelState.OurBalance), + Value: int64(lc.channelState.LocalBalance), }, HashType: txscript.SigHashAll, } @@ -2669,13 +2716,13 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) { close(lc.ForceCloseSignal) return &ForceCloseSummary{ - ChanPoint: *lc.channelState.ChanID, - CloseTx: commitTx, + ChanPoint: lc.channelState.FundingOutpoint, SelfOutpoint: wire.OutPoint{ Hash: commitTx.TxHash(), Index: delayIndex, }, SelfOutputMaturity: csvTimeout, + CloseTx: commitTx, SelfOutputSignDesc: selfSignDesc, }, nil } @@ -2706,8 +2753,8 @@ func (lc *LightningChannel) CreateCloseProposal(feeRate uint64) ([]byte, uint64, // not to persist the adjusted balance, as the feeRate may change // during the channel closing process. proposedFee := uint64(btcutil.Amount(feeRate) * commitWeight / 1000) - ourBalance := lc.channelState.OurBalance - theirBalance := lc.channelState.TheirBalance + ourBalance := lc.channelState.LocalBalance + theirBalance := lc.channelState.RemoteBalance if lc.channelState.IsInitiator { ourBalance = ourBalance - btcutil.Amount(proposedFee) @@ -2716,9 +2763,9 @@ func (lc *LightningChannel) CreateCloseProposal(feeRate uint64) ([]byte, uint64, } closeTx := CreateCooperativeCloseTx(lc.fundingTxIn, - lc.channelState.OurDustLimit, lc.channelState.TheirDustLimit, - ourBalance, theirBalance, lc.channelState.OurDeliveryScript, - lc.channelState.TheirDeliveryScript, lc.channelState.IsInitiator) + lc.localChanCfg.DustLimit, lc.remoteChanCfg.DustLimit, + ourBalance, theirBalance, localDeliveryScript, + remoteDeliveryScript, lc.channelState.IsInitiator) // Ensure that the transaction doesn't explicitly violate any // consensus rules such as being too big, or having any value with a @@ -2766,8 +2813,8 @@ func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte, // not to persist the adjusted balance, as the feeRate may change // during the channel closing process. proposedFee := uint64(btcutil.Amount(feeRate) * commitWeight / 1000) - ourBalance := lc.channelState.OurBalance - theirBalance := lc.channelState.TheirBalance + ourBalance := lc.channelState.LocalBalance + theirBalance := lc.channelState.RemoteBalance if lc.channelState.IsInitiator { ourBalance = ourBalance - btcutil.Amount(proposedFee) @@ -2779,9 +2826,9 @@ func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte, // on this active channel back to both parties. In this current model, // the initiator pays full fees for the cooperative close transaction. closeTx := CreateCooperativeCloseTx(lc.fundingTxIn, - lc.channelState.OurDustLimit, lc.channelState.TheirDustLimit, - ourBalance, theirBalance, lc.channelState.OurDeliveryScript, - lc.channelState.TheirDeliveryScript, lc.channelState.IsInitiator) + lc.localChanCfg.DustLimit, lc.remoteChanCfg.DustLimit, + ourBalance, theirBalance, localDeliveryScript, + remoteDeliveryScript, lc.channelState.IsInitiator) // Ensure that the transaction doesn't explicitly validate any // consensus rules such as being too big, or having any value with a @@ -2794,8 +2841,8 @@ func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte, // Finally, construct the witness stack minding the order of the // pubkeys+sigs on the stack. - ourKey := lc.channelState.OurMultiSigKey.SerializeCompressed() - theirKey := lc.channelState.TheirMultiSigKey.SerializeCompressed() + ourKey := lc.localChanCfg.MultiSigKey.SerializeCompressed() + theirKey := lc.remoteChanCfg.MultiSigKey.SerializeCompressed() witness := SpendMultiSig(lc.signDesc.WitnessScript, ourKey, localSig, theirKey, remoteSig) closeTx.TxIn[0].Witness = witness @@ -2844,9 +2891,9 @@ func (lc *LightningChannel) UpdateFee(feePerKw btcutil.Amount) error { defer lc.Unlock() // Only initiator can send fee update, so trying to send one as - // non-initiatior will fail. + // non-initiator will fail. if !lc.channelState.IsInitiator { - return fmt.Errorf("local fee update as non-initiatior") + return fmt.Errorf("local fee update as non-initiator") } lc.pendingFeeUpdate = &feePerKw @@ -2861,9 +2908,9 @@ func (lc *LightningChannel) ReceiveUpdateFee(feePerKw btcutil.Amount) error { defer lc.Unlock() // Only initiator can send fee update, and we must fail if we receive - // fee update as initiatior + // fee update as initiator if lc.channelState.IsInitiator { - return fmt.Errorf("received fee update as initiatior") + return fmt.Errorf("received fee update as initiator") } // TODO(halseth): should fail if fee update is unreasonable,