Revert "Improve configuration form for readability and UX"
This commit is contained in:
parent
771427a0b2
commit
93ba3ba628
12 changed files with 512 additions and 552 deletions
16
.github/workflows/main.yml
vendored
16
.github/workflows/main.yml
vendored
|
|
@ -1,7 +1,7 @@
|
|||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
with:
|
||||
token: ${{ secrets.EXT_GITHUB }}
|
||||
repository: lnbits/lnbits-extensions
|
||||
path: './lnbits-extensions'
|
||||
path: "./lnbits-extensions"
|
||||
|
||||
- name: setup git user
|
||||
run: |
|
||||
|
|
@ -31,12 +31,12 @@ jobs:
|
|||
- name: Create pull request in extensions repo
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.EXT_GITHUB }}
|
||||
repo_name: '${{ github.event.repository.name }}'
|
||||
tag: '${{ github.ref_name }}'
|
||||
branch: 'update-${{ github.event.repository.name }}-${{ github.ref_name }}'
|
||||
title: '[UPDATE] ${{ github.event.repository.name }} to ${{ github.ref_name }}'
|
||||
body: 'https://github.com/lnbits/${{ github.event.repository.name }}/releases/${{ github.ref_name }}'
|
||||
archive: 'https://github.com/lnbits/${{ github.event.repository.name }}/archive/refs/tags/${{ github.ref_name }}.zip'
|
||||
repo_name: "${{ github.event.repository.name }}"
|
||||
tag: "${{ github.ref_name }}"
|
||||
branch: "update-${{ github.event.repository.name }}-${{ github.ref_name }}"
|
||||
title: "[UPDATE] ${{ github.event.repository.name }} to ${{ github.ref_name }}"
|
||||
body: "https://github.com/lnbits/${{ github.event.repository.name }}/releases/${{ github.ref_name }}"
|
||||
archive: "https://github.com/lnbits/${{ github.event.repository.name }}/archive/refs/tags/${{ github.ref_name }}.zip"
|
||||
run: |
|
||||
cd lnbits-extensions
|
||||
git checkout -b $branch
|
||||
|
|
|
|||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
|
|
@ -1,7 +1,7 @@
|
|||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
with:
|
||||
token: ${{ secrets.EXT_GITHUB }}
|
||||
repository: lnbits/lnbits-extensions
|
||||
path: './lnbits-extensions'
|
||||
path: "./lnbits-extensions"
|
||||
|
||||
- name: setup git user
|
||||
run: |
|
||||
|
|
@ -31,12 +31,12 @@ jobs:
|
|||
- name: Create pull request in extensions repo
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.EXT_GITHUB }}
|
||||
repo_name: '${{ github.event.repository.name }}'
|
||||
tag: '${{ github.ref_name }}'
|
||||
branch: 'update-${{ github.event.repository.name }}-${{ github.ref_name }}'
|
||||
title: '[UPDATE] ${{ github.event.repository.name }} to ${{ github.ref_name }}'
|
||||
body: 'https://github.com/lnbits/${{ github.event.repository.name }}/releases/${{ github.ref_name }}'
|
||||
archive: 'https://github.com/lnbits/${{ github.event.repository.name }}/archive/refs/tags/${{ github.ref_name }}.zip'
|
||||
repo_name: "${{ github.event.repository.name }}"
|
||||
tag: "${{ github.ref_name }}"
|
||||
branch: "update-${{ github.event.repository.name }}-${{ github.ref_name }}"
|
||||
title: "[UPDATE] ${{ github.event.repository.name }} to ${{ github.ref_name }}"
|
||||
body: "https://github.com/lnbits/${{ github.event.repository.name }}/releases/${{ github.ref_name }}"
|
||||
archive: "https://github.com/lnbits/${{ github.event.repository.name }}/archive/refs/tags/${{ github.ref_name }}.zip"
|
||||
run: |
|
||||
cd lnbits-extensions
|
||||
git checkout -b $branch
|
||||
|
|
|
|||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ def nwcprovider_start():
|
|||
__all__ = [
|
||||
"db",
|
||||
"nwcprovider_ext",
|
||||
"nwcprovider_start",
|
||||
"nwcprovider_static_files",
|
||||
"nwcprovider_start",
|
||||
"nwcprovider_stop",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -87,13 +87,12 @@ async def m004_default_config2(db):
|
|||
Default config
|
||||
"""
|
||||
await db.execute(
|
||||
"""
|
||||
"""
|
||||
INSERT INTO nwcprovider.config (key, value) VALUES ('relay_alias', :value)
|
||||
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
|
||||
""",
|
||||
{"value": ""},
|
||||
)
|
||||
|
||||
{"value": ""},
|
||||
)
|
||||
|
||||
async def m005_key_last_used(db):
|
||||
"""
|
||||
|
|
|
|||
3
nwcp.py
3
nwcp.py
|
|
@ -4,8 +4,7 @@ import hashlib
|
|||
import json
|
||||
import random
|
||||
import time
|
||||
from collections.abc import Awaitable
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import secp256k1
|
||||
import websockets.client as websockets
|
||||
|
|
|
|||
|
|
@ -1,81 +1,87 @@
|
|||
window.app = Vue.createApp({
|
||||
el: '#vue',
|
||||
el: "#vue",
|
||||
mixins: [windowMixin],
|
||||
delimiters: ['${', '}'],
|
||||
delimiters: ["${", "}"],
|
||||
data: function () {
|
||||
return {
|
||||
config: {},
|
||||
entries: [],
|
||||
columns: [
|
||||
{
|
||||
name: 'key',
|
||||
name: "key",
|
||||
required: true,
|
||||
label: 'Key',
|
||||
align: 'left',
|
||||
field: row => row.key,
|
||||
sortable: true
|
||||
label: "Key",
|
||||
align: "left",
|
||||
field: (row) => row.key,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
name: "value",
|
||||
required: true,
|
||||
label: 'Value',
|
||||
align: 'left',
|
||||
field: row => row.value,
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
}
|
||||
label: "Value",
|
||||
align: "left",
|
||||
field: (row) => row.value,
|
||||
sortable: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchConfig() {
|
||||
this.config = {}
|
||||
this.entries = [];
|
||||
LNbits.api
|
||||
.request(
|
||||
'GET',
|
||||
'/nwcprovider/api/v1/config',
|
||||
this.g.user.wallets[0].adminkey
|
||||
"GET",
|
||||
"/nwcprovider/api/v1/config",
|
||||
this.g.user.wallets[0].adminkey,
|
||||
)
|
||||
.then(response => {
|
||||
this.config = response.data
|
||||
console.log('Config fetched:', this.config)
|
||||
.then((response) => {
|
||||
const newEntries = [];
|
||||
for (const [key, value] of Object.entries(response.data)) {
|
||||
newEntries.push({
|
||||
key: key,
|
||||
value: value,
|
||||
});
|
||||
}
|
||||
this.entries.length = 0;
|
||||
this.entries.push(...newEntries);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error('Error fetching config:', error)
|
||||
})
|
||||
console.error("Error fetching config:", error);
|
||||
});
|
||||
},
|
||||
async saveConfig() {
|
||||
const data = {}
|
||||
for (const [key, value] of Object.entries(this.config)) {
|
||||
data[key] = value
|
||||
const data = {};
|
||||
for (const entry of this.entries) {
|
||||
data[entry.key] = entry.value;
|
||||
}
|
||||
console.log('Saving config:', data)
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'POST',
|
||||
'/nwcprovider/api/v1/config',
|
||||
"POST",
|
||||
"/nwcprovider/api/v1/config",
|
||||
this.g.user.wallets[0].adminkey,
|
||||
data
|
||||
)
|
||||
data,
|
||||
);
|
||||
Quasar.Notify.create({
|
||||
type: 'positive',
|
||||
message: 'Config saved!'
|
||||
})
|
||||
type: "positive",
|
||||
message: "Config saved!",
|
||||
});
|
||||
Quasar.Notify.create({
|
||||
type: 'warning',
|
||||
type: "warning",
|
||||
message:
|
||||
'You need to restart the server for the changes to take effect!'
|
||||
})
|
||||
"You need to restart the server for the changes to take effect!",
|
||||
});
|
||||
} catch (error) {
|
||||
Quasar.Notify.create({
|
||||
type: 'negative',
|
||||
message: 'Error saving config: ' + String(error)
|
||||
})
|
||||
console.error('Error saving config:', error)
|
||||
type: "negative",
|
||||
message: "Error saving config: " + String(error),
|
||||
});
|
||||
console.error("Error saving config:", error);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
created: function () {
|
||||
this.fetchConfig()
|
||||
}
|
||||
})
|
||||
this.fetchConfig();
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
window.app = Vue.createApp({
|
||||
el: '#vue',
|
||||
el: "#vue",
|
||||
mixins: [windowMixin],
|
||||
delimiters: ['${', '}'],
|
||||
delimiters: ["${", "}"],
|
||||
data: function () {
|
||||
return {
|
||||
selectedWallet: null,
|
||||
|
|
@ -10,244 +10,244 @@ window.app = Vue.createApp({
|
|||
nwcsTable: {
|
||||
columns: [
|
||||
{
|
||||
name: 'description',
|
||||
align: 'left',
|
||||
label: 'Description',
|
||||
field: 'description'
|
||||
name: "description",
|
||||
align: "left",
|
||||
label: "Description",
|
||||
field: "description",
|
||||
},
|
||||
{name: 'status', align: 'left', label: 'Status', field: 'status'},
|
||||
{ name: "status", align: "left", label: "Status", field: "status" },
|
||||
{
|
||||
name: 'last_used',
|
||||
align: 'left',
|
||||
label: 'Last used',
|
||||
field: 'last_used'
|
||||
name: "last_used",
|
||||
align: "left",
|
||||
label: "Last used",
|
||||
field: "last_used",
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
align: 'left',
|
||||
label: 'Created',
|
||||
field: 'created_at'
|
||||
name: "created_at",
|
||||
align: "left",
|
||||
label: "Created",
|
||||
field: "created_at",
|
||||
},
|
||||
{
|
||||
name: 'expires_at',
|
||||
align: 'left',
|
||||
label: 'Expires',
|
||||
field: 'expires_at'
|
||||
}
|
||||
name: "expires_at",
|
||||
align: "left",
|
||||
label: "Expires",
|
||||
field: "expires_at",
|
||||
},
|
||||
],
|
||||
pagination: {
|
||||
rowsPerPage: 10
|
||||
}
|
||||
rowsPerPage: 10,
|
||||
},
|
||||
},
|
||||
connectDialog: {
|
||||
show: false,
|
||||
data: {}
|
||||
data: {},
|
||||
},
|
||||
pairingDialog: {
|
||||
show: false,
|
||||
data: {
|
||||
pairingUrl: ''
|
||||
}
|
||||
pairingUrl: "",
|
||||
},
|
||||
},
|
||||
pairingQrDialog: {
|
||||
show: false,
|
||||
data: {
|
||||
pairingUrl: ''
|
||||
}
|
||||
pairingUrl: "",
|
||||
},
|
||||
},
|
||||
connectionInfoDialog: {
|
||||
show: false,
|
||||
data: {}
|
||||
}
|
||||
}
|
||||
data: {},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
showConnectDialog() {
|
||||
const wallet = this.getWallet()
|
||||
const wallet = this.getWallet();
|
||||
if (!wallet) {
|
||||
Quasar.Notify.create({
|
||||
type: 'negative',
|
||||
message: 'Please select a wallet first'
|
||||
})
|
||||
return
|
||||
type: "negative",
|
||||
message: "Please select a wallet first",
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
this.connectDialog.show = true
|
||||
this.connectDialog.show = true;
|
||||
}
|
||||
},
|
||||
openConnectionInfoDialog(data) {
|
||||
this.connectionInfoDialog.data = data
|
||||
this.connectionInfoDialog.show = true
|
||||
this.connectionInfoDialog.data = data;
|
||||
this.connectionInfoDialog.show = true;
|
||||
},
|
||||
closeConnectionInfoDialog() {
|
||||
this.connectionInfoDialog.show = false
|
||||
this.connectionInfoDialog.show = false;
|
||||
},
|
||||
openPairingUrl() {
|
||||
const url = this.pairingDialog.data.pairingUrl
|
||||
if (url) window.open(url, '_blank')
|
||||
const url = this.pairingDialog.data.pairingUrl;
|
||||
if (url) window.open(url, "_blank");
|
||||
},
|
||||
go(url) {
|
||||
window.open(url, '_blank')
|
||||
window.open(url, "_blank");
|
||||
},
|
||||
async copyPairingUrl() {
|
||||
const url = this.pairingDialog.data.pairingUrl
|
||||
const url = this.pairingDialog.data.pairingUrl;
|
||||
if (url) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(url)
|
||||
await navigator.clipboard.writeText(url);
|
||||
Quasar.Notify.create({
|
||||
type: 'positive',
|
||||
message: 'URL copied to clipboard'
|
||||
})
|
||||
type: "positive",
|
||||
message: "URL copied to clipboard",
|
||||
});
|
||||
} catch (err) {
|
||||
Quasar.Notify.create({
|
||||
type: 'negative',
|
||||
message: 'Failed to copy URL.'
|
||||
})
|
||||
type: "negative",
|
||||
message: "Failed to copy URL.",
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
showPairingQR() {
|
||||
this.pairingQrDialog.data.pairingUrl = this.pairingDialog.data.pairingUrl
|
||||
this.pairingQrDialog.show = true
|
||||
this.pairingQrDialog.data.pairingUrl = this.pairingDialog.data.pairingUrl;
|
||||
this.pairingQrDialog.show = true;
|
||||
},
|
||||
closePairingQrDialog() {
|
||||
this.pairingQrDialog.show = false
|
||||
this.pairingQrDialog.show = false;
|
||||
},
|
||||
loadConnectDialogData() {
|
||||
this.connectDialog.data = {
|
||||
description: '',
|
||||
description: "",
|
||||
expires_at: Date.now() + 1000 * 60 * 60 * 24 * 7,
|
||||
neverExpires: true,
|
||||
permissions: [],
|
||||
budgets: []
|
||||
}
|
||||
budgets: [],
|
||||
};
|
||||
for (const permission of this.nodePermissions) {
|
||||
this.connectDialog.data.permissions.push({
|
||||
key: permission.key,
|
||||
name: permission.name,
|
||||
value: permission.value
|
||||
})
|
||||
value: permission.value,
|
||||
});
|
||||
}
|
||||
},
|
||||
deleteBudget(index) {
|
||||
this.connectDialog.data.budgets.splice(index, 1)
|
||||
this.connectDialog.data.budgets.splice(index, 1);
|
||||
},
|
||||
addBudget() {
|
||||
this.connectDialog.data.budgets.push({
|
||||
budget_sats: 1000,
|
||||
used_budget_sats: 0,
|
||||
created_at: new Date(new Date().setHours(0, 0, 0, 0)).getTime() / 1000,
|
||||
expiration: 'never'
|
||||
})
|
||||
expiration: "never",
|
||||
});
|
||||
},
|
||||
closeConnectDialog() {
|
||||
this.connectDialog.show = false
|
||||
this.loadConnectDialogData()
|
||||
this.connectDialog.show = false;
|
||||
this.loadConnectDialogData();
|
||||
},
|
||||
getWallet: function () {
|
||||
let wallet = undefined
|
||||
let wallet = undefined;
|
||||
for (let i = 0; i < this.g.user.wallets.length; i++) {
|
||||
if (this.g.user.wallets[i].id == this.selectedWallet) {
|
||||
wallet = this.g.user.wallets[i]
|
||||
break
|
||||
wallet = this.g.user.wallets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return wallet
|
||||
return wallet;
|
||||
},
|
||||
async generateKeyPair() {
|
||||
while (!window.NobleSecp256k1) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1))
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
}
|
||||
const privKeyBytes = window.NobleSecp256k1.utils.randomPrivateKey()
|
||||
const pubKeyBytes = window.NobleSecp256k1.getPublicKey(privKeyBytes)
|
||||
const privKeyBytes = window.NobleSecp256k1.utils.randomPrivateKey();
|
||||
const pubKeyBytes = window.NobleSecp256k1.getPublicKey(privKeyBytes);
|
||||
const out = {
|
||||
privKeyBytes: privKeyBytes,
|
||||
pubKeyBytes: pubKeyBytes,
|
||||
privKey: window.NobleSecp256k1.etc.bytesToHex(privKeyBytes),
|
||||
pubKey: window.NobleSecp256k1.etc.bytesToHex(pubKeyBytes.slice(1))
|
||||
}
|
||||
return out
|
||||
pubKey: window.NobleSecp256k1.etc.bytesToHex(pubKeyBytes.slice(1)),
|
||||
};
|
||||
return out;
|
||||
},
|
||||
deleteNWC: async function (pubkey) {
|
||||
Quasar.Dialog.create({
|
||||
title: 'Confirm Deletion',
|
||||
message: 'Are you sure you want to delete this connection?',
|
||||
title: "Confirm Deletion",
|
||||
message: "Are you sure you want to delete this connection?",
|
||||
cancel: true,
|
||||
persistent: true
|
||||
persistent: true,
|
||||
})
|
||||
.onOk(async () => {
|
||||
try {
|
||||
const wallet = this.getWallet()
|
||||
const wallet = this.getWallet();
|
||||
const response = await LNbits.api.request(
|
||||
'DELETE',
|
||||
"DELETE",
|
||||
`/nwcprovider/api/v1/nwc/${pubkey}`,
|
||||
wallet.adminkey
|
||||
)
|
||||
this.loadNwcs()
|
||||
wallet.adminkey,
|
||||
);
|
||||
this.loadNwcs();
|
||||
Quasar.Notify.create({
|
||||
type: 'positive',
|
||||
message: 'Deleted successfully'
|
||||
})
|
||||
type: "positive",
|
||||
message: "Deleted successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
LNbits.utils.notifyApiError(error);
|
||||
}
|
||||
})
|
||||
.onCancel(() => {
|
||||
// User canceled the operation
|
||||
})
|
||||
});
|
||||
},
|
||||
loadNwcs: async function () {
|
||||
const wallet = this.getWallet()
|
||||
const wallet = this.getWallet();
|
||||
if (!wallet) {
|
||||
this.nwcs = []
|
||||
return
|
||||
this.nwcs = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'GET',
|
||||
'/nwcprovider/api/v1/nwc?include_expired=true&calculate_spent_budget=true',
|
||||
wallet.adminkey
|
||||
)
|
||||
this.nwcs = response.data
|
||||
"GET",
|
||||
"/nwcprovider/api/v1/nwc?include_expired=true&calculate_spent_budget=true",
|
||||
wallet.adminkey,
|
||||
);
|
||||
this.nwcs = response.data;
|
||||
} catch (error) {
|
||||
this.nwcs = []
|
||||
this.nwcs = [];
|
||||
}
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'GET',
|
||||
'/nwcprovider/api/v1/permissions',
|
||||
wallet.adminkey
|
||||
)
|
||||
const permissions = []
|
||||
"GET",
|
||||
"/nwcprovider/api/v1/permissions",
|
||||
wallet.adminkey,
|
||||
);
|
||||
const permissions = [];
|
||||
for (const [key, value] of Object.entries(response.data)) {
|
||||
permissions.push({
|
||||
key: key,
|
||||
name: value.name,
|
||||
value: value.default
|
||||
})
|
||||
value: value.default,
|
||||
});
|
||||
}
|
||||
this.nodePermissions = permissions
|
||||
this.nodePermissions = permissions;
|
||||
} catch (error) {
|
||||
Lnbits.utils.notifyApiError(error)
|
||||
Lnbits.utils.notifyApiError(error);
|
||||
}
|
||||
this.loadConnectDialogData()
|
||||
const newTableEntries = []
|
||||
this.loadConnectDialogData();
|
||||
const newTableEntries = [];
|
||||
for (const nwc of this.nwcs) {
|
||||
const t = Quasar.date.formatDate(
|
||||
new Date(nwc.data.created_at * 1000),
|
||||
'YYYY-MM-DD HH:mm'
|
||||
)
|
||||
"YYYY-MM-DD HH:mm",
|
||||
);
|
||||
const e =
|
||||
nwc.data.expires_at > 0
|
||||
? Quasar.date.formatDate(
|
||||
new Date(nwc.data.expires_at * 1000),
|
||||
'YYYY-MM-DD HH:mm'
|
||||
"YYYY-MM-DD HH:mm",
|
||||
)
|
||||
: 'Never'
|
||||
: "Never";
|
||||
const l = Quasar.date.formatDate(
|
||||
new Date(nwc.data.last_used * 1000),
|
||||
'YYYY-MM-DD HH:mm'
|
||||
)
|
||||
"YYYY-MM-DD HH:mm",
|
||||
);
|
||||
const nwcTableEntry = {
|
||||
description: nwc.data.description,
|
||||
created_at: t,
|
||||
|
|
@ -256,130 +256,131 @@ window.app = Vue.createApp({
|
|||
pubkey: nwc.data.pubkey,
|
||||
permissions: nwc.data.permissions,
|
||||
budgets: [],
|
||||
status: 'Active'
|
||||
}
|
||||
status: "Active",
|
||||
};
|
||||
if (
|
||||
nwc.data.expires_at > 0 &&
|
||||
nwc.data.expires_at < new Date().getTime() / 1000
|
||||
) {
|
||||
nwcTableEntry.status = 'Expired'
|
||||
nwcTableEntry.status = "Expired";
|
||||
}
|
||||
for (const budget of nwc.budgets) {
|
||||
const createdAt = Quasar.date.formatDate(
|
||||
new Date(budget.created_at * 1000),
|
||||
'YYYY-MM-DD HH:mm'
|
||||
)
|
||||
let refreshWindow = budget.refresh_window
|
||||
"YYYY-MM-DD HH:mm",
|
||||
);
|
||||
let refreshWindow = budget.refresh_window;
|
||||
if (refreshWindow <= 0) {
|
||||
refreshWindow = 'Never'
|
||||
refreshWindow = "Never";
|
||||
} else if (refreshWindow == 60 * 60 * 24) {
|
||||
refreshWindow = 'Daily'
|
||||
refreshWindow = "Daily";
|
||||
} else if (refreshWindow == 60 * 60 * 24 * 7) {
|
||||
refreshWindow = 'Weekly'
|
||||
refreshWindow = "Weekly";
|
||||
} else if (refreshWindow == 60 * 60 * 24 * 30) {
|
||||
refreshWindow = 'Monthly'
|
||||
refreshWindow = "Monthly";
|
||||
} else if (refreshWindow == 60 * 60 * 24 * 365) {
|
||||
refreshWindow = 'Yearly'
|
||||
refreshWindow = "Yearly";
|
||||
}
|
||||
nwcTableEntry.budgets.push({
|
||||
budget_sats: budget.budget_msats / 1000,
|
||||
used_budget_sats: budget.used_budget_msats / 1000,
|
||||
created_at: createdAt,
|
||||
refresh_window: refreshWindow
|
||||
})
|
||||
refresh_window: refreshWindow,
|
||||
});
|
||||
}
|
||||
newTableEntries.push(nwcTableEntry)
|
||||
newTableEntries.push(nwcTableEntry);
|
||||
}
|
||||
this.nwcEntries = newTableEntries
|
||||
this.nwcEntries = newTableEntries;
|
||||
},
|
||||
closePairingDialog() {
|
||||
this.pairingDialog.show = false
|
||||
this.pairingDialog.show = false;
|
||||
},
|
||||
async showPairingDialog(secret) {
|
||||
let response = await LNbits.api.request(
|
||||
'GET',
|
||||
'/nwcprovider/api/v1/pairing/{SECRET}'
|
||||
)
|
||||
response = response.data
|
||||
response = response.replace('{SECRET}', secret)
|
||||
this.pairingDialog.data.pairingUrl = response
|
||||
this.pairingDialog.show = true
|
||||
"GET",
|
||||
"/nwcprovider/api/v1/pairing/{SECRET}",
|
||||
);
|
||||
response = response.data;
|
||||
response = response.replace("{SECRET}", secret);
|
||||
this.pairingDialog.data.pairingUrl = response;
|
||||
this.pairingDialog.show = true;
|
||||
},
|
||||
async confirmConnectDialog() {
|
||||
const keyPair = await this.generateKeyPair()
|
||||
const keyPair = await this.generateKeyPair();
|
||||
// timestamp
|
||||
let expires_at = 0
|
||||
let expires_at = 0;
|
||||
if (!this.connectDialog.data.neverExpires) {
|
||||
expires_at =
|
||||
new Date(this.connectDialog.data.expires_at).getTime() / 1000
|
||||
new Date(this.connectDialog.data.expires_at).getTime() / 1000;
|
||||
}
|
||||
const data = {
|
||||
permissions: [],
|
||||
description: this.connectDialog.data.description,
|
||||
expires_at: expires_at,
|
||||
budgets: []
|
||||
}
|
||||
budgets: [],
|
||||
};
|
||||
for (const permission of this.connectDialog.data.permissions) {
|
||||
if (permission.value) data.permissions.push(permission.key)
|
||||
if (permission.value) data.permissions.push(permission.key);
|
||||
}
|
||||
for (const budget of this.connectDialog.data.budgets) {
|
||||
const budget_msats = budget.budget_sats * 1000
|
||||
let refresh_window = 0
|
||||
const budget_msats = budget.budget_sats * 1000;
|
||||
let refresh_window = 0;
|
||||
switch (budget.expiry) {
|
||||
case 'Daily':
|
||||
refresh_window = 60 * 60 * 24
|
||||
break
|
||||
case 'Weekly':
|
||||
refresh_window = 60 * 60 * 24 * 7
|
||||
break
|
||||
case 'Monthly':
|
||||
refresh_window = 60 * 60 * 24 * 30
|
||||
break
|
||||
case 'Yearly':
|
||||
refresh_window = 60 * 60 * 24 * 365
|
||||
break
|
||||
case 'Never':
|
||||
refresh_window = 0
|
||||
break
|
||||
case "Daily":
|
||||
refresh_window = 60 * 60 * 24;
|
||||
break;
|
||||
case "Weekly":
|
||||
refresh_window = 60 * 60 * 24 * 7;
|
||||
break;
|
||||
case "Monthly":
|
||||
refresh_window = 60 * 60 * 24 * 30;
|
||||
break;
|
||||
case "Yearly":
|
||||
refresh_window = 60 * 60 * 24 * 365;
|
||||
break;
|
||||
case "Never":
|
||||
refresh_window = 0;
|
||||
break;
|
||||
}
|
||||
data.budgets.push({
|
||||
budget_msats: budget_msats,
|
||||
refresh_window: refresh_window,
|
||||
created_at: new Date(new Date().setHours(0, 0, 0, 0)).getTime() / 1000
|
||||
})
|
||||
created_at:
|
||||
new Date(new Date().setHours(0, 0, 0, 0)).getTime() / 1000,
|
||||
});
|
||||
}
|
||||
const wallet = this.getWallet()
|
||||
const wallet = this.getWallet();
|
||||
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'PUT',
|
||||
'/nwcprovider/api/v1/nwc/' + keyPair.pubKey,
|
||||
"PUT",
|
||||
"/nwcprovider/api/v1/nwc/" + keyPair.pubKey,
|
||||
wallet.adminkey,
|
||||
data
|
||||
)
|
||||
this.closeConnectDialog()
|
||||
data,
|
||||
);
|
||||
this.closeConnectDialog();
|
||||
if (
|
||||
!response.data ||
|
||||
!response.data.data ||
|
||||
!response.data.data.pubkey
|
||||
) {
|
||||
LNbits.utils.notifyApiError('Error creating nwc pairing')
|
||||
return
|
||||
LNbits.utils.notifyApiError("Error creating nwc pairing");
|
||||
return;
|
||||
}
|
||||
this.showPairingDialog(keyPair.privKey)
|
||||
this.showPairingDialog(keyPair.privKey);
|
||||
} catch (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
LNbits.utils.notifyApiError(error);
|
||||
}
|
||||
this.loadNwcs()
|
||||
}
|
||||
this.loadNwcs();
|
||||
},
|
||||
},
|
||||
|
||||
created: function () {
|
||||
this.loadNwcs()
|
||||
this.loadNwcs();
|
||||
},
|
||||
watch: {
|
||||
selectedWallet(newValue, oldValue) {
|
||||
this.loadNwcs()
|
||||
}
|
||||
}
|
||||
})
|
||||
this.loadNwcs();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
479
static/js/noble-secp256k1.min.js
vendored
479
static/js/noble-secp256k1.min.js
vendored
|
|
@ -4,93 +4,93 @@ const B256 = 2n ** 256n,
|
|||
N = B256 - 0x14551231950b75fc4402da1732fc9bebfn,
|
||||
Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n,
|
||||
Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n,
|
||||
CURVE = {p: P, n: N, a: 0n, b: 7n, Gx, Gy},
|
||||
CURVE = { p: P, n: N, a: 0n, b: 7n, Gx, Gy },
|
||||
fLen = 32,
|
||||
crv = t => mod(mod(t * t) * t + CURVE.b),
|
||||
err = (t = '') => {
|
||||
throw new Error(t)
|
||||
crv = (t) => mod(mod(t * t) * t + CURVE.b),
|
||||
err = (t = "") => {
|
||||
throw new Error(t);
|
||||
},
|
||||
big = t => 'bigint' == typeof t,
|
||||
str = t => 'string' == typeof t,
|
||||
fe = t => big(t) && 0n < t && t < P,
|
||||
ge = t => big(t) && 0n < t && t < N,
|
||||
isu8 = t =>
|
||||
big = (t) => "bigint" == typeof t,
|
||||
str = (t) => "string" == typeof t,
|
||||
fe = (t) => big(t) && 0n < t && t < P,
|
||||
ge = (t) => big(t) && 0n < t && t < N,
|
||||
isu8 = (t) =>
|
||||
t instanceof Uint8Array ||
|
||||
(null != t && 'object' == typeof t && 'Uint8Array' === t.constructor.name),
|
||||
(null != t && "object" == typeof t && "Uint8Array" === t.constructor.name),
|
||||
au8 = (t, n) =>
|
||||
!isu8(t) || ('number' == typeof n && n > 0 && t.length !== n)
|
||||
? err('Uint8Array expected')
|
||||
!isu8(t) || ("number" == typeof n && n > 0 && t.length !== n)
|
||||
? err("Uint8Array expected")
|
||||
: t,
|
||||
u8n = t => new Uint8Array(t),
|
||||
u8n = (t) => new Uint8Array(t),
|
||||
toU8 = (t, n) => au8(str(t) ? h2b(t) : u8n(au8(t)), n),
|
||||
mod = (t, n = P) => {
|
||||
let e = t % n
|
||||
return e >= 0n ? e : n + e
|
||||
let e = t % n;
|
||||
return e >= 0n ? e : n + e;
|
||||
},
|
||||
isPoint = t => (t instanceof Point ? t : err('Point expected'))
|
||||
isPoint = (t) => (t instanceof Point ? t : err("Point expected"));
|
||||
class Point {
|
||||
constructor(t, n, e) {
|
||||
;((this.px = t), (this.py = n), (this.pz = e))
|
||||
(this.px = t), (this.py = n), (this.pz = e);
|
||||
}
|
||||
static fromAffine(t) {
|
||||
return 0n === t.x && 0n === t.y ? Point.ZERO : new Point(t.x, t.y, 1n)
|
||||
return 0n === t.x && 0n === t.y ? Point.ZERO : new Point(t.x, t.y, 1n);
|
||||
}
|
||||
static fromHex(t) {
|
||||
let n
|
||||
let n;
|
||||
const e = (t = toU8(t))[0],
|
||||
r = t.subarray(1),
|
||||
o = slcNum(r, 0, 32),
|
||||
i = t.length
|
||||
i = t.length;
|
||||
if (33 === i && [2, 3].includes(e)) {
|
||||
fe(o) || err('Point hex invalid: x not FE')
|
||||
let t = sqrt(crv(o))
|
||||
;(!(1 & ~e) !== (1n === (1n & t)) && (t = mod(-t)),
|
||||
(n = new Point(o, t, 1n)))
|
||||
fe(o) || err("Point hex invalid: x not FE");
|
||||
let t = sqrt(crv(o));
|
||||
!(1 & ~e) !== (1n === (1n & t)) && (t = mod(-t)),
|
||||
(n = new Point(o, t, 1n));
|
||||
}
|
||||
return (
|
||||
65 === i && 4 === e && (n = new Point(o, slcNum(r, 32, 64), 1n)),
|
||||
n ? n.ok() : err('Point is not on curve')
|
||||
)
|
||||
n ? n.ok() : err("Point is not on curve")
|
||||
);
|
||||
}
|
||||
static fromPrivateKey(t) {
|
||||
return G.mul(toPriv(t))
|
||||
return G.mul(toPriv(t));
|
||||
}
|
||||
get x() {
|
||||
return this.aff().x
|
||||
return this.aff().x;
|
||||
}
|
||||
get y() {
|
||||
return this.aff().y
|
||||
return this.aff().y;
|
||||
}
|
||||
equals(t) {
|
||||
const {px: n, py: e, pz: r} = this,
|
||||
{px: o, py: i, pz: s} = isPoint(t),
|
||||
const { px: n, py: e, pz: r } = this,
|
||||
{ px: o, py: i, pz: s } = isPoint(t),
|
||||
a = mod(n * s),
|
||||
c = mod(o * r),
|
||||
d = mod(e * s),
|
||||
u = mod(i * r)
|
||||
return a === c && d === u
|
||||
u = mod(i * r);
|
||||
return a === c && d === u;
|
||||
}
|
||||
negate() {
|
||||
return new Point(this.px, mod(-this.py), this.pz)
|
||||
return new Point(this.px, mod(-this.py), this.pz);
|
||||
}
|
||||
double() {
|
||||
return this.add(this)
|
||||
return this.add(this);
|
||||
}
|
||||
add(t) {
|
||||
const {px: n, py: e, pz: r} = this,
|
||||
{px: o, py: i, pz: s} = isPoint(t),
|
||||
{a, b: c} = CURVE
|
||||
const { px: n, py: e, pz: r } = this,
|
||||
{ px: o, py: i, pz: s } = isPoint(t),
|
||||
{ a, b: c } = CURVE;
|
||||
let d = 0n,
|
||||
u = 0n,
|
||||
m = 0n
|
||||
const h = mod(3n * c)
|
||||
m = 0n;
|
||||
const h = mod(3n * c);
|
||||
let l = mod(n * o),
|
||||
y = mod(e * i),
|
||||
f = mod(r * s),
|
||||
p = mod(n + e),
|
||||
b = mod(o + i)
|
||||
;((p = mod(p * b)), (b = mod(l + y)), (p = mod(p - b)), (b = mod(n + r)))
|
||||
let g = mod(o + s)
|
||||
b = mod(o + i);
|
||||
(p = mod(p * b)), (b = mod(l + y)), (p = mod(p - b)), (b = mod(n + r));
|
||||
let g = mod(o + s);
|
||||
return (
|
||||
(b = mod(b * g)),
|
||||
(g = mod(l + f)),
|
||||
|
|
@ -123,301 +123,300 @@ class Point {
|
|||
(m = mod(g * m)),
|
||||
(m = mod(m + l)),
|
||||
new Point(d, u, m)
|
||||
)
|
||||
);
|
||||
}
|
||||
mul(t, n = !0) {
|
||||
if (!n && 0n === t) return I
|
||||
if ((ge(t) || err('invalid scalar'), this.equals(G))) return wNAF(t).p
|
||||
if (!n && 0n === t) return I;
|
||||
if ((ge(t) || err("invalid scalar"), this.equals(G))) return wNAF(t).p;
|
||||
let e = I,
|
||||
r = G
|
||||
r = G;
|
||||
for (let o = this; t > 0n; o = o.double(), t >>= 1n)
|
||||
1n & t ? (e = e.add(o)) : n && (r = r.add(o))
|
||||
return e
|
||||
1n & t ? (e = e.add(o)) : n && (r = r.add(o));
|
||||
return e;
|
||||
}
|
||||
mulAddQUns(t, n, e) {
|
||||
return this.mul(n, !1).add(t.mul(e, !1)).ok()
|
||||
return this.mul(n, !1).add(t.mul(e, !1)).ok();
|
||||
}
|
||||
toAffine() {
|
||||
const {px: t, py: n, pz: e} = this
|
||||
if (this.equals(I)) return {x: 0n, y: 0n}
|
||||
if (1n === e) return {x: t, y: n}
|
||||
const r = inv(e)
|
||||
const { px: t, py: n, pz: e } = this;
|
||||
if (this.equals(I)) return { x: 0n, y: 0n };
|
||||
if (1n === e) return { x: t, y: n };
|
||||
const r = inv(e);
|
||||
return (
|
||||
1n !== mod(e * r) && err('invalid inverse'),
|
||||
{x: mod(t * r), y: mod(n * r)}
|
||||
)
|
||||
1n !== mod(e * r) && err("invalid inverse"),
|
||||
{ x: mod(t * r), y: mod(n * r) }
|
||||
);
|
||||
}
|
||||
assertValidity() {
|
||||
const {x: t, y: n} = this.aff()
|
||||
const { x: t, y: n } = this.aff();
|
||||
return (
|
||||
(fe(t) && fe(n)) || err('Point invalid: x or y'),
|
||||
mod(n * n) === crv(t) ? this : err('Point invalid: not on curve')
|
||||
)
|
||||
(fe(t) && fe(n)) || err("Point invalid: x or y"),
|
||||
mod(n * n) === crv(t) ? this : err("Point invalid: not on curve")
|
||||
);
|
||||
}
|
||||
multiply(t) {
|
||||
return this.mul(t)
|
||||
return this.mul(t);
|
||||
}
|
||||
aff() {
|
||||
return this.toAffine()
|
||||
return this.toAffine();
|
||||
}
|
||||
ok() {
|
||||
return this.assertValidity()
|
||||
return this.assertValidity();
|
||||
}
|
||||
toHex(t = !0) {
|
||||
const {x: n, y: e} = this.aff()
|
||||
const { x: n, y: e } = this.aff();
|
||||
return (
|
||||
(t ? (0n === (1n & e) ? '02' : '03') : '04') + n2h(n) + (t ? '' : n2h(e))
|
||||
)
|
||||
(t ? (0n === (1n & e) ? "02" : "03") : "04") + n2h(n) + (t ? "" : n2h(e))
|
||||
);
|
||||
}
|
||||
toRawBytes(t = !0) {
|
||||
return h2b(this.toHex(t))
|
||||
return h2b(this.toHex(t));
|
||||
}
|
||||
}
|
||||
;((Point.BASE = new Point(Gx, Gy, 1n)), (Point.ZERO = new Point(0n, 1n, 0n)))
|
||||
const {BASE: G, ZERO: I} = Point,
|
||||
padh = (t, n) => t.toString(16).padStart(n, '0'),
|
||||
b2h = t =>
|
||||
(Point.BASE = new Point(Gx, Gy, 1n)), (Point.ZERO = new Point(0n, 1n, 0n));
|
||||
const { BASE: G, ZERO: I } = Point,
|
||||
padh = (t, n) => t.toString(16).padStart(n, "0"),
|
||||
b2h = (t) =>
|
||||
Array.from(t)
|
||||
.map(t => padh(t, 2))
|
||||
.join(''),
|
||||
h2b = t => {
|
||||
const n = t.length
|
||||
;(!str(t) || n % 2) && err('hex invalid 1')
|
||||
const e = u8n(n / 2)
|
||||
.map((t) => padh(t, 2))
|
||||
.join(""),
|
||||
h2b = (t) => {
|
||||
const n = t.length;
|
||||
(!str(t) || n % 2) && err("hex invalid 1");
|
||||
const e = u8n(n / 2);
|
||||
for (let n = 0; n < e.length; n++) {
|
||||
const r = 2 * n,
|
||||
o = t.slice(r, r + 2),
|
||||
i = Number.parseInt(o, 16)
|
||||
;((Number.isNaN(i) || i < 0) && err('hex invalid 2'), (e[n] = i))
|
||||
i = Number.parseInt(o, 16);
|
||||
(Number.isNaN(i) || i < 0) && err("hex invalid 2"), (e[n] = i);
|
||||
}
|
||||
return e
|
||||
return e;
|
||||
},
|
||||
b2n = t => BigInt('0x' + (b2h(t) || '0')),
|
||||
b2n = (t) => BigInt("0x" + (b2h(t) || "0")),
|
||||
slcNum = (t, n, e) => b2n(t.slice(n, e)),
|
||||
n2b = t =>
|
||||
big(t) && t >= 0n && t < B256 ? h2b(padh(t, 64)) : err('bigint expected'),
|
||||
n2h = t => b2h(n2b(t)),
|
||||
n2b = (t) =>
|
||||
big(t) && t >= 0n && t < B256 ? h2b(padh(t, 64)) : err("bigint expected"),
|
||||
n2h = (t) => b2h(n2b(t)),
|
||||
concatB = (...t) => {
|
||||
const n = u8n(t.reduce((t, n) => t + au8(n).length, 0))
|
||||
let e = 0
|
||||
const n = u8n(t.reduce((t, n) => t + au8(n).length, 0));
|
||||
let e = 0;
|
||||
return (
|
||||
t.forEach(t => {
|
||||
;(n.set(t, e), (e += t.length))
|
||||
t.forEach((t) => {
|
||||
n.set(t, e), (e += t.length);
|
||||
}),
|
||||
n
|
||||
)
|
||||
);
|
||||
},
|
||||
inv = (t, n = P) => {
|
||||
;(0n === t || n <= 0n) && err('no inverse n=' + t + ' mod=' + n)
|
||||
(0n === t || n <= 0n) && err("no inverse n=" + t + " mod=" + n);
|
||||
let e = mod(t, n),
|
||||
r = n,
|
||||
o = 0n,
|
||||
i = 1n,
|
||||
s = 1n,
|
||||
a = 0n
|
||||
a = 0n;
|
||||
for (; 0n !== e; ) {
|
||||
const t = r / e,
|
||||
n = r % e,
|
||||
c = o - s * t,
|
||||
d = i - a * t
|
||||
;((r = e), (e = n), (o = s), (i = a), (s = c), (a = d))
|
||||
d = i - a * t;
|
||||
(r = e), (e = n), (o = s), (i = a), (s = c), (a = d);
|
||||
}
|
||||
return 1n === r ? mod(o, n) : err('no inverse')
|
||||
return 1n === r ? mod(o, n) : err("no inverse");
|
||||
},
|
||||
sqrt = t => {
|
||||
let n = 1n
|
||||
sqrt = (t) => {
|
||||
let n = 1n;
|
||||
for (let e = t, r = (P + 1n) / 4n; r > 0n; r >>= 1n)
|
||||
(1n & r && (n = (n * e) % P), (e = (e * e) % P))
|
||||
return mod(n * n) === t ? n : err('sqrt invalid')
|
||||
1n & r && (n = (n * e) % P), (e = (e * e) % P);
|
||||
return mod(n * n) === t ? n : err("sqrt invalid");
|
||||
},
|
||||
toPriv = t => (
|
||||
toPriv = (t) => (
|
||||
big(t) || (t = b2n(toU8(t, 32))),
|
||||
ge(t) ? t : err('private key out of range')
|
||||
ge(t) ? t : err("private key out of range")
|
||||
),
|
||||
moreThanHalfN = t => t > N >> 1n,
|
||||
getPublicKey = (t, n = !0) => Point.fromPrivateKey(t).toRawBytes(n)
|
||||
moreThanHalfN = (t) => t > N >> 1n,
|
||||
getPublicKey = (t, n = !0) => Point.fromPrivateKey(t).toRawBytes(n);
|
||||
class Signature {
|
||||
constructor(t, n, e) {
|
||||
;((this.r = t), (this.s = n), (this.recovery = e), this.assertValidity())
|
||||
(this.r = t), (this.s = n), (this.recovery = e), this.assertValidity();
|
||||
}
|
||||
static fromCompact(t) {
|
||||
return (
|
||||
(t = toU8(t, 64)),
|
||||
new Signature(slcNum(t, 0, 32), slcNum(t, 32, 64))
|
||||
)
|
||||
(t = toU8(t, 64)), new Signature(slcNum(t, 0, 32), slcNum(t, 32, 64))
|
||||
);
|
||||
}
|
||||
assertValidity() {
|
||||
return ge(this.r) && ge(this.s) ? this : err()
|
||||
return ge(this.r) && ge(this.s) ? this : err();
|
||||
}
|
||||
addRecoveryBit(t) {
|
||||
return new Signature(this.r, this.s, t)
|
||||
return new Signature(this.r, this.s, t);
|
||||
}
|
||||
hasHighS() {
|
||||
return moreThanHalfN(this.s)
|
||||
return moreThanHalfN(this.s);
|
||||
}
|
||||
normalizeS() {
|
||||
return this.hasHighS()
|
||||
? new Signature(this.r, mod(this.s, N), this.recovery)
|
||||
: this
|
||||
: this;
|
||||
}
|
||||
recoverPublicKey(t) {
|
||||
const {r: n, s: e, recovery: r} = this
|
||||
;[0, 1, 2, 3].includes(r) || err('recovery id invalid')
|
||||
const { r: n, s: e, recovery: r } = this;
|
||||
[0, 1, 2, 3].includes(r) || err("recovery id invalid");
|
||||
const o = bits2int_modN(toU8(t, 32)),
|
||||
i = 2 === r || 3 === r ? n + N : n
|
||||
i >= P && err('q.x invalid')
|
||||
const s = 1 & r ? '03' : '02',
|
||||
i = 2 === r || 3 === r ? n + N : n;
|
||||
i >= P && err("q.x invalid");
|
||||
const s = 1 & r ? "03" : "02",
|
||||
a = Point.fromHex(s + n2h(i)),
|
||||
c = inv(i, N),
|
||||
d = mod(-o * c, N),
|
||||
u = mod(e * c, N)
|
||||
return G.mulAddQUns(a, d, u)
|
||||
u = mod(e * c, N);
|
||||
return G.mulAddQUns(a, d, u);
|
||||
}
|
||||
toCompactRawBytes() {
|
||||
return h2b(this.toCompactHex())
|
||||
return h2b(this.toCompactHex());
|
||||
}
|
||||
toCompactHex() {
|
||||
return n2h(this.r) + n2h(this.s)
|
||||
return n2h(this.r) + n2h(this.s);
|
||||
}
|
||||
}
|
||||
const bits2int = t => {
|
||||
const bits2int = (t) => {
|
||||
const n = 8 * t.length - 256,
|
||||
e = b2n(t)
|
||||
return n > 0 ? e >> BigInt(n) : e
|
||||
e = b2n(t);
|
||||
return n > 0 ? e >> BigInt(n) : e;
|
||||
},
|
||||
bits2int_modN = t => mod(bits2int(t), N),
|
||||
i2o = t => n2b(t),
|
||||
bits2int_modN = (t) => mod(bits2int(t), N),
|
||||
i2o = (t) => n2b(t),
|
||||
cr = () =>
|
||||
'object' == typeof globalThis && 'crypto' in globalThis
|
||||
"object" == typeof globalThis && "crypto" in globalThis
|
||||
? globalThis.crypto
|
||||
: void 0
|
||||
let _hmacSync
|
||||
const optS = {lowS: !0},
|
||||
optV = {lowS: !0},
|
||||
: void 0;
|
||||
let _hmacSync;
|
||||
const optS = { lowS: !0 },
|
||||
optV = { lowS: !0 },
|
||||
prepSig = (t, n, e = optS) => {
|
||||
;['der', 'recovered', 'canonical'].some(t => t in e) &&
|
||||
err('sign() legacy options not supported')
|
||||
let {lowS: r} = e
|
||||
null == r && (r = !0)
|
||||
["der", "recovered", "canonical"].some((t) => t in e) &&
|
||||
err("sign() legacy options not supported");
|
||||
let { lowS: r } = e;
|
||||
null == r && (r = !0);
|
||||
const o = bits2int_modN(toU8(t)),
|
||||
i = i2o(o),
|
||||
s = toPriv(n),
|
||||
a = [i2o(s), i]
|
||||
let c = e.extraEntropy
|
||||
a = [i2o(s), i];
|
||||
let c = e.extraEntropy;
|
||||
if (c) {
|
||||
!0 === c && (c = etc.randomBytes(32))
|
||||
const t = toU8(c)
|
||||
;(32 !== t.length && err(), a.push(t))
|
||||
!0 === c && (c = etc.randomBytes(32));
|
||||
const t = toU8(c);
|
||||
32 !== t.length && err(), a.push(t);
|
||||
}
|
||||
const d = o
|
||||
const d = o;
|
||||
return {
|
||||
seed: concatB(...a),
|
||||
k2sig: t => {
|
||||
const n = bits2int(t)
|
||||
if (!ge(n)) return
|
||||
k2sig: (t) => {
|
||||
const n = bits2int(t);
|
||||
if (!ge(n)) return;
|
||||
const e = inv(n, N),
|
||||
o = G.mul(n).aff(),
|
||||
i = mod(o.x, N)
|
||||
if (0n === i) return
|
||||
const a = mod(e * mod(d + mod(s * i, N), N), N)
|
||||
if (0n === a) return
|
||||
i = mod(o.x, N);
|
||||
if (0n === i) return;
|
||||
const a = mod(e * mod(d + mod(s * i, N), N), N);
|
||||
if (0n === a) return;
|
||||
let c = a,
|
||||
u = (o.x === i ? 0 : 2) | Number(1n & o.y)
|
||||
u = (o.x === i ? 0 : 2) | Number(1n & o.y);
|
||||
return (
|
||||
r && moreThanHalfN(a) && ((c = mod(-a, N)), (u ^= 1)),
|
||||
new Signature(i, c, u)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
function hmacDrbg(t) {
|
||||
let n = u8n(32),
|
||||
e = u8n(32),
|
||||
r = 0
|
||||
r = 0;
|
||||
const o = () => {
|
||||
;(n.fill(1), e.fill(0), (r = 0))
|
||||
n.fill(1), e.fill(0), (r = 0);
|
||||
},
|
||||
i = 'drbg: tried 1000 values'
|
||||
i = "drbg: tried 1000 values";
|
||||
if (t) {
|
||||
const t = (...t) => etc.hmacSha256Async(e, n, ...t),
|
||||
s = async (r = u8n()) => {
|
||||
;((e = await t(u8n([0]), r)),
|
||||
(e = await t(u8n([0]), r)),
|
||||
(n = await t()),
|
||||
0 !== r.length && ((e = await t(u8n([1]), r)), (n = await t())))
|
||||
0 !== r.length && ((e = await t(u8n([1]), r)), (n = await t()));
|
||||
},
|
||||
a = async () => (r++ >= 1e3 && err(i), (n = await t()), n)
|
||||
a = async () => (r++ >= 1e3 && err(i), (n = await t()), n);
|
||||
return async (t, n) => {
|
||||
let e
|
||||
for (o(), await s(t); !(e = n(await a())); ) await s()
|
||||
return (o(), e)
|
||||
}
|
||||
let e;
|
||||
for (o(), await s(t); !(e = n(await a())); ) await s();
|
||||
return o(), e;
|
||||
};
|
||||
}
|
||||
{
|
||||
const t = (...t) => {
|
||||
const r = _hmacSync
|
||||
return (r || err('etc.hmacSha256Sync not set'), r(e, n, ...t))
|
||||
const r = _hmacSync;
|
||||
return r || err("etc.hmacSha256Sync not set"), r(e, n, ...t);
|
||||
},
|
||||
s = (r = u8n()) => {
|
||||
;((e = t(u8n([0]), r)),
|
||||
(e = t(u8n([0]), r)),
|
||||
(n = t()),
|
||||
0 !== r.length && ((e = t(u8n([1]), r)), (n = t())))
|
||||
0 !== r.length && ((e = t(u8n([1]), r)), (n = t()));
|
||||
},
|
||||
a = () => (r++ >= 1e3 && err(i), (n = t()), n)
|
||||
a = () => (r++ >= 1e3 && err(i), (n = t()), n);
|
||||
return (t, n) => {
|
||||
let e
|
||||
for (o(), s(t); !(e = n(a())); ) s()
|
||||
return (o(), e)
|
||||
}
|
||||
let e;
|
||||
for (o(), s(t); !(e = n(a())); ) s();
|
||||
return o(), e;
|
||||
};
|
||||
}
|
||||
}
|
||||
const signAsync = async (t, n, e = optS) => {
|
||||
const {seed: r, k2sig: o} = prepSig(t, n, e)
|
||||
return hmacDrbg(!0)(r, o)
|
||||
const { seed: r, k2sig: o } = prepSig(t, n, e);
|
||||
return hmacDrbg(!0)(r, o);
|
||||
},
|
||||
sign = (t, n, e = optS) => {
|
||||
const {seed: r, k2sig: o} = prepSig(t, n, e)
|
||||
return hmacDrbg(!1)(r, o)
|
||||
const { seed: r, k2sig: o } = prepSig(t, n, e);
|
||||
return hmacDrbg(!1)(r, o);
|
||||
},
|
||||
verify = (t, n, e, r = optV) => {
|
||||
let o,
|
||||
i,
|
||||
s,
|
||||
{lowS: a} = r
|
||||
;(null == a && (a = !0),
|
||||
'strict' in r && err('verify() legacy options not supported'))
|
||||
const c = t && 'object' == typeof t && 'r' in t
|
||||
c || 64 === toU8(t).length || err('signature must be 64 bytes')
|
||||
{ lowS: a } = r;
|
||||
null == a && (a = !0),
|
||||
"strict" in r && err("verify() legacy options not supported");
|
||||
const c = t && "object" == typeof t && "r" in t;
|
||||
c || 64 === toU8(t).length || err("signature must be 64 bytes");
|
||||
try {
|
||||
;((o = c
|
||||
(o = c
|
||||
? new Signature(t.r, t.s).assertValidity()
|
||||
: Signature.fromCompact(t)),
|
||||
(i = bits2int_modN(toU8(n))),
|
||||
(s = e instanceof Point ? e.ok() : Point.fromHex(e)))
|
||||
(s = e instanceof Point ? e.ok() : Point.fromHex(e));
|
||||
} catch (t) {
|
||||
return !1
|
||||
return !1;
|
||||
}
|
||||
if (!o) return !1
|
||||
const {r: d, s: u} = o
|
||||
if (a && moreThanHalfN(u)) return !1
|
||||
let m
|
||||
if (!o) return !1;
|
||||
const { r: d, s: u } = o;
|
||||
if (a && moreThanHalfN(u)) return !1;
|
||||
let m;
|
||||
try {
|
||||
const t = inv(u, N),
|
||||
n = mod(i * t, N),
|
||||
e = mod(d * t, N)
|
||||
m = G.mulAddQUns(s, n, e).aff()
|
||||
e = mod(d * t, N);
|
||||
m = G.mulAddQUns(s, n, e).aff();
|
||||
} catch (t) {
|
||||
return !1
|
||||
return !1;
|
||||
}
|
||||
if (!m) return !1
|
||||
return mod(m.x, N) === d
|
||||
if (!m) return !1;
|
||||
return mod(m.x, N) === d;
|
||||
},
|
||||
getSharedSecret = (t, n, e = !0) =>
|
||||
Point.fromHex(n).mul(toPriv(t)).toRawBytes(e),
|
||||
hashToPrivateKey = t => {
|
||||
;((t = toU8(t)).length < 40 || t.length > 1024) &&
|
||||
err('expected proper params')
|
||||
const n = mod(b2n(t), N - 1n) + 1n
|
||||
return n2b(n)
|
||||
hashToPrivateKey = (t) => {
|
||||
((t = toU8(t)).length < 40 || t.length > 1024) &&
|
||||
err("expected proper params");
|
||||
const n = mod(b2n(t), N - 1n) + 1n;
|
||||
return n2b(n);
|
||||
},
|
||||
etc = {
|
||||
hexToBytes: h2b,
|
||||
|
|
@ -429,84 +428,84 @@ const signAsync = async (t, n, e = optS) => {
|
|||
invert: inv,
|
||||
hmacSha256Async: async (t, ...n) => {
|
||||
const e = cr(),
|
||||
r = e && e.subtle
|
||||
if (!r) return err('etc.hmacSha256Async not set')
|
||||
r = e && e.subtle;
|
||||
if (!r) return err("etc.hmacSha256Async not set");
|
||||
const o = await r.importKey(
|
||||
'raw',
|
||||
"raw",
|
||||
t,
|
||||
{name: 'HMAC', hash: {name: 'SHA-256'}},
|
||||
{ name: "HMAC", hash: { name: "SHA-256" } },
|
||||
!1,
|
||||
['sign']
|
||||
)
|
||||
return u8n(await r.sign('HMAC', o, concatB(...n)))
|
||||
["sign"],
|
||||
);
|
||||
return u8n(await r.sign("HMAC", o, concatB(...n)));
|
||||
},
|
||||
hmacSha256Sync: _hmacSync,
|
||||
hashToPrivateKey,
|
||||
randomBytes: (t = 32) => {
|
||||
const n = cr()
|
||||
const n = cr();
|
||||
return (
|
||||
(n && n.getRandomValues) ||
|
||||
err('crypto.getRandomValues must be defined'),
|
||||
err("crypto.getRandomValues must be defined"),
|
||||
n.getRandomValues(u8n(t))
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
utils = {
|
||||
normPrivateKeyToScalar: toPriv,
|
||||
isValidPrivateKey: t => {
|
||||
isValidPrivateKey: (t) => {
|
||||
try {
|
||||
return !!toPriv(t)
|
||||
return !!toPriv(t);
|
||||
} catch (t) {
|
||||
return !1
|
||||
return !1;
|
||||
}
|
||||
},
|
||||
randomPrivateKey: () => hashToPrivateKey(etc.randomBytes(48)),
|
||||
precompute: (t = 8, n = G) => (n.multiply(3n), n)
|
||||
}
|
||||
precompute: (t = 8, n = G) => (n.multiply(3n), n),
|
||||
};
|
||||
Object.defineProperties(etc, {
|
||||
hmacSha256Sync: {
|
||||
configurable: !1,
|
||||
get: () => _hmacSync,
|
||||
set(t) {
|
||||
_hmacSync || (_hmacSync = t)
|
||||
}
|
||||
}
|
||||
})
|
||||
_hmacSync || (_hmacSync = t);
|
||||
},
|
||||
},
|
||||
});
|
||||
const W = 8,
|
||||
precompute = () => {
|
||||
const t = []
|
||||
const t = [];
|
||||
let n = G,
|
||||
e = n
|
||||
e = n;
|
||||
for (let r = 0; r < 33; r++) {
|
||||
;((e = n), t.push(e))
|
||||
for (let r = 1; r < 128; r++) ((e = e.add(n)), t.push(e))
|
||||
n = e.double()
|
||||
(e = n), t.push(e);
|
||||
for (let r = 1; r < 128; r++) (e = e.add(n)), t.push(e);
|
||||
n = e.double();
|
||||
}
|
||||
return t
|
||||
}
|
||||
let Gpows
|
||||
const wNAF = t => {
|
||||
return t;
|
||||
};
|
||||
let Gpows;
|
||||
const wNAF = (t) => {
|
||||
const n = Gpows || (Gpows = precompute()),
|
||||
e = (t, n) => {
|
||||
let e = n.negate()
|
||||
return t ? e : n
|
||||
}
|
||||
let e = n.negate();
|
||||
return t ? e : n;
|
||||
};
|
||||
let r = I,
|
||||
o = G
|
||||
o = G;
|
||||
const i = BigInt(255),
|
||||
s = BigInt(8)
|
||||
s = BigInt(8);
|
||||
for (let a = 0; a < 33; a++) {
|
||||
const c = 128 * a
|
||||
let d = Number(t & i)
|
||||
;((t >>= s), d > 128 && ((d -= 256), (t += 1n)))
|
||||
const c = 128 * a;
|
||||
let d = Number(t & i);
|
||||
(t >>= s), d > 128 && ((d -= 256), (t += 1n));
|
||||
const u = c,
|
||||
m = c + Math.abs(d) - 1,
|
||||
h = a % 2 != 0,
|
||||
l = d < 0
|
||||
0 === d ? (o = o.add(e(h, n[u]))) : (r = r.add(e(l, n[m])))
|
||||
l = d < 0;
|
||||
0 === d ? (o = o.add(e(h, n[u]))) : (r = r.add(e(l, n[m])));
|
||||
}
|
||||
return {p: r, f: o}
|
||||
}
|
||||
return { p: r, f: o };
|
||||
};
|
||||
export {
|
||||
getPublicKey,
|
||||
sign,
|
||||
|
|
@ -517,5 +516,5 @@ export {
|
|||
etc,
|
||||
utils,
|
||||
Point as ProjectivePoint,
|
||||
Signature
|
||||
}
|
||||
Signature,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,56 +6,21 @@
|
|||
<div class="col-12 q-gutter-y-md">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row items-center wrap q-mb-md">
|
||||
<div class="row items-center no-wrap q-mb-md">
|
||||
<div class="col">
|
||||
<h5 class="text-subtitle1 q-my-none">
|
||||
NWC Service Provider Configuration
|
||||
NWC Service Provider - Config
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
<q-markup-table flat wrap-cells="true">
|
||||
<q-markup-table flat>
|
||||
<tbody>
|
||||
<q-tr>
|
||||
<q-tr v-for="entry in entries" :key="entry.key">
|
||||
<q-td>
|
||||
<q-input
|
||||
v-model="config.relay"
|
||||
label="Nostr Relay URL"
|
||||
filled
|
||||
wrap
|
||||
:hint="'URL of the Nostr relay for dispatching and receiving NWC events. Use public relays or a custom one. Specify `nostrclient` to use the Nostr Client extension'"
|
||||
>
|
||||
</q-input>
|
||||
<span> ${entry.key} </span>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
<q-tr>
|
||||
<q-td>
|
||||
<q-input
|
||||
filled
|
||||
label="Relay Alias"
|
||||
v-model="config.relay_alias"
|
||||
:hint="'Relay URL to display in pairing URLs. If your relay has a different public URL than the one set in \'Nostr Relay URL\' set it here.'"
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
<q-tr>
|
||||
<q-td>
|
||||
<q-input
|
||||
filled
|
||||
label="NWC Provider Secret Key "
|
||||
v-model="config.provider_key"
|
||||
:hint="'The secret key for the NWC Service Provider. You don\'t need to change this unless your key has been compromised.'"
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
<q-tr>
|
||||
<q-td>
|
||||
<q-input
|
||||
filled
|
||||
label="Time Period To Handle Missed Events"
|
||||
v-model="config.handle_missed_events"
|
||||
type="number"
|
||||
:hint="'Number of seconds to look back for processing events missed while offline. Setting it to 0 disables this functionality.'"
|
||||
/>
|
||||
<q-input v-model="entry.value" />
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
|
||||
%} {% block scripts %} {{ window_vars(user) }}
|
||||
<script type="module">
|
||||
import * as NobleSecp256k1 from '/nwcprovider/static/js/noble-secp256k1.min.js'
|
||||
window.NobleSecp256k1 = NobleSecp256k1
|
||||
import * as NobleSecp256k1 from "/nwcprovider/static/js/noble-secp256k1.min.js";
|
||||
window.NobleSecp256k1 = NobleSecp256k1;
|
||||
</script>
|
||||
<script src="{{ static_url_for('nwcprovider/static', path='js/index.js') }}"></script>
|
||||
{% endblock %} {% block page %}
|
||||
|
|
|
|||
|
|
@ -997,32 +997,23 @@ async def create_valid_invoice(wallet, amount=1000):
|
|||
raise Exception(f"Failed to create invoice: {error}")
|
||||
return result["invoice"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_transactions():
|
||||
# Create wallets with required permissions
|
||||
nwc1 = await create_nwc(
|
||||
"wallet1",
|
||||
"test_list_transactions",
|
||||
["invoice", "pay", "balance", "history"],
|
||||
[],
|
||||
0,
|
||||
"wallet1", "test_list_transactions", ["invoice", "pay", "balance", "history"], [], 0
|
||||
)
|
||||
nwc2 = await create_nwc(
|
||||
"wallet2",
|
||||
"test_list_transactions",
|
||||
["invoice", "pay", "balance", "history"],
|
||||
[],
|
||||
0,
|
||||
"wallet2", "test_list_transactions", ["invoice", "pay", "balance", "history"], [], 0
|
||||
)
|
||||
|
||||
|
||||
wallet1 = NWCWallet(nwc1["pairing"])
|
||||
wallet2 = NWCWallet(nwc2["pairing"])
|
||||
|
||||
|
||||
try:
|
||||
await wallet1.start()
|
||||
await wallet2.start()
|
||||
|
||||
|
||||
# First invoice
|
||||
await wallet1.send_event(
|
||||
"make_invoice", {"amount": 1000, "description": "test invoice 1"}
|
||||
|
|
@ -1030,12 +1021,12 @@ async def test_list_transactions():
|
|||
result1, _, error = await wallet1.wait_for("make_invoice")
|
||||
assert not error
|
||||
invoice1 = result1["invoice"]
|
||||
|
||||
|
||||
# Pay first invoice
|
||||
await wallet2.send_event("pay_invoice", {"invoice": invoice1})
|
||||
_, _, error = await wallet2.wait_for("pay_invoice")
|
||||
assert not error
|
||||
|
||||
|
||||
# Second invoice
|
||||
await wallet1.send_event(
|
||||
"make_invoice", {"amount": 2000, "description": "test invoice 2"}
|
||||
|
|
@ -1043,12 +1034,12 @@ async def test_list_transactions():
|
|||
result2, _, error = await wallet1.wait_for("make_invoice")
|
||||
assert not error
|
||||
invoice2 = result2["invoice"]
|
||||
|
||||
|
||||
# Pay second invoice
|
||||
await wallet2.send_event("pay_invoice", {"invoice": invoice2})
|
||||
_, _, error = await wallet2.wait_for("pay_invoice")
|
||||
assert not error
|
||||
|
||||
|
||||
# Test basic transaction listing
|
||||
await wallet1.send_event("list_transactions", {})
|
||||
result, _, error = await wallet1.wait_for("list_transactions")
|
||||
|
|
@ -1056,14 +1047,14 @@ async def test_list_transactions():
|
|||
assert "transactions" in result
|
||||
transactions = result["transactions"]
|
||||
assert len(transactions) >= 2
|
||||
|
||||
|
||||
# Test limit
|
||||
await wallet1.send_event("list_transactions", {"limit": 1})
|
||||
result, _, error = await wallet1.wait_for("list_transactions")
|
||||
assert not error
|
||||
limited_txs = result["transactions"]
|
||||
assert len(limited_txs) == 1
|
||||
|
||||
|
||||
finally:
|
||||
await wallet1.close()
|
||||
await wallet2.close()
|
||||
await wallet2.close()
|
||||
Loading…
Add table
Add a link
Reference in a new issue