-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4488 from cfromknecht/enforce-min-cltv-18
multi: enforce conservative minimum for user-chosen CLTV deltas
- Loading branch information
Showing
15 changed files
with
2,133 additions
and
837 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
package zpay32 | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" | ||
|
||
var gen = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} | ||
|
||
// NOTE: This method it a slight modification of the method bech32.Decode found | ||
// btcutil, allowing strings to be more than 90 characters. | ||
|
||
// decodeBech32 decodes a bech32 encoded string, returning the human-readable | ||
// part and the data part excluding the checksum. | ||
// Note: the data will be base32 encoded, that is each element of the returned | ||
// byte array will encode 5 bits of data. Use the ConvertBits method to convert | ||
// this to 8-bit representation. | ||
func decodeBech32(bech string) (string, []byte, error) { | ||
// The maximum allowed length for a bech32 string is 90. It must also | ||
// be at least 8 characters, since it needs a non-empty HRP, a | ||
// separator, and a 6 character checksum. | ||
// NB: The 90 character check specified in BIP173 is skipped here, to | ||
// allow strings longer than 90 characters. | ||
if len(bech) < 8 { | ||
return "", nil, fmt.Errorf("invalid bech32 string length %d", | ||
len(bech)) | ||
} | ||
// Only ASCII characters between 33 and 126 are allowed. | ||
for i := 0; i < len(bech); i++ { | ||
if bech[i] < 33 || bech[i] > 126 { | ||
return "", nil, fmt.Errorf("invalid character in "+ | ||
"string: '%c'", bech[i]) | ||
} | ||
} | ||
|
||
// The characters must be either all lowercase or all uppercase. | ||
lower := strings.ToLower(bech) | ||
upper := strings.ToUpper(bech) | ||
if bech != lower && bech != upper { | ||
return "", nil, fmt.Errorf("string not all lowercase or all " + | ||
"uppercase") | ||
} | ||
|
||
// We'll work with the lowercase string from now on. | ||
bech = lower | ||
|
||
// The string is invalid if the last '1' is non-existent, it is the | ||
// first character of the string (no human-readable part) or one of the | ||
// last 6 characters of the string (since checksum cannot contain '1'), | ||
// or if the string is more than 90 characters in total. | ||
one := strings.LastIndexByte(bech, '1') | ||
if one < 1 || one+7 > len(bech) { | ||
return "", nil, fmt.Errorf("invalid index of 1") | ||
} | ||
|
||
// The human-readable part is everything before the last '1'. | ||
hrp := bech[:one] | ||
data := bech[one+1:] | ||
|
||
// Each character corresponds to the byte with value of the index in | ||
// 'charset'. | ||
decoded, err := toBytes(data) | ||
if err != nil { | ||
return "", nil, fmt.Errorf("failed converting data to bytes: "+ | ||
"%v", err) | ||
} | ||
|
||
if !bech32VerifyChecksum(hrp, decoded) { | ||
moreInfo := "" | ||
checksum := bech[len(bech)-6:] | ||
expected, err := toChars(bech32Checksum(hrp, | ||
decoded[:len(decoded)-6])) | ||
if err == nil { | ||
moreInfo = fmt.Sprintf("Expected %v, got %v.", | ||
expected, checksum) | ||
} | ||
return "", nil, fmt.Errorf("checksum failed. " + moreInfo) | ||
} | ||
|
||
// We exclude the last 6 bytes, which is the checksum. | ||
return hrp, decoded[:len(decoded)-6], nil | ||
} | ||
|
||
// toBytes converts each character in the string 'chars' to the value of the | ||
// index of the corresponding character in 'charset'. | ||
func toBytes(chars string) ([]byte, error) { | ||
decoded := make([]byte, 0, len(chars)) | ||
for i := 0; i < len(chars); i++ { | ||
index := strings.IndexByte(charset, chars[i]) | ||
if index < 0 { | ||
return nil, fmt.Errorf("invalid character not part of "+ | ||
"charset: %v", chars[i]) | ||
} | ||
decoded = append(decoded, byte(index)) | ||
} | ||
return decoded, nil | ||
} | ||
|
||
// toChars converts the byte slice 'data' to a string where each byte in 'data' | ||
// encodes the index of a character in 'charset'. | ||
func toChars(data []byte) (string, error) { | ||
result := make([]byte, 0, len(data)) | ||
for _, b := range data { | ||
if int(b) >= len(charset) { | ||
return "", fmt.Errorf("invalid data byte: %v", b) | ||
} | ||
result = append(result, charset[b]) | ||
} | ||
return string(result), nil | ||
} | ||
|
||
// For more details on the checksum calculation, please refer to BIP 173. | ||
func bech32Checksum(hrp string, data []byte) []byte { | ||
// Convert the bytes to list of integers, as this is needed for the | ||
// checksum calculation. | ||
integers := make([]int, len(data)) | ||
for i, b := range data { | ||
integers[i] = int(b) | ||
} | ||
values := append(bech32HrpExpand(hrp), integers...) | ||
values = append(values, []int{0, 0, 0, 0, 0, 0}...) | ||
polymod := bech32Polymod(values) ^ 1 | ||
var res []byte | ||
for i := 0; i < 6; i++ { | ||
res = append(res, byte((polymod>>uint(5*(5-i)))&31)) | ||
} | ||
return res | ||
} | ||
|
||
// For more details on the polymod calculation, please refer to BIP 173. | ||
func bech32Polymod(values []int) int { | ||
chk := 1 | ||
for _, v := range values { | ||
b := chk >> 25 | ||
chk = (chk&0x1ffffff)<<5 ^ v | ||
for i := 0; i < 5; i++ { | ||
if (b>>uint(i))&1 == 1 { | ||
chk ^= gen[i] | ||
} | ||
} | ||
} | ||
return chk | ||
} | ||
|
||
// For more details on HRP expansion, please refer to BIP 173. | ||
func bech32HrpExpand(hrp string) []int { | ||
v := make([]int, 0, len(hrp)*2+1) | ||
for i := 0; i < len(hrp); i++ { | ||
v = append(v, int(hrp[i]>>5)) | ||
} | ||
v = append(v, 0) | ||
for i := 0; i < len(hrp); i++ { | ||
v = append(v, int(hrp[i]&31)) | ||
} | ||
return v | ||
} | ||
|
||
// For more details on the checksum verification, please refer to BIP 173. | ||
func bech32VerifyChecksum(hrp string, data []byte) bool { | ||
integers := make([]int, len(data)) | ||
for i, b := range data { | ||
integers[i] = int(b) | ||
} | ||
concat := append(bech32HrpExpand(hrp), integers...) | ||
return bech32Polymod(concat) == 1 | ||
} |
Oops, something went wrong.