lnd: handles the FundMax field inside parseOpenChannelReq

Adds handling of the `FundMax` field when parsing an
`OpenChannelRequest` with `rpcServer.parseOpenChannelReq`.
This commit is contained in:
Bjarne Magnussen 2021-04-22 20:14:44 +02:00 committed by Slyghtning
parent e82410d24a
commit 0e5b3e77cb
2 changed files with 87 additions and 25 deletions

View file

@ -266,6 +266,15 @@ type InitFundingMsg struct {
// peer.
MaxLocalCsv uint16
// FundUpToMaxAmt is the maximum amount to try to commit to. If set, the
// MinFundAmt field denotes the acceptable minimum amount to commit to,
// while trying to commit as many coins as possible up to this value.
FundUpToMaxAmt btcutil.Amount
// MinFundAmt must be set iff FundUpToMaxAmt is set. It denotes the
// minimum amount to commit to.
MinFundAmt btcutil.Amount
// ChanFunder is an optional channel funder that allows the caller to
// control exactly how the channel funding is carried out. If not
// specified, then the default chanfunding.WalletAssembler will be

View file

@ -1934,6 +1934,62 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
localFundingAmt := btcutil.Amount(in.LocalFundingAmount)
remoteInitialBalance := btcutil.Amount(in.PushSat)
// If we are not committing the maximum viable balance towards a channel
// then the local funding amount must be specified. In case FundMax is
// set the funding amount is specified as the interval between minimum
// funding amount and by the configured maximum channel size.
if !in.FundMax && localFundingAmt == 0 {
return nil, fmt.Errorf("local funding amount must be non-zero")
}
// Ensure that the initial balance of the remote party (if pushing
// satoshis) does not exceed the amount the local party has requested
// for funding. This is only checked if we are not committing the
// maximum viable amount towards the channel balance. If we do commit
// the maximum then the remote balance is checked in a dedicated FundMax
// check.
if !in.FundMax && remoteInitialBalance >= localFundingAmt {
return nil, fmt.Errorf("amount pushed to remote peer for " +
"initial state must be below the local funding amount")
}
// We either allow the fundmax or the psbt flow hence we return an error
// if both are set.
if in.FundingShim != nil && in.FundMax {
return nil, fmt.Errorf("cannot provide a psbt funding shim " +
"while committing the maxium wallet balance towards " +
"the channel opening")
}
// If the FundMax flag is set, ensure that the acceptable minimum local
// amount adheres to the amount to be pushed to the remote, and to
// current rules, while also respecting the settings for the maximum
// channel size.
var minFundAmt, fundUpToMaxAmt btcutil.Amount
if in.FundMax {
// We assume the configured maximum channel size to be the upper
// bound of our "maxed" out funding attempt.
fundUpToMaxAmt = btcutil.Amount(r.cfg.MaxChanSize)
// Since the standard non-fundmax flow requires the minimum
// funding amount to be at least in the amount of the initial
// remote balance(push amount) we need to adjust the minimum
// funding amount accordingly. We initially assume the minimum
// allowed channel size as minimum funding amount.
minFundAmt = funding.MinChanFundingSize
// If minFundAmt is less than the initial remote balance we
// simply assign the initial remote balance to minFundAmt in
// order to fullfil the criterion. Whether or not this so
// determined minimum amount is actually available is
// ascertained downstream in the lnwallet's reservation
// workflow.
if remoteInitialBalance >= minFundAmt {
minFundAmt = remoteInitialBalance
}
}
minHtlcIn := lnwire.MilliSatoshi(in.MinHtlcMsat)
remoteCsvDelay := uint16(in.RemoteCsvDelay)
maxValue := lnwire.MilliSatoshi(in.RemoteMaxValueInFlightMsat)
@ -1942,20 +1998,6 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
globalFeatureSet := r.server.featureMgr.Get(feature.SetNodeAnn)
// Ensure that a local funding amount has been specified.
if localFundingAmt == 0 {
return nil, fmt.Errorf("local funding amount must be non-zero")
}
// Ensure that the initial balance of the remote party (if pushing
// satoshis) does not exceed the amount the local party has requested
// for funding.
//
if remoteInitialBalance >= localFundingAmt {
return nil, fmt.Errorf("amount pushed to remote peer for " +
"initial state must be below the local funding amount")
}
// Determine if the user provided channel fees
// and if so pass them on to the funding workflow.
var channelBaseFee, channelFeeRate *uint64
@ -1968,7 +2010,7 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
// Ensure that the remote channel reserve does not exceed 20% of the
// channel capacity.
if remoteChanReserve >= localFundingAmt/5 {
if !in.FundMax && remoteChanReserve >= localFundingAmt/5 {
return nil, fmt.Errorf("remote channel reserve must be less " +
"than the %%20 of the channel capacity")
}
@ -1976,18 +2018,25 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
// Ensure that the user doesn't exceed the current soft-limit for
// channel size. If the funding amount is above the soft-limit, then
// we'll reject the request.
// If the FundMax flag is set the local amount is determined downstream
// in the wallet hence we do not check it here against the maximum
// funding amount. Only if the localFundingAmt is specified we can check
// if it exceeds the maximum funding amount.
wumboEnabled := globalFeatureSet.HasFeature(
lnwire.WumboChannelsOptional,
)
if !wumboEnabled && localFundingAmt > MaxFundingAmount {
if !in.FundMax && !wumboEnabled && localFundingAmt > MaxFundingAmount {
return nil, fmt.Errorf("funding amount is too large, the max "+
"channel size is: %v", MaxFundingAmount)
}
// Restrict the size of the channel we'll actually open. At a later
// level, we'll ensure that the output we create after accounting for
// fees that a dust output isn't created.
if localFundingAmt < funding.MinChanFundingSize {
// level, we'll ensure that the output we create, after accounting for
// fees, does not leave a dust output. In case of the FundMax flow
// dedicated checks ensure that the lower boundary of the channel size
// is at least in the amount of MinChanFundingSize or potentially higher
// if a remote balance is specified.
if !in.FundMax && localFundingAmt < funding.MinChanFundingSize {
return nil, fmt.Errorf("channel is too small, the minimum "+
"channel size is: %v SAT", int64(funding.MinChanFundingSize))
}
@ -2128,12 +2177,14 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
// open a new channel. A stream is returned in place, this stream will
// be used to consume updates of the state of the pending channel.
return &funding.InitFundingMsg{
TargetPubkey: nodePubKey,
ChainHash: *r.cfg.ActiveNetParams.GenesisHash,
LocalFundingAmt: localFundingAmt,
BaseFee: channelBaseFee,
FeeRate: channelFeeRate,
PushAmt: lnwire.NewMSatFromSatoshis(remoteInitialBalance),
TargetPubkey: nodePubKey,
ChainHash: *r.cfg.ActiveNetParams.GenesisHash,
LocalFundingAmt: localFundingAmt,
BaseFee: channelBaseFee,
FeeRate: channelFeeRate,
PushAmt: lnwire.NewMSatFromSatoshis(
remoteInitialBalance,
),
MinHtlcIn: minHtlcIn,
FundingFeePerKw: feeRate,
Private: in.Private,
@ -2145,6 +2196,8 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
MaxHtlcs: maxHtlcs,
MaxLocalCsv: uint16(in.MaxLocalCsv),
ChannelType: channelType,
FundUpToMaxAmt: fundUpToMaxAmt,
MinFundAmt: minFundAmt,
}, nil
}