lnwallet: add update_fee message support.

This commit adds the possibility for the initiator of a
channel to send the update_fee message, as specified
in BOLT#2. After the message is sent and both parties
have committed to the updated fee, all new commitment
messages in the channel will use the specified fee.
This commit is contained in:
Johan T. Halseth 2017-07-14 20:38:35 +02:00 committed by Olaoluwa Osuntokun
parent a3836d5241
commit ebe05f6568
3 changed files with 552 additions and 9 deletions

@ -428,6 +428,7 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *wire.MsgTx,
c.NumUpdates = delta.UpdateNum
c.Htlcs = delta.Htlcs
c.CommitFee = delta.CommitFee
c.FeePerKw = delta.FeePerKw
// First we'll write out the current latest dynamic channel
// state: the current channel balance, the number of updates,
@ -445,6 +446,9 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *wire.MsgTx,
if err := putChanCommitFee(chanBucket, c); err != nil {
return err
}
if err := putChanFeePerKw(chanBucket, c); err != nil {
return err
}
if err := putChanCommitTxns(nodeChanBucket, c); err != nil {
return err
}
@ -517,6 +521,10 @@ type ChannelDelta struct {
// initiator's balance at this point in the commitment chain.
CommitFee btcutil.Amount
// FeePerKw is the fee per kw used to calculate the commit fee at this point
// in the commit chain.
FeePerKw btcutil.Amount
// UpdateNum is the update number that this ChannelDelta represents the
// total number of commitment updates to this point. This can be viewed
// as sort of a "commitment height" as this number is monotonically
@ -2206,6 +2214,11 @@ func serializeChannelDelta(w io.Writer, delta *ChannelDelta) error {
return err
}
byteOrder.PutUint64(scratch[:], uint64(delta.FeePerKw))
if _, err := w.Write(scratch[:]); err != nil {
return err
}
return nil
}
@ -2249,6 +2262,11 @@ func deserializeChannelDelta(r io.Reader) (*ChannelDelta, error) {
}
delta.CommitFee = btcutil.Amount(byteOrder.Uint64(scratch[:]))
if _, err := r.Read(scratch[:]); err != nil {
return nil, err
}
delta.FeePerKw = btcutil.Amount(byteOrder.Uint64(scratch[:]))
return delta, nil
}

