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
|
||||
// BTC total. He also generates 2 BTC in change.
|
||||
chanReservation, err := wallet.InitChannelReservation(fundingAmount*2,
|
||||
fundingAmount, bobNode.id, bobAddr, numReqConfs, 4, 540)
|
||||
fundingAmount, bobNode.id, bobAddr, numReqConfs, 4, 540, 0)
|
||||
if err != nil {
|
||||
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.
|
||||
fundingAmount := btcutil.Amount(8 * 1e8)
|
||||
_, err := wallet.InitChannelReservation(fundingAmount, fundingAmount,
|
||||
testPub, bobAddr, numReqConfs, 4, 540)
|
||||
testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||
if err != nil {
|
||||
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.
|
||||
amt := btcutil.Amount(900 * 1e8)
|
||||
failedReservation, err := wallet.InitChannelReservation(amt, amt,
|
||||
testPub, bobAddr, numReqConfs, 4, 540)
|
||||
testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||
if err == nil {
|
||||
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.
|
||||
fundingAmount := btcutil.Amount(44 * 1e8)
|
||||
chanReservation, err := wallet.InitChannelReservation(fundingAmount,
|
||||
fundingAmount, testPub, bobAddr, numReqConfs, 4, 540)
|
||||
fundingAmount, testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize funding reservation: %v", err)
|
||||
}
|
||||
|
||||
// Attempt to create another channel with 44 BTC, this should fail.
|
||||
_, err = wallet.InitChannelReservation(fundingAmount,
|
||||
fundingAmount, testPub, bobAddr, numReqConfs, 4, 540)
|
||||
fundingAmount, testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||
if _, ok := err.(*lnwallet.ErrInsufficientFunds); !ok {
|
||||
t.Fatalf("coin selection succeded should have insufficient funds: %v",
|
||||
err)
|
||||
@ -594,7 +594,7 @@ func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
|
||||
|
||||
// Request to fund a new channel should now succeed.
|
||||
_, err = wallet.InitChannelReservation(fundingAmount, fundingAmount,
|
||||
testPub, bobAddr, numReqConfs, 4, 540)
|
||||
testPub, bobAddr, numReqConfs, 4, 540, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize funding reservation: %v", err)
|
||||
}
|
||||
@ -606,7 +606,8 @@ func testCancelNonExistantReservation(miner *rpctest.Harness,
|
||||
t.Log("Running cancel reservation tests")
|
||||
|
||||
// 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
|
||||
// nothing of it.
|
||||
@ -630,10 +631,13 @@ func testSingleFunderReservationWorkflowInitiator(miner *rpctest.Harness,
|
||||
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)
|
||||
pushAmt := btcutil.Amount(btcutil.SatoshiPerBitcoin)
|
||||
chanReservation, err := wallet.InitChannelReservation(fundingAmt,
|
||||
fundingAmt, bobNode.id, bobAddr, numReqConfs, 4, 540)
|
||||
fundingAmt, bobNode.id, bobAddr, numReqConfs, 4, 540, pushAmt)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
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
|
||||
@ -779,7 +783,7 @@ func testSingleFunderReservationWorkflowResponder(miner *rpctest.Harness,
|
||||
// contribution and the necessary resources.
|
||||
fundingAmt := btcutil.Amount(0)
|
||||
chanReservation, err := wallet.InitChannelReservation(capacity,
|
||||
fundingAmt, bobNode.id, bobAddr, numReqConfs, 4, 540)
|
||||
fundingAmt, bobNode.id, bobAddr, numReqConfs, 4, 540, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to init channel reservation: %v", err)
|
||||
}
|
||||
|
@ -121,6 +121,11 @@ type ChannelReservation struct {
|
||||
// channel should be considered open.
|
||||
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
|
||||
// confirmation details will be sent on once the channel is considered
|
||||
// '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
|
||||
// used only internally by lnwallet. In order to concurrent safety, the creation
|
||||
// of all channel reservations should be carried out via the
|
||||
// used only internally by lnwallet. In order to concurrent safety, the
|
||||
// creation of all channel reservations should be carried out via the
|
||||
// lnwallet.InitChannelReservation interface.
|
||||
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 (
|
||||
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
|
||||
// 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 {
|
||||
ourBalance = 0
|
||||
theirBalance = capacity - commitFee
|
||||
ourBalance = pushSat
|
||||
theirBalance = capacity - commitFee - pushSat
|
||||
} else {
|
||||
// TODO(roasbeef): need to rework fee structure in general and
|
||||
// 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 we're initiating a single funder workflow, then
|
||||
// we pay all the initial fees within the commitment
|
||||
// transaction.
|
||||
ourBalance = capacity - commitFee
|
||||
// transaction. We also deduct our balance by the
|
||||
// amount pushed as part of the initial state.
|
||||
ourBalance = capacity - commitFee - pushSat
|
||||
} else {
|
||||
// Otherwise, this is a dual funder workflow where both
|
||||
// slides split the amount funded and the commitment
|
||||
@ -163,7 +171,7 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
|
||||
ourBalance = fundingAmt - commitFee
|
||||
}
|
||||
|
||||
theirBalance = capacity - fundingAmt - commitFee
|
||||
theirBalance = capacity - fundingAmt - commitFee + pushSat
|
||||
}
|
||||
|
||||
var (
|
||||
@ -171,14 +179,19 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
|
||||
chanType channeldb.ChannelType
|
||||
)
|
||||
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.
|
||||
case pushSat != 0 && ourBalance-pushSat == 0:
|
||||
fallthrough
|
||||
case ourBalance == 0:
|
||||
initiator = false
|
||||
chanType = channeldb.SingleFunder
|
||||
|
||||
// Or, if their balance is zero, then we're the initiator to a single
|
||||
// funder channel workflow.
|
||||
// If their balance is zero, or being pushed entirely to them, then
|
||||
// we're the initiator to a single funder channel workflow.
|
||||
case pushSat != 0 && theirBalance-pushSat == 0:
|
||||
fallthrough
|
||||
case theirBalance == 0:
|
||||
initiator = true
|
||||
chanType = channeldb.SingleFunder
|
||||
@ -208,6 +221,7 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
|
||||
Db: wallet.ChannelDB,
|
||||
},
|
||||
numConfsToOpen: numConfs,
|
||||
pushSat: pushSat,
|
||||
reservationID: id,
|
||||
chanOpen: make(chan *openChanDetails, 1),
|
||||
wallet: wallet,
|
||||
|
@ -105,6 +105,10 @@ type initFundingReserveMsg struct {
|
||||
// this amount are not enforceable onchain from our point of view.
|
||||
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.
|
||||
csvDelay uint32
|
||||
|
||||
@ -489,7 +493,11 @@ out:
|
||||
func (l *LightningWallet) InitChannelReservation(capacity,
|
||||
ourFundAmt btcutil.Amount, theirID *btcec.PublicKey,
|
||||
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)
|
||||
respChan := make(chan *ChannelReservation, 1)
|
||||
@ -500,6 +508,7 @@ func (l *LightningWallet) InitChannelReservation(capacity,
|
||||
fundingAmount: ourFundAmt,
|
||||
csvDelay: csvDelay,
|
||||
ourDustLimit: ourDustLimit,
|
||||
pushSat: pushSat,
|
||||
nodeID: theirID,
|
||||
nodeAddr: theirAddr,
|
||||
err: errChan,
|
||||
@ -523,7 +532,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
|
||||
id := atomic.AddUint64(&l.nextFundingID, 1)
|
||||
totalCapacity := req.capacity + commitFee
|
||||
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
|
||||
reservation.Lock()
|
||||
@ -778,8 +787,8 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
|
||||
|
||||
// With the funding tx complete, create both commitment transactions.
|
||||
// TODO(roasbeef): much cleanup + de-duplication
|
||||
ourBalance := ourContribution.FundingAmount
|
||||
theirBalance := theirContribution.FundingAmount
|
||||
ourBalance := pendingReservation.partialState.OurBalance
|
||||
theirBalance := pendingReservation.partialState.TheirBalance
|
||||
ourCommitKey := ourContribution.CommitKey
|
||||
ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
|
||||
ourRevokeKey, ourContribution.CsvDelay,
|
||||
@ -1099,8 +1108,8 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
||||
// remote node's commitment transactions.
|
||||
ourCommitKey := pendingReservation.ourContribution.CommitKey
|
||||
theirCommitKey := pendingReservation.theirContribution.CommitKey
|
||||
ourBalance := pendingReservation.ourContribution.FundingAmount
|
||||
theirBalance := pendingReservation.theirContribution.FundingAmount
|
||||
ourBalance := pendingReservation.partialState.OurBalance
|
||||
theirBalance := pendingReservation.partialState.TheirBalance
|
||||
ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
|
||||
pendingReservation.ourContribution.RevocationKey,
|
||||
pendingReservation.ourContribution.CsvDelay, ourBalance, theirBalance)
|
||||
|
Loading…
Reference in New Issue
Block a user