FEAT: use versioning for frontend (npm) and copy it to lnbits/static/vendor for easier updating (#1590)
* remove static/vendor * add node dependencies * add bolt11-decoder * run npm install inside dockerimage * only use bundle.js and bundle.css * use node_modules for bundling vendor assets * remove dead code * make argument optional * reintroduce vendor dir * reintroduce vendor and single javascript files, minification * wrong moment, remove minification * lock packages with non critical issues * black
This commit is contained in:
+20776
File diff suppressed because it is too large
Load Diff
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* DOM element rendering detection
|
||||
* https://davidwalsh.name/detect-node-insertion
|
||||
*/
|
||||
@keyframes chartjs-render-animation {
|
||||
from { opacity: 0.99; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.chartjs-render-monitor {
|
||||
animation: chartjs-render-animation 0.001s;
|
||||
}
|
||||
|
||||
/*
|
||||
* DOM element resizing detection
|
||||
* https://github.com/marcj/css-element-queries
|
||||
*/
|
||||
.chartjs-size-monitor,
|
||||
.chartjs-size-monitor-expand,
|
||||
.chartjs-size-monitor-shrink {
|
||||
position: absolute;
|
||||
direction: ltr;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.chartjs-size-monitor-expand > div {
|
||||
position: absolute;
|
||||
width: 1000000px;
|
||||
height: 1000000px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.chartjs-size-monitor-shrink > div {
|
||||
position: absolute;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
Vendored
+2964
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
-347
@@ -1,347 +0,0 @@
|
||||
//TODO - A reader MUST check that the signature is valid (see the n tagged field)
|
||||
//TODO - Tagged part of type f: the fallback on-chain address should be decoded into an address format
|
||||
//TODO - A reader MUST check that the SHA-2 256 in the h field exactly matches the hashed description.
|
||||
//TODO - A reader MUST use the n field to validate the signature instead of performing signature recovery if a valid n field is provided.
|
||||
|
||||
function decode(paymentRequest) {
|
||||
let input = paymentRequest.toLowerCase()
|
||||
let splitPosition = input.lastIndexOf('1')
|
||||
let humanReadablePart = input.substring(0, splitPosition)
|
||||
let data = input.substring(splitPosition + 1, input.length - 6)
|
||||
let checksum = input.substring(input.length - 6, input.length)
|
||||
if (
|
||||
!verify_checksum(humanReadablePart, bech32ToFiveBitArray(data + checksum))
|
||||
) {
|
||||
throw 'Malformed request: checksum is incorrect' // A reader MUST fail if the checksum is incorrect.
|
||||
}
|
||||
return {
|
||||
human_readable_part: decodeHumanReadablePart(humanReadablePart),
|
||||
data: decodeData(data, humanReadablePart),
|
||||
checksum: checksum
|
||||
}
|
||||
}
|
||||
|
||||
function decodeHumanReadablePart(humanReadablePart) {
|
||||
let prefixes = ['lnbc', 'lntb', 'lnbcrt', 'lnsb']
|
||||
let prefix
|
||||
prefixes.forEach(value => {
|
||||
if (humanReadablePart.substring(0, value.length) === value) {
|
||||
prefix = value
|
||||
}
|
||||
})
|
||||
if (prefix == null) throw 'Malformed request: unknown prefix' // A reader MUST fail if it does not understand the prefix.
|
||||
let amount = decodeAmount(
|
||||
humanReadablePart.substring(prefix.length, humanReadablePart.length)
|
||||
)
|
||||
return {
|
||||
prefix: prefix,
|
||||
amount: amount
|
||||
}
|
||||
}
|
||||
|
||||
function decodeData(data, humanReadablePart) {
|
||||
let date32 = data.substring(0, 7)
|
||||
let dateEpoch = bech32ToInt(date32)
|
||||
let signature = data.substring(data.length - 104, data.length)
|
||||
let tagData = data.substring(7, data.length - 104)
|
||||
let decodedTags = decodeTags(tagData)
|
||||
let value = bech32ToFiveBitArray(date32 + tagData)
|
||||
value = fiveBitArrayTo8BitArray(value, true)
|
||||
value = textToHexString(humanReadablePart).concat(byteArrayToHexString(value))
|
||||
return {
|
||||
time_stamp: dateEpoch,
|
||||
tags: decodedTags,
|
||||
signature: decodeSignature(signature),
|
||||
signing_data: value
|
||||
}
|
||||
}
|
||||
|
||||
function decodeSignature(signature) {
|
||||
let data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(signature))
|
||||
let recoveryFlag = data[data.length - 1]
|
||||
let r = byteArrayToHexString(data.slice(0, 32))
|
||||
let s = byteArrayToHexString(data.slice(32, data.length - 1))
|
||||
return {
|
||||
r: r,
|
||||
s: s,
|
||||
recovery_flag: recoveryFlag
|
||||
}
|
||||
}
|
||||
|
||||
function decodeAmount(str) {
|
||||
let multiplier = str.charAt(str.length - 1)
|
||||
let amount = str.substring(0, str.length - 1)
|
||||
if (amount.substring(0, 1) === '0') {
|
||||
throw 'Malformed request: amount cannot contain leading zeros'
|
||||
}
|
||||
amount = Number(amount)
|
||||
if (amount < 0 || !Number.isInteger(amount)) {
|
||||
throw 'Malformed request: amount must be a positive decimal integer' // A reader SHOULD fail if amount contains a non-digit
|
||||
}
|
||||
|
||||
switch (multiplier) {
|
||||
case '':
|
||||
return 'Any amount' // A reader SHOULD indicate if amount is unspecified
|
||||
case 'p':
|
||||
return amount / 10
|
||||
case 'n':
|
||||
return amount * 100
|
||||
case 'u':
|
||||
return amount * 100000
|
||||
case 'm':
|
||||
return amount * 100000000
|
||||
default:
|
||||
// A reader SHOULD fail if amount is followed by anything except a defined multiplier.
|
||||
throw 'Malformed request: undefined amount multiplier'
|
||||
}
|
||||
}
|
||||
|
||||
function decodeTags(tagData) {
|
||||
let tags = extractTags(tagData)
|
||||
let decodedTags = []
|
||||
tags.forEach(value =>
|
||||
decodedTags.push(decodeTag(value.type, value.length, value.data))
|
||||
)
|
||||
return decodedTags
|
||||
}
|
||||
|
||||
function extractTags(str) {
|
||||
let tags = []
|
||||
while (str.length > 0) {
|
||||
let type = str.charAt(0)
|
||||
let dataLength = bech32ToInt(str.substring(1, 3))
|
||||
let data = str.substring(3, dataLength + 3)
|
||||
tags.push({
|
||||
type: type,
|
||||
length: dataLength,
|
||||
data: data
|
||||
})
|
||||
str = str.substring(3 + dataLength, str.length)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
function decodeTag(type, length, data) {
|
||||
switch (type) {
|
||||
case 'p':
|
||||
if (length !== 52) break // A reader MUST skip over a 'p' field that does not have data_length 52
|
||||
return {
|
||||
type: type,
|
||||
length: length,
|
||||
description: 'payment_hash',
|
||||
value: byteArrayToHexString(
|
||||
fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))
|
||||
)
|
||||
}
|
||||
case 'd':
|
||||
return {
|
||||
type: type,
|
||||
length: length,
|
||||
description: 'description',
|
||||
value: bech32ToUTF8String(data)
|
||||
}
|
||||
case 'n':
|
||||
if (length !== 53) break // A reader MUST skip over a 'n' field that does not have data_length 53
|
||||
return {
|
||||
type: type,
|
||||
length: length,
|
||||
description: 'payee_public_key',
|
||||
value: byteArrayToHexString(
|
||||
fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))
|
||||
)
|
||||
}
|
||||
case 'h':
|
||||
if (length !== 52) break // A reader MUST skip over a 'h' field that does not have data_length 52
|
||||
return {
|
||||
type: type,
|
||||
length: length,
|
||||
description: 'description_hash',
|
||||
value: data
|
||||
}
|
||||
case 'x':
|
||||
return {
|
||||
type: type,
|
||||
length: length,
|
||||
description: 'expiry',
|
||||
value: bech32ToInt(data)
|
||||
}
|
||||
case 'c':
|
||||
return {
|
||||
type: type,
|
||||
length: length,
|
||||
description: 'min_final_cltv_expiry',
|
||||
value: bech32ToInt(data)
|
||||
}
|
||||
case 'f':
|
||||
let version = bech32ToFiveBitArray(data.charAt(0))[0]
|
||||
if (version < 0 || version > 18) break // a reader MUST skip over an f field with unknown version.
|
||||
data = data.substring(1, data.length)
|
||||
return {
|
||||
type: type,
|
||||
length: length,
|
||||
description: 'fallback_address',
|
||||
value: {
|
||||
version: version,
|
||||
fallback_address: data
|
||||
}
|
||||
}
|
||||
case 'r':
|
||||
data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))
|
||||
let pubkey = data.slice(0, 33)
|
||||
let shortChannelId = data.slice(33, 41)
|
||||
let feeBaseMsat = data.slice(41, 45)
|
||||
let feeProportionalMillionths = data.slice(45, 49)
|
||||
let cltvExpiryDelta = data.slice(49, 51)
|
||||
return {
|
||||
type: type,
|
||||
length: length,
|
||||
description: 'routing_information',
|
||||
value: {
|
||||
public_key: byteArrayToHexString(pubkey),
|
||||
short_channel_id: byteArrayToHexString(shortChannelId),
|
||||
fee_base_msat: byteArrayToInt(feeBaseMsat),
|
||||
fee_proportional_millionths: byteArrayToInt(
|
||||
feeProportionalMillionths
|
||||
),
|
||||
cltv_expiry_delta: byteArrayToInt(cltvExpiryDelta)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// reader MUST skip over unknown fields
|
||||
}
|
||||
}
|
||||
|
||||
function polymod(values) {
|
||||
let GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
|
||||
let chk = 1
|
||||
values.forEach(value => {
|
||||
let b = chk >> 25
|
||||
chk = ((chk & 0x1ffffff) << 5) ^ value
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (((b >> i) & 1) === 1) {
|
||||
chk ^= GEN[i]
|
||||
} else {
|
||||
chk ^= 0
|
||||
}
|
||||
}
|
||||
})
|
||||
return chk
|
||||
}
|
||||
|
||||
function expand(str) {
|
||||
let array = []
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
array.push(str.charCodeAt(i) >> 5)
|
||||
}
|
||||
array.push(0)
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
array.push(str.charCodeAt(i) & 31)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
function verify_checksum(hrp, data) {
|
||||
hrp = expand(hrp)
|
||||
let all = hrp.concat(data)
|
||||
let bool = polymod(all)
|
||||
return bool === 1
|
||||
}
|
||||
|
||||
const bech32CharValues = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
|
||||
|
||||
function byteArrayToInt(byteArray) {
|
||||
let value = 0
|
||||
for (let i = 0; i < byteArray.length; ++i) {
|
||||
value = (value << 8) + byteArray[i]
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function bech32ToInt(str) {
|
||||
let sum = 0
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
sum = sum * 32
|
||||
sum = sum + bech32CharValues.indexOf(str.charAt(i))
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
function bech32ToFiveBitArray(str) {
|
||||
let array = []
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
array.push(bech32CharValues.indexOf(str.charAt(i)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
function fiveBitArrayTo8BitArray(int5Array, includeOverflow) {
|
||||
let count = 0
|
||||
let buffer = 0
|
||||
let byteArray = []
|
||||
int5Array.forEach(value => {
|
||||
buffer = (buffer << 5) + value
|
||||
count += 5
|
||||
if (count >= 8) {
|
||||
byteArray.push((buffer >> (count - 8)) & 255)
|
||||
count -= 8
|
||||
}
|
||||
})
|
||||
if (includeOverflow && count > 0) {
|
||||
byteArray.push((buffer << (8 - count)) & 255)
|
||||
}
|
||||
return byteArray
|
||||
}
|
||||
|
||||
function bech32ToUTF8String(str) {
|
||||
let int5Array = bech32ToFiveBitArray(str)
|
||||
let byteArray = fiveBitArrayTo8BitArray(int5Array)
|
||||
|
||||
let utf8String = ''
|
||||
for (let i = 0; i < byteArray.length; i++) {
|
||||
utf8String += '%' + ('0' + byteArray[i].toString(16)).slice(-2)
|
||||
}
|
||||
return decodeURIComponent(utf8String)
|
||||
}
|
||||
|
||||
function byteArrayToHexString(byteArray) {
|
||||
return Array.prototype.map
|
||||
.call(byteArray, function (byte) {
|
||||
return ('0' + (byte & 0xff).toString(16)).slice(-2)
|
||||
})
|
||||
.join('')
|
||||
}
|
||||
|
||||
function textToHexString(text) {
|
||||
let hexString = ''
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
hexString += text.charCodeAt(i).toString(16)
|
||||
}
|
||||
return hexString
|
||||
}
|
||||
|
||||
function epochToDate(int) {
|
||||
let date = new Date(int * 1000)
|
||||
return date.toUTCString()
|
||||
}
|
||||
|
||||
function isEmptyOrSpaces(str) {
|
||||
return str === null || str.match(/^ *$/) !== null
|
||||
}
|
||||
|
||||
function toFixed(x) {
|
||||
if (Math.abs(x) < 1.0) {
|
||||
var e = parseInt(x.toString().split('e-')[1])
|
||||
if (e) {
|
||||
x *= Math.pow(10, e - 1)
|
||||
x = '0.' + new Array(e).join('0') + x.toString().substring(2)
|
||||
}
|
||||
} else {
|
||||
var e = parseInt(x.toString().split('+')[1])
|
||||
if (e > 20) {
|
||||
e -= 20
|
||||
x /= Math.pow(10, e)
|
||||
x += new Array(e + 1).join('0')
|
||||
}
|
||||
}
|
||||
return x
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}
|
||||
File diff suppressed because one or more lines are too long
Vendored
+5685
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Vendored
+11680
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+2042
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Vendored
+5509
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-6
File diff suppressed because one or more lines are too long
-6
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user