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.
This commit is contained in:
Olaoluwa Osuntokun 2019-07-31 20:16:52 -07:00
parent b399203e71
commit 0b62126067
No known key found for this signature in database
GPG Key ID: BC13F65E2DC84465
9 changed files with 75 additions and 28 deletions

@ -90,6 +90,11 @@ var (
0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21,
0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, 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{ breachedOutputs = []breachedOutput{
@ -1754,9 +1759,10 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
} }
aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:])
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(channelBal, aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint, channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
*fundingTxIn) bobCommitPoint, *fundingTxIn, true,
)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
@ -1822,7 +1828,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
IdentityPub: aliceKeyPub, IdentityPub: aliceKeyPub,
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
ChanType: channeldb.SingleFunder, ChanType: channeldb.SingleFunderTweakless,
IsInitiator: true, IsInitiator: true,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: bobCommitPoint, RemoteCurrentRevocation: bobCommitPoint,
@ -1840,7 +1846,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
IdentityPub: bobKeyPub, IdentityPub: bobKeyPub,
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
ChanType: channeldb.SingleFunder, ChanType: channeldb.SingleFunderTweakless,
IsInitiator: false, IsInitiator: false,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: aliceCommitPoint, RemoteCurrentRevocation: aliceCommitPoint,

@ -34,7 +34,7 @@ func TestChainArbitratorRepublishCommitment(t *testing.T) {
const numChans = 10 const numChans = 10
var channels []*channeldb.OpenChannel var channels []*channeldb.OpenChannel
for i := 0; i < numChans; i++ { for i := 0; i < numChans; i++ {
lChannel, _, cleanup, err := lnwallet.CreateTestChannels() lChannel, _, cleanup, err := lnwallet.CreateTestChannels(true)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -62,7 +62,7 @@ func TestChainWatcherRemoteUnilateralClose(t *testing.T) {
// First, we'll create two channels which already have established a // First, we'll create two channels which already have established a
// commitment contract between themselves. // commitment contract between themselves.
aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels(true)
if err != nil { if err != nil {
t.Fatalf("unable to create test channels: %v", err) 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 // First, we'll create two channels which already have established a
// commitment contract between themselves. // commitment contract between themselves.
aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels(true)
if err != nil { if err != nil {
t.Fatalf("unable to create test channels: %v", err) 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 { dlpScenario := func(t *testing.T, testCase dlpTestCase) bool {
// First, we'll create two channels which already have // First, we'll create two channels which already have
// established a commitment contract between themselves. // established a commitment contract between themselves.
aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels(
false,
)
if err != nil { if err != nil {
t.Fatalf("unable to create test channels: %v", err) t.Fatalf("unable to create test channels: %v", err)
} }
@ -430,7 +432,7 @@ func TestChainWatcherDataLossProtect(t *testing.T) {
func TestChainWatcherLocalForceCloseDetect(t *testing.T) { func TestChainWatcherLocalForceCloseDetect(t *testing.T) {
t.Parallel() 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 // table driven tests. We'll assert that for any number of state
// updates, and if the commitment transaction has our output or not, // updates, and if the commitment transaction has our output or not,
// we're able to properly detect a local force close. // 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 // First, we'll create two channels which already have
// established a commitment contract between themselves. // established a commitment contract between themselves.
aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels(
false,
)
if err != nil { if err != nil {
t.Fatalf("unable to create test channels: %v", err) t.Fatalf("unable to create test channels: %v", err)
} }

@ -327,7 +327,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
RemoteChanCfg: bobCfg, RemoteChanCfg: bobCfg,
IdentityPub: aliceKeyPub, IdentityPub: aliceKeyPub,
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ChanType: channeldb.SingleFunder, ChanType: channeldb.SingleFunderTweakless,
IsInitiator: true, IsInitiator: true,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: bobCommitPoint, RemoteCurrentRevocation: bobCommitPoint,
@ -346,7 +346,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
RemoteChanCfg: aliceCfg, RemoteChanCfg: aliceCfg,
IdentityPub: bobKeyPub, IdentityPub: bobKeyPub,
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ChanType: channeldb.SingleFunder, ChanType: channeldb.SingleFunderTweakless,
IsInitiator: false, IsInitiator: false,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: aliceCommitPoint, RemoteCurrentRevocation: aliceCommitPoint,

@ -698,7 +698,7 @@ func testCancelNonExistentReservation(miner *rpctest.Harness,
// Create our own reservation, give it some ID. // Create our own reservation, give it some ID.
res, err := lnwallet.NewChannelReservation( res, err := lnwallet.NewChannelReservation(
10000, 10000, feePerKw, alice, 22, 10, &testHdSeed, 10000, 10000, feePerKw, alice, 22, 10, &testHdSeed,
lnwire.FFAnnounceChannel, lnwire.FFAnnounceChannel, true,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create res: %v", err) t.Fatalf("unable to create res: %v", err)
@ -736,6 +736,7 @@ func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness,
FundingFeePerKw: 1000, FundingFeePerKw: 1000,
PushMSat: 0, PushMSat: 0,
Flags: lnwire.FFAnnounceChannel, Flags: lnwire.FFAnnounceChannel,
Tweakless: true,
} }
_, err = alice.InitChannelReservation(req) _, err = alice.InitChannelReservation(req)
switch { switch {
@ -790,7 +791,7 @@ func assertContributionInitPopulated(t *testing.T, c *lnwallet.ChannelContributi
} }
func testSingleFunderReservationWorkflow(miner *rpctest.Harness, 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 // For this scenario, Alice will be the channel initiator while bob
// will act as the responder to the workflow. // will act as the responder to the workflow.
@ -817,6 +818,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
FundingFeePerKw: feePerKw, FundingFeePerKw: feePerKw,
PushMSat: pushAmt, PushMSat: pushAmt,
Flags: lnwire.FFAnnounceChannel, Flags: lnwire.FFAnnounceChannel,
Tweakless: tweakless,
} }
aliceChanReservation, err := alice.InitChannelReservation(aliceReq) aliceChanReservation, err := alice.InitChannelReservation(aliceReq)
if err != nil { if err != nil {
@ -860,6 +862,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
FundingFeePerKw: feePerKw, FundingFeePerKw: feePerKw,
PushMSat: pushAmt, PushMSat: pushAmt,
Flags: lnwire.FFAnnounceChannel, Flags: lnwire.FFAnnounceChannel,
Tweakless: tweakless,
} }
bobChanReservation, err := bob.InitChannelReservation(bobReq) bobChanReservation, err := bob.InitChannelReservation(bobReq)
if err != nil { if err != nil {
@ -966,7 +969,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
if !aliceChannels[0].IsInitiator { if !aliceChannels[0].IsInitiator {
t.Fatalf("alice not detected as channel initiator") 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", t.Fatalf("channel type is incorrect, expected %v instead got %v",
channeldb.SingleFunder, aliceChannels[0].ChanType) channeldb.SingleFunder, aliceChannels[0].ChanType)
} }
@ -986,7 +989,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
if bobChannels[0].IsInitiator { if bobChannels[0].IsInitiator {
t.Fatalf("bob not detected as channel responder") 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", t.Fatalf("channel type is incorrect, expected %v instead got %v",
channeldb.SingleFunder, bobChannels[0].ChanType) channeldb.SingleFunder, bobChannels[0].ChanType)
} }
@ -2515,7 +2518,23 @@ var walletTests = []walletTestCase{
}, },
{ {
name: "single funding workflow", 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", name: "dual funder workflow",

@ -130,7 +130,8 @@ type ChannelReservation struct {
func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
commitFeePerKw SatPerKWeight, wallet *LightningWallet, commitFeePerKw SatPerKWeight, wallet *LightningWallet,
id uint64, pushMSat lnwire.MilliSatoshi, chainHash *chainhash.Hash, id uint64, pushMSat lnwire.MilliSatoshi, chainHash *chainhash.Hash,
flags lnwire.FundingFlag) (*ChannelReservation, error) { flags lnwire.FundingFlag,
tweaklessCommit bool) (*ChannelReservation, error) {
var ( var (
ourBalance lnwire.MilliSatoshi ourBalance lnwire.MilliSatoshi
@ -140,7 +141,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
commitFee := commitFeePerKw.FeeForWeight(input.CommitWeight) commitFee := commitFeePerKw.FeeForWeight(input.CommitWeight)
localFundingMSat := lnwire.NewMSatFromSatoshis(localFundingAmt) 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. // instead of inferring it from capacity and local amt.
capacityMSat := lnwire.NewMSatFromSatoshis(capacity) capacityMSat := lnwire.NewMSatFromSatoshis(capacity)
feeMSat := lnwire.NewMSatFromSatoshis(commitFee) 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 // non-zero push amt (there's no pushing for dual funder), then this is
// a single-funder channel. // a single-funder channel.
if ourBalance == 0 || theirBalance == 0 || pushMSat != 0 { if ourBalance == 0 || theirBalance == 0 || pushMSat != 0 {
chanType = channeldb.SingleFunder if tweaklessCommit {
chanType = channeldb.SingleFunderTweakless
} else {
chanType = channeldb.SingleFunder
}
} else { } else {
// Otherwise, this is a dual funder channel, and no side is // Otherwise, this is a dual funder channel, and no side is
// technically the "initiator" // technically the "initiator"

@ -88,8 +88,11 @@ var (
// allocated to each side. Within the channel, Alice is the initiator. The // 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 // 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 // the test has been finalized. The clean up function will remote all temporary
// files created // files created. If tweaklessCommits is true, then the commits within the
func CreateTestChannels(tweaklessCommits bool) (*LightningChannel, *LightningChannel, func(), error) { // channels will use the new format, otherwise the legacy format.
func CreateTestChannels(tweaklessCommits bool) (
*LightningChannel, *LightningChannel, func(), error) {
channelCapacity, err := btcutil.NewAmount(10) channelCapacity, err := btcutil.NewAmount(10)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
@ -271,7 +274,7 @@ func CreateTestChannels(tweaklessCommits bool) (*LightningChannel, *LightningCha
IdentityPub: aliceKeys[0].PubKey(), IdentityPub: aliceKeys[0].PubKey(),
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
ChanType: channeldb.SingleFunder, ChanType: channeldb.SingleFunderTweakless,
IsInitiator: true, IsInitiator: true,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: bobCommitPoint, RemoteCurrentRevocation: bobCommitPoint,
@ -289,7 +292,7 @@ func CreateTestChannels(tweaklessCommits bool) (*LightningChannel, *LightningCha
IdentityPub: bobKeys[0].PubKey(), IdentityPub: bobKeys[0].PubKey(),
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
ChanType: channeldb.SingleFunder, ChanType: channeldb.SingleFunderTweakless,
IsInitiator: false, IsInitiator: false,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: aliceCommitPoint, RemoteCurrentRevocation: aliceCommitPoint,
@ -301,6 +304,11 @@ func CreateTestChannels(tweaklessCommits bool) (*LightningChannel, *LightningCha
Packager: channeldb.NewChannelPackager(shortChanID), Packager: channeldb.NewChannelPackager(shortChanID),
} }
if !tweaklessCommits {
aliceChannelState.ChanType = channeldb.SingleFunder
bobChannelState.ChanType = channeldb.SingleFunder
}
aliceSigner := &input.MockSigner{Privkeys: aliceKeys} aliceSigner := &input.MockSigner{Privkeys: aliceKeys}
bobSigner := &input.MockSigner{Privkeys: bobKeys} bobSigner := &input.MockSigner{Privkeys: bobKeys}

@ -106,6 +106,10 @@ type InitFundingReserveMsg struct {
// output selected to fund the channel should satisfy. // output selected to fund the channel should satisfy.
MinConfs int32 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 // err is a channel in which all errors will be sent across. Will be
// nil if this initial set is successful. // nil if this initial set is successful.
// //
@ -489,6 +493,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
reservation, err := NewChannelReservation( reservation, err := NewChannelReservation(
capacity, localFundingAmt, req.CommitFeePerKw, l, id, capacity, localFundingAmt, req.CommitFeePerKw, l, id,
req.PushMSat, l.Cfg.NetParams.GenesisHash, req.Flags, req.PushMSat, l.Cfg.NetParams.GenesisHash, req.Flags,
req.Tweakless,
) )
if err != nil { if err != nil {
selected.unlockCoins() selected.unlockCoins()
@ -849,7 +854,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
// obfuscator then use it to encode the current state number within // obfuscator then use it to encode the current state number within
// both commitment transactions. // both commitment transactions.
var stateObfuscator [StateHintSize]byte var stateObfuscator [StateHintSize]byte
if chanState.ChanType == channeldb.SingleFunder { if chanState.ChanType.IsSingleFunder() {
stateObfuscator = DeriveStateHintObfuscator( stateObfuscator = DeriveStateHintObfuscator(
ourContribution.PaymentBasePoint.PubKey, ourContribution.PaymentBasePoint.PubKey,
theirContribution.PaymentBasePoint.PubKey, theirContribution.PaymentBasePoint.PubKey,

@ -255,7 +255,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
IdentityPub: aliceKeyPub, IdentityPub: aliceKeyPub,
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
ChanType: channeldb.SingleFunder, ChanType: channeldb.SingleFunderTweakless,
IsInitiator: true, IsInitiator: true,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: bobCommitPoint, RemoteCurrentRevocation: bobCommitPoint,
@ -272,7 +272,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
RemoteChanCfg: aliceCfg, RemoteChanCfg: aliceCfg,
IdentityPub: bobKeyPub, IdentityPub: bobKeyPub,
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ChanType: channeldb.SingleFunder, ChanType: channeldb.SingleFunderTweakless,
IsInitiator: false, IsInitiator: false,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: aliceCommitPoint, RemoteCurrentRevocation: aliceCommitPoint,