@@ -4,9 +4,26 @@ import (
44 "math"
55
66 "github.com/btcsuite/btcd/btcutil"
7+ "github.com/lightningnetwork/lnd/input"
8+ "github.com/lightningnetwork/lnd/lnwallet"
79 "github.com/lightningnetwork/lnd/lnwire"
810)
911
12+ var (
13+ // DefaultOnChainHtlcSat is the default amount that we consider as the
14+ // smallest HTLC amount that can be sent on-chain. This needs to be
15+ // greater than the dust limit for an HTLC.
16+ DefaultOnChainHtlcSat = lnwallet .DustLimitForSize (
17+ input .UnknownWitnessSize ,
18+ )
19+
20+ // DefaultOnChainHtlcMSat is the default amount that we consider as the
21+ // smallest HTLC amount that can be sent on-chain in milli-satoshis.
22+ DefaultOnChainHtlcMSat = lnwire .NewMSatFromSatoshis (
23+ DefaultOnChainHtlcSat ,
24+ )
25+ )
26+
1027// defaultArithmeticScale is the default scale used for arithmetic operations.
1128// This is used to ensure that we don't lose precision when doing arithmetic
1229// operations.
@@ -97,3 +114,61 @@ func UnitsToMilliSatoshi[N Int[N]](assetUnits,
97114 // along the way.
98115 return lnwire .MilliSatoshi (amtMsat .ScaleTo (0 ).ToUint64 ())
99116}
117+
118+ // MinTransportableUnits computes the minimum number of transportable units
119+ // of an asset given its asset rate and the constant HTLC dust limit. This
120+ // function can be used to enforce a minimum invoice amount to prevent
121+ // forwarding failures due to invalid fees.
122+ //
123+ // Given a wallet end user A, an edge node B, an asset rate of 100 milli-
124+ // satoshi per asset unit and a flat 0.1% routing fee (to simplify the
125+ // scenario), the following invoice based receive events can occur:
126+ // 1. Success case: User A creates an invoice over 5,000 units (500,000 milli-
127+ // satoshis) that is paid by the network. An HTLC over 500,500 milli-
128+ // satoshis arrives at B. B converts the HTLC to 5,000 units and sends
129+ // 354,000 milli-satoshis to A.
130+ // A receives a total "worth" of 854,000 milli-satoshis, which is already
131+ // more than the invoice amount. But at least the forwarding rule in `lnd`
132+ // for B is not violated (outgoing amount mSat < incoming amount mSat).
133+ // 2. Failure case: User A creates an invoice over 3,530 units (353,000 milli-
134+ // satoshis) that is paid by the network. An HTLC over 353,530 milli-
135+ // satoshis arrives at B. B converts the HTLC to 3,530 units and sends
136+ // 354,000 milli-satoshis to A.
137+ // This fails in the `lnd` forwarding logic, because the outgoing amount
138+ // (354,000 milli-satoshis) is greater than the incoming amount (353,530
139+ // milli-satoshis).
140+ func MinTransportableUnits (dustLimit lnwire.MilliSatoshi ,
141+ rate BigIntFixedPoint ) BigIntFixedPoint {
142+
143+ // We can only transport an asset unit equivalent amount that's greater
144+ // than the dust limit for an HTLC, since we'll always want an HTLC that
145+ // carries an HTLC to be reflected in an on-chain output.
146+ units := MilliSatoshiToUnits (dustLimit , rate )
147+
148+ // If the asset's rate is such that a single unit represents more than
149+ // the dust limit in satoshi, then the above calculation will come out
150+ // as 0. But we can't transport zero units, so we'll set the minimum to
151+ // one unit.
152+ if units .ScaleTo (0 ).ToUint64 () == 0 {
153+ units = NewBigIntFixedPoint (1 , 0 )
154+ }
155+
156+ return units
157+ }
158+
159+ // MinTransportableMSat computes the minimum amount of milli-satoshis that can
160+ // be represented in a Lightning Network payment when transferring an asset,
161+ // given the asset rate and the constant HTLC dust limit. This function can be
162+ // used to enforce a minimum payable amount with assets, as any invoice amount
163+ // below this value would be uneconomical as the total amount sent would exceed
164+ // the total invoice amount.
165+ func MinTransportableMSat (dustLimit lnwire.MilliSatoshi ,
166+ rate BigIntFixedPoint ) lnwire.MilliSatoshi {
167+
168+ // We can only transport at least one asset unit in an HTLC. And we
169+ // always have to send out an HTLC with a BTC amount of 354 satoshi. So
170+ // the minimum amount of milli-satoshi we can transport is 354,000 plus
171+ // the milli-satoshi equivalent of a single asset unit.
172+ oneAssetUnit := NewBigIntFixedPoint (1 , 0 )
173+ return dustLimit + UnitsToMilliSatoshi (oneAssetUnit , rate )
174+ }
0 commit comments