lnd.xprv/channeldb/migration_01_to_11/zpay32/amountunits.go
Conner Fromknecht 213c51a281
channeldb/migration_01_to_11: isolate zpay32 decoding
This commit copies over the relevant zpay32 decoding logic to ensure
that our prior migrations aren't affected by upcoming changes to the
zpay32 package, most notably changes to the default final_cltv_expiry
and expiry values.
2020-07-24 13:13:47 -07:00

159 lines
4.1 KiB
Go

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 millisatoshis to picoBTC.
func mSatToPBtc(msat lnwire.MilliSatoshi) (uint64, error) {
return uint64(msat * 10), nil
}
// decodeAmount returns the amount encoded by the provided string in
// millisatoshi.
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
}