lnwallet+test: no dust outputs in commitment transaction + tests
Currently non-HTLC outputs will be accepted in the commitment transaction as long as it is non-zero. We change this by not allowing outputs with a value lower than the dust limit. The value of such an output will go towards transaction fees.
This commit is contained in:
parent
9665bb7e54
commit
52b56b8cf2
@ -1095,7 +1095,7 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
|
|||||||
// unsettled/un-timed out HTLCs.
|
// unsettled/un-timed out HTLCs.
|
||||||
ourCommitTx := !remoteChain
|
ourCommitTx := !remoteChain
|
||||||
commitTx, err := CreateCommitTx(lc.fundingTxIn, selfKey, remoteKey,
|
commitTx, err := CreateCommitTx(lc.fundingTxIn, selfKey, remoteKey,
|
||||||
revocationKey, delay, delayBalance, p2wkhBalance)
|
revocationKey, delay, delayBalance, p2wkhBalance, dustLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -2361,7 +2361,7 @@ func (lc *LightningChannel) StateSnapshot() *channeldb.ChannelSnapshot {
|
|||||||
// counterparty within the channel, which can be spent immediately.
|
// counterparty within the channel, which can be spent immediately.
|
||||||
func CreateCommitTx(fundingOutput *wire.TxIn, selfKey, theirKey *btcec.PublicKey,
|
func CreateCommitTx(fundingOutput *wire.TxIn, selfKey, theirKey *btcec.PublicKey,
|
||||||
revokeKey *btcec.PublicKey, csvTimeout uint32, amountToSelf,
|
revokeKey *btcec.PublicKey, csvTimeout uint32, amountToSelf,
|
||||||
amountToThem btcutil.Amount) (*wire.MsgTx, error) {
|
amountToThem, dustLimit btcutil.Amount) (*wire.MsgTx, error) {
|
||||||
|
|
||||||
// First, we create the script for the delayed "pay-to-self" output.
|
// First, we create the script for the delayed "pay-to-self" output.
|
||||||
// This output has 2 main redemption clauses: either we can redeem the
|
// This output has 2 main redemption clauses: either we can redeem the
|
||||||
@ -2391,11 +2391,11 @@ func CreateCommitTx(fundingOutput *wire.TxIn, selfKey, theirKey *btcec.PublicKey
|
|||||||
commitTx := wire.NewMsgTx(2)
|
commitTx := wire.NewMsgTx(2)
|
||||||
commitTx.AddTxIn(fundingOutput)
|
commitTx.AddTxIn(fundingOutput)
|
||||||
|
|
||||||
// Avoid creating zero value outputs within the commitment transaction.
|
// Avoid creating dust outputs within the commitment transaction.
|
||||||
if amountToSelf != 0 {
|
if amountToSelf >= dustLimit {
|
||||||
commitTx.AddTxOut(wire.NewTxOut(int64(amountToSelf), payToUsScriptHash))
|
commitTx.AddTxOut(wire.NewTxOut(int64(amountToSelf), payToUsScriptHash))
|
||||||
}
|
}
|
||||||
if amountToThem != 0 {
|
if amountToThem >= dustLimit {
|
||||||
commitTx.AddTxOut(wire.NewTxOut(int64(amountToThem), theirWitnessKeyHash))
|
commitTx.AddTxOut(wire.NewTxOut(int64(amountToThem), theirWitnessKeyHash))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,12 +219,12 @@ func createTestChannels(revocationWindow int) (*LightningChannel, *LightningChan
|
|||||||
aliceRevokeKey := DeriveRevocationPubkey(bobKeyPub, aliceFirstRevoke[:])
|
aliceRevokeKey := DeriveRevocationPubkey(bobKeyPub, aliceFirstRevoke[:])
|
||||||
|
|
||||||
aliceCommitTx, err := CreateCommitTx(fundingTxIn, aliceKeyPub,
|
aliceCommitTx, err := CreateCommitTx(fundingTxIn, aliceKeyPub,
|
||||||
bobKeyPub, aliceRevokeKey, csvTimeoutAlice, channelBal, channelBal)
|
bobKeyPub, aliceRevokeKey, csvTimeoutAlice, channelBal, channelBal, aliceDustLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
bobCommitTx, err := CreateCommitTx(fundingTxIn, bobKeyPub,
|
bobCommitTx, err := CreateCommitTx(fundingTxIn, bobKeyPub,
|
||||||
aliceKeyPub, bobRevokeKey, csvTimeoutBob, channelBal, channelBal)
|
aliceKeyPub, bobRevokeKey, csvTimeoutBob, channelBal, channelBal, bobDustLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
@ -956,6 +956,87 @@ func TestCheckDustLimit(t *testing.T) {
|
|||||||
if commitment.theirBalance != aliceAmount-htlcAmount {
|
if commitment.theirBalance != aliceAmount-htlcAmount {
|
||||||
t.Fatal("their balance wasn't updated")
|
t.Fatal("their balance wasn't updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Next we will test when a non-HTLC output in the commitment transaction is below the dust limit.
|
||||||
|
// We create an HTLC that will only leave a small enough amount to Alice such that Bob will consider
|
||||||
|
// it a dust output.
|
||||||
|
aliceAmount = aliceChannel.channelState.OurBalance
|
||||||
|
bobAmount = bobChannel.channelState.OurBalance
|
||||||
|
htlcAmount2 := aliceAmount - htlcAmount
|
||||||
|
htlc, preimage = createHTLC(0, htlcAmount2)
|
||||||
|
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
|
||||||
|
t.Fatalf("alice unable to add htlc: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
||||||
|
t.Fatalf("bob unable to receive htlc: %v", err)
|
||||||
|
}
|
||||||
|
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
|
||||||
|
t.Fatalf("Can't update the channel state: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// From Alices' point of view, her output is bigger than the dust limit
|
||||||
|
commitment = aliceChannel.localCommitChain.tip()
|
||||||
|
if len(commitment.txn.TxOut) != 3 {
|
||||||
|
t.Fatal("incorrect number of outputs in commitment transaction "+
|
||||||
|
"expected %v, got %v", 3, commitment.txn.TxOut)
|
||||||
|
}
|
||||||
|
if commitment.ourBalance != aliceAmount-htlcAmount2 {
|
||||||
|
t.Fatal("our balance wasn't updated")
|
||||||
|
}
|
||||||
|
if commitment.theirBalance != bobAmount {
|
||||||
|
t.Fatal("their balance was updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
// From Bobs' point of view, Alice's output is lower than the dust limit
|
||||||
|
commitment = bobChannel.localCommitChain.tip()
|
||||||
|
if len(commitment.txn.TxOut) != 2 {
|
||||||
|
t.Fatal("incorrect number of outputs in commitment transaction "+
|
||||||
|
"expected %v, got %v", 2, commitment.txn.TxOut)
|
||||||
|
}
|
||||||
|
if commitment.theirBalance != aliceAmount-htlcAmount2 {
|
||||||
|
t.Fatal("their balance wasn't updated")
|
||||||
|
}
|
||||||
|
if commitment.ourBalance != bobAmount {
|
||||||
|
t.Fatal("our balance was updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settle HTLC and sign new commitment.
|
||||||
|
settleIndex, err = bobChannel.SettleHTLC(preimage)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("bob unable to settle inbound htlc: %v", err)
|
||||||
|
}
|
||||||
|
err = aliceChannel.ReceiveHTLCSettle(preimage, settleIndex)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("alice unable to accept settle of outbound htlc: %v", err)
|
||||||
|
}
|
||||||
|
if err := forceStateTransition(bobChannel, aliceChannel); err != nil {
|
||||||
|
t.Fatalf("Can't update the channel state: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
commitment = aliceChannel.localCommitChain.tip()
|
||||||
|
if len(commitment.txn.TxOut) != 2 {
|
||||||
|
t.Fatal("incorrect number of outputs in commitment transaction, "+
|
||||||
|
"expected %v got %v", 2, len(commitment.txn.TxOut))
|
||||||
|
}
|
||||||
|
if commitment.ourBalance != aliceAmount-htlcAmount2 {
|
||||||
|
t.Fatal("our balance wasn't updated")
|
||||||
|
}
|
||||||
|
if commitment.theirBalance != bobAmount+htlcAmount2 {
|
||||||
|
t.Fatal("their balance wasn't updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
commitment = bobChannel.localCommitChain.tip()
|
||||||
|
if len(commitment.txn.TxOut) != 1 {
|
||||||
|
t.Fatal("incorrect number of outputs in commitment transaction, "+
|
||||||
|
"expected %v got %v", 1, len(commitment.txn.TxOut))
|
||||||
|
}
|
||||||
|
if commitment.ourBalance != bobAmount+htlcAmount2 {
|
||||||
|
t.Fatal("our balance wasn't updated")
|
||||||
|
}
|
||||||
|
if commitment.theirBalance != aliceAmount-htlcAmount2 {
|
||||||
|
t.Fatal("their balance wasn't updated")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateUpdatePersistence(t *testing.T) {
|
func TestStateUpdatePersistence(t *testing.T) {
|
||||||
|
@ -843,7 +843,7 @@ func testSingleFunderReservationWorkflowResponder(miner *rpctest.Harness,
|
|||||||
aliceCommitTx, err := lnwallet.CreateCommitTx(fundingTxIn,
|
aliceCommitTx, err := lnwallet.CreateCommitTx(fundingTxIn,
|
||||||
ourContribution.CommitKey, bobContribution.CommitKey,
|
ourContribution.CommitKey, bobContribution.CommitKey,
|
||||||
ourContribution.RevocationKey, ourContribution.CsvDelay, 0,
|
ourContribution.RevocationKey, ourContribution.CsvDelay, 0,
|
||||||
capacity)
|
capacity, 540)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create alice's commit tx: %v", err)
|
t.Fatalf("unable to create alice's commit tx: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ func TestCommitmentSpendValidation(t *testing.T) {
|
|||||||
// of 5 blocks before sweeping the output, while bob can spend
|
// of 5 blocks before sweeping the output, while bob can spend
|
||||||
// immediately with either the revocation key, or his regular key.
|
// immediately with either the revocation key, or his regular key.
|
||||||
commitmentTx, err := CreateCommitTx(fakeFundingTxIn, aliceKeyPub,
|
commitmentTx, err := CreateCommitTx(fakeFundingTxIn, aliceKeyPub,
|
||||||
bobKeyPub, revokePubKey, csvTimeout, channelBalance, channelBalance)
|
bobKeyPub, revokePubKey, csvTimeout, channelBalance, channelBalance, 540)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create commitment transaction: %v", nil)
|
t.Fatalf("unable to create commitment transaction: %v", nil)
|
||||||
}
|
}
|
||||||
|
@ -783,14 +783,14 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
|
|||||||
ourCommitKey := ourContribution.CommitKey
|
ourCommitKey := ourContribution.CommitKey
|
||||||
ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
|
ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
|
||||||
ourRevokeKey, ourContribution.CsvDelay,
|
ourRevokeKey, ourContribution.CsvDelay,
|
||||||
ourBalance, theirBalance)
|
ourBalance, theirBalance, pendingReservation.partialState.OurDustLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.err <- err
|
req.err <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
theirCommitTx, err := CreateCommitTx(fundingTxIn, theirCommitKey, ourCommitKey,
|
theirCommitTx, err := CreateCommitTx(fundingTxIn, theirCommitKey, ourCommitKey,
|
||||||
theirContribution.RevocationKey, theirContribution.CsvDelay,
|
theirContribution.RevocationKey, theirContribution.CsvDelay,
|
||||||
theirBalance, ourBalance)
|
theirBalance, ourBalance, pendingReservation.partialState.TheirDustLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.err <- err
|
req.err <- err
|
||||||
return
|
return
|
||||||
@ -1100,14 +1100,15 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
|||||||
theirBalance := pendingReservation.partialState.TheirBalance
|
theirBalance := pendingReservation.partialState.TheirBalance
|
||||||
ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
|
ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
|
||||||
pendingReservation.ourContribution.RevocationKey,
|
pendingReservation.ourContribution.RevocationKey,
|
||||||
pendingReservation.ourContribution.CsvDelay, ourBalance, theirBalance)
|
pendingReservation.ourContribution.CsvDelay, ourBalance, theirBalance,
|
||||||
|
pendingReservation.partialState.OurDustLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.err <- err
|
req.err <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
theirCommitTx, err := CreateCommitTx(fundingTxIn, theirCommitKey, ourCommitKey,
|
theirCommitTx, err := CreateCommitTx(fundingTxIn, theirCommitKey, ourCommitKey,
|
||||||
req.revokeKey, pendingReservation.theirContribution.CsvDelay,
|
req.revokeKey, pendingReservation.theirContribution.CsvDelay,
|
||||||
theirBalance, ourBalance)
|
theirBalance, ourBalance, pendingReservation.partialState.TheirDustLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.err <- err
|
req.err <- err
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user