mirror of
https://github.com/lnbits/lnbits.git
synced 2025-06-05 12:41:21 +02:00
poor man's flask-assets.
This commit is contained in:
parent
3a0762ff82
commit
6928f431a7
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,3 +30,4 @@ venv
|
|||||||
__bundle__
|
__bundle__
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
|
lnbits/static/bundle.*
|
||||||
|
5
Makefile
5
Makefile
@ -1,4 +1,4 @@
|
|||||||
all: format check lnbits/static/css/base.css requirements.txt
|
all: format check requirements.txt
|
||||||
|
|
||||||
format: prettier black
|
format: prettier black
|
||||||
|
|
||||||
@ -19,8 +19,5 @@ checkprettier: $(shell find lnbits -name "*.js" -name ".html")
|
|||||||
checkblack: $(shell find lnbits -name "*.py")
|
checkblack: $(shell find lnbits -name "*.py")
|
||||||
./venv/bin/black --check lnbits
|
./venv/bin/black --check lnbits
|
||||||
|
|
||||||
lnbits/static/css/base.css: lnbits/static/scss/base.scss
|
|
||||||
./venv/bin/pyscss -o lnbits/static/css/base.css lnbits/static/scss/base.scss
|
|
||||||
|
|
||||||
requirements.txt: Pipfile.lock
|
requirements.txt: Pipfile.lock
|
||||||
cat Pipfile.lock | jq -r '.default | map_values(.version) | to_entries | map("\(.key)\(.value)") | join("\n")' > requirements.txt
|
cat Pipfile.lock | jq -r '.default | map_values(.version) | to_entries | map("\(.key)\(.value)") | join("\n")' > requirements.txt
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from .app import create_app
|
from .app import create_app
|
||||||
from .commands import migrate_databases
|
from .commands import migrate_databases, transpile_scss, bundle_vendored
|
||||||
|
|
||||||
|
|
||||||
migrate_databases()
|
migrate_databases()
|
||||||
|
transpile_scss()
|
||||||
|
bundle_vendored()
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
app.run(host=app.config["HOST"], port=app.config["PORT"])
|
app.run(host=app.config["HOST"], port=app.config["PORT"])
|
||||||
|
@ -8,7 +8,7 @@ from secure import SecureHeaders # type: ignore
|
|||||||
from .commands import db_migrate
|
from .commands import db_migrate
|
||||||
from .core import core_app
|
from .core import core_app
|
||||||
from .db import open_db
|
from .db import open_db
|
||||||
from .helpers import get_valid_extensions
|
from .helpers import get_valid_extensions, get_js_vendored, get_css_vendored, url_for_vendored
|
||||||
from .proxy_fix import ProxyFix
|
from .proxy_fix import ProxyFix
|
||||||
|
|
||||||
secure_headers = SecureHeaders(hsts=False)
|
secure_headers = SecureHeaders(hsts=False)
|
||||||
@ -25,6 +25,7 @@ def create_app(config_object="lnbits.settings") -> Quart:
|
|||||||
Compress(app)
|
Compress(app)
|
||||||
ProxyFix(app)
|
ProxyFix(app)
|
||||||
|
|
||||||
|
register_assets(app)
|
||||||
register_blueprints(app)
|
register_blueprints(app)
|
||||||
register_filters(app)
|
register_filters(app)
|
||||||
register_commands(app)
|
register_commands(app)
|
||||||
@ -50,10 +51,23 @@ def register_commands(app):
|
|||||||
app.cli.add_command(db_migrate)
|
app.cli.add_command(db_migrate)
|
||||||
|
|
||||||
|
|
||||||
|
def register_assets(app):
|
||||||
|
"""Serve each vendored asset separately or a bundle."""
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
async def vendored_assets_variable():
|
||||||
|
if app.config["DEBUG"]:
|
||||||
|
g.VENDORED_JS = map(url_for_vendored, get_js_vendored())
|
||||||
|
g.VENDORED_CSS = map(url_for_vendored, get_css_vendored())
|
||||||
|
else:
|
||||||
|
g.VENDORED_JS = ["/static/bundle.js"]
|
||||||
|
g.VENDORED_CSS = ["/static/bundle.css"]
|
||||||
|
|
||||||
|
|
||||||
def register_filters(app):
|
def register_filters(app):
|
||||||
"""Jinja filters."""
|
"""Jinja filters."""
|
||||||
app.jinja_env.globals["EXTENSIONS"] = get_valid_extensions()
|
|
||||||
app.jinja_env.globals["SITE_TITLE"] = app.config["LNBITS_SITE_TITLE"]
|
app.jinja_env.globals["SITE_TITLE"] = app.config["LNBITS_SITE_TITLE"]
|
||||||
|
app.jinja_env.globals["EXTENSIONS"] = get_valid_extensions()
|
||||||
|
|
||||||
|
|
||||||
def register_request_hooks(app):
|
def register_request_hooks(app):
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import click
|
import click
|
||||||
import importlib
|
import importlib
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
from scss.compiler import compile_string
|
||||||
|
|
||||||
from .core import migrations as core_migrations
|
from .core import migrations as core_migrations
|
||||||
from .db import open_db, open_ext_db
|
from .db import open_db, open_ext_db
|
||||||
from .helpers import get_valid_extensions
|
from .helpers import get_valid_extensions, get_css_vendored, get_js_vendored, url_for_vendored
|
||||||
|
from .settings import LNBITS_PATH
|
||||||
|
|
||||||
|
|
||||||
@click.command("migrate")
|
@click.command("migrate")
|
||||||
@ -13,6 +17,31 @@ def db_migrate():
|
|||||||
migrate_databases()
|
migrate_databases()
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("assets")
|
||||||
|
def handle_assets():
|
||||||
|
transpile_scss()
|
||||||
|
bundle_vendored()
|
||||||
|
|
||||||
|
|
||||||
|
def transpile_scss():
|
||||||
|
with open(os.path.join(LNBITS_PATH, "static/scss/base.scss")) as scss:
|
||||||
|
with open(os.path.join(LNBITS_PATH, "static/css/base.css"), "w") as css:
|
||||||
|
css.write(compile_string(scss.read()))
|
||||||
|
|
||||||
|
|
||||||
|
def bundle_vendored():
|
||||||
|
for getfiles, outputpath in [
|
||||||
|
(get_js_vendored, os.path.join(LNBITS_PATH, "static/bundle.js")),
|
||||||
|
(get_css_vendored, os.path.join(LNBITS_PATH, "static/bundle.css")),
|
||||||
|
]:
|
||||||
|
output = ""
|
||||||
|
for path in getfiles():
|
||||||
|
with open(path) as f:
|
||||||
|
output += "/* " + url_for_vendored(path) + " */\n" + f.read() + ";\n"
|
||||||
|
with open(outputpath, "w") as f:
|
||||||
|
f.write(output)
|
||||||
|
|
||||||
|
|
||||||
def migrate_databases():
|
def migrate_databases():
|
||||||
"""Creates the necessary databases if they don't exist already; or migrates them."""
|
"""Creates the necessary databases if they don't exist already; or migrates them."""
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import glob
|
||||||
import shortuuid # type: ignore
|
import shortuuid # type: ignore
|
||||||
|
|
||||||
from typing import List, NamedTuple, Optional
|
from typing import List, NamedTuple, Optional
|
||||||
@ -54,3 +55,54 @@ def get_valid_extensions() -> List[Extension]:
|
|||||||
|
|
||||||
def urlsafe_short_hash() -> str:
|
def urlsafe_short_hash() -> str:
|
||||||
return shortuuid.uuid()
|
return shortuuid.uuid()
|
||||||
|
|
||||||
|
|
||||||
|
def get_js_vendored(prefer_minified: bool = False) -> List[str]:
|
||||||
|
paths = get_vendored(".js", prefer_minified)
|
||||||
|
|
||||||
|
def sorter(key: str):
|
||||||
|
if "moment@" in key:
|
||||||
|
return 1
|
||||||
|
if "vue@" in key:
|
||||||
|
return 2
|
||||||
|
if "vue-router@" in key:
|
||||||
|
return 3
|
||||||
|
if "polyfills" in key:
|
||||||
|
return 4
|
||||||
|
return 9
|
||||||
|
|
||||||
|
return sorted(paths, key=sorter)
|
||||||
|
|
||||||
|
|
||||||
|
def get_css_vendored(prefer_minified: bool = False) -> List[str]:
|
||||||
|
return get_vendored(".css", prefer_minified)
|
||||||
|
|
||||||
|
|
||||||
|
def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]:
|
||||||
|
paths: List[str] = []
|
||||||
|
for path in glob.glob(os.path.join(LNBITS_PATH, "static/vendor/**"), recursive=True):
|
||||||
|
if path.endswith(".min" + ext):
|
||||||
|
# path is minified
|
||||||
|
unminified = path.replace(".min" + ext, ext)
|
||||||
|
if prefer_minified:
|
||||||
|
paths.append(path)
|
||||||
|
if unminified in paths:
|
||||||
|
paths.remove(unminified)
|
||||||
|
elif unminified not in paths:
|
||||||
|
paths.append(path)
|
||||||
|
|
||||||
|
elif path.endswith(ext):
|
||||||
|
# path is not minified
|
||||||
|
minified = path.replace(ext, ".min" + ext)
|
||||||
|
if not prefer_minified:
|
||||||
|
paths.append(path)
|
||||||
|
if minified in paths:
|
||||||
|
paths.remove(minified)
|
||||||
|
elif minified not in paths:
|
||||||
|
paths.append(path)
|
||||||
|
|
||||||
|
return paths
|
||||||
|
|
||||||
|
|
||||||
|
def url_for_vendored(abspath: str) -> str:
|
||||||
|
return "/" + os.path.relpath(abspath, LNBITS_PATH)
|
||||||
|
@ -12,7 +12,7 @@ wallets_module = importlib.import_module("lnbits.wallets")
|
|||||||
wallet_class = getattr(wallets_module, env.str("LNBITS_BACKEND_WALLET_CLASS", default="VoidWallet"))
|
wallet_class = getattr(wallets_module, env.str("LNBITS_BACKEND_WALLET_CLASS", default="VoidWallet"))
|
||||||
|
|
||||||
ENV = env.str("QUART_ENV", default="production")
|
ENV = env.str("QUART_ENV", default="production")
|
||||||
DEBUG = env.bool("QUART_DEBUG") or ENV == "development"
|
DEBUG = env.bool("QUART_DEBUG", default=False) or ENV == "development"
|
||||||
HOST = env.str("HOST", default="127.0.0.1")
|
HOST = env.str("HOST", default="127.0.0.1")
|
||||||
PORT = env.int("PORT", default=5000)
|
PORT = env.int("PORT", default=5000)
|
||||||
|
|
||||||
|
1
lnbits/static/css/.gitignore
vendored
Normal file
1
lnbits/static/css/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
base.css
|
@ -1 +0,0 @@
|
|||||||
[v-cloak]{display:none}.bg-lnbits-dark{background-color:#1f2234}body.body--dark,body.body--dark .q-drawer--dark,body.body--dark .q-menu--dark{background:#1f2234}body.body--dark .q-card--dark{background:#333646}body.body--dark .q-table--dark{background:transparent}body.body--light,body.body--light .q-drawer{background:#f5f5f5}body.body--dark .q-field--error .text-negative,body.body--dark .q-field--error .q-field__messages{color:#ff0 !important}.lnbits-drawer__q-list .q-item{padding-top:5px !important;padding-bottom:5px !important;border-top-right-radius:3px;border-bottom-right-radius:3px}.lnbits-drawer__q-list .q-item.q-item--active{color:inherit;font-weight:bold}.lnbits__dialog-card{width:500px}.q-table--dense th:first-child,.q-table--dense td:first-child,.q-table--dense .q-table__bottom{padding-left:6px !important}.q-table--dense th:last-child,.q-table--dense td:last-child,.q-table--dense .q-table__bottom{padding-right:6px !important}a.inherit{color:inherit;text-decoration:none}video{border-radius:3px}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(../fonts/material-icons-v50.woff2) format('woff2')}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-moz-font-feature-settings:'liga';-moz-osx-font-smoothing:grayscale}
|
|
505
lnbits/static/vendor/bolt11/decoder.js
vendored
505
lnbits/static/vendor/bolt11/decoder.js
vendored
@ -4,233 +4,344 @@
|
|||||||
//TODO - A reader MUST use the n field to validate the signature instead of performing signature recovery if a valid n field is provided.
|
//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) {
|
function decode(paymentRequest) {
|
||||||
let input = paymentRequest.toLowerCase();
|
let input = paymentRequest.toLowerCase()
|
||||||
let splitPosition = input.lastIndexOf('1');
|
let splitPosition = input.lastIndexOf('1')
|
||||||
let humanReadablePart = input.substring(0, splitPosition);
|
let humanReadablePart = input.substring(0, splitPosition)
|
||||||
let data = input.substring(splitPosition + 1, input.length - 6);
|
let data = input.substring(splitPosition + 1, input.length - 6)
|
||||||
let checksum = input.substring(input.length - 6, input.length);
|
let checksum = input.substring(input.length - 6, input.length)
|
||||||
if (!verify_checksum(humanReadablePart, bech32ToFiveBitArray(data + checksum))) {
|
if (
|
||||||
throw 'Malformed request: checksum is incorrect'; // A reader MUST fail if the checksum is incorrect.
|
!verify_checksum(humanReadablePart, bech32ToFiveBitArray(data + checksum))
|
||||||
}
|
) {
|
||||||
return {
|
throw 'Malformed request: checksum is incorrect' // A reader MUST fail if the checksum is incorrect.
|
||||||
'human_readable_part': decodeHumanReadablePart(humanReadablePart),
|
}
|
||||||
'data': decodeData(data, humanReadablePart),
|
return {
|
||||||
'checksum': checksum
|
human_readable_part: decodeHumanReadablePart(humanReadablePart),
|
||||||
}
|
data: decodeData(data, humanReadablePart),
|
||||||
|
checksum: checksum
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeHumanReadablePart(humanReadablePart) {
|
function decodeHumanReadablePart(humanReadablePart) {
|
||||||
let prefixes = ['lnbc', 'lntb', 'lnbcrt', 'lnsb'];
|
let prefixes = ['lnbc', 'lntb', 'lnbcrt', 'lnsb']
|
||||||
let prefix;
|
let prefix
|
||||||
prefixes.forEach(value => {
|
prefixes.forEach(value => {
|
||||||
if (humanReadablePart.substring(0, value.length) === value) {
|
if (humanReadablePart.substring(0, value.length) === value) {
|
||||||
prefix = 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
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
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) {
|
function decodeData(data, humanReadablePart) {
|
||||||
let date32 = data.substring(0, 7);
|
let date32 = data.substring(0, 7)
|
||||||
let dateEpoch = bech32ToInt(date32);
|
let dateEpoch = bech32ToInt(date32)
|
||||||
let signature = data.substring(data.length - 104, data.length);
|
let signature = data.substring(data.length - 104, data.length)
|
||||||
let tagData = data.substring(7, data.length - 104);
|
let tagData = data.substring(7, data.length - 104)
|
||||||
let decodedTags = decodeTags(tagData);
|
let decodedTags = decodeTags(tagData)
|
||||||
let value = bech32ToFiveBitArray(date32 + tagData);
|
let value = bech32ToFiveBitArray(date32 + tagData)
|
||||||
value = fiveBitArrayTo8BitArray(value, true);
|
value = fiveBitArrayTo8BitArray(value, true)
|
||||||
value = textToHexString(humanReadablePart).concat(byteArrayToHexString(value));
|
value = textToHexString(humanReadablePart).concat(byteArrayToHexString(value))
|
||||||
return {
|
return {
|
||||||
'time_stamp': dateEpoch,
|
time_stamp: dateEpoch,
|
||||||
'tags': decodedTags,
|
tags: decodedTags,
|
||||||
'signature': decodeSignature(signature),
|
signature: decodeSignature(signature),
|
||||||
'signing_data': value
|
signing_data: value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeSignature(signature) {
|
function decodeSignature(signature) {
|
||||||
let data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(signature));
|
let data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(signature))
|
||||||
let recoveryFlag = data[data.length - 1];
|
let recoveryFlag = data[data.length - 1]
|
||||||
let r = byteArrayToHexString(data.slice(0, 32));
|
let r = byteArrayToHexString(data.slice(0, 32))
|
||||||
let s = byteArrayToHexString(data.slice(32, data.length - 1));
|
let s = byteArrayToHexString(data.slice(32, data.length - 1))
|
||||||
return {
|
return {
|
||||||
'r': r,
|
r: r,
|
||||||
's': s,
|
s: s,
|
||||||
'recovery_flag': recoveryFlag
|
recovery_flag: recoveryFlag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeAmount(str) {
|
function decodeAmount(str) {
|
||||||
let multiplier = str.charAt(str.length - 1);
|
let multiplier = str.charAt(str.length - 1)
|
||||||
let amount = str.substring(0, str.length - 1);
|
let amount = str.substring(0, str.length - 1)
|
||||||
if (amount.substring(0, 1) === '0') {
|
if (amount.substring(0, 1) === '0') {
|
||||||
throw 'Malformed request: amount cannot contain leading zeros';
|
throw 'Malformed request: amount cannot contain leading zeros'
|
||||||
}
|
}
|
||||||
amount = Number(amount);
|
amount = Number(amount)
|
||||||
if (amount < 0 || !Number.isInteger(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
|
throw 'Malformed request: amount must be a positive decimal integer' // A reader SHOULD fail if amount contains a non-digit
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (multiplier) {
|
switch (multiplier) {
|
||||||
case '':
|
case '':
|
||||||
return 'Any amount'; // A reader SHOULD indicate if amount is unspecified
|
return 'Any amount' // A reader SHOULD indicate if amount is unspecified
|
||||||
case 'p':
|
case 'p':
|
||||||
return amount / 10;
|
return amount / 10
|
||||||
case 'n':
|
case 'n':
|
||||||
return amount * 100;
|
return amount * 100
|
||||||
case 'u':
|
case 'u':
|
||||||
return amount * 100000;
|
return amount * 100000
|
||||||
case 'm':
|
case 'm':
|
||||||
return amount * 100000000;
|
return amount * 100000000
|
||||||
default:
|
default:
|
||||||
// A reader SHOULD fail if amount is followed by anything except a defined multiplier.
|
// A reader SHOULD fail if amount is followed by anything except a defined multiplier.
|
||||||
throw 'Malformed request: undefined amount multiplier';
|
throw 'Malformed request: undefined amount multiplier'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeTags(tagData) {
|
function decodeTags(tagData) {
|
||||||
let tags = extractTags(tagData);
|
let tags = extractTags(tagData)
|
||||||
let decodedTags = [];
|
let decodedTags = []
|
||||||
tags.forEach(value => decodedTags.push(decodeTag(value.type, value.length, value.data)));
|
tags.forEach(value =>
|
||||||
return decodedTags;
|
decodedTags.push(decodeTag(value.type, value.length, value.data))
|
||||||
|
)
|
||||||
|
return decodedTags
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractTags(str) {
|
function extractTags(str) {
|
||||||
let tags = [];
|
let tags = []
|
||||||
while (str.length > 0) {
|
while (str.length > 0) {
|
||||||
let type = str.charAt(0);
|
let type = str.charAt(0)
|
||||||
let dataLength = bech32ToInt(str.substring(1, 3));
|
let dataLength = bech32ToInt(str.substring(1, 3))
|
||||||
let data = str.substring(3, dataLength + 3);
|
let data = str.substring(3, dataLength + 3)
|
||||||
tags.push({
|
tags.push({
|
||||||
'type': type,
|
type: type,
|
||||||
'length': dataLength,
|
length: dataLength,
|
||||||
'data': data
|
data: data
|
||||||
});
|
})
|
||||||
str = str.substring(3 + dataLength, str.length);
|
str = str.substring(3 + dataLength, str.length)
|
||||||
}
|
}
|
||||||
return tags;
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeTag(type, length, data) {
|
function decodeTag(type, length, data) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'p':
|
case 'p':
|
||||||
if (length !== 52) break; // A reader MUST skip over a 'p' field that does not have data_length 52
|
if (length !== 52) break // A reader MUST skip over a 'p' field that does not have data_length 52
|
||||||
return {
|
return {
|
||||||
'type': type,
|
type: type,
|
||||||
'length': length,
|
length: length,
|
||||||
'description': 'payment_hash',
|
description: 'payment_hash',
|
||||||
'value': byteArrayToHexString(fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data)))
|
value: byteArrayToHexString(
|
||||||
};
|
fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))
|
||||||
case 'd':
|
)
|
||||||
return {
|
}
|
||||||
'type': type,
|
case 'd':
|
||||||
'length': length,
|
return {
|
||||||
'description': 'description',
|
type: type,
|
||||||
'value': bech32ToUTF8String(data)
|
length: length,
|
||||||
};
|
description: 'description',
|
||||||
case 'n':
|
value: bech32ToUTF8String(data)
|
||||||
if (length !== 53) break; // A reader MUST skip over a 'n' field that does not have data_length 53
|
}
|
||||||
return {
|
case 'n':
|
||||||
'type': type,
|
if (length !== 53) break // A reader MUST skip over a 'n' field that does not have data_length 53
|
||||||
'length': length,
|
return {
|
||||||
'description': 'payee_public_key',
|
type: type,
|
||||||
'value': byteArrayToHexString(fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data)))
|
length: length,
|
||||||
};
|
description: 'payee_public_key',
|
||||||
case 'h':
|
value: byteArrayToHexString(
|
||||||
if (length !== 52) break; // A reader MUST skip over a 'h' field that does not have data_length 52
|
fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))
|
||||||
return {
|
)
|
||||||
'type': type,
|
}
|
||||||
'length': length,
|
case 'h':
|
||||||
'description': 'description_hash',
|
if (length !== 52) break // A reader MUST skip over a 'h' field that does not have data_length 52
|
||||||
'value': data
|
return {
|
||||||
};
|
type: type,
|
||||||
case 'x':
|
length: length,
|
||||||
return {
|
description: 'description_hash',
|
||||||
'type': type,
|
value: data
|
||||||
'length': length,
|
}
|
||||||
'description': 'expiry',
|
case 'x':
|
||||||
'value': bech32ToInt(data)
|
return {
|
||||||
};
|
type: type,
|
||||||
case 'c':
|
length: length,
|
||||||
return {
|
description: 'expiry',
|
||||||
'type': type,
|
value: bech32ToInt(data)
|
||||||
'length': length,
|
}
|
||||||
'description': 'min_final_cltv_expiry',
|
case 'c':
|
||||||
'value': bech32ToInt(data)
|
return {
|
||||||
};
|
type: type,
|
||||||
case 'f':
|
length: length,
|
||||||
let version = bech32ToFiveBitArray(data.charAt(0))[0];
|
description: 'min_final_cltv_expiry',
|
||||||
if (version < 0 || version > 18) break; // a reader MUST skip over an f field with unknown version.
|
value: bech32ToInt(data)
|
||||||
data = data.substring(1, data.length);
|
}
|
||||||
return {
|
case 'f':
|
||||||
'type': type,
|
let version = bech32ToFiveBitArray(data.charAt(0))[0]
|
||||||
'length': length,
|
if (version < 0 || version > 18) break // a reader MUST skip over an f field with unknown version.
|
||||||
'description': 'fallback_address',
|
data = data.substring(1, data.length)
|
||||||
'value': {
|
return {
|
||||||
'version': version,
|
type: type,
|
||||||
'fallback_address': data
|
length: length,
|
||||||
}
|
description: 'fallback_address',
|
||||||
};
|
value: {
|
||||||
case 'r':
|
version: version,
|
||||||
data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data));
|
fallback_address: data
|
||||||
let pubkey = data.slice(0, 33);
|
}
|
||||||
let shortChannelId = data.slice(33, 41);
|
}
|
||||||
let feeBaseMsat = data.slice(41, 45);
|
case 'r':
|
||||||
let feeProportionalMillionths = data.slice(45, 49);
|
data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))
|
||||||
let cltvExpiryDelta = data.slice(49, 51);
|
let pubkey = data.slice(0, 33)
|
||||||
return {
|
let shortChannelId = data.slice(33, 41)
|
||||||
'type': type,
|
let feeBaseMsat = data.slice(41, 45)
|
||||||
'length': length,
|
let feeProportionalMillionths = data.slice(45, 49)
|
||||||
'description': 'routing_information',
|
let cltvExpiryDelta = data.slice(49, 51)
|
||||||
'value': {
|
return {
|
||||||
'public_key': byteArrayToHexString(pubkey),
|
type: type,
|
||||||
'short_channel_id': byteArrayToHexString(shortChannelId),
|
length: length,
|
||||||
'fee_base_msat': byteArrayToInt(feeBaseMsat),
|
description: 'routing_information',
|
||||||
'fee_proportional_millionths': byteArrayToInt(feeProportionalMillionths),
|
value: {
|
||||||
'cltv_expiry_delta': byteArrayToInt(cltvExpiryDelta)
|
public_key: byteArrayToHexString(pubkey),
|
||||||
}
|
short_channel_id: byteArrayToHexString(shortChannelId),
|
||||||
};
|
fee_base_msat: byteArrayToInt(feeBaseMsat),
|
||||||
default:
|
fee_proportional_millionths: byteArrayToInt(
|
||||||
// reader MUST skip over unknown fields
|
feeProportionalMillionths
|
||||||
}
|
),
|
||||||
|
cltv_expiry_delta: byteArrayToInt(cltvExpiryDelta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// reader MUST skip over unknown fields
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function polymod(values) {
|
function polymod(values) {
|
||||||
let GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
|
let GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
|
||||||
let chk = 1;
|
let chk = 1
|
||||||
values.forEach((value) => {
|
values.forEach(value => {
|
||||||
let b = (chk >> 25);
|
let b = chk >> 25
|
||||||
chk = (chk & 0x1ffffff) << 5 ^ value;
|
chk = ((chk & 0x1ffffff) << 5) ^ value
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
if (((b >> i) & 1) === 1) {
|
if (((b >> i) & 1) === 1) {
|
||||||
chk ^= GEN[i];
|
chk ^= GEN[i]
|
||||||
} else {
|
} else {
|
||||||
chk ^= 0;
|
chk ^= 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return chk;
|
return chk
|
||||||
}
|
}
|
||||||
|
|
||||||
function expand(str) {
|
function expand(str) {
|
||||||
let array = [];
|
let array = []
|
||||||
for (let i = 0; i < str.length; i++) {
|
for (let i = 0; i < str.length; i++) {
|
||||||
array.push(str.charCodeAt(i) >> 5);
|
array.push(str.charCodeAt(i) >> 5)
|
||||||
}
|
}
|
||||||
array.push(0);
|
array.push(0)
|
||||||
for (let i = 0; i < str.length; i++) {
|
for (let i = 0; i < str.length; i++) {
|
||||||
array.push(str.charCodeAt(i) & 31);
|
array.push(str.charCodeAt(i) & 31)
|
||||||
}
|
}
|
||||||
return array;
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
function verify_checksum(hrp, data) {
|
function verify_checksum(hrp, data) {
|
||||||
hrp = expand(hrp);
|
hrp = expand(hrp)
|
||||||
let all = hrp.concat(data);
|
let all = hrp.concat(data)
|
||||||
let bool = polymod(all);
|
let bool = polymod(all)
|
||||||
return bool === 1;
|
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
|
||||||
}
|
}
|
96
lnbits/static/vendor/bolt11/utils.js
vendored
96
lnbits/static/vendor/bolt11/utils.js
vendored
@ -1,96 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -2,16 +2,10 @@
|
|||||||
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<link
|
{% for url in g.VENDORED_CSS %}
|
||||||
rel="stylesheet"
|
<link rel="stylesheet" type="text/css" href="{{ url }}" />
|
||||||
type="text/css"
|
{% endfor %}
|
||||||
href="/static/vendor/quasar@1.13.2/quasar.min.css"
|
<!---->
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
href="/static/vendor/vue-qrcode-reader@2.2.0/vue-qrcode-reader.min.css"
|
|
||||||
/>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/css/base.css" />
|
<link rel="stylesheet" type="text/css" href="/static/css/base.css" />
|
||||||
{% block styles %}{% endblock %}
|
{% block styles %}{% endblock %}
|
||||||
<title>
|
<title>
|
||||||
@ -113,18 +107,11 @@
|
|||||||
</q-layout>
|
</q-layout>
|
||||||
|
|
||||||
{% block vue_templates %}{% endblock %}
|
{% block vue_templates %}{% endblock %}
|
||||||
<script src="/static/vendor/vue@2.6.12/vue.js"></script>
|
<!---->
|
||||||
<script src="/static/vendor/vuex@3.5.1/vuex.js"></script>
|
{% for url in g.VENDORED_JS %}
|
||||||
<script src="/static/vendor/vue-router@3.4.3/vue-router.js"></script>
|
<script src="{{ url }}"></script>
|
||||||
<script src="/static/vendor/quasar@1.13.2/quasar.umd.js"></script>
|
{% endfor %}
|
||||||
<script src="/static/vendor/axios@0.20.0/axios.min.js"></script>
|
<!---->
|
||||||
<script src="/static/vendor/underscore@1.10.2/underscore.min.js"></script>
|
|
||||||
<script src="/static/vendor/vue-qrcode@1.0.2/vue-qrcode.min.js"></script>
|
|
||||||
<script src="/static/vendor/moment@2.27.0/moment.min.js"></script>
|
|
||||||
<script src="/static/vendor/chart.js@2.9.3/chart.min.js"></script>
|
|
||||||
<script src="/static/vendor/bolt11/utils.js"></script>
|
|
||||||
<script src="/static/vendor/bolt11/decoder.js"></script>
|
|
||||||
<script src="/static/vendor/vue-qrcode-reader@2.2.0/vue-qrcode-reader.min.js"></script>
|
|
||||||
<script src="/static/js/base.js"></script>
|
<script src="/static/js/base.js"></script>
|
||||||
<script src="/static/js/components.js"></script>
|
<script src="/static/js/components.js"></script>
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
|
@ -2,11 +2,9 @@
|
|||||||
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<link
|
{% for url in g.VENDORED_CSS %}
|
||||||
rel="stylesheet"
|
<link rel="stylesheet" type="text/css" href="{{ url }}" />
|
||||||
href="//fonts.googleapis.com/css?family=Material+Icons"
|
{% endfor %}
|
||||||
type="text/css"
|
|
||||||
/>
|
|
||||||
<style>
|
<style>
|
||||||
@page {
|
@page {
|
||||||
size: A4 portrait;
|
size: A4 portrait;
|
||||||
@ -38,12 +36,10 @@
|
|||||||
</q-page-container>
|
</q-page-container>
|
||||||
</q-layout>
|
</q-layout>
|
||||||
|
|
||||||
<script src="/static/vendor/quasar@1.13.2/quasar.ie.polyfills.umd.min.js"></script>
|
{% for url in g.VENDORED_JS %}
|
||||||
<script src="/static/vendor/vue@2.6.12/vue.min.js"></script>
|
<script src="{{ url }}"></script>
|
||||||
<script src="/static/vendor/vuex@3.5.1/vuex.js"></script>
|
{% endfor %}
|
||||||
<script src="/static/vendor/vue-router@3.4.3/vue-router.js"></script>
|
<!---->
|
||||||
<script src="/static/vendor/quasar@1.13.2/quasar.umd.min.js"></script>
|
|
||||||
<script src="/static/vendor/vue-qrcode@1.0.2/vue-qrcode.min.js"></script>
|
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user