@ -243,6 +243,10 @@ type commitment struct {
// back and recalculated for each new update to the channel state.
fee btcutil.Amount
// feePerKw is the fee per kw used to calculate this commitment
// transaction's fee.
feePerKw btcutil.Amount
// htlcs is the set of HTLCs which remain unsettled within this
// commitment.
outgoingHTLCs []PaymentDescriptor
@ -263,6 +267,7 @@ func (c *commitment) toChannelDelta(ourCommit bool) (*channeldb.ChannelDelta, er
RemoteBalance: c.theirBalance,
UpdateNum: c.height,
CommitFee: c.fee,
FeePerKw: c.feePerKw,
Htlcs: make([]*channeldb.HTLC, 0, numHtlcs),
}
@ -631,6 +636,16 @@ type LightningChannel struct {
localUpdateLog *updateLog
remoteUpdateLog *updateLog
// pendingFeeUpdate is set to the fee-per-kw we last sent (if we are
// channel initiator) or received (if non-initiator) in an update fee
// message, which haven't yet been included in a commitment.
// It will be nil if no fee update is un-committed.
pendingFeeUpdate *btcutil.Amount
// pendingAckFeeUpdate is set to the last committed fee update which is
// not yet ACKed. Set to nil if no such update.
pendingAckFeeUpdate *btcutil.Amount
// rHashMap is a map with PaymentHashes pointing to their respective
// PaymentDescriptors. We insert *PaymentDescriptors whenever we
// receive HTLCs. When a state transition happens (settling or
@ -727,6 +742,7 @@ func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier,
theirBalance: state.TheirBalance,
theirMessageIndex: 0,
fee: state.CommitFee,
feePerKw: state.FeePerKw,
})
walletLog.Debugf("ChannelPoint(%v), starting local commitment: %v",
state.ChanID, newLogClosure(func() string {
@ -748,6 +764,7 @@ func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier,
theirBalance: state.TheirBalance,
theirMessageIndex: 0,
fee: state.CommitFee,
feePerKw: state.FeePerKw,
}
if logTail == nil {
remoteCommitment.height = 0
@ -1329,12 +1346,45 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
numHTLCs++
}
// Initiate feePerKw to the last committed fee for this chain.
feePerKw := commitChain.tail().feePerKw
// Check if any fee updates have taken place since that last commitment.
if lc.channelState.IsInitiator {
// The case where we sent an update_fee message since our last
// commitment, and now we are signing that one.
if remoteChain && lc.pendingFeeUpdate != nil {
feePerKw = *lc.pendingFeeUpdate
}
// The case where we committed to a sent fee update, and now we
// got a commitment that ACKed that update.
if !remoteChain && lc.pendingAckFeeUpdate != nil {
feePerKw = *lc.pendingAckFeeUpdate
}
} else {
// We received a fee update since last received commitment, so
// this received commitment will sign that update.
if !remoteChain && lc.pendingFeeUpdate != nil {
feePerKw = *lc.pendingFeeUpdate
}
// Earlier we received a commitment that signed an earlier fee
// update, and now we must ACK that update.
if remoteChain && lc.pendingAckFeeUpdate != nil {
feePerKw = *lc.pendingAckFeeUpdate
}
}
// Next, we'll calculate the fee for the commitment transaction based
// on its total weight. Once we have the total weight, we'll multiply
// by the current fee-per-kw, then divide by 1000 to get the proper
// fee.
totalCommitWeight := commitWeight + btcutil.Amount(htlcWeight*numHTLCs)
commitFee := lc.channelState.FeePerKw * totalCommitWeight / 1000
commitFee := (feePerKw * totalCommitWeight) / 1000
commitFee -= dustFees
// Currently, within the protocol, the initiator always pays the fees.
@ -1421,6 +1471,7 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
theirMessageIndex: theirLogIndex,
theirBalance: theirBalance,
fee: commitFee,
feePerKw: feePerKw,
}
// In order to ensure _none_ of the HTLC's associated with this new
@ -1685,6 +1736,16 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) {
// latest commitment update.
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.
if lc.channelState.IsInitiator {
lc.pendingAckFeeUpdate = lc.pendingFeeUpdate
lc.pendingFeeUpdate = nil
}
// Move the now used revocation hash from the unused set to the used set.
// We only do this at the end, as we know at this point the procedure will
// succeed without any errors.
@ -1858,6 +1919,16 @@ func (lc *LightningChannel) ReceiveNewCommitment(rawSig []byte) error {
localCommitmentView.sig = rawSig
lc.localCommitChain.addCommitment(localCommitmentView)
// If we are not channel initiator, then the commitment just received
// would've signed any received fee update since last commitment. Mark
// any such fee update as pending ACK (so we remember to ACK it on our
// next commitment), and set pendingFeeUpdate to nil. We can do this
// since we won't receive any new commitment before ACKing.
if !lc.channelState.IsInitiator {
lc.pendingAckFeeUpdate = lc.pendingFeeUpdate
lc.pendingFeeUpdate = nil
}
// Finally we'll keep track of the current pending index for the remote
// party so we can ACK up to this value once we revoke our current
// commitment.
@ -2763,6 +2834,43 @@ func (lc *LightningChannel) StateSnapshot() *channeldb.ChannelSnapshot {
return lc.channelState.Snapshot()
}
// UpdateFee initiates a fee update for this channel. Must only be called by
// the channel initiator, and must be called before sending update_fee to
// the remote.
func (lc *LightningChannel) UpdateFee(feePerKw btcutil.Amount) error {
lc.Lock()
defer lc.Unlock()
// Only initiator can send fee update, so trying to send one as
// non-initiatior will fail.
if !lc.channelState.IsInitiator {
return fmt.Errorf("local fee update as non-initiatior")
}
lc.pendingFeeUpdate = &feePerKw
return nil
}
// ReceiveUpdateFee handles an updated fee sent from remote. This method will
// return an error if called as channel initiator.
func (lc *LightningChannel) ReceiveUpdateFee(feePerKw btcutil.Amount) error {
lc.Lock()
defer lc.Unlock()
// Only initiator can send fee update, and we must fail if we receive
// fee update as initiatior
if lc.channelState.IsInitiator {
return fmt.Errorf("received fee update as initiatior")
}
// TODO(halseth): should fail if fee update is unreasonable,
// as specified in BOLT#2.
lc.pendingFeeUpdate = &feePerKw
return nil
}
// CreateCommitTx creates a commitment transaction, spending from specified
// funding output. The commitment transaction contains two outputs: one paying
// to the "owner" of the commitment transaction which can be spent after a

@ -402,8 +402,8 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
}
// First Alice adds the outgoing HTLC to her local channel's state
// update log. Then Alice sends this wire message over to Bob who also
// adds this htlc to his local state update log.
// update log. Then Alice sends this wire message over to Bob who
// adds this htlc to his remote state update log.
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
@ -411,22 +411,31 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
t.Fatalf("unable to recv htlc: %v", err)
}
// Next alice commits this change by sending a signature message.
// Next alice commits this change by sending a signature message. Since
// we expect the messages to be ordered, Bob will receive the HTLC we
// just sent before he receives this signature, so the signature will
// cover the HTLC.
aliceSig, err := aliceChannel.SignNextCommitment()
if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err)
}
// Bob receives this signature message, revokes his prior commitment
// given to him by Alice,a nd then finally send a signature for Alice's
// commitment transaction.
// Bob receives this signature message, and checks that this covers the
// state he has in his remote log. This includes the HTLC just sent
// from Alice.
if err := bobChannel.ReceiveNewCommitment(aliceSig); err != nil {
t.Fatalf("bob unable to process alice's new commitment: %v", err)
}
// Bob revokes his prior commitment given to him by Alice, since he now
// has a valid signature for a newer commitment.
bobRevocation, err := bobChannel.RevokeCurrentCommitment()
if err != nil {
t.Fatalf("unable to generate bob revocation: %v", err)
}
// Bob finally send a signature for Alice's commitment transaction.
// This signature will cover the HTLC, since Bob will first send the
// revocation just created. The revocation also acks every received
// HTLC up to the point where Alice sent here signature.
bobSig, err := bobChannel.SignNextCommitment()
if err != nil {
t.Fatalf("bob unable to sign alice's commitment: %v", err)
@ -441,12 +450,14 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
t.Fatalf("alice forwards %v htlcs, should forward none: ", len(htlcs))
}
// Alice then processes bob's signature, and generates a revocation for
// bob.
// Alice then processes bob's signature, and since she just received
// the revocation, she expect this signature to cover everything up to
// the point where she sent her signature, including the HTLC.
if err := aliceChannel.ReceiveNewCommitment(bobSig); err != nil {
t.Fatalf("alice unable to process bob's new commitment: %v", err)
}
// Alice then generates a revocation for bob.
aliceRevocation, err := aliceChannel.RevokeCurrentCommitment()
if err != nil {
t.Fatalf("unable to revoke alice channel: %v", err)
@ -1599,3 +1610,409 @@ func TestCooperativeCloseDustAdherence(t *testing.T) {
aliceBal, closeTx.TxOut[0].Value)
}
}
// TestUpdateFeeFail tests that the signature verification will fail if they
// fee updates are out of sync.
func TestUpdateFeeFail(t *testing.T) {
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
defer cleanUp()
// Bob receives the update, that will apply to his commitment
// transaction.
bobChannel.ReceiveUpdateFee(111)
// Alice sends signature for commitment that does not cover any fee
// update.
aliceSig, err := aliceChannel.SignNextCommitment()
if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err)
}
// Bob verifies this commit, meaning that he checks that it is
// consistent everything he has received. This should fail, since he got
// the fee update, but Alice never sent it.
err = bobChannel.ReceiveNewCommitment(aliceSig)
if err == nil {
t.Fatalf("expected bob to fail receiving alice's signature")
}
}
// TestUpdateFeeSenderCommits veriefies that the state machine progresses as
// expected if we send a fee update, and then the sender of the fee update
// sends a commitment signature.
func TestUpdateFeeSenderCommits(t *testing.T) {
// Create a test channel which will be used for the duration of this
// unittest. The channel will be funded evenly with Alice having 5 BTC,
// and Bob having 5 BTC.
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
defer cleanUp()
paymentPreimage := bytes.Repeat([]byte{1}, 32)
paymentHash := sha256.Sum256(paymentPreimage)
htlc := &lnwire.UpdateAddHTLC{
PaymentHash: paymentHash,
Amount: btcutil.SatoshiPerBitcoin,
Expiry: uint32(5),
}
// First Alice adds the outgoing HTLC to her local channel's state
// update log. Then Alice sends this wire message over to Bob who
// adds this htlc to his remote state update log.
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
// Simulate Alice sending update fee message to bob.
fee := btcutil.Amount(111)
aliceChannel.UpdateFee(fee)
bobChannel.ReceiveUpdateFee(fee)
// Alice signs a commitment, which will cover everything sent to Bob
// (the HTLC and the fee update), and everything acked by Bob (nothing
// so far).
aliceSig, err := aliceChannel.SignNextCommitment()
if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err)
}
// Bob receives this signature message, and verifies that it is
// consistent with the state he had for Alice, including the received
// HTLC and fee update.
if err := bobChannel.ReceiveNewCommitment(aliceSig); err != nil {
t.Fatalf("bob unable to process alice's new commitment: %v", err)
}
if bobChannel.channelState.FeePerKw == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in")
}
// Bob can revoke the prior commitment he had. This should lock in the
// fee update for him.
bobRevocation, err := bobChannel.RevokeCurrentCommitment()
if err != nil {
t.Fatalf("unable to generate bob revocation: %v", err)
}
if bobChannel.channelState.FeePerKw != fee {
t.Fatalf("bob's feePerKw was not locked in")
}
// Bob commits to all updates he has received from Alice. This includes
// the HTLC he received, and the fee update.
bobSig, err := bobChannel.SignNextCommitment()
if err != nil {
t.Fatalf("bob unable to sign alice's commitment: %v", err)
}
// Alice receives the revocation of the old one, and can now assume that
// Bob's received everything up to the signature she sent, including the
// HTLC and fee update.
if _, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil {
t.Fatalf("alice unable to rocess bob's revocation: %v", err)
}
// Alice receives new signature from Bob, and assumes this covers the
// changes.
if err := aliceChannel.ReceiveNewCommitment(bobSig); err != nil {
t.Fatalf("alice unable to process bob's new commitment: %v", err)
}
if aliceChannel.channelState.FeePerKw == fee {
t.Fatalf("alice's feePerKw was unexpectedly locked in")
}
// Alice can revoke the old commitment, which will lock in the fee
// update.
aliceRevocation, err := aliceChannel.RevokeCurrentCommitment()
if err != nil {
t.Fatalf("unable to revoke alice channel: %v", err)
}
if aliceChannel.channelState.FeePerKw != fee {
t.Fatalf("alice's feePerKw was not locked in")
}
// Bob receives revocation from Alice.
if _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil {
t.Fatalf("bob unable to process alive's revocation: %v", err)
}
}
// TestUpdateFeeReceiverCommits tests that the state machine progresses as
// expected if we send a fee update, and then the receiver of the fee update
// sends a commitment signature.
func TestUpdateFeeReceiverCommits(t *testing.T) {
// Create a test channel which will be used for the duration of this
// unittest. The channel will be funded evenly with Alice having 5 BTC,
// and Bob having 5 BTC.
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
defer cleanUp()
paymentPreimage := bytes.Repeat([]byte{1}, 32)
paymentHash := sha256.Sum256(paymentPreimage)
htlc := &lnwire.UpdateAddHTLC{
PaymentHash: paymentHash,
Amount: btcutil.SatoshiPerBitcoin,
Expiry: uint32(5),
}
// First Alice adds the outgoing HTLC to her local channel's state
// update log. Then Alice sends this wire message over to Bob who
// adds this htlc to his remote state update log.
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
t.Fatalf("unable to add htlc: %v", err)
}
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
t.Fatalf("unable to recv htlc: %v", err)
}
// Simulate Alice sending update fee message to bob
fee := btcutil.Amount(111)
aliceChannel.UpdateFee(fee)
bobChannel.ReceiveUpdateFee(fee)
// Bob commits to every change he has sent since last time (none). He
// does not commit to the received HTLC and fee update, since Alice
// cannot know if he has received them.
bobSig, err := bobChannel.SignNextCommitment()
if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err)
}
// Alice receives this signature message, and verifies that it is
// consistent with the remote state, not including any of the updates.
if err := aliceChannel.ReceiveNewCommitment(bobSig); err != nil {
t.Fatalf("bob unable to process alice's new commitment: %v", err)
}
// Alice can revoke the prior commitment she had, this will ack
// everything received before last commitment signature, but in this
// case that is nothing.
aliceRevocation, err := aliceChannel.RevokeCurrentCommitment()
if err != nil {
t.Fatalf("unable to generate bob revocation: %v", err)
}
// Bob receives the revocation of the old commitment
if _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil {
t.Fatalf("alice unable to rocess bob's revocation: %v", err)
}
// Alice will sign next commitment. Since she sent the revocation, she
// also ack'ed everything received, but in this case this is nothing.
// Since she sent the two updates, this signature will cover those two.
aliceSig, err := aliceChannel.SignNextCommitment()
if err != nil {
t.Fatalf("bob unable to sign alice's commitment: %v", err)
}
// Bob gets the signature for the new commitment from Alice. He assumes
// this covers everything received from alice, including the two updates.
if err := bobChannel.ReceiveNewCommitment(aliceSig); err != nil {
t.Fatalf("alice unable to process bob's new commitment: %v", err)
}
if bobChannel.channelState.FeePerKw == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in")
}
// Bob can revoke the old commitment. This will ack what he has
// received, including the HTLC and fee update. This will lock in the
// fee update for bob.
bobRevocation, err := bobChannel.RevokeCurrentCommitment()
if err != nil {
t.Fatalf("unable to revoke alice channel: %v", err)
}
if bobChannel.channelState.FeePerKw != fee {
t.Fatalf("bob's feePerKw was not locked in")
}
// Bob will send a new signature, which will cover what he just acked:
// the HTLC and fee update.
bobSig, err = bobChannel.SignNextCommitment()
if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err)
}
// Alice receives revokation from Bob, and can now be sure that Bob
// received the two updates, and they are considered locked in.
if _, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil {
t.Fatalf("bob unable to process alive's revocation: %v", err)
}
// Alice will receive the signature from Bob, which will cover what was
// just acked by his revocation.
if err := aliceChannel.ReceiveNewCommitment(bobSig); err != nil {
t.Fatalf("alice unable to process bob's new commitment: %v", err)
}
if aliceChannel.channelState.FeePerKw == fee {
t.Fatalf("alice's feePerKw was unexpectedly locked in")
}
// After Alice now revokes her old commitment, the fee update should
// lock in.
aliceRevocation, err = aliceChannel.RevokeCurrentCommitment()
if err != nil {
t.Fatalf("unable to generate bob revocation: %v", err)
}
if aliceChannel.channelState.FeePerKw != fee {
t.Fatalf("Alice's feePerKw was not locked in")
}
// Bob receives revokation from Alice.
if _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil {
t.Fatalf("bob unable to process alive's revocation: %v", err)
}
}
// TestUpdateFeeReceiverSendsUpdate tests that receiving a fee update as channel
// initiator fails, and that trying to initiate fee update as non-initiatior
// fails.
func TestUpdateFeeReceiverSendsUpdate(t *testing.T) {
// Create a test channel which will be used for the duration of this
// unittest. The channel will be funded evenly with Alice having 5 BTC,
// and Bob having 5 BTC.
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
defer cleanUp()
// Since Alice is the channel initiator, she should fail when receiving
// fee update
fee := btcutil.Amount(111)
err = aliceChannel.ReceiveUpdateFee(fee)
if err == nil {
t.Fatalf("expected alice to fail receiving fee update")
}
// Similarly, initiating fee update should fail for Bob.
err = bobChannel.UpdateFee(fee)
if err == nil {
t.Fatalf("expected bob to fail initiating fee update")
}
}
// Test that if multiple update fee messages are sent consecutively, then the
// last one is the one that is being committed to.
func TestUpdateFeeMultipleUpdates(t *testing.T) {
// Create a test channel which will be used for the duration of this
// unittest. The channel will be funded evenly with Alice having 5 BTC,
// and Bob having 5 BTC.
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
defer cleanUp()
// Simulate Alice sending update fee message to bob.
fee1 := btcutil.Amount(111)
fee2 := btcutil.Amount(222)
fee := btcutil.Amount(333)
aliceChannel.UpdateFee(fee1)
aliceChannel.UpdateFee(fee2)
aliceChannel.UpdateFee(fee)
// Alice signs a commitment, which will cover everything sent to Bob
// (the HTLC and the fee update), and everything acked by Bob (nothing
// so far).
aliceSig, err := aliceChannel.SignNextCommitment()
if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err)
}
bobChannel.ReceiveUpdateFee(fee1)
bobChannel.ReceiveUpdateFee(fee2)
bobChannel.ReceiveUpdateFee(fee)
// Bob receives this signature message, and verifies that it is
// consistent with the state he had for Alice, including the received
// HTLC and fee update.
if err := bobChannel.ReceiveNewCommitment(aliceSig); err != nil {
t.Fatalf("bob unable to process alice's new commitment: %v", err)
}
if bobChannel.channelState.FeePerKw == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in")
}
// Alice sending more fee updates now should not mess up the old fee
// they both committed to.
fee3 := btcutil.Amount(444)
fee4 := btcutil.Amount(555)
fee5 := btcutil.Amount(666)
aliceChannel.UpdateFee(fee3)
aliceChannel.UpdateFee(fee4)
aliceChannel.UpdateFee(fee5)
bobChannel.ReceiveUpdateFee(fee3)
bobChannel.ReceiveUpdateFee(fee4)
bobChannel.ReceiveUpdateFee(fee5)
// Bob can revoke the prior commitment he had. This should lock in the
// fee update for him.
bobRevocation, err := bobChannel.RevokeCurrentCommitment()
if err != nil {
t.Fatalf("unable to generate bob revocation: %v", err)
}
if bobChannel.channelState.FeePerKw != fee {
t.Fatalf("bob's feePerKw was not locked in")
}
// Bob commits to all updates he has received from Alice. This includes
// the HTLC he received, and the fee update.
bobSig, err := bobChannel.SignNextCommitment()
if err != nil {
t.Fatalf("bob unable to sign alice's commitment: %v", err)
}
// Alice receives the revocation of the old one, and can now assume that
// Bob's received everything up to the signature she sent, including the
// HTLC and fee update.
if _, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil {
t.Fatalf("alice unable to rocess bob's revocation: %v", err)
}
// Alice receives new signature from Bob, and assumes this covers the
// changes.
if err := aliceChannel.ReceiveNewCommitment(bobSig); err != nil {
t.Fatalf("alice unable to process bob's new commitment: %v", err)
}
if aliceChannel.channelState.FeePerKw == fee {
t.Fatalf("alice's feePerKw was unexpectedly locked in")
}
// Alice can revoke the old commitment, which will lock in the fee
// update.
aliceRevocation, err := aliceChannel.RevokeCurrentCommitment()
if err != nil {
t.Fatalf("unable to revoke alice channel: %v", err)
}
if aliceChannel.channelState.FeePerKw != fee {
t.Fatalf("alice's feePerKw was not locked in")
}
// Bob receives revocation from Alice.
if _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil {
t.Fatalf("bob unable to process alive's revocation: %v", err)
}
}