From 0b62126067c00434530fdc49659a04c9846e89ec Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 31 Jul 2019 20:16:52 -0700 Subject: [PATCH] multi: update funding workflow to be aware of new tweakless commits In this commit, we update the funding workflow to be aware of the new channel type that doesn't tweak the remote party's output within the non-delay script on their commitment transaction. To do this, we now allow the caller of `InnitChannelReservation` to signal if they want the old or new (tweakless) commitment style. The funding tests are also updated to test both funding variants, as we'll still need to understand the legacy format for older nodes. --- breacharbiter_test.go | 16 +++++++++----- contractcourt/chain_arbitrator_test.go | 2 +- contractcourt/chain_watcher_test.go | 14 ++++++++----- htlcswitch/test_utils.go | 4 ++-- lnwallet/interface_test.go | 29 +++++++++++++++++++++----- lnwallet/reservation.go | 11 +++++++--- lnwallet/test_utils.go | 16 ++++++++++---- lnwallet/wallet.go | 7 ++++++- test_utils.go | 4 ++-- 9 files changed, 75 insertions(+), 28 deletions(-) diff --git a/breacharbiter_test.go b/breacharbiter_test.go index 9d95ea4f..b4f1c568 100644 --- a/breacharbiter_test.go +++ b/breacharbiter_test.go @@ -90,6 +90,11 @@ var ( 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, }, + {0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, + 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, + 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, + 0xa3, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, + }, } breachedOutputs = []breachedOutput{ @@ -1754,9 +1759,10 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa } aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) - aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(channelBal, - channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint, - *fundingTxIn) + aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns( + channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, + bobCommitPoint, *fundingTxIn, true, + ) if err != nil { return nil, nil, nil, err } @@ -1822,7 +1828,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa IdentityPub: aliceKeyPub, FundingOutpoint: *prevOut, ShortChannelID: shortChanID, - ChanType: channeldb.SingleFunder, + ChanType: channeldb.SingleFunderTweakless, IsInitiator: true, Capacity: channelCapacity, RemoteCurrentRevocation: bobCommitPoint, @@ -1840,7 +1846,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa IdentityPub: bobKeyPub, FundingOutpoint: *prevOut, ShortChannelID: shortChanID, - ChanType: channeldb.SingleFunder, + ChanType: channeldb.SingleFunderTweakless, IsInitiator: false, Capacity: channelCapacity, RemoteCurrentRevocation: aliceCommitPoint, diff --git a/contractcourt/chain_arbitrator_test.go b/contractcourt/chain_arbitrator_test.go index 4ff3d064..38ea2a35 100644 --- a/contractcourt/chain_arbitrator_test.go +++ b/contractcourt/chain_arbitrator_test.go @@ -34,7 +34,7 @@ func TestChainArbitratorRepublishCommitment(t *testing.T) { const numChans = 10 var channels []*channeldb.OpenChannel for i := 0; i < numChans; i++ { - lChannel, _, cleanup, err := lnwallet.CreateTestChannels() + lChannel, _, cleanup, err := lnwallet.CreateTestChannels(true) if err != nil { t.Fatal(err) } diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index 2a511b65..f0e07628 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -62,7 +62,7 @@ func TestChainWatcherRemoteUnilateralClose(t *testing.T) { // First, we'll create two channels which already have established a // commitment contract between themselves. - aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -149,7 +149,7 @@ func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) { // First, we'll create two channels which already have established a // commitment contract between themselves. - aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -272,7 +272,9 @@ func TestChainWatcherDataLossProtect(t *testing.T) { dlpScenario := func(t *testing.T, testCase dlpTestCase) bool { // First, we'll create two channels which already have // established a commitment contract between themselves. - aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels( + false, + ) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -430,7 +432,7 @@ func TestChainWatcherDataLossProtect(t *testing.T) { func TestChainWatcherLocalForceCloseDetect(t *testing.T) { t.Parallel() - // localForceCloseScenario is the primary test we'll use to execut eout + // localForceCloseScenario is the primary test we'll use to execute our // table driven tests. We'll assert that for any number of state // updates, and if the commitment transaction has our output or not, // we're able to properly detect a local force close. @@ -439,7 +441,9 @@ func TestChainWatcherLocalForceCloseDetect(t *testing.T) { // First, we'll create two channels which already have // established a commitment contract between themselves. - aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels( + false, + ) if err != nil { t.Fatalf("unable to create test channels: %v", err) } diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 4f48c4f6..f2f86b75 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -327,7 +327,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte, RemoteChanCfg: bobCfg, IdentityPub: aliceKeyPub, FundingOutpoint: *prevOut, - ChanType: channeldb.SingleFunder, + ChanType: channeldb.SingleFunderTweakless, IsInitiator: true, Capacity: channelCapacity, RemoteCurrentRevocation: bobCommitPoint, @@ -346,7 +346,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte, RemoteChanCfg: aliceCfg, IdentityPub: bobKeyPub, FundingOutpoint: *prevOut, - ChanType: channeldb.SingleFunder, + ChanType: channeldb.SingleFunderTweakless, IsInitiator: false, Capacity: channelCapacity, RemoteCurrentRevocation: aliceCommitPoint, diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index deda71e7..16bb88e0 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -698,7 +698,7 @@ func testCancelNonExistentReservation(miner *rpctest.Harness, // Create our own reservation, give it some ID. res, err := lnwallet.NewChannelReservation( 10000, 10000, feePerKw, alice, 22, 10, &testHdSeed, - lnwire.FFAnnounceChannel, + lnwire.FFAnnounceChannel, true, ) if err != nil { t.Fatalf("unable to create res: %v", err) @@ -736,6 +736,7 @@ func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness, FundingFeePerKw: 1000, PushMSat: 0, Flags: lnwire.FFAnnounceChannel, + Tweakless: true, } _, err = alice.InitChannelReservation(req) switch { @@ -790,7 +791,7 @@ func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContributi } func testSingleFunderReservationWorkflow(miner *rpctest.Harness, - alice, bob *lnwallet.LightningWallet, t *testing.T) { + alice, bob *lnwallet.LightningWallet, t *testing.T, tweakless bool) { // For this scenario, Alice will be the channel initiator while bob // will act as the responder to the workflow. @@ -817,6 +818,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, FundingFeePerKw: feePerKw, PushMSat: pushAmt, Flags: lnwire.FFAnnounceChannel, + Tweakless: tweakless, } aliceChanReservation, err := alice.InitChannelReservation(aliceReq) if err != nil { @@ -860,6 +862,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, FundingFeePerKw: feePerKw, PushMSat: pushAmt, Flags: lnwire.FFAnnounceChannel, + Tweakless: tweakless, } bobChanReservation, err := bob.InitChannelReservation(bobReq) if err != nil { @@ -966,7 +969,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, if !aliceChannels[0].IsInitiator { t.Fatalf("alice not detected as channel initiator") } - if aliceChannels[0].ChanType != channeldb.SingleFunder { + if !aliceChannels[0].ChanType.IsSingleFunder() { t.Fatalf("channel type is incorrect, expected %v instead got %v", channeldb.SingleFunder, aliceChannels[0].ChanType) } @@ -986,7 +989,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, if bobChannels[0].IsInitiator { t.Fatalf("bob not detected as channel responder") } - if bobChannels[0].ChanType != channeldb.SingleFunder { + if !bobChannels[0].ChanType.IsSingleFunder() { t.Fatalf("channel type is incorrect, expected %v instead got %v", channeldb.SingleFunder, bobChannels[0].ChanType) } @@ -2515,7 +2518,23 @@ var walletTests = []walletTestCase{ }, { name: "single funding workflow", - test: testSingleFunderReservationWorkflow, + test: func(miner *rpctest.Harness, alice, + bob *lnwallet.LightningWallet, t *testing.T) { + + testSingleFunderReservationWorkflow( + miner, alice, bob, t, false, + ) + }, + }, + { + name: "single funding workflow tweakless", + test: func(miner *rpctest.Harness, alice, + bob *lnwallet.LightningWallet, t *testing.T) { + + testSingleFunderReservationWorkflow( + miner, alice, bob, t, true, + ) + }, }, { name: "dual funder workflow", diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index 98c88d72..6ac592b9 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -130,7 +130,8 @@ type ChannelReservation struct { func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, commitFeePerKw SatPerKWeight, wallet *LightningWallet, id uint64, pushMSat lnwire.MilliSatoshi, chainHash *chainhash.Hash, - flags lnwire.FundingFlag) (*ChannelReservation, error) { + flags lnwire.FundingFlag, + tweaklessCommit bool) (*ChannelReservation, error) { var ( ourBalance lnwire.MilliSatoshi @@ -140,7 +141,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, commitFee := commitFeePerKw.FeeForWeight(input.CommitWeight) localFundingMSat := lnwire.NewMSatFromSatoshis(localFundingAmt) - // TODO(halseth): make method take remote funding amount direcly + // TODO(halseth): make method take remote funding amount directly // instead of inferring it from capacity and local amt. capacityMSat := lnwire.NewMSatFromSatoshis(capacity) feeMSat := lnwire.NewMSatFromSatoshis(commitFee) @@ -213,7 +214,11 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, // non-zero push amt (there's no pushing for dual funder), then this is // a single-funder channel. if ourBalance == 0 || theirBalance == 0 || pushMSat != 0 { - chanType = channeldb.SingleFunder + if tweaklessCommit { + chanType = channeldb.SingleFunderTweakless + } else { + chanType = channeldb.SingleFunder + } } else { // Otherwise, this is a dual funder channel, and no side is // technically the "initiator" diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index 579622fa..efb30ac1 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -88,8 +88,11 @@ var ( // allocated to each side. Within the channel, Alice is the initiator. The // function also returns a "cleanup" function that is meant to be called once // the test has been finalized. The clean up function will remote all temporary -// files created -func CreateTestChannels(tweaklessCommits bool) (*LightningChannel, *LightningChannel, func(), error) { +// files created. If tweaklessCommits is true, then the commits within the +// channels will use the new format, otherwise the legacy format. +func CreateTestChannels(tweaklessCommits bool) ( + *LightningChannel, *LightningChannel, func(), error) { + channelCapacity, err := btcutil.NewAmount(10) if err != nil { return nil, nil, nil, err @@ -271,7 +274,7 @@ func CreateTestChannels(tweaklessCommits bool) (*LightningChannel, *LightningCha IdentityPub: aliceKeys[0].PubKey(), FundingOutpoint: *prevOut, ShortChannelID: shortChanID, - ChanType: channeldb.SingleFunder, + ChanType: channeldb.SingleFunderTweakless, IsInitiator: true, Capacity: channelCapacity, RemoteCurrentRevocation: bobCommitPoint, @@ -289,7 +292,7 @@ func CreateTestChannels(tweaklessCommits bool) (*LightningChannel, *LightningCha IdentityPub: bobKeys[0].PubKey(), FundingOutpoint: *prevOut, ShortChannelID: shortChanID, - ChanType: channeldb.SingleFunder, + ChanType: channeldb.SingleFunderTweakless, IsInitiator: false, Capacity: channelCapacity, RemoteCurrentRevocation: aliceCommitPoint, @@ -301,6 +304,11 @@ func CreateTestChannels(tweaklessCommits bool) (*LightningChannel, *LightningCha Packager: channeldb.NewChannelPackager(shortChanID), } + if !tweaklessCommits { + aliceChannelState.ChanType = channeldb.SingleFunder + bobChannelState.ChanType = channeldb.SingleFunder + } + aliceSigner := &input.MockSigner{Privkeys: aliceKeys} bobSigner := &input.MockSigner{Privkeys: bobKeys} diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index f86afb61..0e75e585 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -106,6 +106,10 @@ type InitFundingReserveMsg struct { // output selected to fund the channel should satisfy. MinConfs int32 + // Tweakless indicates if the channel should use the new tweakless + // commitment format or not. + Tweakless bool + // err is a channel in which all errors will be sent across. Will be // nil if this initial set is successful. // @@ -489,6 +493,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg reservation, err := NewChannelReservation( capacity, localFundingAmt, req.CommitFeePerKw, l, id, req.PushMSat, l.Cfg.NetParams.GenesisHash, req.Flags, + req.Tweakless, ) if err != nil { selected.unlockCoins() @@ -849,7 +854,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // obfuscator then use it to encode the current state number within // both commitment transactions. var stateObfuscator [StateHintSize]byte - if chanState.ChanType == channeldb.SingleFunder { + if chanState.ChanType.IsSingleFunder() { stateObfuscator = DeriveStateHintObfuscator( ourContribution.PaymentBasePoint.PubKey, theirContribution.PaymentBasePoint.PubKey, diff --git a/test_utils.go b/test_utils.go index cce87d54..671bc0f4 100644 --- a/test_utils.go +++ b/test_utils.go @@ -255,7 +255,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, IdentityPub: aliceKeyPub, FundingOutpoint: *prevOut, ShortChannelID: shortChanID, - ChanType: channeldb.SingleFunder, + ChanType: channeldb.SingleFunderTweakless, IsInitiator: true, Capacity: channelCapacity, RemoteCurrentRevocation: bobCommitPoint, @@ -272,7 +272,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, RemoteChanCfg: aliceCfg, IdentityPub: bobKeyPub, FundingOutpoint: *prevOut, - ChanType: channeldb.SingleFunder, + ChanType: channeldb.SingleFunderTweakless, IsInitiator: false, Capacity: channelCapacity, RemoteCurrentRevocation: aliceCommitPoint,