mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-07 11:22:10 +02:00
zpay32: move the BOLT-11 compatible invoice format to zpay32 namespace
This commit is contained in:
158
zpay32/amountunits.go
Normal file
158
zpay32/amountunits.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package zpay32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
var (
|
||||
// toMSat is a map from a unit to a function that converts an amount
|
||||
// of that unit to millisatoshis.
|
||||
toMSat = map[byte]func(uint64) (lnwire.MilliSatoshi, error){
|
||||
'm': mBtcToMSat,
|
||||
'u': uBtcToMSat,
|
||||
'n': nBtcToMSat,
|
||||
'p': pBtcToMSat,
|
||||
}
|
||||
|
||||
// fromMSat is a map from a unit to a function that converts an amount
|
||||
// in millisatoshis to an amount of that unit.
|
||||
fromMSat = map[byte]func(lnwire.MilliSatoshi) (uint64, error){
|
||||
'm': mSatToMBtc,
|
||||
'u': mSatToUBtc,
|
||||
'n': mSatToNBtc,
|
||||
'p': mSatToPBtc,
|
||||
}
|
||||
)
|
||||
|
||||
// mBtcToMSat converts the given amount in milliBTC to millisatoshis.
|
||||
func mBtcToMSat(m uint64) (lnwire.MilliSatoshi, error) {
|
||||
return lnwire.MilliSatoshi(m) * 100000000, nil
|
||||
}
|
||||
|
||||
// uBtcToMSat converts the given amount in microBTC to millisatoshis.
|
||||
func uBtcToMSat(u uint64) (lnwire.MilliSatoshi, error) {
|
||||
return lnwire.MilliSatoshi(u * 100000), nil
|
||||
}
|
||||
|
||||
// nBtcToMSat converts the given amount in nanoBTC to millisatoshis.
|
||||
func nBtcToMSat(n uint64) (lnwire.MilliSatoshi, error) {
|
||||
return lnwire.MilliSatoshi(n * 100), nil
|
||||
}
|
||||
|
||||
// pBtcToMSat converts the given amount in picoBTC to millisatoshis.
|
||||
func pBtcToMSat(p uint64) (lnwire.MilliSatoshi, error) {
|
||||
if p < 10 {
|
||||
return 0, fmt.Errorf("minimum amount is 10p")
|
||||
}
|
||||
if p%10 != 0 {
|
||||
return 0, fmt.Errorf("amount %d pBTC not expressible in msat",
|
||||
p)
|
||||
}
|
||||
return lnwire.MilliSatoshi(p / 10), nil
|
||||
}
|
||||
|
||||
// mSatToMBtc converts the given amount in millisatoshis to milliBTC.
|
||||
func mSatToMBtc(msat lnwire.MilliSatoshi) (uint64, error) {
|
||||
if msat%100000000 != 0 {
|
||||
return 0, fmt.Errorf("%d msat not expressible "+
|
||||
"in mBTC", msat)
|
||||
}
|
||||
return uint64(msat / 100000000), nil
|
||||
}
|
||||
|
||||
// mSatToUBtc converts the given amount in millisatoshis to microBTC.
|
||||
func mSatToUBtc(msat lnwire.MilliSatoshi) (uint64, error) {
|
||||
if msat%100000 != 0 {
|
||||
return 0, fmt.Errorf("%d msat not expressible "+
|
||||
"in uBTC", msat)
|
||||
}
|
||||
return uint64(msat / 100000), nil
|
||||
}
|
||||
|
||||
// mSatToNBtc converts the given amount in millisatoshis to nanoBTC.
|
||||
func mSatToNBtc(msat lnwire.MilliSatoshi) (uint64, error) {
|
||||
if msat%100 != 0 {
|
||||
return 0, fmt.Errorf("%d msat not expressible in nBTC", msat)
|
||||
}
|
||||
return uint64(msat / 100), nil
|
||||
}
|
||||
|
||||
// mSatToPBtc converts the given amount in milllisatoshis to picoBTC.
|
||||
func mSatToPBtc(msat lnwire.MilliSatoshi) (uint64, error) {
|
||||
return uint64(msat * 10), nil
|
||||
}
|
||||
|
||||
// decodeAmount returns the amount encoded by the provided string in
|
||||
// milllisatoshi.
|
||||
func decodeAmount(amount string) (lnwire.MilliSatoshi, error) {
|
||||
if len(amount) < 1 {
|
||||
return 0, fmt.Errorf("amount must be non-empty")
|
||||
}
|
||||
|
||||
// If last character is a digit, then the amount can just be
|
||||
// interpreted as BTC.
|
||||
char := amount[len(amount)-1]
|
||||
digit := char - '0'
|
||||
if digit >= 0 && digit <= 9 {
|
||||
btc, err := strconv.ParseUint(amount, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return lnwire.MilliSatoshi(btc) * mSatPerBtc, nil
|
||||
}
|
||||
|
||||
// If not a digit, it must be part of the known units.
|
||||
conv, ok := toMSat[char]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("unknown multiplier %c", char)
|
||||
}
|
||||
|
||||
// Known unit.
|
||||
num := amount[:len(amount)-1]
|
||||
if len(num) < 1 {
|
||||
return 0, fmt.Errorf("number must be non-empty")
|
||||
}
|
||||
|
||||
am, err := strconv.ParseUint(num, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return conv(am)
|
||||
}
|
||||
|
||||
// encodeAmount encodes the provided millisatoshi amount using as few characters
|
||||
// as possible.
|
||||
func encodeAmount(msat lnwire.MilliSatoshi) (string, error) {
|
||||
// If possible to express in BTC, that will always be the shortest
|
||||
// representation.
|
||||
if msat%mSatPerBtc == 0 {
|
||||
return strconv.FormatInt(int64(msat/mSatPerBtc), 10), nil
|
||||
}
|
||||
|
||||
// Should always be expressible in pico BTC.
|
||||
pico, err := fromMSat['p'](msat)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to express %d msat as pBTC: %v",
|
||||
msat, err)
|
||||
}
|
||||
shortened := strconv.FormatUint(pico, 10) + "p"
|
||||
for unit, conv := range fromMSat {
|
||||
am, err := conv(msat)
|
||||
if err != nil {
|
||||
// Not expressible using this unit.
|
||||
continue
|
||||
}
|
||||
|
||||
// Save the shortest found representation.
|
||||
str := strconv.FormatUint(am, 10) + string(unit)
|
||||
if len(str) < len(shortened) {
|
||||
shortened = str
|
||||
}
|
||||
}
|
||||
|
||||
return shortened, nil
|
||||
}
|
Reference in New Issue
Block a user