lnwallet: add support for the push-during-funding workflow
This commit adds support to the wallet’s internal funding workflow for pushing a certain amount of BTC to the responder’s side for a single funder workflow as part of the first commitment.
This commit is contained in:
parent
35776a9906
commit
285ba711a1
@ -364,7 +364,7 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, wallet *lnwallet
|
|||||||
// Bob initiates a channel funded with 5 BTC for each side, so 10
|
// Bob initiates a channel funded with 5 BTC for each side, so 10
|
||||||
// BTC total. He also generates 2 BTC in change.
|
// BTC total. He also generates 2 BTC in change.
|
||||||
chanReservation, err := wallet.InitChannelReservation(fundingAmount*2,
|
chanReservation, err := wallet.InitChannelReservation(fundingAmount*2,
|
||||||
fundingAmount, bobNode.id, bobAddr, numReqConfs, 4, 540)
|
fundingAmount, bobNode.id, bobAddr, numReqConfs, 4, 540, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to initialize funding reservation: %v", err)
|
t.Fatalf("unable to initialize funding reservation: %v", err)
|
||||||
}
|
}
|
||||||
@ -527,7 +527,7 @@ func testFundingTransactionLockedOutputs(miner *rpctest.Harness,
|
|||||||
// Create a single channel asking for 16 BTC total.
|
// Create a single channel asking for 16 BTC total.
|
||||||
fundingAmount := btcutil.Amount(8 * 1e8)
|
fundingAmount := btcutil.Amount(8 * 1e8)
|
||||||
_, err := wallet.InitChannelReservation(fundingAmount, fundingAmount,
|
_, err := wallet.InitChannelReservation(fundingAmount, fundingAmount,
|
||||||
testPub, bobAddr, numReqConfs, 4, 540)
|
testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to initialize funding reservation 1: %v", err)
|
t.Fatalf("unable to initialize funding reservation 1: %v", err)
|
||||||
}
|
}
|
||||||
@ -537,7 +537,7 @@ func testFundingTransactionLockedOutputs(miner *rpctest.Harness,
|
|||||||
// that aren't locked, so this should fail.
|
// that aren't locked, so this should fail.
|
||||||
amt := btcutil.Amount(900 * 1e8)
|
amt := btcutil.Amount(900 * 1e8)
|
||||||
failedReservation, err := wallet.InitChannelReservation(amt, amt,
|
failedReservation, err := wallet.InitChannelReservation(amt, amt,
|
||||||
testPub, bobAddr, numReqConfs, 4, 540)
|
testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("not error returned, should fail on coin selection")
|
t.Fatalf("not error returned, should fail on coin selection")
|
||||||
}
|
}
|
||||||
@ -557,14 +557,14 @@ func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
|
|||||||
// Create a reservation for 44 BTC.
|
// Create a reservation for 44 BTC.
|
||||||
fundingAmount := btcutil.Amount(44 * 1e8)
|
fundingAmount := btcutil.Amount(44 * 1e8)
|
||||||
chanReservation, err := wallet.InitChannelReservation(fundingAmount,
|
chanReservation, err := wallet.InitChannelReservation(fundingAmount,
|
||||||
fundingAmount, testPub, bobAddr, numReqConfs, 4, 540)
|
fundingAmount, testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to initialize funding reservation: %v", err)
|
t.Fatalf("unable to initialize funding reservation: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to create another channel with 44 BTC, this should fail.
|
// Attempt to create another channel with 44 BTC, this should fail.
|
||||||
_, err = wallet.InitChannelReservation(fundingAmount,
|
_, err = wallet.InitChannelReservation(fundingAmount,
|
||||||
fundingAmount, testPub, bobAddr, numReqConfs, 4, 540)
|
fundingAmount, testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||||
if _, ok := err.(*lnwallet.ErrInsufficientFunds); !ok {
|
if _, ok := err.(*lnwallet.ErrInsufficientFunds); !ok {
|
||||||
t.Fatalf("coin selection succeded should have insufficient funds: %v",
|
t.Fatalf("coin selection succeded should have insufficient funds: %v",
|
||||||
err)
|
err)
|
||||||
@ -594,7 +594,7 @@ func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
|
|||||||
|
|
||||||
// Request to fund a new channel should now succeed.
|
// Request to fund a new channel should now succeed.
|
||||||
_, err = wallet.InitChannelReservation(fundingAmount, fundingAmount,
|
_, err = wallet.InitChannelReservation(fundingAmount, fundingAmount,
|
||||||
testPub, bobAddr, numReqConfs, 4, 540)
|
testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to initialize funding reservation: %v", err)
|
t.Fatalf("unable to initialize funding reservation: %v", err)
|
||||||
}
|
}
|
||||||
@ -606,7 +606,8 @@ func testCancelNonExistantReservation(miner *rpctest.Harness,
|
|||||||
t.Log("Running cancel reservation tests")
|
t.Log("Running cancel reservation tests")
|
||||||
|
|
||||||
// Create our own reservation, give it some ID.
|
// Create our own reservation, give it some ID.
|
||||||
res := lnwallet.NewChannelReservation(1000, 1000, 5000, wallet, 22, numReqConfs)
|
res := lnwallet.NewChannelReservation(1000, 1000, 5000, wallet, 22,
|
||||||
|
numReqConfs, 10)
|
||||||
|
|
||||||
// Attempt to cancel this reservation. This should fail, we know
|
// Attempt to cancel this reservation. This should fail, we know
|
||||||
// nothing of it.
|
// nothing of it.
|
||||||
@ -630,10 +631,13 @@ func testSingleFunderReservationWorkflowInitiator(miner *rpctest.Harness,
|
|||||||
t.Fatalf("unable to create bob node: %v", err)
|
t.Fatalf("unable to create bob node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a reservation for a channel with 4 BTC funded solely by us.
|
// Initialize a reservation for a channel with 4 BTC funded solely by
|
||||||
|
// us. We'll also initially push 1 BTC of the channel towards Bob's
|
||||||
|
// side.
|
||||||
fundingAmt := btcutil.Amount(4 * 1e8)
|
fundingAmt := btcutil.Amount(4 * 1e8)
|
||||||
|
pushAmt := btcutil.Amount(btcutil.SatoshiPerBitcoin)
|
||||||
chanReservation, err := wallet.InitChannelReservation(fundingAmt,
|
chanReservation, err := wallet.InitChannelReservation(fundingAmt,
|
||||||
fundingAmt, bobNode.id, bobAddr, numReqConfs, 4, 540)
|
fundingAmt, bobNode.id, bobAddr, numReqConfs, 4, 540, pushAmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to init channel reservation: %v", err)
|
t.Fatalf("unable to init channel reservation: %v", err)
|
||||||
}
|
}
|
||||||
@ -705,7 +709,7 @@ func testSingleFunderReservationWorkflowInitiator(miner *rpctest.Harness,
|
|||||||
t.Fatalf("bob's final delivery address not found")
|
t.Fatalf("bob's final delivery address not found")
|
||||||
}
|
}
|
||||||
if theirContribution.RevocationKey == nil {
|
if theirContribution.RevocationKey == nil {
|
||||||
t.Fatalf("bob's revocaiton hash not found")
|
t.Fatalf("bob's revocation hash not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// With this contribution processed, we're able to create the
|
// With this contribution processed, we're able to create the
|
||||||
@ -779,7 +783,7 @@ func testSingleFunderReservationWorkflowResponder(miner *rpctest.Harness,
|
|||||||
// contribution and the necessary resources.
|
// contribution and the necessary resources.
|
||||||
fundingAmt := btcutil.Amount(0)
|
fundingAmt := btcutil.Amount(0)
|
||||||
chanReservation, err := wallet.InitChannelReservation(capacity,
|
chanReservation, err := wallet.InitChannelReservation(capacity,
|
||||||
fundingAmt, bobNode.id, bobAddr, numReqConfs, 4, 540)
|
fundingAmt, bobNode.id, bobAddr, numReqConfs, 4, 540, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to init channel reservation: %v", err)
|
t.Fatalf("unable to init channel reservation: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,11 @@ type ChannelReservation struct {
|
|||||||
// channel should be considered open.
|
// channel should be considered open.
|
||||||
numConfsToOpen uint16
|
numConfsToOpen uint16
|
||||||
|
|
||||||
|
// pushSat the amount of satoshis that should be pushed to the
|
||||||
|
// responder of a single funding channel as part of the initial
|
||||||
|
// commitment state.
|
||||||
|
pushSat btcutil.Amount
|
||||||
|
|
||||||
// chanOpen houses a struct containing the channel and additional
|
// chanOpen houses a struct containing the channel and additional
|
||||||
// confirmation details will be sent on once the channel is considered
|
// confirmation details will be sent on once the channel is considered
|
||||||
// 'open'. A channel is open once the funding transaction has reached a
|
// 'open'. A channel is open once the funding transaction has reached a
|
||||||
@ -131,11 +136,12 @@ type ChannelReservation struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewChannelReservation creates a new channel reservation. This function is
|
// NewChannelReservation creates a new channel reservation. This function is
|
||||||
// used only internally by lnwallet. In order to concurrent safety, the creation
|
// used only internally by lnwallet. In order to concurrent safety, the
|
||||||
// of all channel reservations should be carried out via the
|
// creation of all channel reservations should be carried out via the
|
||||||
// lnwallet.InitChannelReservation interface.
|
// lnwallet.InitChannelReservation interface.
|
||||||
func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcutil.Amount,
|
func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcutil.Amount,
|
||||||
wallet *LightningWallet, id uint64, numConfs uint16) *ChannelReservation {
|
wallet *LightningWallet, id uint64, numConfs uint16,
|
||||||
|
pushSat btcutil.Amount) *ChannelReservation {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ourBalance btcutil.Amount
|
ourBalance btcutil.Amount
|
||||||
@ -143,10 +149,11 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
|
|||||||
)
|
)
|
||||||
|
|
||||||
// If we're the responder to a single-funder reservation, then we have
|
// If we're the responder to a single-funder reservation, then we have
|
||||||
// no initial balance in the channel.
|
// no initial balance in the channel unless the remote party is pushing
|
||||||
|
// some funds to us within the first commitment state.
|
||||||
if fundingAmt == 0 {
|
if fundingAmt == 0 {
|
||||||
ourBalance = 0
|
ourBalance = pushSat
|
||||||
theirBalance = capacity - commitFee
|
theirBalance = capacity - commitFee - pushSat
|
||||||
} else {
|
} else {
|
||||||
// TODO(roasbeef): need to rework fee structure in general and
|
// TODO(roasbeef): need to rework fee structure in general and
|
||||||
// also when we "unlock" dual funder within the daemon
|
// also when we "unlock" dual funder within the daemon
|
||||||
@ -154,8 +161,9 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
|
|||||||
if capacity == fundingAmt+commitFee {
|
if capacity == fundingAmt+commitFee {
|
||||||
// If we're initiating a single funder workflow, then
|
// If we're initiating a single funder workflow, then
|
||||||
// we pay all the initial fees within the commitment
|
// we pay all the initial fees within the commitment
|
||||||
// transaction.
|
// transaction. We also deduct our balance by the
|
||||||
ourBalance = capacity - commitFee
|
// amount pushed as part of the initial state.
|
||||||
|
ourBalance = capacity - commitFee - pushSat
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, this is a dual funder workflow where both
|
// Otherwise, this is a dual funder workflow where both
|
||||||
// slides split the amount funded and the commitment
|
// slides split the amount funded and the commitment
|
||||||
@ -163,7 +171,7 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
|
|||||||
ourBalance = fundingAmt - commitFee
|
ourBalance = fundingAmt - commitFee
|
||||||
}
|
}
|
||||||
|
|
||||||
theirBalance = capacity - fundingAmt - commitFee
|
theirBalance = capacity - fundingAmt - commitFee + pushSat
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -171,14 +179,19 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
|
|||||||
chanType channeldb.ChannelType
|
chanType channeldb.ChannelType
|
||||||
)
|
)
|
||||||
switch {
|
switch {
|
||||||
// If our balance is zero, then we're the responder to a single funder
|
// If our balance is zero, or we're being pushed our entire balance in
|
||||||
|
// the first state, then we're the responder to a single funder
|
||||||
// channel workflow.
|
// channel workflow.
|
||||||
|
case pushSat != 0 && ourBalance-pushSat == 0:
|
||||||
|
fallthrough
|
||||||
case ourBalance == 0:
|
case ourBalance == 0:
|
||||||
initiator = false
|
initiator = false
|
||||||
chanType = channeldb.SingleFunder
|
chanType = channeldb.SingleFunder
|
||||||
|
|
||||||
// Or, if their balance is zero, then we're the initiator to a single
|
// If their balance is zero, or being pushed entirely to them, then
|
||||||
// funder channel workflow.
|
// we're the initiator to a single funder channel workflow.
|
||||||
|
case pushSat != 0 && theirBalance-pushSat == 0:
|
||||||
|
fallthrough
|
||||||
case theirBalance == 0:
|
case theirBalance == 0:
|
||||||
initiator = true
|
initiator = true
|
||||||
chanType = channeldb.SingleFunder
|
chanType = channeldb.SingleFunder
|
||||||
@ -208,6 +221,7 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
|
|||||||
Db: wallet.ChannelDB,
|
Db: wallet.ChannelDB,
|
||||||
},
|
},
|
||||||
numConfsToOpen: numConfs,
|
numConfsToOpen: numConfs,
|
||||||
|
pushSat: pushSat,
|
||||||
reservationID: id,
|
reservationID: id,
|
||||||
chanOpen: make(chan *openChanDetails, 1),
|
chanOpen: make(chan *openChanDetails, 1),
|
||||||
wallet: wallet,
|
wallet: wallet,
|
||||||
|
@ -105,6 +105,10 @@ type initFundingReserveMsg struct {
|
|||||||
// this amount are not enforceable onchain from our point of view.
|
// this amount are not enforceable onchain from our point of view.
|
||||||
ourDustLimit btcutil.Amount
|
ourDustLimit btcutil.Amount
|
||||||
|
|
||||||
|
// pushSat is the number of satoshis that should be pushed over the the
|
||||||
|
// responder as part of the initial channel creation.
|
||||||
|
pushSat btcutil.Amount
|
||||||
|
|
||||||
// The delay on the "pay-to-self" output(s) of the commitment transaction.
|
// The delay on the "pay-to-self" output(s) of the commitment transaction.
|
||||||
csvDelay uint32
|
csvDelay uint32
|
||||||
|
|
||||||
@ -489,7 +493,11 @@ out:
|
|||||||
func (l *LightningWallet) InitChannelReservation(capacity,
|
func (l *LightningWallet) InitChannelReservation(capacity,
|
||||||
ourFundAmt btcutil.Amount, theirID *btcec.PublicKey,
|
ourFundAmt btcutil.Amount, theirID *btcec.PublicKey,
|
||||||
theirAddr *net.TCPAddr, numConfs uint16,
|
theirAddr *net.TCPAddr, numConfs uint16,
|
||||||
csvDelay uint32, ourDustLimit btcutil.Amount) (*ChannelReservation, error) {
|
csvDelay uint32, ourDustLimit btcutil.Amount,
|
||||||
|
pushSat btcutil.Amount) (*ChannelReservation, error) {
|
||||||
|
|
||||||
|
// TODO(roasbeef): make the above into an initial config as part of the
|
||||||
|
// refactor to implement spec compliant funding flow
|
||||||
|
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
respChan := make(chan *ChannelReservation, 1)
|
respChan := make(chan *ChannelReservation, 1)
|
||||||
@ -500,6 +508,7 @@ func (l *LightningWallet) InitChannelReservation(capacity,
|
|||||||
fundingAmount: ourFundAmt,
|
fundingAmount: ourFundAmt,
|
||||||
csvDelay: csvDelay,
|
csvDelay: csvDelay,
|
||||||
ourDustLimit: ourDustLimit,
|
ourDustLimit: ourDustLimit,
|
||||||
|
pushSat: pushSat,
|
||||||
nodeID: theirID,
|
nodeID: theirID,
|
||||||
nodeAddr: theirAddr,
|
nodeAddr: theirAddr,
|
||||||
err: errChan,
|
err: errChan,
|
||||||
@ -523,7 +532,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
|
|||||||
id := atomic.AddUint64(&l.nextFundingID, 1)
|
id := atomic.AddUint64(&l.nextFundingID, 1)
|
||||||
totalCapacity := req.capacity + commitFee
|
totalCapacity := req.capacity + commitFee
|
||||||
reservation := NewChannelReservation(totalCapacity, req.fundingAmount,
|
reservation := NewChannelReservation(totalCapacity, req.fundingAmount,
|
||||||
req.minFeeRate, l, id, req.numConfs)
|
req.minFeeRate, l, id, req.numConfs, req.pushSat)
|
||||||
|
|
||||||
// Grab the mutex on the ChannelReservation to ensure thread-safety
|
// Grab the mutex on the ChannelReservation to ensure thread-safety
|
||||||
reservation.Lock()
|
reservation.Lock()
|
||||||
@ -778,8 +787,8 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
|
|||||||
|
|
||||||
// With the funding tx complete, create both commitment transactions.
|
// With the funding tx complete, create both commitment transactions.
|
||||||
// TODO(roasbeef): much cleanup + de-duplication
|
// TODO(roasbeef): much cleanup + de-duplication
|
||||||
ourBalance := ourContribution.FundingAmount
|
ourBalance := pendingReservation.partialState.OurBalance
|
||||||
theirBalance := theirContribution.FundingAmount
|
theirBalance := pendingReservation.partialState.TheirBalance
|
||||||
ourCommitKey := ourContribution.CommitKey
|
ourCommitKey := ourContribution.CommitKey
|
||||||
ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
|
ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
|
||||||
ourRevokeKey, ourContribution.CsvDelay,
|
ourRevokeKey, ourContribution.CsvDelay,
|
||||||
@ -1099,8 +1108,8 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
|||||||
// remote node's commitment transactions.
|
// remote node's commitment transactions.
|
||||||
ourCommitKey := pendingReservation.ourContribution.CommitKey
|
ourCommitKey := pendingReservation.ourContribution.CommitKey
|
||||||
theirCommitKey := pendingReservation.theirContribution.CommitKey
|
theirCommitKey := pendingReservation.theirContribution.CommitKey
|
||||||
ourBalance := pendingReservation.ourContribution.FundingAmount
|
ourBalance := pendingReservation.partialState.OurBalance
|
||||||
theirBalance := pendingReservation.theirContribution.FundingAmount
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user