lnwallet: don't use persistent pointer to funding tx within channel state machine

This commit fixes a lingering bug that could at times cause
incompatibilities with other implementations when attempting a
cooperative channel close. Before this commit, we would use a pointer
to the funding txin everywhere. As a result, each time we made a new
state, or verified one, we would modify the sequence field of the main
txin of the commitment transaction. Due to this if we updated the
channel, then went to do a cooperative channel closure, the sequence of
the txin would still be set to the value we used as the state hint.

To remedy this, we now copy the txin each time when making the
commitment transaction, and also the cooperative closure transaction.
This avoids accidentally mutating the txin itself.

Fixes #502.
This commit is contained in:
Olaoluwa Osuntokun 2017-12-22 19:26:16 +01:00
parent 3f2a5241c1
commit 9777176d7d
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
6 changed files with 13 additions and 13 deletions

@ -154,7 +154,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(aliceAmount,
bobAmount, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint,
fundingTxIn)
*fundingTxIn)
if err != nil {
return nil, nil, nil, nil, err
}

@ -1171,7 +1171,7 @@ type LightningChannel struct {
// that opened the channel.
FundingWitnessScript []byte
fundingTxIn *wire.TxIn
fundingTxIn wire.TxIn
fundingP2WSH []byte
// ForceCloseSignal is a channel that is closed to indicate that a
@ -1297,7 +1297,7 @@ func NewLightningChannel(signer Signer, events chainntnfs.ChainNotifier,
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.localChanCfg.MultiSigKey,
@ -4990,7 +4990,7 @@ func (lc *LightningChannel) generateRevocation(height uint64) (*lnwire.RevokeAnd
// to the "owner" of the commitment transaction which can be spent after a
// relative block delay or revocation event, and the other paying the
// counterparty within the channel, which can be spent immediately.
func CreateCommitTx(fundingOutput *wire.TxIn,
func CreateCommitTx(fundingOutput wire.TxIn,
keyRing *commitmentKeyRing, csvTimeout uint32,
amountToSelf, amountToThem, dustLimit btcutil.Amount) (*wire.MsgTx, error) {
@ -5020,7 +5020,7 @@ func CreateCommitTx(fundingOutput *wire.TxIn,
// the transaction itself. We use a transaction version of 2 since CSV
// will fail unless the tx version is >= 2.
commitTx := wire.NewMsgTx(2)
commitTx.AddTxIn(fundingOutput)
commitTx.AddTxIn(&fundingOutput)
// Avoid creating dust outputs within the commitment transaction.
if amountToSelf >= dustLimit {
@ -5045,7 +5045,7 @@ func CreateCommitTx(fundingOutput *wire.TxIn,
// constructing the channel is the initiator of the closure. Currently it is
// expected that the initiator pays the transaction fees for the closing
// transaction in full.
func CreateCooperativeCloseTx(fundingTxIn *wire.TxIn,
func CreateCooperativeCloseTx(fundingTxIn wire.TxIn,
localDust, remoteDust, ourBalance, theirBalance btcutil.Amount,
ourDeliveryScript, theirDeliveryScript []byte,
initiator bool) *wire.MsgTx {
@ -5055,7 +5055,7 @@ func CreateCooperativeCloseTx(fundingTxIn *wire.TxIn,
// within the channel then a refund output for that particular side can
// be omitted.
closeTx := wire.NewMsgTx(2)
closeTx.AddTxIn(fundingTxIn)
closeTx.AddTxIn(&fundingTxIn)
// Create both cooperative closure outputs, properly respecting the
// dust limits of both parties.

@ -275,7 +275,7 @@ func createTestChannels(revocationWindow int) (*LightningChannel, *LightningChan
aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns(channelBal,
channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint,
fundingTxIn)
*fundingTxIn)
if err != nil {
return nil, nil, nil, err
}

@ -76,7 +76,7 @@ func TestCommitmentSpendValidation(t *testing.T) {
revocationKey: revokePubKey,
noDelayKey: bobPayKey,
}
commitmentTx, err := CreateCommitTx(fakeFundingTxIn, keyRing, csvTimeout,
commitmentTx, err := CreateCommitTx(*fakeFundingTxIn, keyRing, csvTimeout,
channelBalance, channelBalance, DefaultDustLimit())
if err != nil {
t.Fatalf("unable to create commitment transaction: %v", nil)

@ -666,7 +666,7 @@ func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMs
func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
ourChanCfg, theirChanCfg *channeldb.ChannelConfig,
localCommitPoint, remoteCommitPoint *btcec.PublicKey,
fundingTxIn *wire.TxIn) (*wire.MsgTx, *wire.MsgTx, error) {
fundingTxIn wire.TxIn) (*wire.MsgTx, *wire.MsgTx, error) {
localCommitmentKeys := deriveCommitmentKeys(localCommitPoint, true,
ourChanCfg, theirChanCfg)
@ -819,7 +819,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
// Create the txin to our commitment transaction; required to construct
// the commitment transactions.
fundingTxIn := &wire.TxIn{
fundingTxIn := wire.TxIn{
PreviousOutPoint: wire.OutPoint{
Hash: fundingTxID,
Index: multiSigIndex,
@ -1150,7 +1150,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
pendingReservation.theirContribution.ChannelConfig,
pendingReservation.ourContribution.FirstCommitmentPoint,
pendingReservation.theirContribution.FirstCommitmentPoint,
fundingTxIn,
*fundingTxIn,
)
if err != nil {
req.err <- err

@ -119,7 +119,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(channelBal,
channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint,
fundingTxIn)
*fundingTxIn)
if err != nil {
return nil, nil, nil, nil, err
}