diff --git a/lnbits/extensions/watchonly/README.md b/lnbits/extensions/watchonly/README.md
deleted file mode 100644
index 4a8f2841..00000000
--- a/lnbits/extensions/watchonly/README.md
+++ /dev/null
@@ -1,87 +0,0 @@
-# Onchain Wallet (watch-only)
-
-## Monitor an onchain wallet and generate addresses for onchain payments
-
-Monitor an extended public key and generate deterministic fresh public keys with this simple watch only wallet. Invoice payments can also be generated, both through a publically shareable page and API.
-
-You can now use this wallet on the LNbits [SatsPayServer](https://github.com/lnbits/lnbits/blob/master/lnbits/extensions/satspay/README.md) extension
-
-Video demo
-
-### Wallet Account
- - a user can add one or more `xPubs` or `descriptors`
- - the `xPub` must be unique per user
- - such and entry is called an `Wallet Account`
- - the addresses in a `Wallet Account` are split into `Receive Addresses` and `Change Address`
- - the user interacts directly only with the `Receive Addresses` (by sharing them)
- - see [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account-discovery) for more details
- - same `xPub` will always generate the same addresses (deterministic)
- - when a `Wallet Account` is created, there are generated `20 Receive Addresses` and `5 Change Address`
- - the limits can be change from the `Config` page (see `screenshot 1`)
- - regular wallets only scan up to `20` empty receive addresses. If the user generates addresses beyond this limit a warning is shown (see `screenshot 4`)
- - an account can be added `From Hardware Device`
-
-### Scan Blockchain
- - when the user clicks `Scan Blockchain`, the wallet will loop over the all addresses (for each account)
- - if funds are found, then the list is extended
- - will scan addresses for all wallet accounts
- - the search is done on the client-side (using the `mempool.space` API). `mempool.space` has a limit on the number of req/sec, therefore it is expected for the scanning to start fast, but slow down as more HTTP requests have to be retried
- - addresses can also be rescanned individually form the `Address Details` section (`Addresses` tab) of each address
-
-### New Receive Address
- - the `New Receive Address` button show the user the NEXT un-used address
- - un-used means funds have not already been sent to that address AND the address has not already been shared
- - internally there is a counter that keeps track of the last shared address
- - it is possible to add a `Note` to each address in order to remember when/with whom it was shared
- - mind the gap (`screenshot 4`)
-
-### Addresses Tab
-- the `Addresses` tab contains a list with the addresses for all the `Wallet Accounts`
- - only one entry per address will be shown (even if there are multiple UTXOs at that address)
- - several filter criteria can be applied
- - unconfirmed funds are also taken into account
- - `Address Details` can be viewed by clicking the `Expand` button
-
-### History Tap
- - shows the chronological order of transactions
- - it shows unconfirmed transactions at the top
- - it can be exported as CSV file
-
-### Coins Tab
- - shows the UTXOs for all wallets
- - there can be multiple UTXOs for the same address
-
-### New Payment
- - create a new `Partially Signed Bitcoin Transaction`
- - multiple `Send Addresses` can be added
- - the `Max` button next to an address is for sending the remaining funds to this address (no change)
- - the user can select the inputs (UTXOs) manually, or it can use of the basic selection algorithms
- - amounts have to be provided for the `Send Addresses` beforehand (so the algorithm knows the amount to be selected)
- - `Show Change` allows to select from which account the change address will be selected (defaults to the first one)
- - `Show Custom Fee` allows to manually select the fee
- - it defaults to the `Medium` value at the moment the `New Payment` button was clicked
- - it can be refreshed
- - warnings are shown if the fee is too Low or to High
-
-### Check & Send
- - creates the PSBT and sends it to the Hardware Wallet
- - a confirmation will be shown for each Output and for the Fee
- - after the user confirms the addresses and amounts, the transaction will be signed on the Hardware Device
-
-### Share PSBT
- - Show the PSBT without sending it to the Hardware Wallet
-
-## Screensots
-- screenshot 1:
-
-
-- screenshot 2:
-
-
-- screenshot 3:
-
-
-- screenshot 4:
-
-
-
diff --git a/lnbits/extensions/watchonly/__init__.py b/lnbits/extensions/watchonly/__init__.py
deleted file mode 100644
index 49adb462..00000000
--- a/lnbits/extensions/watchonly/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from fastapi import APIRouter
-from fastapi.staticfiles import StaticFiles
-
-from lnbits.db import Database
-from lnbits.helpers import template_renderer
-
-db = Database("ext_watchonly")
-
-watchonly_static_files = [
- {
- "path": "/watchonly/static",
- "app": StaticFiles(directory="lnbits/extensions/watchonly/static"),
- "name": "watchonly_static",
- }
-]
-
-watchonly_ext: APIRouter = APIRouter(prefix="/watchonly", tags=["watchonly"])
-
-
-def watchonly_renderer():
- return template_renderer(["lnbits/extensions/watchonly/templates"])
-
-
-from .views import * # noqa: F401,F403
-from .views_api import * # noqa: F401,F403
diff --git a/lnbits/extensions/watchonly/config.json b/lnbits/extensions/watchonly/config.json
deleted file mode 100644
index c9fec893..00000000
--- a/lnbits/extensions/watchonly/config.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "Onchain Wallet",
- "short_description": "Onchain watch only wallets",
- "tile": "/watchonly/static/bitcoin-wallet.png",
- "contributors": [
- "arcbtc",
- "motorina0"
- ]
-}
diff --git a/lnbits/extensions/watchonly/crud.py b/lnbits/extensions/watchonly/crud.py
deleted file mode 100644
index 8472fa01..00000000
--- a/lnbits/extensions/watchonly/crud.py
+++ /dev/null
@@ -1,246 +0,0 @@
-import json
-from typing import List, Optional
-
-from lnbits.helpers import urlsafe_short_hash
-
-from . import db
-from .helpers import derive_address
-from .models import Address, Config, WalletAccount
-
-##########################WALLETS####################
-
-
-async def create_watch_wallet(user: str, w: WalletAccount) -> WalletAccount:
- wallet_id = urlsafe_short_hash()
- await db.execute(
- """
- INSERT INTO watchonly.wallets (
- id,
- "user",
- masterpub,
- fingerprint,
- title,
- type,
- address_no,
- balance,
- network,
- meta
- )
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- """,
- (
- wallet_id,
- user,
- w.masterpub,
- w.fingerprint,
- w.title,
- w.type,
- w.address_no,
- w.balance,
- w.network,
- w.meta,
- ),
- )
- wallet = await get_watch_wallet(wallet_id)
- assert wallet
- return wallet
-
-
-async def get_watch_wallet(wallet_id: str) -> Optional[WalletAccount]:
- row = await db.fetchone(
- "SELECT * FROM watchonly.wallets WHERE id = ?", (wallet_id,)
- )
- return WalletAccount.from_row(row) if row else None
-
-
-async def get_watch_wallets(user: str, network: str) -> List[WalletAccount]:
- rows = await db.fetchall(
- """SELECT * FROM watchonly.wallets WHERE "user" = ? AND network = ?""",
- (user, network),
- )
- return [WalletAccount(**row) for row in rows]
-
-
-async def update_watch_wallet(wallet_id: str, **kwargs) -> Optional[WalletAccount]:
- q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
-
- await db.execute(
- f"UPDATE watchonly.wallets SET {q} WHERE id = ?", (*kwargs.values(), wallet_id)
- )
- row = await db.fetchone(
- "SELECT * FROM watchonly.wallets WHERE id = ?", (wallet_id,)
- )
- return WalletAccount.from_row(row) if row else None
-
-
-async def delete_watch_wallet(wallet_id: str) -> None:
- await db.execute("DELETE FROM watchonly.wallets WHERE id = ?", (wallet_id,))
-
-
-########################ADDRESSES#######################
-
-
-async def get_fresh_address(wallet_id: str) -> Optional[Address]:
- # todo: move logic to views_api after satspay refactoring
- wallet = await get_watch_wallet(wallet_id)
-
- if not wallet:
- return None
-
- wallet_addresses = await get_addresses(wallet_id)
- receive_addresses = list(
- filter(
- lambda addr: addr.branch_index == 0 and addr.has_activity, wallet_addresses
- )
- )
- last_receive_index = (
- receive_addresses.pop().address_index if receive_addresses else -1
- )
- address_index = (
- last_receive_index
- if last_receive_index > wallet.address_no
- else wallet.address_no
- )
-
- address = await get_address_at_index(wallet_id, 0, address_index + 1)
-
- if not address:
- addresses = await create_fresh_addresses(
- wallet_id, address_index + 1, address_index + 2
- )
- address = addresses.pop()
-
- await update_watch_wallet(wallet_id, **{"address_no": address_index + 1})
-
- return address
-
-
-async def create_fresh_addresses(
- wallet_id: str,
- start_address_index: int,
- end_address_index: int,
- change_address=False,
-) -> List[Address]:
- if start_address_index > end_address_index:
- return []
-
- wallet = await get_watch_wallet(wallet_id)
- if not wallet:
- return []
-
- branch_index = 1 if change_address else 0
-
- for address_index in range(start_address_index, end_address_index):
- address = await derive_address(wallet.masterpub, address_index, branch_index)
-
- await db.execute(
- """
- INSERT INTO watchonly.addresses (
- id,
- address,
- wallet,
- amount,
- branch_index,
- address_index
- )
- VALUES (?, ?, ?, ?, ?, ?)
- """,
- (urlsafe_short_hash(), address, wallet_id, 0, branch_index, address_index),
- )
-
- # return fresh addresses
- rows = await db.fetchall(
- """
- SELECT * FROM watchonly.addresses
- WHERE wallet = ? AND branch_index = ? AND address_index >= ? AND address_index < ?
- ORDER BY branch_index, address_index
- """,
- (wallet_id, branch_index, start_address_index, end_address_index),
- )
-
- return [Address(**row) for row in rows]
-
-
-async def get_address(address: str) -> Optional[Address]:
- row = await db.fetchone(
- "SELECT * FROM watchonly.addresses WHERE address = ?", (address,)
- )
- return Address.from_row(row) if row else None
-
-
-async def get_address_at_index(
- wallet_id: str, branch_index: int, address_index: int
-) -> Optional[Address]:
- row = await db.fetchone(
- """
- SELECT * FROM watchonly.addresses
- WHERE wallet = ? AND branch_index = ? AND address_index = ?
- """,
- (
- wallet_id,
- branch_index,
- address_index,
- ),
- )
- return Address.from_row(row) if row else None
-
-
-async def get_addresses(wallet_id: str) -> List[Address]:
- rows = await db.fetchall(
- """
- SELECT * FROM watchonly.addresses WHERE wallet = ?
- ORDER BY branch_index, address_index
- """,
- (wallet_id,),
- )
-
- return [Address(**row) for row in rows]
-
-
-async def update_address(id: str, **kwargs) -> Optional[Address]:
- q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
-
- await db.execute(
- f"""UPDATE watchonly.addresses SET {q} WHERE id = ? """,
- (*kwargs.values(), id),
- )
- row = await db.fetchone("SELECT * FROM watchonly.addresses WHERE id = ?", (id,))
- return Address.from_row(row) if row else None
-
-
-async def delete_addresses_for_wallet(wallet_id: str) -> None:
- await db.execute("DELETE FROM watchonly.addresses WHERE wallet = ?", (wallet_id,))
-
-
-######################CONFIG#######################
-async def create_config(user: str) -> Config:
- config = Config()
- await db.execute(
- """
- INSERT INTO watchonly.config ("user", json_data)
- VALUES (?, ?)
- """,
- (user, json.dumps(config.dict())),
- )
- row = await db.fetchone(
- """SELECT json_data FROM watchonly.config WHERE "user" = ?""", (user,)
- )
- return json.loads(row[0], object_hook=lambda d: Config(**d))
-
-
-async def update_config(config: Config, user: str) -> Optional[Config]:
- await db.execute(
- """UPDATE watchonly.config SET json_data = ? WHERE "user" = ?""",
- (json.dumps(config.dict()), user),
- )
- row = await db.fetchone(
- """SELECT json_data FROM watchonly.config WHERE "user" = ?""", (user,)
- )
- return json.loads(row[0], object_hook=lambda d: Config(**d))
-
-
-async def get_config(user: str) -> Optional[Config]:
- row = await db.fetchone(
- """SELECT json_data FROM watchonly.config WHERE "user" = ?""", (user,)
- )
- return json.loads(row[0], object_hook=lambda d: Config(**d)) if row else None
diff --git a/lnbits/extensions/watchonly/helpers.py b/lnbits/extensions/watchonly/helpers.py
deleted file mode 100644
index 0ac36454..00000000
--- a/lnbits/extensions/watchonly/helpers.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from typing import Optional, Tuple
-
-from embit.descriptor import Descriptor, Key
-from embit.descriptor.arguments import AllowedDerivation
-from embit.networks import NETWORKS
-
-
-def detect_network(k):
- version = k.key.version
- for network_name in NETWORKS:
- net = NETWORKS[network_name]
- # not found in this network
- if version in [net["xpub"], net["ypub"], net["zpub"], net["Zpub"], net["Ypub"]]:
- return net
-
-
-def parse_key(masterpub: str) -> Tuple[Descriptor, Optional[dict]]:
- """Parses masterpub or descriptor and returns a tuple: (Descriptor, network)
- To create addresses use descriptor.derive(num).address(network=network)
- """
- network = None
- desc = None
- # probably a single key
- if "(" not in masterpub:
- k = Key.from_string(masterpub)
- if not k.is_extended:
- raise ValueError("The key is not a master public key")
- if k.is_private:
- raise ValueError("Private keys are not allowed")
- # check depth
- if k.key.depth != 3:
- raise ValueError(
- "Non-standard depth. Only bip44, bip49 and bip84 are supported with bare xpubs. For custom derivation paths use descriptors."
- )
- # if allowed derivation is not provided use default /{0,1}/*
- if k.allowed_derivation is None:
- k.allowed_derivation = AllowedDerivation.default()
- # get version bytes
- version = k.key.version
- for network_name in NETWORKS:
- net = NETWORKS[network_name]
- # not found in this network
- if version in [net["xpub"], net["ypub"], net["zpub"]]:
- network = net
- if version == net["xpub"]:
- desc = Descriptor.from_string("pkh(%s)" % str(k))
- elif version == net["ypub"]:
- desc = Descriptor.from_string("sh(wpkh(%s))" % str(k))
- elif version == net["zpub"]:
- desc = Descriptor.from_string("wpkh(%s)" % str(k))
- break
- # we didn't find correct version
- if not network:
- raise ValueError("Unknown master public key version")
- if not desc:
- raise ValueError("descriptor not found, because version did not match")
-
- else:
- desc = Descriptor.from_string(masterpub)
- if not desc.is_wildcard:
- raise ValueError("Descriptor should have wildcards")
- for k in desc.keys:
- if k.is_extended:
- net = detect_network(k)
- if net is None:
- raise ValueError(f"Unknown version: {k}")
- if network is not None and network != net:
- raise ValueError("Keys from different networks")
- network = net
-
- return desc, network
-
-
-async def derive_address(masterpub: str, num: int, branch_index=0):
- desc, network = parse_key(masterpub)
- return desc.derive(num, branch_index).address(network=network)
diff --git a/lnbits/extensions/watchonly/migrations.py b/lnbits/extensions/watchonly/migrations.py
deleted file mode 100644
index bbef40a8..00000000
--- a/lnbits/extensions/watchonly/migrations.py
+++ /dev/null
@@ -1,102 +0,0 @@
-async def m001_initial(db):
- """
- Initial wallet table.
- """
- await db.execute(
- f"""
- CREATE TABLE watchonly.wallets (
- id TEXT NOT NULL PRIMARY KEY,
- "user" TEXT,
- masterpub TEXT NOT NULL,
- title TEXT NOT NULL,
- address_no INTEGER NOT NULL DEFAULT 0,
- balance {db.big_int} NOT NULL
- );
- """
- )
-
- await db.execute(
- f"""
- CREATE TABLE watchonly.addresses (
- id TEXT NOT NULL PRIMARY KEY,
- address TEXT NOT NULL,
- wallet TEXT NOT NULL,
- amount {db.big_int} NOT NULL
- );
- """
- )
-
- await db.execute(
- """
- CREATE TABLE watchonly.mempool (
- "user" TEXT NOT NULL,
- endpoint TEXT NOT NULL
- );
- """
- )
-
-
-async def m002_add_columns_to_adresses(db):
- """
- Add 'branch_index', 'address_index', 'has_activity' and 'note' columns to the 'addresses' table
- """
-
- await db.execute(
- "ALTER TABLE watchonly.addresses ADD COLUMN branch_index INTEGER NOT NULL DEFAULT 0;"
- )
- await db.execute(
- "ALTER TABLE watchonly.addresses ADD COLUMN address_index INTEGER NOT NULL DEFAULT 0;"
- )
- await db.execute(
- "ALTER TABLE watchonly.addresses ADD COLUMN has_activity BOOLEAN DEFAULT false;"
- )
- await db.execute("ALTER TABLE watchonly.addresses ADD COLUMN note TEXT;")
-
-
-async def m003_add_columns_to_wallets(db):
- """
- Add 'type' and 'fingerprint' columns to the 'wallets' table
- """
-
- await db.execute("ALTER TABLE watchonly.wallets ADD COLUMN type TEXT;")
- await db.execute(
- "ALTER TABLE watchonly.wallets ADD COLUMN fingerprint TEXT NOT NULL DEFAULT '';"
- )
-
-
-async def m004_create_config_table(db):
- """
- Allow the extension to persist and retrieve any number of config values.
- Each user has its configurations saved as a JSON string
- """
-
- await db.execute(
- """CREATE TABLE watchonly.config (
- "user" TEXT NOT NULL,
- json_data TEXT NOT NULL
- );"""
- )
-
-
-async def m005_add_network_column_to_wallets(db):
- """
- Add network' column to the 'wallets' table
- """
-
- await db.execute(
- "ALTER TABLE watchonly.wallets ADD COLUMN network TEXT DEFAULT 'Mainnet';"
- )
-
-
-async def m006_drop_mempool_table(db):
- """
- Mempool data is now part of `config`
- """
- await db.execute("DROP TABLE watchonly.mempool;")
-
-
-async def m007_add_wallet_meta_data(db):
- """
- Add 'meta' for storing various metadata about the wallet
- """
- await db.execute("ALTER TABLE watchonly.wallets ADD COLUMN meta TEXT DEFAULT '{}';")
diff --git a/lnbits/extensions/watchonly/models.py b/lnbits/extensions/watchonly/models.py
deleted file mode 100644
index 24d63bfd..00000000
--- a/lnbits/extensions/watchonly/models.py
+++ /dev/null
@@ -1,99 +0,0 @@
-from sqlite3 import Row
-from typing import List, Optional
-
-from fastapi import Query
-from pydantic import BaseModel
-
-
-class CreateWallet(BaseModel):
- masterpub: str = Query("")
- title: str = Query("")
- network: str = "Mainnet"
- meta: str = "{}"
-
-
-class WalletAccount(BaseModel):
- id: str
- masterpub: str
- fingerprint: str
- title: str
- address_no: int
- balance: int
- type: Optional[str] = ""
- network: str = "Mainnet"
- meta: str = "{}"
-
- @classmethod
- def from_row(cls, row: Row) -> "WalletAccount":
- return cls(**dict(row))
-
-
-class Address(BaseModel):
- id: str
- address: str
- wallet: str
- amount: int = 0
- branch_index: int = 0
- address_index: int
- note: Optional[str] = None
- has_activity: bool = False
-
- @classmethod
- def from_row(cls, row: Row) -> "Address":
- return cls(**dict(row))
-
-
-class TransactionInput(BaseModel):
- tx_id: str
- vout: int
- amount: int
- address: str
- branch_index: int
- address_index: int
- wallet: str
- tx_hex: str
-
-
-class TransactionOutput(BaseModel):
- amount: int
- address: str
- branch_index: Optional[int] = None
- address_index: Optional[int] = None
- wallet: Optional[str] = None
-
-
-class MasterPublicKey(BaseModel):
- id: str
- public_key: str
- fingerprint: str
-
-
-class CreatePsbt(BaseModel):
- masterpubs: List[MasterPublicKey]
- inputs: List[TransactionInput]
- outputs: List[TransactionOutput]
- fee_rate: int
- tx_size: int
-
-
-class SerializedTransaction(BaseModel):
- tx_hex: str
-
-
-class ExtractPsbt(BaseModel):
- psbtBase64 = "" # // todo snake case
- inputs: List[SerializedTransaction]
- network = "Mainnet"
-
-
-class SignedTransaction(BaseModel):
- tx_hex: Optional[str]
- tx_json: Optional[str]
-
-
-class Config(BaseModel):
- mempool_endpoint = "https://mempool.space"
- receive_gap_limit = 20
- change_gap_limit = 5
- sats_denominated = True
- network = "Mainnet"
diff --git a/lnbits/extensions/watchonly/static/bitcoin-wallet.png b/lnbits/extensions/watchonly/static/bitcoin-wallet.png
deleted file mode 100644
index 3cd5ac0f..00000000
Binary files a/lnbits/extensions/watchonly/static/bitcoin-wallet.png and /dev/null differ
diff --git a/lnbits/extensions/watchonly/static/components/address-list/address-list.html b/lnbits/extensions/watchonly/static/components/address-list/address-list.html
deleted file mode 100644
index f397ee97..00000000
--- a/lnbits/extensions/watchonly/static/components/address-list/address-list.html
+++ /dev/null
@@ -1,215 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{satBtc(props.row.amount)}}
-
-
-
- {{props.row.note}}
-
-
- {{getWalletName(props.row.wallet)}}
-
-
-
-
-
-
-
-
- QR Code
-
-
- Copy
-
-
-
- Rescan
-
-
- History
-
-
- View Coins
-
-
-
-
-
Note:
-
-
-
-
- Update
-
-
-
-
-
-
-
- {{props.row.error}}
-
-
-
-
-
- Gap limit of 20 addresses exceeded. Other wallets might not
- detect funds at this address.
-
-
-
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/address-list/address-list.js b/lnbits/extensions/watchonly/static/components/address-list/address-list.js
deleted file mode 100644
index 61545df0..00000000
--- a/lnbits/extensions/watchonly/static/components/address-list/address-list.js
+++ /dev/null
@@ -1,131 +0,0 @@
-async function addressList(path) {
- const template = await loadTemplateAsync(path)
- Vue.component('address-list', {
- name: 'address-list',
- template,
-
- props: [
- 'addresses',
- 'accounts',
- 'mempool-endpoint',
- 'inkey',
- 'sats-denominated'
- ],
- data: function () {
- return {
- show: false,
- history: [],
- selectedWallet: null,
- note: '',
- filterOptions: [
- 'Show Change Addresses',
- 'Show Gap Addresses',
- 'Only With Amount'
- ],
- filterValues: [],
-
- addressesTable: {
- columns: [
- {
- name: 'expand',
- align: 'left',
- label: ''
- },
- {
- name: 'address',
- align: 'left',
- label: 'Address',
- field: 'address',
- sortable: true
- },
- {
- name: 'amount',
- align: 'left',
- label: 'Amount',
- field: 'amount',
- sortable: true
- },
- {
- name: 'note',
- align: 'left',
- label: 'Note',
- field: 'note',
- sortable: true
- },
- {
- name: 'wallet',
- align: 'left',
- label: 'Account',
- field: 'wallet',
- sortable: true
- }
- ],
- pagination: {
- rowsPerPage: 0,
- sortBy: 'amount',
- descending: true
- },
- filter: ''
- }
- }
- },
-
- methods: {
- satBtc(val, showUnit = true) {
- return satOrBtc(val, showUnit, this.satsDenominated)
- },
- // todo: bad. base.js not present in custom components
- copyText: function (text, message, position) {
- var notify = this.$q.notify
- Quasar.utils.copyToClipboard(text).then(function () {
- notify({
- message: message || 'Copied to clipboard!',
- position: position || 'bottom'
- })
- })
- },
- getWalletName: function (walletId) {
- const wallet = (this.accounts || []).find(wl => wl.id === walletId)
- return wallet ? wallet.title : 'unknown'
- },
- getFilteredAddresses: function () {
- const selectedWalletId = this.selectedWallet?.id
- const filter = this.filterValues || []
- const includeChangeAddrs = filter.includes('Show Change Addresses')
- const includeGapAddrs = filter.includes('Show Gap Addresses')
- const excludeNoAmount = filter.includes('Only With Amount')
-
- const walletsLimit = (this.accounts || []).reduce((r, w) => {
- r[`_${w.id}`] = w.address_no
- return r
- }, {})
-
- const fAddresses = this.addresses.filter(
- a =>
- (includeChangeAddrs || !a.isChange) &&
- (includeGapAddrs ||
- a.isChange ||
- a.addressIndex <= walletsLimit[`_${a.wallet}`]) &&
- !(excludeNoAmount && a.amount === 0) &&
- (!selectedWalletId || a.wallet === selectedWalletId)
- )
- return fAddresses
- },
-
- scanAddress: async function (addressData) {
- this.$emit('scan:address', addressData)
- },
- showAddressDetails: function (addressData) {
- this.$emit('show-address-details', addressData)
- },
- searchInTab: function (tab, value) {
- this.$emit('search:tab', {tab, value})
- },
- updateNoteForAddress: async function (addressData, note) {
- this.$emit('update:note', {addressId: addressData.id, note})
- }
- },
-
- created: async function () {}
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.html b/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.html
deleted file mode 100644
index 0df5bebf..00000000
--- a/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.html
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
Fee Rate:
-
-
-
-
-
-
-
-
-
-
-
- Warning! The fee is too low. The transaction might take a long time to
- confirm.
-
-
- Warning! The fee is too high. You might be overpaying for this
- transaction.
-
-
-
-
-
-
Fee:
-
{{feeValue}} sats
-
- Refresh Fee Rates
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.js b/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.js
deleted file mode 100644
index 7a920a9a..00000000
--- a/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.js
+++ /dev/null
@@ -1,64 +0,0 @@
-async function feeRate(path) {
- const template = await loadTemplateAsync(path)
- Vue.component('fee-rate', {
- name: 'fee-rate',
- template,
-
- props: ['rate', 'fee-value', 'sats-denominated', 'mempool-endpoint'],
-
- computed: {
- feeRate: {
- get: function () {
- return this['rate']
- },
- set: function (value) {
- this.$emit('update:rate', +value)
- }
- }
- },
-
- data: function () {
- return {
- recommededFees: {
- fastestFee: 1,
- halfHourFee: 1,
- hourFee: 1,
- economyFee: 1,
- minimumFee: 1
- }
- }
- },
-
- methods: {
- satBtc(val, showUnit = true) {
- return satOrBtc(val, showUnit, this.satsDenominated)
- },
-
- refreshRecommendedFees: async function () {
- const fn = async () => {
- const {
- bitcoin: {fees: feesAPI}
- } = mempoolJS({
- hostname: this.mempoolEndpoint
- })
- return feesAPI.getFeesRecommended()
- }
- this.recommededFees = await retryWithDelay(fn)
- },
- getFeeRateLabel: function (feeRate) {
- const fees = this.recommededFees
- if (feeRate >= fees.fastestFee)
- return `High Priority (${feeRate} sat/vB)`
- if (feeRate >= fees.halfHourFee)
- return `Medium Priority (${feeRate} sat/vB)`
- if (feeRate >= fees.hourFee) return `Low Priority (${feeRate} sat/vB)`
- return `No Priority (${feeRate} sat/vB)`
- }
- },
-
- created: async function () {
- await this.refreshRecommendedFees()
- this.feeRate = this.recommededFees.halfHourFee
- }
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/history/history.html b/lnbits/extensions/watchonly/static/components/history/history.html
deleted file mode 100644
index ced03b26..00000000
--- a/lnbits/extensions/watchonly/static/components/history/history.html
+++ /dev/null
@@ -1,146 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Export to CSV
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{props.row.confirmed ? 'Sent' : 'Sending...'}}
-
-
- {{props.row.confirmed ? 'Received' : 'Receiving...'}}
-
-
-
- {{satBtc(props.row.totalAmount || props.row.amount)}}
-
-
-
- {{props.row.address}}
-
- ...
-
-
- {{ props.row.date }}
-
-
-
-
-
-
UTXOs
-
{{satBtc(props.row.amount)}}
-
{{props.row.address}}
-
-
-
-
{{satBtc(s.amount)}}
-
{{s.address}}
-
-
-
Fee
-
{{satBtc(props.row.fee)}}
-
-
-
Block Height
-
{{props.row.height}}
-
-
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/history/history.js b/lnbits/extensions/watchonly/static/components/history/history.js
deleted file mode 100644
index 81cf44cc..00000000
--- a/lnbits/extensions/watchonly/static/components/history/history.js
+++ /dev/null
@@ -1,98 +0,0 @@
-async function history(path) {
- const template = await loadTemplateAsync(path)
- Vue.component('history', {
- name: 'history',
- template,
-
- props: ['history', 'mempool-endpoint', 'sats-denominated', 'filter'],
- data: function () {
- return {
- historyTable: {
- columns: [
- {
- name: 'expand',
- align: 'left',
- label: ''
- },
- {
- name: 'status',
- align: 'left',
- label: 'Status'
- },
- {
- name: 'amount',
- align: 'left',
- label: 'Amount',
- field: 'amount',
- sortable: true
- },
- {
- name: 'address',
- align: 'left',
- label: 'Address',
- field: 'address',
- sortable: true
- },
- {
- name: 'date',
- align: 'left',
- label: 'Date',
- field: 'date',
- sortable: true
- },
- {
- name: 'txId',
- field: 'txId'
- }
- ],
- exportColums: [
- {
- label: 'Action',
- field: 'action'
- },
- {
- label: 'Date&Time',
- field: 'date'
- },
- {
- label: 'Amount',
- field: 'amount'
- },
- {
- label: 'Fee',
- field: 'fee'
- },
- {
- label: 'Transaction Id',
- field: 'txId'
- }
- ],
- pagination: {
- rowsPerPage: 0
- }
- }
- }
- },
-
- methods: {
- satBtc(val, showUnit = true) {
- return satOrBtc(val, showUnit, this.satsDenominated)
- },
- getFilteredAddressesHistory: function () {
- return this.history.filter(a => (!a.isChange || a.sent) && !a.isSubItem)
- },
- exportHistoryToCSV: function () {
- const history = this.getFilteredAddressesHistory().map(a => ({
- ...a,
- action: a.sent ? 'Sent' : 'Received'
- }))
- LNbits.utils.exportCSV(
- this.historyTable.exportColums,
- history,
- 'address-history'
- )
- }
- },
- created: async function () {}
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/my-checkbox/my-checkbox.html b/lnbits/extensions/watchonly/static/components/my-checkbox/my-checkbox.html
deleted file mode 100644
index 83af1248..00000000
--- a/lnbits/extensions/watchonly/static/components/my-checkbox/my-checkbox.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/lnbits/extensions/watchonly/static/components/my-checkbox/my-checkbox.js b/lnbits/extensions/watchonly/static/components/my-checkbox/my-checkbox.js
deleted file mode 100644
index 3d22c3a0..00000000
--- a/lnbits/extensions/watchonly/static/components/my-checkbox/my-checkbox.js
+++ /dev/null
@@ -1,16 +0,0 @@
-async function initMyCheckbox(path) {
- const t = await loadTemplateAsync(path)
- Vue.component('my-checkbox', {
- name: 'my-checkbox',
- template: t,
- data() {
- return {checked: false, title: 'Check me'}
- },
- methods: {
- check() {
- this.checked = !this.checked
- console.log('### checked', this.checked)
- }
- }
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/payment/payment.html b/lnbits/extensions/watchonly/static/components/payment/payment.html
deleted file mode 100644
index 6e1d94c7..00000000
--- a/lnbits/extensions/watchonly/static/components/payment/payment.html
+++ /dev/null
@@ -1,312 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Fee Rate:
-
- {{feeRate}} sats/vbyte
- Fee:
- {{satBtc(feeValue)}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Balance:
- {{satBtc(balance)}}
- Selected:
-
- {{satBtc(selectedAmount)}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Below dust limit. Will be used as fee.
-
-
-
-
- Change:
-
- {{satBtc(0)}}
-
-
- {{satBtc(changeAmount)}}
-
-
-
-
-
-
-
-
-
-
Change Account:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Serial Port
-
- Sign using a Serial Port device
-
-
-
-
- Share PSBT
- Share the PSBT as text or Animated QR Code
-
-
-
-
-
-
-
-
-
- The payed amount is higher than the selected amount!
-
-
-
-
-
-
-
-
-
- Close
-
-
-
-
-
-
-
-
- Transaction Details
-
-
-
-
-
-
-
Version
-
{{signedTx.version}}
-
-
-
Locktime
-
{{signedTx.locktime}}
-
-
-
Fee
-
- {{satBtc(signedTx.fee)}}
-
-
-
-
Outputs
-
-
-
- {{satBtc(out.amount)}}
-
-
-
- {{out.address}}
-
-
-
-
-
-
-
-
-
- Send
- Close
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/payment/payment.js b/lnbits/extensions/watchonly/static/components/payment/payment.js
deleted file mode 100644
index e9689003..00000000
--- a/lnbits/extensions/watchonly/static/components/payment/payment.js
+++ /dev/null
@@ -1,379 +0,0 @@
-async function payment(path) {
- const t = await loadTemplateAsync(path)
- Vue.component('payment', {
- name: 'payment',
- template: t,
-
- props: [
- 'accounts',
- 'addresses',
- 'utxos',
- 'mempool-endpoint',
- 'sats-denominated',
- 'serial-signer-ref',
- 'adminkey',
- 'network'
- ],
- watch: {
- immediate: true,
- accounts() {
- this.updateChangeAddress()
- },
- addresses() {
- this.updateChangeAddress()
- }
- },
-
- data: function () {
- return {
- DUST_LIMIT: 546,
- tx: null,
- psbtBase64: null,
- psbtBase64Signed: null,
- signedTx: null,
- signedTxHex: null,
- sentTxId: null,
- signedTxId: null,
- sendToList: [{address: '', amount: undefined}],
- changeWallet: null,
- changeAddress: {},
- showCustomFee: false,
- showCoinSelect: false,
- showChecking: false,
- showChange: false,
- showPsbt: false,
- showFinalTx: false,
- feeRate: 1
- }
- },
-
- computed: {
- txSize: function () {
- const tx = this.createTx()
- return Math.round(txSize(tx))
- },
- txSizeNoChange: function () {
- const tx = this.createTx(true)
- return Math.round(txSize(tx))
- },
- feeValue: function () {
- return this.feeRate * this.txSize
- },
- selectedAmount: function () {
- return this.utxos
- .filter(utxo => utxo.selected)
- .reduce((t, a) => t + (a.amount || 0), 0)
- },
- changeAmount: function () {
- return (
- this.selectedAmount -
- this.totalPayedAmount -
- this.feeRate * this.txSize
- )
- },
- balance: function () {
- return this.utxos.reduce((t, a) => t + (a.amount || 0), 0)
- },
- totalPayedAmount: function () {
- return this.sendToList.reduce((t, a) => t + (a.amount || 0), 0)
- }
- },
-
- methods: {
- satBtc(val, showUnit = true) {
- return satOrBtc(val, showUnit, this.satsDenominated)
- },
- clearState: function () {
- this.psbtBase64 = null
- this.psbtBase64Signed = null
- this.signedTx = null
- this.signedTxHex = null
- this.signedTxId = null
- this.sendToList = [{address: '', amount: undefined}]
- this.showChecking = false
- this.showPsbt = false
- this.showFinalTx = false
- },
- checkAndSend: async function () {
- this.showChecking = true
- try {
- if (!this.serialSignerRef.isConnected()) {
- this.$q.notify({
- type: 'warning',
- message: 'Please connect to a Signing device first!',
- timeout: 10000
- })
- return
- }
- const p2trUtxo = this.utxos.find(
- u => u.selected && u.accountType === 'p2tr'
- )
- if (p2trUtxo) {
- this.$q.notify({
- type: 'warning',
- message: 'Taproot Signing not supported yet!',
- caption: 'Please manually deselect the Taproot UTXOs',
- timeout: 10000
- })
- return
- }
- if (!this.serialSignerRef.isAuthenticated()) {
- await this.serialSignerRef.hwwShowPasswordDialog()
- const authenticated = await this.serialSignerRef.isAuthenticating()
- if (!authenticated) return
- }
-
- await this.createPsbt()
-
- if (this.psbtBase64) {
- const txData = {
- outputs: this.tx.outputs,
- feeRate: this.tx.fee_rate,
- feeValue: this.feeValue
- }
- await this.serialSignerRef.hwwSendPsbt(this.psbtBase64, txData)
- await this.serialSignerRef.isSendingPsbt()
- }
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Cannot check and sign PSBT!',
- caption: `${error}`,
- timeout: 10000
- })
- } finally {
- this.showChecking = false
- this.psbtBase64 = null
- }
- },
- showPsbtDialog: async function () {
- try {
- const valid = await this.$refs.paymentFormRef.validate()
- if (!valid) return
-
- const data = await this.createPsbt()
- if (data) {
- this.showPsbt = true
- }
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to create PSBT!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- createPsbt: async function () {
- try {
- this.tx = this.createTx()
- for (const input of this.tx.inputs) {
- input.tx_hex = await this.fetchTxHex(input.tx_id)
- }
-
- const changeOutput = this.tx.outputs.find(o => o.branch_index === 1)
- if (changeOutput) changeOutput.amount = this.changeAmount
-
- const {data} = await LNbits.api.request(
- 'POST',
- '/watchonly/api/v1/psbt',
- this.adminkey,
- this.tx
- )
-
- this.psbtBase64 = data
- return data
- } catch (err) {
- LNbits.utils.notifyApiError(err)
- }
- },
- createTx: function (excludeChange = false) {
- const tx = {
- fee_rate: this.feeRate,
- masterpubs: this.accounts.map(w => ({
- id: w.id,
- public_key: w.masterpub,
- fingerprint: w.fingerprint
- }))
- }
- tx.inputs = this.utxos
- .filter(utxo => utxo.selected)
- .map(mapUtxoToPsbtInput)
- .sort((a, b) =>
- a.tx_id < b.tx_id ? -1 : a.tx_id > b.tx_id ? 1 : a.vout - b.vout
- )
-
- tx.outputs = this.sendToList.map(out => ({
- address: out.address,
- amount: out.amount
- }))
-
- if (!excludeChange) {
- const change = this.createChangeOutput()
- const diffAmount = this.selectedAmount - this.totalPayedAmount
- if (diffAmount >= this.DUST_LIMIT) {
- tx.outputs.push(change)
- }
- }
- tx.tx_size = Math.round(txSize(tx))
- tx.inputs = _.shuffle(tx.inputs)
- tx.outputs = _.shuffle(tx.outputs)
-
- return tx
- },
- createChangeOutput: function () {
- const change = this.changeAddress
- const walletAcount =
- this.accounts.find(w => w.id === change.wallet) || {}
-
- return {
- address: change.address,
- address_index: change.addressIndex,
- branch_index: change.isChange ? 1 : 0,
- wallet: walletAcount.id
- }
- },
- selectChangeAddress: function (account) {
- if (!account) this.changeAddress = ''
- this.changeAddress =
- this.addresses.find(
- a => a.wallet === account.id && a.isChange && !a.hasActivity
- ) || {}
- },
- updateChangeAddress: function () {
- if (this.changeWallet) {
- const changeAccount = (this.accounts || []).find(
- w => w.id === this.changeWallet.id
- )
- // change account deleted
- if (!changeAccount) {
- this.changeWallet = this.accounts[0]
- }
- } else {
- this.changeWallet = this.accounts[0]
- }
- this.selectChangeAddress(this.changeWallet)
- },
- updateSignedPsbt: async function (psbtBase64) {
- try {
- this.showChecking = true
- this.psbtBase64Signed = psbtBase64
-
- const data = await this.extractTxFromPsbt(psbtBase64)
- this.showFinalTx = true
- if (data) {
- this.signedTx = JSON.parse(data.tx_json)
- this.signedTxHex = data.tx_hex
- } else {
- this.signedTx = null
- this.signedTxHex = null
- }
- } finally {
- this.showChecking = false
- }
- },
-
- fetchUtxoHexForPsbt: async function (psbtBase64) {
- if (this.tx?.inputs && this.tx?.inputs.length) return this.tx.inputs
-
- const {data: psbtUtxos} = await LNbits.api.request(
- 'PUT',
- '/watchonly/api/v1/psbt/utxos',
- this.adminkey,
- {psbtBase64}
- )
-
- const inputs = []
- for (const utxo of psbtUtxos) {
- const txHex = await this.fetchTxHex(utxo.tx_id)
- inputs.push({tx_hex: txHex})
- }
- return inputs
- },
- extractTxFromPsbt: async function (psbtBase64) {
- try {
- const inputs = await this.fetchUtxoHexForPsbt(psbtBase64)
-
- const {data} = await LNbits.api.request(
- 'PUT',
- '/watchonly/api/v1/psbt/extract',
- this.adminkey,
- {
- psbtBase64,
- inputs,
- network: this.network
- }
- )
- return data
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Cannot finalize PSBT!',
- caption: `${error}`,
- timeout: 10000
- })
- LNbits.utils.notifyApiError(error)
- }
- },
- broadcastTransaction: async function () {
- try {
- const {data} = await LNbits.api.request(
- 'POST',
- '/watchonly/api/v1/tx',
- this.adminkey,
- {tx_hex: this.signedTxHex}
- )
- this.sentTxId = data
-
- this.$q.notify({
- type: 'positive',
- message: 'Transaction broadcasted!',
- caption: `${data}`,
- timeout: 10000
- })
-
- this.clearState()
- this.$emit('broadcast-done', this.sentTxId)
- } catch (error) {
- this.sentTxId = null
- this.$q.notify({
- type: 'warning',
- message: 'Failed to broadcast!',
- caption: `${error}`,
- timeout: 10000
- })
- } finally {
- this.showFinalTx = false
- }
- },
- fetchTxHex: async function (txId) {
- const {
- bitcoin: {transactions: transactionsAPI}
- } = mempoolJS({
- hostname: this.mempoolEndpoint
- })
-
- try {
- const response = await transactionsAPI.getTxHex({txid: txId})
- return response
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: `Failed to fetch transaction details for tx id: '${txId}'`,
- timeout: 10000
- })
- LNbits.utils.notifyApiError(error)
- throw error
- }
- },
- handleOutputsChange: function () {
- this.$refs.utxoList.refreshUtxoSelection(this.totalPayedAmount)
- },
- getTotalPaymentAmount: function () {
- return this.sendToList.reduce((t, a) => t + (a.amount || 0), 0)
- }
- },
-
- created: async function () {}
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/seed-input/seed-input.html b/lnbits/extensions/watchonly/static/components/seed-input/seed-input.html
deleted file mode 100644
index 60cdaaa8..00000000
--- a/lnbits/extensions/watchonly/static/components/seed-input/seed-input.html
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
-
-
-
Enter word at position: {{actualPosition}}
-
-
-
-
- Previous
-
-
-
-
-
-
- Next
- Done
-
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/seed-input/seed-input.js b/lnbits/extensions/watchonly/static/components/seed-input/seed-input.js
deleted file mode 100644
index 5e415abb..00000000
--- a/lnbits/extensions/watchonly/static/components/seed-input/seed-input.js
+++ /dev/null
@@ -1,102 +0,0 @@
-async function seedInput(path) {
- const template = await loadTemplateAsync(path)
- Vue.component('seed-input', {
- name: 'seed-input',
- template,
-
- computed: {
- actualPosition: function () {
- return this.words[this.currentPosition].position
- }
- },
-
- data: function () {
- return {
- wordCountOptions: ['12', '15', '18', '21', '24'],
- wordCount: 24,
- words: [],
- currentPosition: 0,
- stringOptions: [],
- options: [],
- currentWord: '',
- done: false
- }
- },
-
- methods: {
- filterFn(val, update, abort) {
- update(() => {
- const needle = val.toLocaleLowerCase()
- this.options = this.stringOptions
- .filter(v => v.toLocaleLowerCase().indexOf(needle) != -1)
- .sort((a, b) => {
- if (a.startsWith(needle)) {
- if (b.startsWith(needle)) {
- return a - b
- }
- return -1
- } else {
- if (b.startsWith(needle)) {
- return 1
- }
- return a - b
- }
- })
- })
- },
- initWords() {
- const words = []
- for (let i = 1; i <= this.wordCount; i++) {
- words.push({
- position: i,
- value: ''
- })
- }
- this.currentPosition = 0
- this.words = _.shuffle(words)
- },
- setModel(val) {
- this.currentWord = val
- this.words[this.currentPosition].value = this.currentWord
- },
- nextPosition() {
- if (this.currentPosition < this.wordCount - 1) {
- this.currentPosition++
- }
- this.currentWord = this.words[this.currentPosition].value
- },
- previousPosition() {
- if (this.currentPosition > 0) {
- this.currentPosition--
- }
- this.currentWord = this.words[this.currentPosition].value
- },
- seedInputDone() {
- const badWordPositions = this.words
- .filter(w => !w.value || !this.stringOptions.includes(w.value))
- .map(w => w.position)
- if (badWordPositions.length) {
- this.$q.notify({
- timeout: 10000,
- type: 'warning',
- message:
- 'The seed has incorrect words. Please check at these positions: ',
- caption: 'Position: ' + badWordPositions.join(', ')
- })
- return
- }
- const mnemonic = this.words
- .sort((a, b) => a.position - b.position)
- .map(w => w.value)
- .join(' ')
- this.$emit('on-seed-input-done', mnemonic)
- this.done = true
- }
- },
-
- created: async function () {
- this.stringOptions = bip39WordList
- this.initWords()
- }
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/send-to/send-to.html b/lnbits/extensions/watchonly/static/components/send-to/send-to.html
deleted file mode 100644
index c16ebf95..00000000
--- a/lnbits/extensions/watchonly/static/components/send-to/send-to.html
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Max
-
-
-
-
-
-
-
- Add
-
-
-
- Payed Amount:
-
- {{satBtc(getTotalPaymentAmount())}}
-
-
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/send-to/send-to.js b/lnbits/extensions/watchonly/static/components/send-to/send-to.js
deleted file mode 100644
index 2b93cea7..00000000
--- a/lnbits/extensions/watchonly/static/components/send-to/send-to.js
+++ /dev/null
@@ -1,81 +0,0 @@
-async function sendTo(path) {
- const template = await loadTemplateAsync(path)
- Vue.component('send-to', {
- name: 'send-to',
- template,
-
- props: [
- 'data',
- 'tx-size',
- 'selected-amount',
- 'fee-rate',
- 'sats-denominated'
- ],
-
- computed: {
- dataLocal: {
- get: function () {
- return this.data
- },
- set: function (value) {
- console.log('### computed update data', value)
- this.$emit('update:data', value)
- }
- }
- },
-
- data: function () {
- return {
- DUST_LIMIT: 546,
- paymentTable: {
- columns: [
- {
- name: 'data',
- align: 'left'
- }
- ],
- pagination: {
- rowsPerPage: 10
- },
- filter: ''
- }
- }
- },
-
- methods: {
- satBtc(val, showUnit = true) {
- return satOrBtc(val, showUnit, this.satsDenominated)
- },
- addPaymentAddress: function () {
- this.dataLocal.push({address: '', amount: undefined})
- this.handleOutputsChange()
- },
- deletePaymentAddress: function (v) {
- const index = this.dataLocal.indexOf(v)
- if (index !== -1) {
- this.dataLocal.splice(index, 1)
- }
- this.handleOutputsChange()
- },
-
- sendMaxToAddress: function (paymentAddress = {}) {
- const feeValue = this.feeRate * this.txSize
- const inputAmount = this.selectedAmount
- const currentAmount = Math.max(0, paymentAddress.amount || 0)
- const payedAmount = this.getTotalPaymentAmount() - currentAmount
- paymentAddress.amount = Math.max(
- 0,
- inputAmount - payedAmount - feeValue
- )
- },
- handleOutputsChange: function () {
- this.$emit('update:outputs')
- },
- getTotalPaymentAmount: function () {
- return this.dataLocal.reduce((t, a) => t + (a.amount || 0), 0)
- }
- },
-
- created: async function () {}
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/serial-port-config/serial-port-config.html b/lnbits/extensions/watchonly/static/components/serial-port-config/serial-port-config.html
deleted file mode 100644
index 18e52058..00000000
--- a/lnbits/extensions/watchonly/static/components/serial-port-config/serial-port-config.html
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/serial-port-config/serial-port-config.js b/lnbits/extensions/watchonly/static/components/serial-port-config/serial-port-config.js
deleted file mode 100644
index 87b54e38..00000000
--- a/lnbits/extensions/watchonly/static/components/serial-port-config/serial-port-config.js
+++ /dev/null
@@ -1,12 +0,0 @@
-async function serialPortConfig(path) {
- const t = await loadTemplateAsync(path)
- Vue.component('serial-port-config', {
- name: 'serial-port-config',
- props: ['config'],
- template: t,
- data() {
- return {}
- },
- methods: {}
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.html b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.html
deleted file mode 100644
index a95a1906..00000000
--- a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.html
+++ /dev/null
@@ -1,544 +0,0 @@
-
-
-
-
-
- Login
- Enter password for Hardware Wallet.
-
-
-
-
-
- Logout
- Clear password for HWW.
-
-
-
-
- Config & Connect
- Set the Serial Port communication parameters.
-
-
-
-
- Paired Device ({{device.config.name || 'no-name'}})
-
- {{device.id}}
-
-
- Forget
-
-
-
-
-
- Disconnect
- Disconnect from Serial Port.
-
-
-
-
-
- Restore
- Restore wallet from existing word list.
-
-
-
-
- Show Seed
- Show seed on the Hardware Wallet display.
-
-
-
-
- Wipe
- Clean-up the wallet. New random seed.
-
-
-
-
- Help
- View available comands.
-
-
-
-
- Console
- Show the serial port communication messages
-
-
-
-
-
-
-
-
- Enter Config
-
-
-
- Connect
- Cancel
-
-
-
-
-
-
-
-
- Enter password for Hardware Wallet (8 numbers/letters)
-
-
-
-
-
-
-
-
-
-
-
-
- Login
- Cancel
-
-
-
-
-
-
-
-
-
-
-
- Output {{hww.confirm.outputIndex}}
-
- change
-
-
-
-
-
- Address:
-
-
- {{tx.outputs[hww.confirm.outputIndex].address}}
-
-
-
-
- Amount:
-
-
- {{satBtc(tx.outputs[hww.confirm.outputIndex].amount)}}
-
-
-
-
- Fee:
-
-
- {{satBtc(tx.feeValue)}}
-
-
-
-
- Fee Rate:
-
-
- {{tx.feeRate}} sats/vbyte
-
-
-
-
-
-
- Confirm then check the Hardware Device.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Cancel
-
-
-
-
-
-
-
-
-
-
- This action will remove all data from the Hardware Wallet. Please
- create a back-up for the seed!
-
- Enter new password for Hardware Wallet (8 numbers/letters)
-
-
-
-
- This action cannot be reversed!
-
-
-
- Wipe
- Cancel
-
-
-
-
-
-
-
-
-
-
- Close
-
-
-
-
-
-
-
-
-
- Open the browser Developer Console for more Details!
-
-
-
-
-
-
- Close
-
-
-
-
-
-
- Check word at position {{hww.seedWordPosition}} on device
-
-
-
-
-
- Prev
-
-
- Next
-
-
- Close
-
-
-
-
-
-
-
-
-
- For test purposes only. Do not enter word list with real funds!!!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Enter new password (8 numbers/letters)
-
-
-
-
-
-
-
-
-
-
- ALL existing data on the Hardware Device will be lost.
-
-
-
- Restore
- Cancel
-
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
deleted file mode 100644
index 2e80b1e0..00000000
--- a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
+++ /dev/null
@@ -1,1008 +0,0 @@
-async function serialSigner(path) {
- const t = await loadTemplateAsync(path)
- Vue.component('serial-signer', {
- name: 'serial-signer',
- template: t,
-
- props: ['sats-denominated', 'network'],
- data: function () {
- return {
- selectedPort: null,
- writableStreamClosed: null,
- writer: null,
- readableStreamClosed: null,
- reader: null,
- receivedData: '',
- config: {},
- decryptionKey: null,
- sharedSecret: null,
-
- hww: {
- password: null,
- showPassword: false,
- mnemonic: null,
- showMnemonic: false,
- quickMnemonicInput: false,
- passphrase: null,
- showPassphrase: false,
- hasPassphrase: false,
- authenticated: false,
- showPasswordDialog: false,
- showConfigDialog: false,
- showWipeDialog: false,
- showRestoreDialog: false,
- showConfirmationDialog: false,
- showSignedPsbt: false,
- sendingPsbt: false,
- signingPsbt: false,
- loginResolve: null,
- psbtSentResolve: null,
- xpubResolve: null,
- seedWordPosition: 1,
- seedWord: null,
- showSeedWord: false,
- showSeedDialog: false,
- // config: null,
-
- confirm: {
- outputIndex: 0,
- showFee: false
- }
- },
- tx: null, // todo: move to hww
-
- showConsole: false,
- showPairedDevices: true
- }
- },
-
- computed: {
- pairedDevices: {
- cache: false,
- get: function () {
- return (
- JSON.parse(window.localStorage.getItem('lnbits-paired-devices')) ||
- []
- )
- },
- set: function (devices) {
- window.localStorage.setItem(
- 'lnbits-paired-devices',
- JSON.stringify(devices)
- )
- }
- }
- },
-
- methods: {
- satBtc(val, showUnit = true) {
- return satOrBtc(val, showUnit, this.satsDenominated)
- },
- openSerialPortDialog: async function () {
- this.config = {...HWW_DEFAULT_CONFIG}
- await this.openSerialPort(this.config)
- },
- openSerialPort: async function (config = {baudRate: 9600}) {
- if (!this.checkSerialPortSupported()) return false
- if (this.selectedPort) {
- this.$q.notify({
- type: 'warning',
- message: 'Already connected. Disconnect first!',
- timeout: 10000
- })
- return true
- }
-
- try {
- this.selectedPort = await navigator.serial.requestPort()
- this.selectedPort.addEventListener('connect', event => {
- // do nothing
- })
-
- this.selectedPort.addEventListener('disconnect', () => {
- this.selectedPort = null
- this.hww.authenticated = false
- this.$q.notify({
- type: 'warning',
- message: 'Disconnected from Serial Port!',
- timeout: 10000
- })
- })
-
- // Wait for the serial port to open.
- await this.selectedPort.open(config)
- // do not await
- this.startSerialPortReading()
- // wait to init
- sleep(1000)
-
- const textEncoder = new TextEncoderStream()
- this.writableStreamClosed = textEncoder.readable.pipeTo(
- this.selectedPort.writable
- )
-
- this.writer = textEncoder.writable.getWriter()
-
- await this.hwwPing()
-
- return true
- } catch (error) {
- this.selectedPort = null
- this.$q.notify({
- type: 'warning',
- message: 'Cannot open serial port!',
- caption: `${error}`,
- timeout: 10000
- })
- return false
- }
- },
- openSerialPortConfig: async function (deviceId) {
- const device = this.getPairedDevice(deviceId)
- if (device) {
- this.config = device.config
- } else {
- this.config = {...HWW_DEFAULT_CONFIG}
- }
- this.hww.showConfigDialog = true
- },
- closeSerialPort: async function () {
- try {
- if (this.writer) this.writer.close()
- if (this.writableStreamClosed) await this.writableStreamClosed
- if (this.reader) this.reader.cancel()
- if (this.readableStreamClosed)
- await this.readableStreamClosed.catch(() => {
- /* Ignore the error */
- })
- if (this.selectedPort) await this.selectedPort.close()
- this.$q.notify({
- type: 'positive',
- message: 'Serial port disconnected!',
- timeout: 5000
- })
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Cannot close serial port!',
- caption: `${error}`,
- timeout: 10000
- })
- } finally {
- this.selectedPort = null
- this.hww.authenticated = false
- }
- },
-
- isConnected: function () {
- return !!this.selectedPort
- },
- isAuthenticated: function () {
- return this.hww.authenticated
- },
-
- seedInputDone: function (mnemonic) {
- this.hww.mnemonic = mnemonic
- },
- isAuthenticating: function () {
- if (this.isAuthenticated()) return false
- return new Promise(resolve => {
- this.loginResolve = resolve
- })
- },
-
- isSendingPsbt: async function () {
- if (!this.hww.sendingPsbt) return false
- return new Promise(resolve => {
- this.psbtSentResolve = resolve
- })
- },
-
- isFetchingXpub: async function () {
- return new Promise(resolve => {
- this.xpubResolve = resolve
- })
- },
-
- checkSerialPortSupported: function () {
- if (!navigator.serial) {
- this.$q.notify({
- type: 'warning',
- message: 'Serial port communication not supported!',
- caption:
- 'Make sure your browser supports Serial Port and that you are using HTTPS.',
- timeout: 10000
- })
- return false
- }
- return true
- },
- startSerialPortReading: async function () {
- const port = this.selectedPort
-
- while (port && port.readable) {
- const textDecoder = new TextDecoderStream()
- this.readableStreamClosed = port.readable.pipeTo(textDecoder.writable)
- this.reader = textDecoder.readable.getReader()
- const readStringUntil = readFromSerialPort(this.reader)
-
- try {
- while (true) {
- const {value, done} = await readStringUntil('\n')
- if (value) {
- const {command, commandData} = await this.extractCommand(value)
- this.handleSerialPortResponse(command, commandData)
- this.updateSerialPortConsole(command)
- }
- if (done) return
- }
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Serial port communication error!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- }
- },
- handleSerialPortResponse: async function (command, commandData) {
- this.logPublicCommandsResponse(command, commandData)
-
- switch (command) {
- case COMMAND_PING:
- this.handlePingResponse(commandData)
- break
- case COMMAND_CHECK_PAIRING:
- this.handleCheckPairingResponse(commandData)
- break
- case COMMAND_SIGN_PSBT:
- this.handleSignResponse(commandData)
- break
- case COMMAND_PASSWORD:
- this.handleLoginResponse(commandData)
- break
- case COMMAND_PASSWORD_CLEAR:
- this.handleLogoutResponse(commandData)
- break
- case COMMAND_SEND_PSBT:
- this.handleSendPsbtResponse(commandData)
- break
- case COMMAND_WIPE:
- this.handleWipeResponse(commandData)
- break
- case COMMAND_XPUB:
- this.handleXpubResponse(commandData)
- break
- case COMMAND_SEED:
- this.handleShowSeedResponse(commandData)
- break
- case COMMAND_PAIR:
- this.handlePairResponse(commandData)
- break
- case COMMAND_LOG:
- console.log(
- ` %c${commandData}`,
- 'background: #222; color: #bada55'
- )
- break
- default:
- console.log(` %c${command}`, 'background: #222; color: red')
- }
- },
- logPublicCommandsResponse: function (command, commandData) {
- switch (command) {
- case COMMAND_SIGN_PSBT:
- case COMMAND_PASSWORD:
- case COMMAND_PASSWORD_CLEAR:
- case COMMAND_SEND_PSBT:
- case COMMAND_WIPE:
- case COMMAND_XPUB:
- case COMMAND_PAIR:
- console.log(
- ` %c${command} ${commandData}`,
- 'background: #222; color: yellow'
- )
- }
- },
- updateSerialPortConsole: function (value) {
- this.receivedData += value + '\n'
- const textArea = document.getElementById('serial-port-console')
- if (textArea) textArea.scrollTop = textArea.scrollHeight
- },
- hwwPing: async function () {
- try {
- // Send an empty ping. The serial port buffer might have some jubk data. Flush it.
- await this.sendCommandClearText(COMMAND_PING)
- await this.sendCommandClearText(COMMAND_PING, [window.location.host])
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to ping Hardware Wallet!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- handlePingResponse: function (res = '') {
- const [status, deviceId] = res.split(' ')
- this.deviceId = deviceId
-
- if (!this.deviceId) {
- this.$q.notify({
- type: 'warning',
- message: 'Missing device ID for Hardware Wallet',
- timeout: 10000
- })
- return
- }
-
- const device = this.getPairedDevice(deviceId)
-
- if (device) {
- this.sharedSecret = nobleSecp256k1.utils.hexToBytes(
- device.sharedSecretHex
- )
- this.hwwCheckPairing()
- } else {
- this.hwwPair()
- }
- },
- hwwShowPasswordDialog: async function () {
- try {
- this.hww.showPasswordDialog = true
- await this.sendCommandSecure(COMMAND_PASSWORD)
- } catch (error) {
- console.log(error)
- this.$q.notify({
- type: 'warning',
- message: 'Failed to connect to Hardware Wallet!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- hwwShowWipeDialog: async function () {
- try {
- this.hww.showWipeDialog = true
- await this.sendCommandSecure(COMMAND_WIPE)
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to connect to Hardware Wallet!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- hwwShowRestoreDialog: async function () {
- try {
- this.hww.showRestoreDialog = true
- await this.sendCommandSecure(COMMAND_RESTORE)
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to connect to Hardware Wallet!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- closeSeedDialog: function () {
- this.hww.seedWord = null
- this.hww.showSeedWord = false
- },
- hwwConfirmNext: async function () {
- this.hww.confirm.outputIndex += 1
- if (this.hww.confirm.outputIndex >= this.tx.outputs.length) {
- this.hww.confirm.showFee = true
- }
- await this.sendCommandSecure(COMMAND_CONFIRM_NEXT)
- },
- cancelOperation: async function () {
- try {
- await this.sendCommandSecure(COMMAND_CANCEL)
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to send cancel!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- hwwConfigAndConnect: async function () {
- this.hww.showConfigDialog = false
- if (this.config.deviceId) {
- this.updatePairedDeviceConfig(this.config.deviceId, this.config)
- }
- await this.openSerialPort(this.config)
- return true
- },
- hwwLogin: async function () {
- try {
- await this.sendCommandSecure(COMMAND_PASSWORD, [
- this.hww.password,
- this.hww.passphrase
- ])
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to send password to Hardware Wallet!',
- caption: `${error}`,
- timeout: 10000
- })
- } finally {
- this.hww.showPasswordDialog = false
- this.hww.password = null
- this.hww.passphrase = null
- this.hww.showPassword = false
- this.hww.showPassphrase = false
- }
- },
- handleLoginResponse: function (res = '') {
- this.hww.authenticated = res.trim() === '1'
- if (this.loginResolve) {
- this.loginResolve(this.hww.authenticated)
- }
-
- if (this.hww.authenticated) {
- this.$q.notify({
- type: 'positive',
- message: 'Login successfull!',
- timeout: 10000
- })
- } else {
- this.$q.notify({
- type: 'warning',
- message: 'Wrong password, try again!',
- timeout: 10000
- })
- }
- },
- hwwLogout: async function () {
- try {
- await this.sendCommandSecure(COMMAND_PASSWORD_CLEAR)
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to logout from Hardware Wallet!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- hwwShowAddress: async function (path, address) {
- try {
- await this.sendCommandSecure(COMMAND_ADDRESS, [
- this.network,
- path,
- address
- ])
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to logout from Hardware Wallet!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- handleLogoutResponse: function (res = '') {
- const authenticated = !(res.trim() === '1')
- if (this.hww.authenticated && !authenticated) {
- this.$q.notify({
- type: 'positive',
- message: 'Logged Out',
- timeout: 10000
- })
- }
- this.hww.authenticated = authenticated
- },
- hwwSendPsbt: async function (psbtBase64, tx) {
- try {
- this.tx = tx
- this.hww.sendingPsbt = true
- await this.sendCommandSecure(COMMAND_SEND_PSBT, [
- this.network,
- psbtBase64
- ])
- this.$q.notify({
- type: 'positive',
- message: 'Data sent to serial port device!',
- timeout: 5000
- })
- } catch (error) {
- this.hww.sendingPsbt = false
- this.$q.notify({
- type: 'warning',
- message: 'Failed to send data to serial port!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- handleSendPsbtResponse: function (res = '') {
- try {
- const psbtOK = res.trim() === '1'
- if (!psbtOK) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to send PSBT!',
- caption: `${res}`,
- timeout: 10000
- })
- return
- }
- this.hww.confirm.outputIndex = 0
- this.hww.showConfirmationDialog = true
- this.hww.confirm = {
- outputIndex: 0,
- showFee: false
- }
- this.hww.sendingPsbt = false
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to send PSBT!',
- caption: `${error}`,
- timeout: 10000
- })
- } finally {
- this.psbtSentResolve()
- }
- },
- hwwSignPsbt: async function () {
- try {
- this.hww.showConfirmationDialog = false
- this.hww.signingPsbt = true
- await this.sendCommandSecure(COMMAND_SIGN_PSBT)
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to sign PSBT!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- handleSignResponse: function (res = '') {
- this.hww.signingPsbt = false
- const [count, psbt] = res.trim().split(' ')
- if (!psbt || !count || count.trim() === '0') {
- this.$q.notify({
- type: 'warning',
- message: 'No input signed!',
- caption: 'Are you using the right seed?',
- timeout: 10000
- })
- return
- }
- this.updateSignedPsbt(psbt)
- this.$q.notify({
- type: 'positive',
- message: 'Transaction Signed',
- message: `Inputs signed: ${count}`,
- timeout: 10000
- })
- },
- hwwCheckPairing: async function () {
- const iv = window.crypto.getRandomValues(new Uint8Array(16))
- const encrypted = await this.encryptMessage(
- this.sharedSecret, // todo: revisit
- iv,
- PAIRING_CONTROL_TEXT.length + ' ' + PAIRING_CONTROL_TEXT
- )
-
- const encryptedHex = nobleSecp256k1.utils.bytesToHex(encrypted)
- const encryptedIvHex = nobleSecp256k1.utils.bytesToHex(iv)
- try {
- await this.sendCommandClearText(COMMAND_CHECK_PAIRING, [
- encryptedHex + encryptedIvHex
- ])
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to check secure connection!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- handleCheckPairingResponse: async function (res = '') {
- const [statusCode, message] = res.split(' ')
- switch (statusCode) {
- case '0':
- const controlText = await this.decryptData(message)
- if (controlText == PAIRING_CONTROL_TEXT) {
- this.$q.notify({
- type: 'positive',
- message: 'Re-paired with success!',
- timeout: 10000
- })
- } else {
- this.$q.notify({
- type: 'warning',
- message: 'Re-pairing failed!',
- caption: 'Remove (forget) device and try again!',
- timeout: 10000
- })
- }
- break
- case '1':
- this.closeSerialPort()
- this.$q.notify({
- type: 'warning',
- message:
- 'Re-pairing failed. Remove (forget) device and try again!',
- caption: `Error: ${message}`,
- timeout: 10000
- })
- break
- default:
- // noting to do here yet
- break
- }
- },
- hwwPair: async function () {
- try {
- this.decryptionKey = nobleSecp256k1.utils.randomPrivateKey()
- const publicKey = nobleSecp256k1.Point.fromPrivateKey(
- this.decryptionKey
- )
- const publicKeyHex = publicKey.toHex().slice(2)
-
- const args = [publicKeyHex]
- if (Number.isInteger(+this.config.buttonOnePin)) {
- args.push(this.config.buttonOnePin)
- }
- if (Number.isInteger(+this.config.buttonTwoPin)) {
- args.push(this.config.buttonTwoPin)
- }
- await this.sendCommandClearText(COMMAND_PAIR, args)
- this.$q.notify({
- type: 'positive',
- message: 'Pairing started!',
- timeout: 5000
- })
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to pair with device!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- handlePairResponse: async function (res = '') {
- const [statusCode, data] = res.trim().split(' ')
- let pubKeyHex, errorMessage, captionMessage
- switch (statusCode) {
- case '0':
- pubKeyHex = data
- if (!data) errorMessage = 'Failed to exchange DH secret!'
- break
- case '1':
- errorMessage =
- 'Device pairing only possible in the first 10 seconds after start-up!'
- captionMessage = 'Restart and try again'
- break
-
- default:
- errorMessage = 'Unexpected error code'
- break
- }
-
- if (errorMessage) {
- this.$q.notify({
- type: 'warning',
- message: errorMessage,
- caption: captionMessage || '',
- timeout: 10000
- })
- this.closeSerialPort()
- return
- }
- const hwwPublicKey = nobleSecp256k1.Point.fromHex('04' + pubKeyHex)
-
- this.sharedSecret = nobleSecp256k1
- .getSharedSecret(this.decryptionKey, hwwPublicKey)
- .slice(1, 33)
-
- const sharedSecretHex = nobleSecp256k1.utils.bytesToHex(
- this.sharedSecret
- )
- const sharedSecredHash = await nobleSecp256k1.utils.sha256(
- asciiToUint8Array(sharedSecretHex)
- )
- const fingerprint = nobleSecp256k1.utils
- .bytesToHex(sharedSecredHash)
- .substring(0, 5)
- .toUpperCase()
-
- LNbits.utils
- .confirmDialog('Confirm code from display: ' + fingerprint)
- .onOk(() => {
- this.addPairedDevice(
- this.deviceId,
- nobleSecp256k1.utils.bytesToHex(this.sharedSecret),
- this.config
- )
-
- this.$q.notify({
- type: 'positive',
- message: 'Paired with device!',
- timeout: 5000
- })
- })
- .onCancel(() => {
- this.closeSerialPort()
- })
- },
- hwwHelp: async function () {
- try {
- await this.sendCommandSecure(COMMAND_HELP)
- this.$q.notify({
- type: 'positive',
- message: 'Check display or console for details!',
- timeout: 5000
- })
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to ask for help!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- hwwWipe: async function () {
- try {
- this.hww.showWipeDialog = false
- await this.sendCommandSecure(COMMAND_WIPE, [this.hww.password])
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to wipe!',
- caption: `${error}`,
- timeout: 10000
- })
- } finally {
- this.hww.password = null
- this.hww.confirmedPassword = null
- this.hww.showPassword = false
- }
- },
- handleWipeResponse: function (res = '') {
- const wiped = res.trim() === '1'
- if (wiped) {
- this.$q.notify({
- type: 'positive',
- message: 'Wallet wiped!',
- timeout: 10000
- })
- } else {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to wipe wallet!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- hwwXpub: async function (path) {
- try {
- await this.sendCommandSecure(COMMAND_XPUB, [this.network, path])
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to fetch XPub!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- handleXpubResponse: function (res = '') {
- const args = res.trim().split(' ')
- if (args.length < 3 || args[0].trim() !== '1') {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to fetch XPub!',
- caption: `${res}`,
- timeout: 10000
- })
- this.xpubResolve({})
- return
- }
- const xpub = args[1].trim()
- const fingerprint = args[2].trim()
- this.xpubResolve({xpub, fingerprint})
- },
-
- hwwShowSeed: async function () {
- try {
- this.hww.showSeedDialog = true
- this.hww.seedWordPosition = 1
-
- await this.sendCommandSecure(COMMAND_SEED, [
- this.hww.seedWordPosition
- ])
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to show seed!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- showNextSeedWord: async function () {
- this.hww.seedWordPosition++
- await this.sendCommandSecure(COMMAND_SEED, [this.hww.seedWordPosition])
- },
- showPrevSeedWord: async function () {
- this.hww.seedWordPosition = Math.max(1, this.hww.seedWordPosition - 1)
- await this.sendCommandSecure(COMMAND_SEED, [this.hww.seedWordPosition])
- },
- handleShowSeedResponse: function (res = '') {
- const [pos, word] = res.trim().split(' ')
- this.hww.seedWord = `${pos}. ${word}`
- this.hww.seedWordPosition = pos
- },
- hwwRestore: async function () {
- try {
- await this.sendCommandSecure(COMMAND_RESTORE, [
- this.hww.password,
- this.hww.mnemonic
- ])
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to restore from seed!',
- caption: `${error}`,
- timeout: 10000
- })
- } finally {
- this.hww.showRestoreDialog = false
- this.hww.mnemonic = null
- this.hww.showMnemonic = false
- this.hww.password = null
- this.hww.confirmedPassword = null
- this.hww.showPassword = false
- }
- },
-
- updateSignedPsbt: async function (value) {
- this.$emit('signed:psbt', value)
- },
-
- sendCommandSecure: async function (command, attrs = []) {
- const message = [command].concat(attrs).join(' ')
- const iv = window.crypto.getRandomValues(new Uint8Array(16))
- if (!this.sharedSecret || !this.sharedSecret.length) {
- throw new Error(
- `Secure connection not estabileshed. Tried to run command: ${command}`
- )
- }
- const encrypted = await this.encryptMessage(
- this.sharedSecret,
- iv,
- message.length + ' ' + message
- )
-
- const encryptedHex = nobleSecp256k1.utils.bytesToHex(encrypted)
- const encryptedIvHex = nobleSecp256k1.utils.bytesToHex(iv)
- await this.writer.write(encryptedHex + encryptedIvHex + '\n')
- },
- sendCommandClearText: async function (command, attrs = []) {
- const message = [command].concat(attrs).join(' ')
- await this.writer.write(message + '\n')
- },
- extractCommand: async function (value) {
- const command = value.split(' ')[0]
- const commandData = value.substring(command.length).trim()
-
- if (
- command === COMMAND_PAIR ||
- command === COMMAND_LOG ||
- command === COMMAND_PASSWORD_CLEAR ||
- command === COMMAND_PING ||
- command === COMMAND_CHECK_PAIRING
- )
- return {command, commandData}
-
- const decryptedValue = await this.decryptData(value)
- const decryptedCommand = decryptedValue.split(' ')[0]
- const decryptedCommandData = decryptedValue
- .substring(decryptedCommand.length)
- .trim()
- return {
- command: decryptedCommand,
- commandData: decryptedCommandData
- }
- },
- decryptData: async function (value) {
- if (!this.sharedSecret) {
- console.log('/error Secure session not established!')
- return '/error Secure session not established!'
- }
- try {
- const ivSize = 32
- const messageHex = value.substring(0, value.length - ivSize)
- const ivHex = value.substring(value.length - ivSize)
- const messageBytes = nobleSecp256k1.utils.hexToBytes(messageHex)
- const iv = nobleSecp256k1.utils.hexToBytes(ivHex)
- const decrypted1 = await this.decryptMessage(
- this.sharedSecret,
- iv,
- messageBytes
- )
- const data = new TextDecoder().decode(decrypted1)
- const [len] = data.split(' ')
- const command = data
- .substring(len.length + 1, +len + len.length + 1)
- .trim()
- return command
- } catch (error) {
- console.log('/error Failed to decrypt message from device!')
- return '/error Failed to decrypt message from device!'
- }
- },
- encryptMessage: async function (key, iv, message) {
- while (message.length % 16 !== 0) message += ' '
- const encodedMessage = asciiToUint8Array(message)
-
- const aesCbc = new aesjs.ModeOfOperation.cbc(key, iv)
- const encryptedBytes = aesCbc.encrypt(encodedMessage)
-
- return encryptedBytes
- },
- decryptMessage: async function (key, iv, encryptedBytes) {
- const aesCbc = new aesjs.ModeOfOperation.cbc(key, iv)
- const decryptedBytes = aesCbc.decrypt(encryptedBytes)
- return decryptedBytes
- },
-
- getPairedDevice: function (deviceId) {
- return this.pairedDevices.find(d => d.id === deviceId)
- },
- removePairedDevice: function (deviceId) {
- const devices = this.pairedDevices
- const deviceIndex = devices.findIndex(d => d.id === deviceId)
- if (deviceIndex !== -1) {
- devices.splice(deviceIndex, 1)
- }
- this.pairedDevices = devices
- this.showPairedDevices = false
- setTimeout(() => {
- // force UI refresh
- this.showPairedDevices = true
- })
- },
- addPairedDevice: function (deviceId, sharedSecretHex, config) {
- const devices = this.pairedDevices
- config.deviceId = deviceId
- devices.unshift({
- id: deviceId,
- sharedSecretHex: sharedSecretHex,
- pairingDate: new Date().toISOString(),
- config
- })
- this.pairedDevices = devices
- this.showPairedDevices = false
- setTimeout(() => {
- // force UI refresh
- this.showPairedDevices = true
- })
- },
- updatePairedDeviceConfig(deviceId, config) {
- const device = this.getPairedDevice(deviceId)
- if (device) {
- this.removePairedDevice(deviceId)
- this.addPairedDevice(deviceId, device.sharedSecretHex, config)
- }
- }
- },
- created: async function () {}
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/utxo-list/utxo-list.html b/lnbits/extensions/watchonly/static/components/utxo-list/utxo-list.html
deleted file mode 100644
index cb1b930b..00000000
--- a/lnbits/extensions/watchonly/static/components/utxo-list/utxo-list.html
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Confirmed
-
-
- Pending
-
-
-
-
-
-
-
-
- {{satBtc(props.row.amount)}}
-
-
- {{ props.row.date }}
-
- {{getWalletName(props.row.wallet)}}
-
-
-
-
-
-
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/utxo-list/utxo-list.js b/lnbits/extensions/watchonly/static/components/utxo-list/utxo-list.js
deleted file mode 100644
index 6741ed94..00000000
--- a/lnbits/extensions/watchonly/static/components/utxo-list/utxo-list.js
+++ /dev/null
@@ -1,148 +0,0 @@
-async function utxoList(path) {
- const template = await loadTemplateAsync(path)
- Vue.component('utxo-list', {
- name: 'utxo-list',
- template,
-
- props: [
- 'utxos',
- 'accounts',
- 'selectable',
- 'payed-amount',
- 'sats-denominated',
- 'mempool-endpoint',
- 'filter'
- ],
-
- data: function () {
- return {
- utxosTable: {
- columns: [
- {
- name: 'expand',
- align: 'left',
- label: ''
- },
- {
- name: 'selected',
- align: 'left',
- label: '',
- selectable: true
- },
- {
- name: 'status',
- align: 'center',
- label: 'Status',
- sortable: true
- },
- {
- name: 'address',
- align: 'left',
- label: 'Address',
- field: 'address',
- sortable: true
- },
- {
- name: 'amount',
- align: 'left',
- label: 'Amount',
- field: 'amount',
- sortable: true
- },
- {
- name: 'date',
- align: 'left',
- label: 'Date',
- field: 'date',
- sortable: true
- },
- {
- name: 'wallet',
- align: 'left',
- label: 'Account',
- field: 'wallet',
- sortable: true
- }
- ],
- pagination: {
- rowsPerPage: 10
- }
- },
- utxoSelectionModes: [
- 'Manual',
- 'Random',
- 'Select All',
- 'Smaller Inputs First',
- 'Larger Inputs First'
- ],
- utxoSelectionMode: 'Random',
- utxoSelectAmount: 0
- }
- },
-
- computed: {
- columns: function () {
- return this.utxosTable.columns.filter(c =>
- c.selectable ? this.selectable : true
- )
- }
- },
-
- methods: {
- satBtc(val, showUnit = true) {
- return satOrBtc(val, showUnit, this.satsDenominated)
- },
- getWalletName: function (walletId) {
- const wallet = (this.accounts || []).find(wl => wl.id === walletId)
- return wallet ? wallet.title : 'unknown'
- },
- getTotalSelectedUtxoAmount: function () {
- const total = (this.utxos || [])
- .filter(u => u.selected)
- .reduce((t, a) => t + (a.amount || 0), 0)
- return total
- },
- refreshUtxoSelection: function (totalPayedAmount) {
- this.utxoSelectAmount = totalPayedAmount
- this.applyUtxoSelectionMode()
- },
- updateUtxoSelection: function () {
- this.utxoSelectAmount = this.payedAmount
- this.applyUtxoSelectionMode()
- },
- applyUtxoSelectionMode: function () {
- const mode = this.utxoSelectionMode
- const isSelectAll = mode === 'Select All'
- if (isSelectAll) {
- this.utxos.forEach(u => (u.selected = true))
- return
- }
-
- const isManual = mode === 'Manual'
- if (isManual || !this.utxoSelectAmount) return
-
- this.utxos.forEach(u => (u.selected = false))
-
- const isSmallerFirst = mode === 'Smaller Inputs First'
- const isLargerFirst = mode === 'Larger Inputs First'
- let selectedUtxos = this.utxos.slice()
- if (isSmallerFirst || isLargerFirst) {
- const sortFn = isSmallerFirst
- ? (a, b) => a.amount - b.amount
- : (a, b) => b.amount - a.amount
- selectedUtxos.sort(sortFn)
- } else {
- // default to random order
- selectedUtxos = _.shuffle(selectedUtxos)
- }
- selectedUtxos.reduce((total, utxo) => {
- utxo.selected = total < this.utxoSelectAmount
- total += utxo.amount
- return total
- }, 0)
- }
- },
-
- created: async function () {}
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/wallet-config/wallet-config.html b/lnbits/extensions/watchonly/static/components/wallet-config/wallet-config.html
deleted file mode 100644
index 748d650d..00000000
--- a/lnbits/extensions/watchonly/static/components/wallet-config/wallet-config.html
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Update
- Cancel
-
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/wallet-config/wallet-config.js b/lnbits/extensions/watchonly/static/components/wallet-config/wallet-config.js
deleted file mode 100644
index 447dc65c..00000000
--- a/lnbits/extensions/watchonly/static/components/wallet-config/wallet-config.js
+++ /dev/null
@@ -1,67 +0,0 @@
-async function walletConfig(path) {
- const t = await loadTemplateAsync(path)
- Vue.component('wallet-config', {
- name: 'wallet-config',
- template: t,
-
- props: ['total', 'config-data', 'adminkey'],
- data: function () {
- return {
- networOptions: ['Mainnet', 'Testnet'],
- internalConfig: {},
- show: false
- }
- },
-
- computed: {
- config: {
- get() {
- return this.internalConfig
- },
- set(value) {
- value.isLoaded = true
- this.internalConfig = JSON.parse(JSON.stringify(value))
- this.$emit(
- 'update:config-data',
- JSON.parse(JSON.stringify(this.internalConfig))
- )
- }
- }
- },
-
- methods: {
- satBtc(val, showUnit = true) {
- return satOrBtc(val, showUnit, this.config.sats_denominated)
- },
- updateConfig: async function () {
- try {
- const {data} = await LNbits.api.request(
- 'PUT',
- '/watchonly/api/v1/config',
- this.adminkey,
- this.config
- )
- this.show = false
- this.config = data
- } catch (error) {
- LNbits.utils.notifyApiError(error)
- }
- },
- getConfig: async function () {
- try {
- const {data} = await LNbits.api.request(
- 'GET',
- '/watchonly/api/v1/config',
- this.adminkey
- )
- this.config = data
- } catch (error) {
- LNbits.utils.notifyApiError(error)
- }
- }
- },
- created: async function () {
- await this.getConfig()
- }
- })
-}
diff --git a/lnbits/extensions/watchonly/static/components/wallet-list/wallet-list.html b/lnbits/extensions/watchonly/static/components/wallet-list/wallet-list.html
deleted file mode 100644
index b656bdca..00000000
--- a/lnbits/extensions/watchonly/static/components/wallet-list/wallet-list.html
+++ /dev/null
@@ -1,285 +0,0 @@
-
-
-
-
-
-
-
-
-
- New Account
- Enter account Xpub or Descriptor
-
-
-
-
- From Hardware Device
-
- Get Xpub from a Hardware Device
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ col.label }}
-
-
-
-
-
-
-
-
-
-
-
- New Receive Address
-
-
-
-
- {{props.row.title}}
-
-
- {{getAmmountForWallet(props.row.id)}}
-
-
- {{props.row.type}}
-
-
- {{props.row.id}}
-
-
-
-
-
-
-
- New Receive Address
-
-
-
- {{getAccountDescription(props.row.type)}}
-
-
-
-
-
-
Master Pubkey:
-
-
-
-
-
-
-
-
-
-
-
-
XPub:
-
-
-
-
-
-
-
-
-
-
-
-
Last Address Index:
-
- {{props.row.address_no}}
- none
-
-
-
-
-
Fingerprint:
-
{{props.row.fingerprint}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Cancel
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lnbits/extensions/watchonly/static/components/wallet-list/wallet-list.js b/lnbits/extensions/watchonly/static/components/wallet-list/wallet-list.js
deleted file mode 100644
index 27d40ce9..00000000
--- a/lnbits/extensions/watchonly/static/components/wallet-list/wallet-list.js
+++ /dev/null
@@ -1,315 +0,0 @@
-async function walletList(path) {
- const template = await loadTemplateAsync(path)
- Vue.component('wallet-list', {
- name: 'wallet-list',
- template,
-
- props: [
- 'adminkey',
- 'inkey',
- 'sats-denominated',
- 'addresses',
- 'network',
- 'serial-signer-ref'
- ],
- data: function () {
- return {
- walletAccounts: [],
- address: {},
- showQrCodeDialog: false,
- qrCodeValue: null,
- formDialog: {
- show: false,
-
- addressType: {
- label: 'Segwit (P2WPKH)',
- id: 'wpkh',
- pathMainnet: "m/84'/0'/0'",
- pathTestnet: "m/84'/1'/0'"
- },
- useSerialPort: false,
- data: {
- title: '',
- masterpub: ''
- }
- },
- accountPath: '',
- filter: '',
- showCreating: false,
- addressTypeOptions: [
- {
- label: 'Legacy (P2PKH)',
- id: 'pkh',
- pathMainnet: "m/44'/0'/0'",
- pathTestnet: "m/44'/1'/0'"
- },
- {
- label: 'Segwit (P2WPKH)',
- id: 'wpkh',
- pathMainnet: "m/84'/0'/0'",
- pathTestnet: "m/84'/1'/0'"
- },
- {
- label: 'Wrapped Segwit (P2SH-P2WPKH)',
- id: 'sh',
- pathMainnet: "m/49'/0'/0'",
- pathTestnet: "m/49'/1'/0'"
- },
- {
- label: 'Taproot (P2TR)',
- id: 'tr',
- pathMainnet: "m/86'/0'/0'",
- pathTestnet: "m/86'/1'/0'"
- }
- ],
-
- walletsTable: {
- columns: [
- {
- name: 'new',
- align: 'left',
- label: ''
- },
- {
- name: 'title',
- align: 'left',
- label: 'Title',
- field: 'title'
- },
- {
- name: 'amount',
- align: 'left',
- label: 'Amount'
- },
- {
- name: 'type',
- align: 'left',
- label: 'Type',
- field: 'type'
- },
- {name: 'id', align: 'left', label: 'ID', field: 'id'}
- ],
- pagination: {
- rowsPerPage: 10
- },
- filter: ''
- }
- }
- },
- watch: {
- immediate: true,
- async network(newNet, oldNet) {
- if (newNet !== oldNet) {
- await this.refreshWalletAccounts()
- this.handleAddressTypeChanged(this.addressTypeOptions[1])
- }
- }
- },
-
- methods: {
- satBtc(val, showUnit = true) {
- return satOrBtc(val, showUnit, this.satsDenominated)
- },
-
- addWalletAccount: async function () {
- this.showCreating = true
- const data = _.omit(this.formDialog.data, 'wallet')
- data.network = this.network
- await this.createWalletAccount(data)
- this.showCreating = false
- },
- createWalletAccount: async function (data) {
- try {
- const meta = {accountPath: this.accountPath}
- if (this.formDialog.useSerialPort) {
- const {xpub, fingerprint} = await this.fetchXpubFromHww()
- if (!xpub) return
- meta.xpub = xpub
- const path = this.accountPath.substring(2)
- const outputType = this.formDialog.addressType.id
- if (outputType === 'sh') {
- data.masterpub = `${outputType}(wpkh([${fingerprint}/${path}]${xpub}/{0,1}/*))`
- } else {
- data.masterpub = `${outputType}([${fingerprint}/${path}]${xpub}/{0,1}/*)`
- }
- }
- data.meta = JSON.stringify(meta)
- const response = await LNbits.api.request(
- 'POST',
- '/watchonly/api/v1/wallet',
- this.adminkey,
- data
- )
- this.walletAccounts.push(mapWalletAccount(response.data))
- this.formDialog.show = false
-
- await this.refreshWalletAccounts()
- } catch (error) {
- LNbits.utils.notifyApiError(error)
- }
- },
- fetchXpubFromHww: async function () {
- const error = findAccountPathIssues(this.accountPath)
- if (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Invalid derivation path.',
- caption: error,
- timeout: 10000
- })
- return
- }
- await this.serialSignerRef.hwwXpub(this.accountPath)
- return await this.serialSignerRef.isFetchingXpub()
- },
- deleteWalletAccount: function (walletAccountId) {
- LNbits.utils
- .confirmDialog(
- 'Are you sure you want to delete this watch only wallet?'
- )
- .onOk(async () => {
- try {
- await LNbits.api.request(
- 'DELETE',
- '/watchonly/api/v1/wallet/' + walletAccountId,
- this.adminkey
- )
- this.walletAccounts = _.reject(
- this.walletAccounts,
- function (obj) {
- return obj.id === walletAccountId
- }
- )
- await this.refreshWalletAccounts()
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message:
- 'Error while deleting wallet account. Please try again.',
- timeout: 10000
- })
- }
- })
- },
-
- getWatchOnlyWallets: async function () {
- try {
- const {data} = await LNbits.api.request(
- 'GET',
- `/watchonly/api/v1/wallet?network=${this.network}`,
- this.inkey
- )
- return data
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Failed to fetch wallets.',
- timeout: 10000
- })
- LNbits.utils.notifyApiError(error)
- }
- return []
- },
- refreshWalletAccounts: async function () {
- this.walletAccounts = []
- const wallets = await this.getWatchOnlyWallets()
- this.walletAccounts = wallets.map(w => mapWalletAccount(w))
- this.$emit('accounts-update', this.walletAccounts)
- },
- getAmmountForWallet: function (walletId) {
- const amount = this.addresses
- .filter(a => a.wallet === walletId)
- .reduce((t, a) => t + a.amount || 0, 0)
- return this.satBtc(amount)
- },
- closeFormDialog: function () {
- this.formDialog.data = {
- is_unique: false
- }
- },
- getAccountDescription: function (accountType) {
- return getAccountDescription(accountType)
- },
- openGetFreshAddressDialog: async function (walletId) {
- const {data} = await LNbits.api.request(
- 'GET',
- `/watchonly/api/v1/address/${walletId}`,
- this.inkey
- )
- const addressData = mapAddressesData(data)
-
- addressData.note = `Shared on ${currentDateTime()}`
- const lastActiveAddress =
- this.addresses
- .filter(
- a =>
- a.wallet === addressData.wallet && !a.isChange && a.hasActivity
- )
- .pop() || {}
- addressData.gapLimitExceeded =
- !addressData.isChange &&
- addressData.addressIndex >
- lastActiveAddress.addressIndex + DEFAULT_RECEIVE_GAP_LIMIT
-
- const wallet = this.walletAccounts.find(w => w.id === walletId) || {}
- wallet.address_no = addressData.addressIndex
- this.$emit('new-receive-address', {addressData, wallet})
- },
- showAddAccountDialog: function () {
- this.formDialog.show = true
- this.formDialog.useSerialPort = false
- },
- getXpubFromDevice: async function () {
- try {
- if (!this.serialSignerRef.isConnected()) {
- this.$q.notify({
- type: 'warning',
- message: 'Please connect to a hardware Device first!',
- timeout: 10000
- })
- return
- }
- if (!this.serialSignerRef.isAuthenticated()) {
- await this.serialSignerRef.hwwShowPasswordDialog()
- const authenticated = await this.serialSignerRef.isAuthenticating()
- if (!authenticated) return
- }
- this.formDialog.show = true
- this.formDialog.useSerialPort = true
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: 'Cannot fetch Xpub!',
- caption: `${error}`,
- timeout: 10000
- })
- }
- },
- handleAddressTypeChanged: function (value = {}) {
- const addressType =
- this.addressTypeOptions.find(t => t.id === value.id) || {}
- this.accountPath = addressType[`path${this.network}`]
- },
- // todo: bad. base.js not present in custom components
- copyText: function (text, message, position) {
- var notify = this.$q.notify
- Quasar.utils.copyToClipboard(text).then(function () {
- notify({
- message: message || 'Copied to clipboard!',
- position: position || 'bottom'
- })
- })
- },
- openQrCodeDialog: function (qrCodeValue) {
- this.qrCodeValue = qrCodeValue
- this.showQrCodeDialog = true
- }
- },
- created: async function () {
- if (this.inkey) {
- await this.refreshWalletAccounts()
- this.handleAddressTypeChanged(this.addressTypeOptions[1])
- }
- }
- })
-}
diff --git a/lnbits/extensions/watchonly/static/js/bip39-word-list.js b/lnbits/extensions/watchonly/static/js/bip39-word-list.js
deleted file mode 100644
index c0a5eac3..00000000
--- a/lnbits/extensions/watchonly/static/js/bip39-word-list.js
+++ /dev/null
@@ -1,2050 +0,0 @@
-const bip39WordList = Object.freeze([
- 'abandon',
- 'ability',
- 'able',
- 'about',
- 'above',
- 'absent',
- 'absorb',
- 'abstract',
- 'absurd',
- 'abuse',
- 'access',
- 'accident',
- 'account',
- 'accuse',
- 'achieve',
- 'acid',
- 'acoustic',
- 'acquire',
- 'across',
- 'act',
- 'action',
- 'actor',
- 'actress',
- 'actual',
- 'adapt',
- 'add',
- 'addict',
- 'address',
- 'adjust',
- 'admit',
- 'adult',
- 'advance',
- 'advice',
- 'aerobic',
- 'affair',
- 'afford',
- 'afraid',
- 'again',
- 'age',
- 'agent',
- 'agree',
- 'ahead',
- 'aim',
- 'air',
- 'airport',
- 'aisle',
- 'alarm',
- 'album',
- 'alcohol',
- 'alert',
- 'alien',
- 'all',
- 'alley',
- 'allow',
- 'almost',
- 'alone',
- 'alpha',
- 'already',
- 'also',
- 'alter',
- 'always',
- 'amateur',
- 'amazing',
- 'among',
- 'amount',
- 'amused',
- 'analyst',
- 'anchor',
- 'ancient',
- 'anger',
- 'angle',
- 'angry',
- 'animal',
- 'ankle',
- 'announce',
- 'annual',
- 'another',
- 'answer',
- 'antenna',
- 'antique',
- 'anxiety',
- 'any',
- 'apart',
- 'apology',
- 'appear',
- 'apple',
- 'approve',
- 'april',
- 'arch',
- 'arctic',
- 'area',
- 'arena',
- 'argue',
- 'arm',
- 'armed',
- 'armor',
- 'army',
- 'around',
- 'arrange',
- 'arrest',
- 'arrive',
- 'arrow',
- 'art',
- 'artefact',
- 'artist',
- 'artwork',
- 'ask',
- 'aspect',
- 'assault',
- 'asset',
- 'assist',
- 'assume',
- 'asthma',
- 'athlete',
- 'atom',
- 'attack',
- 'attend',
- 'attitude',
- 'attract',
- 'auction',
- 'audit',
- 'august',
- 'aunt',
- 'author',
- 'auto',
- 'autumn',
- 'average',
- 'avocado',
- 'avoid',
- 'awake',
- 'aware',
- 'away',
- 'awesome',
- 'awful',
- 'awkward',
- 'axis',
- 'baby',
- 'bachelor',
- 'bacon',
- 'badge',
- 'bag',
- 'balance',
- 'balcony',
- 'ball',
- 'bamboo',
- 'banana',
- 'banner',
- 'bar',
- 'barely',
- 'bargain',
- 'barrel',
- 'base',
- 'basic',
- 'basket',
- 'battle',
- 'beach',
- 'bean',
- 'beauty',
- 'because',
- 'become',
- 'beef',
- 'before',
- 'begin',
- 'behave',
- 'behind',
- 'believe',
- 'below',
- 'belt',
- 'bench',
- 'benefit',
- 'best',
- 'betray',
- 'better',
- 'between',
- 'beyond',
- 'bicycle',
- 'bid',
- 'bike',
- 'bind',
- 'biology',
- 'bird',
- 'birth',
- 'bitter',
- 'black',
- 'blade',
- 'blame',
- 'blanket',
- 'blast',
- 'bleak',
- 'bless',
- 'blind',
- 'blood',
- 'blossom',
- 'blouse',
- 'blue',
- 'blur',
- 'blush',
- 'board',
- 'boat',
- 'body',
- 'boil',
- 'bomb',
- 'bone',
- 'bonus',
- 'book',
- 'boost',
- 'border',
- 'boring',
- 'borrow',
- 'boss',
- 'bottom',
- 'bounce',
- 'box',
- 'boy',
- 'bracket',
- 'brain',
- 'brand',
- 'brass',
- 'brave',
- 'bread',
- 'breeze',
- 'brick',
- 'bridge',
- 'brief',
- 'bright',
- 'bring',
- 'brisk',
- 'broccoli',
- 'broken',
- 'bronze',
- 'broom',
- 'brother',
- 'brown',
- 'brush',
- 'bubble',
- 'buddy',
- 'budget',
- 'buffalo',
- 'build',
- 'bulb',
- 'bulk',
- 'bullet',
- 'bundle',
- 'bunker',
- 'burden',
- 'burger',
- 'burst',
- 'bus',
- 'business',
- 'busy',
- 'butter',
- 'buyer',
- 'buzz',
- 'cabbage',
- 'cabin',
- 'cable',
- 'cactus',
- 'cage',
- 'cake',
- 'call',
- 'calm',
- 'camera',
- 'camp',
- 'can',
- 'canal',
- 'cancel',
- 'candy',
- 'cannon',
- 'canoe',
- 'canvas',
- 'canyon',
- 'capable',
- 'capital',
- 'captain',
- 'car',
- 'carbon',
- 'card',
- 'cargo',
- 'carpet',
- 'carry',
- 'cart',
- 'case',
- 'cash',
- 'casino',
- 'castle',
- 'casual',
- 'cat',
- 'catalog',
- 'catch',
- 'category',
- 'cattle',
- 'caught',
- 'cause',
- 'caution',
- 'cave',
- 'ceiling',
- 'celery',
- 'cement',
- 'census',
- 'century',
- 'cereal',
- 'certain',
- 'chair',
- 'chalk',
- 'champion',
- 'change',
- 'chaos',
- 'chapter',
- 'charge',
- 'chase',
- 'chat',
- 'cheap',
- 'check',
- 'cheese',
- 'chef',
- 'cherry',
- 'chest',
- 'chicken',
- 'chief',
- 'child',
- 'chimney',
- 'choice',
- 'choose',
- 'chronic',
- 'chuckle',
- 'chunk',
- 'churn',
- 'cigar',
- 'cinnamon',
- 'circle',
- 'citizen',
- 'city',
- 'civil',
- 'claim',
- 'clap',
- 'clarify',
- 'claw',
- 'clay',
- 'clean',
- 'clerk',
- 'clever',
- 'click',
- 'client',
- 'cliff',
- 'climb',
- 'clinic',
- 'clip',
- 'clock',
- 'clog',
- 'close',
- 'cloth',
- 'cloud',
- 'clown',
- 'club',
- 'clump',
- 'cluster',
- 'clutch',
- 'coach',
- 'coast',
- 'coconut',
- 'code',
- 'coffee',
- 'coil',
- 'coin',
- 'collect',
- 'color',
- 'column',
- 'combine',
- 'come',
- 'comfort',
- 'comic',
- 'common',
- 'company',
- 'concert',
- 'conduct',
- 'confirm',
- 'congress',
- 'connect',
- 'consider',
- 'control',
- 'convince',
- 'cook',
- 'cool',
- 'copper',
- 'copy',
- 'coral',
- 'core',
- 'corn',
- 'correct',
- 'cost',
- 'cotton',
- 'couch',
- 'country',
- 'couple',
- 'course',
- 'cousin',
- 'cover',
- 'coyote',
- 'crack',
- 'cradle',
- 'craft',
- 'cram',
- 'crane',
- 'crash',
- 'crater',
- 'crawl',
- 'crazy',
- 'cream',
- 'credit',
- 'creek',
- 'crew',
- 'cricket',
- 'crime',
- 'crisp',
- 'critic',
- 'crop',
- 'cross',
- 'crouch',
- 'crowd',
- 'crucial',
- 'cruel',
- 'cruise',
- 'crumble',
- 'crunch',
- 'crush',
- 'cry',
- 'crystal',
- 'cube',
- 'culture',
- 'cup',
- 'cupboard',
- 'curious',
- 'current',
- 'curtain',
- 'curve',
- 'cushion',
- 'custom',
- 'cute',
- 'cycle',
- 'dad',
- 'damage',
- 'damp',
- 'dance',
- 'danger',
- 'daring',
- 'dash',
- 'daughter',
- 'dawn',
- 'day',
- 'deal',
- 'debate',
- 'debris',
- 'decade',
- 'december',
- 'decide',
- 'decline',
- 'decorate',
- 'decrease',
- 'deer',
- 'defense',
- 'define',
- 'defy',
- 'degree',
- 'delay',
- 'deliver',
- 'demand',
- 'demise',
- 'denial',
- 'dentist',
- 'deny',
- 'depart',
- 'depend',
- 'deposit',
- 'depth',
- 'deputy',
- 'derive',
- 'describe',
- 'desert',
- 'design',
- 'desk',
- 'despair',
- 'destroy',
- 'detail',
- 'detect',
- 'develop',
- 'device',
- 'devote',
- 'diagram',
- 'dial',
- 'diamond',
- 'diary',
- 'dice',
- 'diesel',
- 'diet',
- 'differ',
- 'digital',
- 'dignity',
- 'dilemma',
- 'dinner',
- 'dinosaur',
- 'direct',
- 'dirt',
- 'disagree',
- 'discover',
- 'disease',
- 'dish',
- 'dismiss',
- 'disorder',
- 'display',
- 'distance',
- 'divert',
- 'divide',
- 'divorce',
- 'dizzy',
- 'doctor',
- 'document',
- 'dog',
- 'doll',
- 'dolphin',
- 'domain',
- 'donate',
- 'donkey',
- 'donor',
- 'door',
- 'dose',
- 'double',
- 'dove',
- 'draft',
- 'dragon',
- 'drama',
- 'drastic',
- 'draw',
- 'dream',
- 'dress',
- 'drift',
- 'drill',
- 'drink',
- 'drip',
- 'drive',
- 'drop',
- 'drum',
- 'dry',
- 'duck',
- 'dumb',
- 'dune',
- 'during',
- 'dust',
- 'dutch',
- 'duty',
- 'dwarf',
- 'dynamic',
- 'eager',
- 'eagle',
- 'early',
- 'earn',
- 'earth',
- 'easily',
- 'east',
- 'easy',
- 'echo',
- 'ecology',
- 'economy',
- 'edge',
- 'edit',
- 'educate',
- 'effort',
- 'egg',
- 'eight',
- 'either',
- 'elbow',
- 'elder',
- 'electric',
- 'elegant',
- 'element',
- 'elephant',
- 'elevator',
- 'elite',
- 'else',
- 'embark',
- 'embody',
- 'embrace',
- 'emerge',
- 'emotion',
- 'employ',
- 'empower',
- 'empty',
- 'enable',
- 'enact',
- 'end',
- 'endless',
- 'endorse',
- 'enemy',
- 'energy',
- 'enforce',
- 'engage',
- 'engine',
- 'enhance',
- 'enjoy',
- 'enlist',
- 'enough',
- 'enrich',
- 'enroll',
- 'ensure',
- 'enter',
- 'entire',
- 'entry',
- 'envelope',
- 'episode',
- 'equal',
- 'equip',
- 'era',
- 'erase',
- 'erode',
- 'erosion',
- 'error',
- 'erupt',
- 'escape',
- 'essay',
- 'essence',
- 'estate',
- 'eternal',
- 'ethics',
- 'evidence',
- 'evil',
- 'evoke',
- 'evolve',
- 'exact',
- 'example',
- 'excess',
- 'exchange',
- 'excite',
- 'exclude',
- 'excuse',
- 'execute',
- 'exercise',
- 'exhaust',
- 'exhibit',
- 'exile',
- 'exist',
- 'exit',
- 'exotic',
- 'expand',
- 'expect',
- 'expire',
- 'explain',
- 'expose',
- 'express',
- 'extend',
- 'extra',
- 'eye',
- 'eyebrow',
- 'fabric',
- 'face',
- 'faculty',
- 'fade',
- 'faint',
- 'faith',
- 'fall',
- 'false',
- 'fame',
- 'family',
- 'famous',
- 'fan',
- 'fancy',
- 'fantasy',
- 'farm',
- 'fashion',
- 'fat',
- 'fatal',
- 'father',
- 'fatigue',
- 'fault',
- 'favorite',
- 'feature',
- 'february',
- 'federal',
- 'fee',
- 'feed',
- 'feel',
- 'female',
- 'fence',
- 'festival',
- 'fetch',
- 'fever',
- 'few',
- 'fiber',
- 'fiction',
- 'field',
- 'figure',
- 'file',
- 'film',
- 'filter',
- 'final',
- 'find',
- 'fine',
- 'finger',
- 'finish',
- 'fire',
- 'firm',
- 'first',
- 'fiscal',
- 'fish',
- 'fit',
- 'fitness',
- 'fix',
- 'flag',
- 'flame',
- 'flash',
- 'flat',
- 'flavor',
- 'flee',
- 'flight',
- 'flip',
- 'float',
- 'flock',
- 'floor',
- 'flower',
- 'fluid',
- 'flush',
- 'fly',
- 'foam',
- 'focus',
- 'fog',
- 'foil',
- 'fold',
- 'follow',
- 'food',
- 'foot',
- 'force',
- 'forest',
- 'forget',
- 'fork',
- 'fortune',
- 'forum',
- 'forward',
- 'fossil',
- 'foster',
- 'found',
- 'fox',
- 'fragile',
- 'frame',
- 'frequent',
- 'fresh',
- 'friend',
- 'fringe',
- 'frog',
- 'front',
- 'frost',
- 'frown',
- 'frozen',
- 'fruit',
- 'fuel',
- 'fun',
- 'funny',
- 'furnace',
- 'fury',
- 'future',
- 'gadget',
- 'gain',
- 'galaxy',
- 'gallery',
- 'game',
- 'gap',
- 'garage',
- 'garbage',
- 'garden',
- 'garlic',
- 'garment',
- 'gas',
- 'gasp',
- 'gate',
- 'gather',
- 'gauge',
- 'gaze',
- 'general',
- 'genius',
- 'genre',
- 'gentle',
- 'genuine',
- 'gesture',
- 'ghost',
- 'giant',
- 'gift',
- 'giggle',
- 'ginger',
- 'giraffe',
- 'girl',
- 'give',
- 'glad',
- 'glance',
- 'glare',
- 'glass',
- 'glide',
- 'glimpse',
- 'globe',
- 'gloom',
- 'glory',
- 'glove',
- 'glow',
- 'glue',
- 'goat',
- 'goddess',
- 'gold',
- 'good',
- 'goose',
- 'gorilla',
- 'gospel',
- 'gossip',
- 'govern',
- 'gown',
- 'grab',
- 'grace',
- 'grain',
- 'grant',
- 'grape',
- 'grass',
- 'gravity',
- 'great',
- 'green',
- 'grid',
- 'grief',
- 'grit',
- 'grocery',
- 'group',
- 'grow',
- 'grunt',
- 'guard',
- 'guess',
- 'guide',
- 'guilt',
- 'guitar',
- 'gun',
- 'gym',
- 'habit',
- 'hair',
- 'half',
- 'hammer',
- 'hamster',
- 'hand',
- 'happy',
- 'harbor',
- 'hard',
- 'harsh',
- 'harvest',
- 'hat',
- 'have',
- 'hawk',
- 'hazard',
- 'head',
- 'health',
- 'heart',
- 'heavy',
- 'hedgehog',
- 'height',
- 'hello',
- 'helmet',
- 'help',
- 'hen',
- 'hero',
- 'hidden',
- 'high',
- 'hill',
- 'hint',
- 'hip',
- 'hire',
- 'history',
- 'hobby',
- 'hockey',
- 'hold',
- 'hole',
- 'holiday',
- 'hollow',
- 'home',
- 'honey',
- 'hood',
- 'hope',
- 'horn',
- 'horror',
- 'horse',
- 'hospital',
- 'host',
- 'hotel',
- 'hour',
- 'hover',
- 'hub',
- 'huge',
- 'human',
- 'humble',
- 'humor',
- 'hundred',
- 'hungry',
- 'hunt',
- 'hurdle',
- 'hurry',
- 'hurt',
- 'husband',
- 'hybrid',
- 'ice',
- 'icon',
- 'idea',
- 'identify',
- 'idle',
- 'ignore',
- 'ill',
- 'illegal',
- 'illness',
- 'image',
- 'imitate',
- 'immense',
- 'immune',
- 'impact',
- 'impose',
- 'improve',
- 'impulse',
- 'inch',
- 'include',
- 'income',
- 'increase',
- 'index',
- 'indicate',
- 'indoor',
- 'industry',
- 'infant',
- 'inflict',
- 'inform',
- 'inhale',
- 'inherit',
- 'initial',
- 'inject',
- 'injury',
- 'inmate',
- 'inner',
- 'innocent',
- 'input',
- 'inquiry',
- 'insane',
- 'insect',
- 'inside',
- 'inspire',
- 'install',
- 'intact',
- 'interest',
- 'into',
- 'invest',
- 'invite',
- 'involve',
- 'iron',
- 'island',
- 'isolate',
- 'issue',
- 'item',
- 'ivory',
- 'jacket',
- 'jaguar',
- 'jar',
- 'jazz',
- 'jealous',
- 'jeans',
- 'jelly',
- 'jewel',
- 'job',
- 'join',
- 'joke',
- 'journey',
- 'joy',
- 'judge',
- 'juice',
- 'jump',
- 'jungle',
- 'junior',
- 'junk',
- 'just',
- 'kangaroo',
- 'keen',
- 'keep',
- 'ketchup',
- 'key',
- 'kick',
- 'kid',
- 'kidney',
- 'kind',
- 'kingdom',
- 'kiss',
- 'kit',
- 'kitchen',
- 'kite',
- 'kitten',
- 'kiwi',
- 'knee',
- 'knife',
- 'knock',
- 'know',
- 'lab',
- 'label',
- 'labor',
- 'ladder',
- 'lady',
- 'lake',
- 'lamp',
- 'language',
- 'laptop',
- 'large',
- 'later',
- 'latin',
- 'laugh',
- 'laundry',
- 'lava',
- 'law',
- 'lawn',
- 'lawsuit',
- 'layer',
- 'lazy',
- 'leader',
- 'leaf',
- 'learn',
- 'leave',
- 'lecture',
- 'left',
- 'leg',
- 'legal',
- 'legend',
- 'leisure',
- 'lemon',
- 'lend',
- 'length',
- 'lens',
- 'leopard',
- 'lesson',
- 'letter',
- 'level',
- 'liar',
- 'liberty',
- 'library',
- 'license',
- 'life',
- 'lift',
- 'light',
- 'like',
- 'limb',
- 'limit',
- 'link',
- 'lion',
- 'liquid',
- 'list',
- 'little',
- 'live',
- 'lizard',
- 'load',
- 'loan',
- 'lobster',
- 'local',
- 'lock',
- 'logic',
- 'lonely',
- 'long',
- 'loop',
- 'lottery',
- 'loud',
- 'lounge',
- 'love',
- 'loyal',
- 'lucky',
- 'luggage',
- 'lumber',
- 'lunar',
- 'lunch',
- 'luxury',
- 'lyrics',
- 'machine',
- 'mad',
- 'magic',
- 'magnet',
- 'maid',
- 'mail',
- 'main',
- 'major',
- 'make',
- 'mammal',
- 'man',
- 'manage',
- 'mandate',
- 'mango',
- 'mansion',
- 'manual',
- 'maple',
- 'marble',
- 'march',
- 'margin',
- 'marine',
- 'market',
- 'marriage',
- 'mask',
- 'mass',
- 'master',
- 'match',
- 'material',
- 'math',
- 'matrix',
- 'matter',
- 'maximum',
- 'maze',
- 'meadow',
- 'mean',
- 'measure',
- 'meat',
- 'mechanic',
- 'medal',
- 'media',
- 'melody',
- 'melt',
- 'member',
- 'memory',
- 'mention',
- 'menu',
- 'mercy',
- 'merge',
- 'merit',
- 'merry',
- 'mesh',
- 'message',
- 'metal',
- 'method',
- 'middle',
- 'midnight',
- 'milk',
- 'million',
- 'mimic',
- 'mind',
- 'minimum',
- 'minor',
- 'minute',
- 'miracle',
- 'mirror',
- 'misery',
- 'miss',
- 'mistake',
- 'mix',
- 'mixed',
- 'mixture',
- 'mobile',
- 'model',
- 'modify',
- 'mom',
- 'moment',
- 'monitor',
- 'monkey',
- 'monster',
- 'month',
- 'moon',
- 'moral',
- 'more',
- 'morning',
- 'mosquito',
- 'mother',
- 'motion',
- 'motor',
- 'mountain',
- 'mouse',
- 'move',
- 'movie',
- 'much',
- 'muffin',
- 'mule',
- 'multiply',
- 'muscle',
- 'museum',
- 'mushroom',
- 'music',
- 'must',
- 'mutual',
- 'myself',
- 'mystery',
- 'myth',
- 'naive',
- 'name',
- 'napkin',
- 'narrow',
- 'nasty',
- 'nation',
- 'nature',
- 'near',
- 'neck',
- 'need',
- 'negative',
- 'neglect',
- 'neither',
- 'nephew',
- 'nerve',
- 'nest',
- 'net',
- 'network',
- 'neutral',
- 'never',
- 'news',
- 'next',
- 'nice',
- 'night',
- 'noble',
- 'noise',
- 'nominee',
- 'noodle',
- 'normal',
- 'north',
- 'nose',
- 'notable',
- 'note',
- 'nothing',
- 'notice',
- 'novel',
- 'now',
- 'nuclear',
- 'number',
- 'nurse',
- 'nut',
- 'oak',
- 'obey',
- 'object',
- 'oblige',
- 'obscure',
- 'observe',
- 'obtain',
- 'obvious',
- 'occur',
- 'ocean',
- 'october',
- 'odor',
- 'off',
- 'offer',
- 'office',
- 'often',
- 'oil',
- 'okay',
- 'old',
- 'olive',
- 'olympic',
- 'omit',
- 'once',
- 'one',
- 'onion',
- 'online',
- 'only',
- 'open',
- 'opera',
- 'opinion',
- 'oppose',
- 'option',
- 'orange',
- 'orbit',
- 'orchard',
- 'order',
- 'ordinary',
- 'organ',
- 'orient',
- 'original',
- 'orphan',
- 'ostrich',
- 'other',
- 'outdoor',
- 'outer',
- 'output',
- 'outside',
- 'oval',
- 'oven',
- 'over',
- 'own',
- 'owner',
- 'oxygen',
- 'oyster',
- 'ozone',
- 'pact',
- 'paddle',
- 'page',
- 'pair',
- 'palace',
- 'palm',
- 'panda',
- 'panel',
- 'panic',
- 'panther',
- 'paper',
- 'parade',
- 'parent',
- 'park',
- 'parrot',
- 'party',
- 'pass',
- 'patch',
- 'path',
- 'patient',
- 'patrol',
- 'pattern',
- 'pause',
- 'pave',
- 'payment',
- 'peace',
- 'peanut',
- 'pear',
- 'peasant',
- 'pelican',
- 'pen',
- 'penalty',
- 'pencil',
- 'people',
- 'pepper',
- 'perfect',
- 'permit',
- 'person',
- 'pet',
- 'phone',
- 'photo',
- 'phrase',
- 'physical',
- 'piano',
- 'picnic',
- 'picture',
- 'piece',
- 'pig',
- 'pigeon',
- 'pill',
- 'pilot',
- 'pink',
- 'pioneer',
- 'pipe',
- 'pistol',
- 'pitch',
- 'pizza',
- 'place',
- 'planet',
- 'plastic',
- 'plate',
- 'play',
- 'please',
- 'pledge',
- 'pluck',
- 'plug',
- 'plunge',
- 'poem',
- 'poet',
- 'point',
- 'polar',
- 'pole',
- 'police',
- 'pond',
- 'pony',
- 'pool',
- 'popular',
- 'portion',
- 'position',
- 'possible',
- 'post',
- 'potato',
- 'pottery',
- 'poverty',
- 'powder',
- 'power',
- 'practice',
- 'praise',
- 'predict',
- 'prefer',
- 'prepare',
- 'present',
- 'pretty',
- 'prevent',
- 'price',
- 'pride',
- 'primary',
- 'print',
- 'priority',
- 'prison',
- 'private',
- 'prize',
- 'problem',
- 'process',
- 'produce',
- 'profit',
- 'program',
- 'project',
- 'promote',
- 'proof',
- 'property',
- 'prosper',
- 'protect',
- 'proud',
- 'provide',
- 'public',
- 'pudding',
- 'pull',
- 'pulp',
- 'pulse',
- 'pumpkin',
- 'punch',
- 'pupil',
- 'puppy',
- 'purchase',
- 'purity',
- 'purpose',
- 'purse',
- 'push',
- 'put',
- 'puzzle',
- 'pyramid',
- 'quality',
- 'quantum',
- 'quarter',
- 'question',
- 'quick',
- 'quit',
- 'quiz',
- 'quote',
- 'rabbit',
- 'raccoon',
- 'race',
- 'rack',
- 'radar',
- 'radio',
- 'rail',
- 'rain',
- 'raise',
- 'rally',
- 'ramp',
- 'ranch',
- 'random',
- 'range',
- 'rapid',
- 'rare',
- 'rate',
- 'rather',
- 'raven',
- 'raw',
- 'razor',
- 'ready',
- 'real',
- 'reason',
- 'rebel',
- 'rebuild',
- 'recall',
- 'receive',
- 'recipe',
- 'record',
- 'recycle',
- 'reduce',
- 'reflect',
- 'reform',
- 'refuse',
- 'region',
- 'regret',
- 'regular',
- 'reject',
- 'relax',
- 'release',
- 'relief',
- 'rely',
- 'remain',
- 'remember',
- 'remind',
- 'remove',
- 'render',
- 'renew',
- 'rent',
- 'reopen',
- 'repair',
- 'repeat',
- 'replace',
- 'report',
- 'require',
- 'rescue',
- 'resemble',
- 'resist',
- 'resource',
- 'response',
- 'result',
- 'retire',
- 'retreat',
- 'return',
- 'reunion',
- 'reveal',
- 'review',
- 'reward',
- 'rhythm',
- 'rib',
- 'ribbon',
- 'rice',
- 'rich',
- 'ride',
- 'ridge',
- 'rifle',
- 'right',
- 'rigid',
- 'ring',
- 'riot',
- 'ripple',
- 'risk',
- 'ritual',
- 'rival',
- 'river',
- 'road',
- 'roast',
- 'robot',
- 'robust',
- 'rocket',
- 'romance',
- 'roof',
- 'rookie',
- 'room',
- 'rose',
- 'rotate',
- 'rough',
- 'round',
- 'route',
- 'royal',
- 'rubber',
- 'rude',
- 'rug',
- 'rule',
- 'run',
- 'runway',
- 'rural',
- 'sad',
- 'saddle',
- 'sadness',
- 'safe',
- 'sail',
- 'salad',
- 'salmon',
- 'salon',
- 'salt',
- 'salute',
- 'same',
- 'sample',
- 'sand',
- 'satisfy',
- 'satoshi',
- 'sauce',
- 'sausage',
- 'save',
- 'say',
- 'scale',
- 'scan',
- 'scare',
- 'scatter',
- 'scene',
- 'scheme',
- 'school',
- 'science',
- 'scissors',
- 'scorpion',
- 'scout',
- 'scrap',
- 'screen',
- 'script',
- 'scrub',
- 'sea',
- 'search',
- 'season',
- 'seat',
- 'second',
- 'secret',
- 'section',
- 'security',
- 'seed',
- 'seek',
- 'segment',
- 'select',
- 'sell',
- 'seminar',
- 'senior',
- 'sense',
- 'sentence',
- 'series',
- 'service',
- 'session',
- 'settle',
- 'setup',
- 'seven',
- 'shadow',
- 'shaft',
- 'shallow',
- 'share',
- 'shed',
- 'shell',
- 'sheriff',
- 'shield',
- 'shift',
- 'shine',
- 'ship',
- 'shiver',
- 'shock',
- 'shoe',
- 'shoot',
- 'shop',
- 'short',
- 'shoulder',
- 'shove',
- 'shrimp',
- 'shrug',
- 'shuffle',
- 'shy',
- 'sibling',
- 'sick',
- 'side',
- 'siege',
- 'sight',
- 'sign',
- 'silent',
- 'silk',
- 'silly',
- 'silver',
- 'similar',
- 'simple',
- 'since',
- 'sing',
- 'siren',
- 'sister',
- 'situate',
- 'six',
- 'size',
- 'skate',
- 'sketch',
- 'ski',
- 'skill',
- 'skin',
- 'skirt',
- 'skull',
- 'slab',
- 'slam',
- 'sleep',
- 'slender',
- 'slice',
- 'slide',
- 'slight',
- 'slim',
- 'slogan',
- 'slot',
- 'slow',
- 'slush',
- 'small',
- 'smart',
- 'smile',
- 'smoke',
- 'smooth',
- 'snack',
- 'snake',
- 'snap',
- 'sniff',
- 'snow',
- 'soap',
- 'soccer',
- 'social',
- 'sock',
- 'soda',
- 'soft',
- 'solar',
- 'soldier',
- 'solid',
- 'solution',
- 'solve',
- 'someone',
- 'song',
- 'soon',
- 'sorry',
- 'sort',
- 'soul',
- 'sound',
- 'soup',
- 'source',
- 'south',
- 'space',
- 'spare',
- 'spatial',
- 'spawn',
- 'speak',
- 'special',
- 'speed',
- 'spell',
- 'spend',
- 'sphere',
- 'spice',
- 'spider',
- 'spike',
- 'spin',
- 'spirit',
- 'split',
- 'spoil',
- 'sponsor',
- 'spoon',
- 'sport',
- 'spot',
- 'spray',
- 'spread',
- 'spring',
- 'spy',
- 'square',
- 'squeeze',
- 'squirrel',
- 'stable',
- 'stadium',
- 'staff',
- 'stage',
- 'stairs',
- 'stamp',
- 'stand',
- 'start',
- 'state',
- 'stay',
- 'steak',
- 'steel',
- 'stem',
- 'step',
- 'stereo',
- 'stick',
- 'still',
- 'sting',
- 'stock',
- 'stomach',
- 'stone',
- 'stool',
- 'story',
- 'stove',
- 'strategy',
- 'street',
- 'strike',
- 'strong',
- 'struggle',
- 'student',
- 'stuff',
- 'stumble',
- 'style',
- 'subject',
- 'submit',
- 'subway',
- 'success',
- 'such',
- 'sudden',
- 'suffer',
- 'sugar',
- 'suggest',
- 'suit',
- 'summer',
- 'sun',
- 'sunny',
- 'sunset',
- 'super',
- 'supply',
- 'supreme',
- 'sure',
- 'surface',
- 'surge',
- 'surprise',
- 'surround',
- 'survey',
- 'suspect',
- 'sustain',
- 'swallow',
- 'swamp',
- 'swap',
- 'swarm',
- 'swear',
- 'sweet',
- 'swift',
- 'swim',
- 'swing',
- 'switch',
- 'sword',
- 'symbol',
- 'symptom',
- 'syrup',
- 'system',
- 'table',
- 'tackle',
- 'tag',
- 'tail',
- 'talent',
- 'talk',
- 'tank',
- 'tape',
- 'target',
- 'task',
- 'taste',
- 'tattoo',
- 'taxi',
- 'teach',
- 'team',
- 'tell',
- 'ten',
- 'tenant',
- 'tennis',
- 'tent',
- 'term',
- 'test',
- 'text',
- 'thank',
- 'that',
- 'theme',
- 'then',
- 'theory',
- 'there',
- 'they',
- 'thing',
- 'this',
- 'thought',
- 'three',
- 'thrive',
- 'throw',
- 'thumb',
- 'thunder',
- 'ticket',
- 'tide',
- 'tiger',
- 'tilt',
- 'timber',
- 'time',
- 'tiny',
- 'tip',
- 'tired',
- 'tissue',
- 'title',
- 'toast',
- 'tobacco',
- 'today',
- 'toddler',
- 'toe',
- 'together',
- 'toilet',
- 'token',
- 'tomato',
- 'tomorrow',
- 'tone',
- 'tongue',
- 'tonight',
- 'tool',
- 'tooth',
- 'top',
- 'topic',
- 'topple',
- 'torch',
- 'tornado',
- 'tortoise',
- 'toss',
- 'total',
- 'tourist',
- 'toward',
- 'tower',
- 'town',
- 'toy',
- 'track',
- 'trade',
- 'traffic',
- 'tragic',
- 'train',
- 'transfer',
- 'trap',
- 'trash',
- 'travel',
- 'tray',
- 'treat',
- 'tree',
- 'trend',
- 'trial',
- 'tribe',
- 'trick',
- 'trigger',
- 'trim',
- 'trip',
- 'trophy',
- 'trouble',
- 'truck',
- 'true',
- 'truly',
- 'trumpet',
- 'trust',
- 'truth',
- 'try',
- 'tube',
- 'tuition',
- 'tumble',
- 'tuna',
- 'tunnel',
- 'turkey',
- 'turn',
- 'turtle',
- 'twelve',
- 'twenty',
- 'twice',
- 'twin',
- 'twist',
- 'two',
- 'type',
- 'typical',
- 'ugly',
- 'umbrella',
- 'unable',
- 'unaware',
- 'uncle',
- 'uncover',
- 'under',
- 'undo',
- 'unfair',
- 'unfold',
- 'unhappy',
- 'uniform',
- 'unique',
- 'unit',
- 'universe',
- 'unknown',
- 'unlock',
- 'until',
- 'unusual',
- 'unveil',
- 'update',
- 'upgrade',
- 'uphold',
- 'upon',
- 'upper',
- 'upset',
- 'urban',
- 'urge',
- 'usage',
- 'use',
- 'used',
- 'useful',
- 'useless',
- 'usual',
- 'utility',
- 'vacant',
- 'vacuum',
- 'vague',
- 'valid',
- 'valley',
- 'valve',
- 'van',
- 'vanish',
- 'vapor',
- 'various',
- 'vast',
- 'vault',
- 'vehicle',
- 'velvet',
- 'vendor',
- 'venture',
- 'venue',
- 'verb',
- 'verify',
- 'version',
- 'very',
- 'vessel',
- 'veteran',
- 'viable',
- 'vibrant',
- 'vicious',
- 'victory',
- 'video',
- 'view',
- 'village',
- 'vintage',
- 'violin',
- 'virtual',
- 'virus',
- 'visa',
- 'visit',
- 'visual',
- 'vital',
- 'vivid',
- 'vocal',
- 'voice',
- 'void',
- 'volcano',
- 'volume',
- 'vote',
- 'voyage',
- 'wage',
- 'wagon',
- 'wait',
- 'walk',
- 'wall',
- 'walnut',
- 'want',
- 'warfare',
- 'warm',
- 'warrior',
- 'wash',
- 'wasp',
- 'waste',
- 'water',
- 'wave',
- 'way',
- 'wealth',
- 'weapon',
- 'wear',
- 'weasel',
- 'weather',
- 'web',
- 'wedding',
- 'weekend',
- 'weird',
- 'welcome',
- 'west',
- 'wet',
- 'whale',
- 'what',
- 'wheat',
- 'wheel',
- 'when',
- 'where',
- 'whip',
- 'whisper',
- 'wide',
- 'width',
- 'wife',
- 'wild',
- 'will',
- 'win',
- 'window',
- 'wine',
- 'wing',
- 'wink',
- 'winner',
- 'winter',
- 'wire',
- 'wisdom',
- 'wise',
- 'wish',
- 'witness',
- 'wolf',
- 'woman',
- 'wonder',
- 'wood',
- 'wool',
- 'word',
- 'work',
- 'world',
- 'worry',
- 'worth',
- 'wrap',
- 'wreck',
- 'wrestle',
- 'wrist',
- 'write',
- 'wrong',
- 'yard',
- 'year',
- 'yellow',
- 'you',
- 'young',
- 'youth',
- 'zebra',
- 'zero',
- 'zone',
- 'zoo'
-])
diff --git a/lnbits/extensions/watchonly/static/js/crypto/aes.js b/lnbits/extensions/watchonly/static/js/crypto/aes.js
deleted file mode 100644
index 92a17ca2..00000000
--- a/lnbits/extensions/watchonly/static/js/crypto/aes.js
+++ /dev/null
@@ -1,802 +0,0 @@
-/*! MIT License. Copyright 2015-2018 Richard Moore . See LICENSE.txt. */
-(function(root) {
- "use strict";
-
- function checkInt(value) {
- return (parseInt(value) === value);
- }
-
- function checkInts(arrayish) {
- if (!checkInt(arrayish.length)) { return false; }
-
- for (var i = 0; i < arrayish.length; i++) {
- if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
- return false;
- }
- }
-
- return true;
- }
-
- function coerceArray(arg, copy) {
-
- // ArrayBuffer view
- if (arg.buffer && arg.name === 'Uint8Array') {
-
- if (copy) {
- if (arg.slice) {
- arg = arg.slice();
- } else {
- arg = Array.prototype.slice.call(arg);
- }
- }
-
- return arg;
- }
-
- // It's an array; check it is a valid representation of a byte
- if (Array.isArray(arg)) {
- if (!checkInts(arg)) {
- throw new Error('Array contains invalid value: ' + arg);
- }
-
- return new Uint8Array(arg);
- }
-
- // Something else, but behaves like an array (maybe a Buffer? Arguments?)
- if (checkInt(arg.length) && checkInts(arg)) {
- return new Uint8Array(arg);
- }
- throw new Error('unsupported array-like object');
- }
-
- function createArray(length) {
- return new Uint8Array(length);
- }
-
- function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
- if (sourceStart != null || sourceEnd != null) {
- if (sourceArray.slice) {
- sourceArray = sourceArray.slice(sourceStart, sourceEnd);
- } else {
- sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
- }
- }
- targetArray.set(sourceArray, targetStart);
- }
-
-
-
- var convertUtf8 = (function() {
- function toBytes(text) {
- var result = [], i = 0;
- text = encodeURI(text);
- while (i < text.length) {
- var c = text.charCodeAt(i++);
-
- // if it is a % sign, encode the following 2 bytes as a hex value
- if (c === 37) {
- result.push(parseInt(text.substr(i, 2), 16))
- i += 2;
-
- // otherwise, just the actual byte
- } else {
- result.push(c)
- }
- }
-
- return coerceArray(result);
- }
-
- function fromBytes(bytes) {
- var result = [], i = 0;
-
- while (i < bytes.length) {
- var c = bytes[i];
-
- if (c < 128) {
- result.push(String.fromCharCode(c));
- i++;
- } else if (c > 191 && c < 224) {
- result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f)));
- i += 2;
- } else {
- result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)));
- i += 3;
- }
- }
-
- return result.join('');
- }
-
- return {
- toBytes: toBytes,
- fromBytes: fromBytes,
- }
- })();
-
- var convertHex = (function() {
- function toBytes(text) {
- var result = [];
- for (var i = 0; i < text.length; i += 2) {
- result.push(parseInt(text.substr(i, 2), 16));
- }
-
- return result;
- }
-
- // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
- var Hex = '0123456789abcdef';
-
- function fromBytes(bytes) {
- var result = [];
- for (var i = 0; i < bytes.length; i++) {
- var v = bytes[i];
- result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
- }
- return result.join('');
- }
-
- return {
- toBytes: toBytes,
- fromBytes: fromBytes,
- }
- })();
-
-
- // Number of rounds by keysize
- var numberOfRounds = {16: 10, 24: 12, 32: 14}
-
- // Round constant words
- var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91];
-
- // S-box and Inverse S-box (S is for Substitution)
- var S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16];
- var Si =[0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d];
-
- // Transformations for encryption
- var T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a];
- var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616];
- var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16];
- var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c];
-
- // Transformations for decryption
- var T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742];
- var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857];
- var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8];
- var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0];
-
- // Transformations for decryption key expansion
- var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3];
- var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697];
- var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46];
- var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d];
-
- function convertToInt32(bytes) {
- var result = [];
- for (var i = 0; i < bytes.length; i += 4) {
- result.push(
- (bytes[i ] << 24) |
- (bytes[i + 1] << 16) |
- (bytes[i + 2] << 8) |
- bytes[i + 3]
- );
- }
- return result;
- }
-
- var AES = function(key) {
- if (!(this instanceof AES)) {
- throw Error('AES must be instanitated with `new`');
- }
-
- Object.defineProperty(this, 'key', {
- value: coerceArray(key, true)
- });
-
- this._prepare();
- }
-
-
- AES.prototype._prepare = function() {
-
- var rounds = numberOfRounds[this.key.length];
- if (rounds == null) {
- throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
- }
-
- // encryption round keys
- this._Ke = [];
-
- // decryption round keys
- this._Kd = [];
-
- for (var i = 0; i <= rounds; i++) {
- this._Ke.push([0, 0, 0, 0]);
- this._Kd.push([0, 0, 0, 0]);
- }
-
- var roundKeyCount = (rounds + 1) * 4;
- var KC = this.key.length / 4;
-
- // convert the key into ints
- var tk = convertToInt32(this.key);
-
- // copy values into round key arrays
- var index;
- for (var i = 0; i < KC; i++) {
- index = i >> 2;
- this._Ke[index][i % 4] = tk[i];
- this._Kd[rounds - index][i % 4] = tk[i];
- }
-
- // key expansion (fips-197 section 5.2)
- var rconpointer = 0;
- var t = KC, tt;
- while (t < roundKeyCount) {
- tt = tk[KC - 1];
- tk[0] ^= ((S[(tt >> 16) & 0xFF] << 24) ^
- (S[(tt >> 8) & 0xFF] << 16) ^
- (S[ tt & 0xFF] << 8) ^
- S[(tt >> 24) & 0xFF] ^
- (rcon[rconpointer] << 24));
- rconpointer += 1;
-
- // key expansion (for non-256 bit)
- if (KC != 8) {
- for (var i = 1; i < KC; i++) {
- tk[i] ^= tk[i - 1];
- }
-
- // key expansion for 256-bit keys is "slightly different" (fips-197)
- } else {
- for (var i = 1; i < (KC / 2); i++) {
- tk[i] ^= tk[i - 1];
- }
- tt = tk[(KC / 2) - 1];
-
- tk[KC / 2] ^= (S[ tt & 0xFF] ^
- (S[(tt >> 8) & 0xFF] << 8) ^
- (S[(tt >> 16) & 0xFF] << 16) ^
- (S[(tt >> 24) & 0xFF] << 24));
-
- for (var i = (KC / 2) + 1; i < KC; i++) {
- tk[i] ^= tk[i - 1];
- }
- }
-
- // copy values into round key arrays
- var i = 0, r, c;
- while (i < KC && t < roundKeyCount) {
- r = t >> 2;
- c = t % 4;
- this._Ke[r][c] = tk[i];
- this._Kd[rounds - r][c] = tk[i++];
- t++;
- }
- }
-
- // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
- for (var r = 1; r < rounds; r++) {
- for (var c = 0; c < 4; c++) {
- tt = this._Kd[r][c];
- this._Kd[r][c] = (U1[(tt >> 24) & 0xFF] ^
- U2[(tt >> 16) & 0xFF] ^
- U3[(tt >> 8) & 0xFF] ^
- U4[ tt & 0xFF]);
- }
- }
- }
-
- AES.prototype.encrypt = function(plaintext) {
- if (plaintext.length != 16) {
- throw new Error('invalid plaintext size (must be 16 bytes)');
- }
-
- var rounds = this._Ke.length - 1;
- var a = [0, 0, 0, 0];
-
- // convert plaintext to (ints ^ key)
- var t = convertToInt32(plaintext);
- for (var i = 0; i < 4; i++) {
- t[i] ^= this._Ke[0][i];
- }
-
- // apply round transforms
- for (var r = 1; r < rounds; r++) {
- for (var i = 0; i < 4; i++) {
- a[i] = (T1[(t[ i ] >> 24) & 0xff] ^
- T2[(t[(i + 1) % 4] >> 16) & 0xff] ^
- T3[(t[(i + 2) % 4] >> 8) & 0xff] ^
- T4[ t[(i + 3) % 4] & 0xff] ^
- this._Ke[r][i]);
- }
- t = a.slice();
- }
-
- // the last round is special
- var result = createArray(16), tt;
- for (var i = 0; i < 4; i++) {
- tt = this._Ke[rounds][i];
- result[4 * i ] = (S[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
- result[4 * i + 1] = (S[(t[(i + 1) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
- result[4 * i + 2] = (S[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
- result[4 * i + 3] = (S[ t[(i + 3) % 4] & 0xff] ^ tt ) & 0xff;
- }
-
- return result;
- }
-
- AES.prototype.decrypt = function(ciphertext) {
- if (ciphertext.length != 16) {
- throw new Error('invalid ciphertext size (must be 16 bytes)');
- }
-
- var rounds = this._Kd.length - 1;
- var a = [0, 0, 0, 0];
-
- // convert plaintext to (ints ^ key)
- var t = convertToInt32(ciphertext);
- for (var i = 0; i < 4; i++) {
- t[i] ^= this._Kd[0][i];
- }
-
- // apply round transforms
- for (var r = 1; r < rounds; r++) {
- for (var i = 0; i < 4; i++) {
- a[i] = (T5[(t[ i ] >> 24) & 0xff] ^
- T6[(t[(i + 3) % 4] >> 16) & 0xff] ^
- T7[(t[(i + 2) % 4] >> 8) & 0xff] ^
- T8[ t[(i + 1) % 4] & 0xff] ^
- this._Kd[r][i]);
- }
- t = a.slice();
- }
-
- // the last round is special
- var result = createArray(16), tt;
- for (var i = 0; i < 4; i++) {
- tt = this._Kd[rounds][i];
- result[4 * i ] = (Si[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
- result[4 * i + 1] = (Si[(t[(i + 3) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
- result[4 * i + 2] = (Si[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
- result[4 * i + 3] = (Si[ t[(i + 1) % 4] & 0xff] ^ tt ) & 0xff;
- }
-
- return result;
- }
-
-
- /**
- * Mode Of Operation - Electonic Codebook (ECB)
- */
- var ModeOfOperationECB = function(key) {
- if (!(this instanceof ModeOfOperationECB)) {
- throw Error('AES must be instanitated with `new`');
- }
-
- this.description = "Electronic Code Block";
- this.name = "ecb";
-
- this._aes = new AES(key);
- }
-
- ModeOfOperationECB.prototype.encrypt = function(plaintext) {
- plaintext = coerceArray(plaintext);
-
- if ((plaintext.length % 16) !== 0) {
- throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
- }
-
- var ciphertext = createArray(plaintext.length);
- var block = createArray(16);
-
- for (var i = 0; i < plaintext.length; i += 16) {
- copyArray(plaintext, block, 0, i, i + 16);
- block = this._aes.encrypt(block);
- copyArray(block, ciphertext, i);
- }
-
- return ciphertext;
- }
-
- ModeOfOperationECB.prototype.decrypt = function(ciphertext) {
- ciphertext = coerceArray(ciphertext);
-
- if ((ciphertext.length % 16) !== 0) {
- throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
- }
-
- var plaintext = createArray(ciphertext.length);
- var block = createArray(16);
-
- for (var i = 0; i < ciphertext.length; i += 16) {
- copyArray(ciphertext, block, 0, i, i + 16);
- block = this._aes.decrypt(block);
- copyArray(block, plaintext, i);
- }
-
- return plaintext;
- }
-
-
- /**
- * Mode Of Operation - Cipher Block Chaining (CBC)
- */
- var ModeOfOperationCBC = function(key, iv) {
- if (!(this instanceof ModeOfOperationCBC)) {
- throw Error('AES must be instanitated with `new`');
- }
-
- this.description = "Cipher Block Chaining";
- this.name = "cbc";
-
- if (!iv) {
- iv = createArray(16);
-
- } else if (iv.length != 16) {
- throw new Error('invalid initialation vector size (must be 16 bytes)');
- }
-
- this._lastCipherblock = coerceArray(iv, true);
-
- this._aes = new AES(key);
- }
-
- ModeOfOperationCBC.prototype.encrypt = function(plaintext) {
- plaintext = coerceArray(plaintext);
-
- if ((plaintext.length % 16) !== 0) {
- throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
- }
-
- var ciphertext = createArray(plaintext.length);
- var block = createArray(16);
-
- for (var i = 0; i < plaintext.length; i += 16) {
- copyArray(plaintext, block, 0, i, i + 16);
-
- for (var j = 0; j < 16; j++) {
- block[j] ^= this._lastCipherblock[j];
- }
-
- this._lastCipherblock = this._aes.encrypt(block);
- copyArray(this._lastCipherblock, ciphertext, i);
- }
-
- return ciphertext;
- }
-
- ModeOfOperationCBC.prototype.decrypt = function(ciphertext) {
- ciphertext = coerceArray(ciphertext);
-
- if ((ciphertext.length % 16) !== 0) {
- throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
- }
-
- var plaintext = createArray(ciphertext.length);
- var block = createArray(16);
-
- for (var i = 0; i < ciphertext.length; i += 16) {
- copyArray(ciphertext, block, 0, i, i + 16);
- block = this._aes.decrypt(block);
-
- for (var j = 0; j < 16; j++) {
- plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
- }
-
- copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
- }
-
- return plaintext;
- }
-
-
- /**
- * Mode Of Operation - Cipher Feedback (CFB)
- */
- var ModeOfOperationCFB = function(key, iv, segmentSize) {
- if (!(this instanceof ModeOfOperationCFB)) {
- throw Error('AES must be instanitated with `new`');
- }
-
- this.description = "Cipher Feedback";
- this.name = "cfb";
-
- if (!iv) {
- iv = createArray(16);
-
- } else if (iv.length != 16) {
- throw new Error('invalid initialation vector size (must be 16 size)');
- }
-
- if (!segmentSize) { segmentSize = 1; }
-
- this.segmentSize = segmentSize;
-
- this._shiftRegister = coerceArray(iv, true);
-
- this._aes = new AES(key);
- }
-
- ModeOfOperationCFB.prototype.encrypt = function(plaintext) {
- if ((plaintext.length % this.segmentSize) != 0) {
- throw new Error('invalid plaintext size (must be segmentSize bytes)');
- }
-
- var encrypted = coerceArray(plaintext, true);
-
- var xorSegment;
- for (var i = 0; i < encrypted.length; i += this.segmentSize) {
- xorSegment = this._aes.encrypt(this._shiftRegister);
- for (var j = 0; j < this.segmentSize; j++) {
- encrypted[i + j] ^= xorSegment[j];
- }
-
- // Shift the register
- copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
- copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
- }
-
- return encrypted;
- }
-
- ModeOfOperationCFB.prototype.decrypt = function(ciphertext) {
- if ((ciphertext.length % this.segmentSize) != 0) {
- throw new Error('invalid ciphertext size (must be segmentSize bytes)');
- }
-
- var plaintext = coerceArray(ciphertext, true);
-
- var xorSegment;
- for (var i = 0; i < plaintext.length; i += this.segmentSize) {
- xorSegment = this._aes.encrypt(this._shiftRegister);
-
- for (var j = 0; j < this.segmentSize; j++) {
- plaintext[i + j] ^= xorSegment[j];
- }
-
- // Shift the register
- copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
- copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
- }
-
- return plaintext;
- }
-
- /**
- * Mode Of Operation - Output Feedback (OFB)
- */
- var ModeOfOperationOFB = function(key, iv) {
- if (!(this instanceof ModeOfOperationOFB)) {
- throw Error('AES must be instanitated with `new`');
- }
-
- this.description = "Output Feedback";
- this.name = "ofb";
-
- if (!iv) {
- iv = createArray(16);
-
- } else if (iv.length != 16) {
- throw new Error('invalid initialation vector size (must be 16 bytes)');
- }
-
- this._lastPrecipher = coerceArray(iv, true);
- this._lastPrecipherIndex = 16;
-
- this._aes = new AES(key);
- }
-
- ModeOfOperationOFB.prototype.encrypt = function(plaintext) {
- var encrypted = coerceArray(plaintext, true);
-
- for (var i = 0; i < encrypted.length; i++) {
- if (this._lastPrecipherIndex === 16) {
- this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
- this._lastPrecipherIndex = 0;
- }
- encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
- }
-
- return encrypted;
- }
-
- // Decryption is symetric
- ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
-
-
- /**
- * Counter object for CTR common mode of operation
- */
- var Counter = function(initialValue) {
- if (!(this instanceof Counter)) {
- throw Error('Counter must be instanitated with `new`');
- }
-
- // We allow 0, but anything false-ish uses the default 1
- if (initialValue !== 0 && !initialValue) { initialValue = 1; }
-
- if (typeof(initialValue) === 'number') {
- this._counter = createArray(16);
- this.setValue(initialValue);
-
- } else {
- this.setBytes(initialValue);
- }
- }
-
- Counter.prototype.setValue = function(value) {
- if (typeof(value) !== 'number' || parseInt(value) != value) {
- throw new Error('invalid counter value (must be an integer)');
- }
-
- // We cannot safely handle numbers beyond the safe range for integers
- if (value > Number.MAX_SAFE_INTEGER) {
- throw new Error('integer value out of safe range');
- }
-
- for (var index = 15; index >= 0; --index) {
- this._counter[index] = value % 256;
- value = parseInt(value / 256);
- }
- }
-
- Counter.prototype.setBytes = function(bytes) {
- bytes = coerceArray(bytes, true);
-
- if (bytes.length != 16) {
- throw new Error('invalid counter bytes size (must be 16 bytes)');
- }
-
- this._counter = bytes;
- };
-
- Counter.prototype.increment = function() {
- for (var i = 15; i >= 0; i--) {
- if (this._counter[i] === 255) {
- this._counter[i] = 0;
- } else {
- this._counter[i]++;
- break;
- }
- }
- }
-
-
- /**
- * Mode Of Operation - Counter (CTR)
- */
- var ModeOfOperationCTR = function(key, counter) {
- if (!(this instanceof ModeOfOperationCTR)) {
- throw Error('AES must be instanitated with `new`');
- }
-
- this.description = "Counter";
- this.name = "ctr";
-
- if (!(counter instanceof Counter)) {
- counter = new Counter(counter)
- }
-
- this._counter = counter;
-
- this._remainingCounter = null;
- this._remainingCounterIndex = 16;
-
- this._aes = new AES(key);
- }
-
- ModeOfOperationCTR.prototype.encrypt = function(plaintext) {
- var encrypted = coerceArray(plaintext, true);
-
- for (var i = 0; i < encrypted.length; i++) {
- if (this._remainingCounterIndex === 16) {
- this._remainingCounter = this._aes.encrypt(this._counter._counter);
- this._remainingCounterIndex = 0;
- this._counter.increment();
- }
- encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
- }
-
- return encrypted;
- }
-
- // Decryption is symetric
- ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt;
-
-
- ///////////////////////
- // Padding
-
- // See:https://tools.ietf.org/html/rfc2315
- function pkcs7pad(data) {
- data = coerceArray(data, true);
- var padder = 16 - (data.length % 16);
- var result = createArray(data.length + padder);
- copyArray(data, result);
- for (var i = data.length; i < result.length; i++) {
- result[i] = padder;
- }
- return result;
- }
-
- function pkcs7strip(data) {
- data = coerceArray(data, true);
- if (data.length < 16) { throw new Error('PKCS#7 invalid length'); }
-
- var padder = data[data.length - 1];
- if (padder > 16) { throw new Error('PKCS#7 padding byte out of range'); }
-
- var length = data.length - padder;
- for (var i = 0; i < padder; i++) {
- if (data[length + i] !== padder) {
- throw new Error('PKCS#7 invalid padding byte');
- }
- }
-
- var result = createArray(length);
- copyArray(data, result, 0, 0, length);
- return result;
- }
-
- ///////////////////////
- // Exporting
-
-
- // The block cipher
- var aesjs = {
- AES: AES,
- Counter: Counter,
-
- ModeOfOperation: {
- ecb: ModeOfOperationECB,
- cbc: ModeOfOperationCBC,
- cfb: ModeOfOperationCFB,
- ofb: ModeOfOperationOFB,
- ctr: ModeOfOperationCTR
- },
-
- utils: {
- hex: convertHex,
- utf8: convertUtf8
- },
-
- padding: {
- pkcs7: {
- pad: pkcs7pad,
- strip: pkcs7strip
- }
- },
-
- _arrayTest: {
- coerceArray: coerceArray,
- createArray: createArray,
- copyArray: copyArray,
- }
- };
-
-
- // node.js
- if (typeof exports !== 'undefined') {
- module.exports = aesjs
-
- // RequireJS/AMD
- // http://www.requirejs.org/docs/api.html
- // https://github.com/amdjs/amdjs-api/wiki/AMD
- } else if (typeof(define) === 'function' && define.amd) {
- define([], function() { return aesjs; });
-
- // Web Browsers
- } else {
-
- // If there was an existing library at "aesjs" make sure it's still available
- if (root.aesjs) {
- aesjs._aesjs = root.aesjs;
- }
-
- root.aesjs = aesjs;
- }
-
-
-})(this);
\ No newline at end of file
diff --git a/lnbits/extensions/watchonly/static/js/crypto/noble-secp256k1.js b/lnbits/extensions/watchonly/static/js/crypto/noble-secp256k1.js
deleted file mode 100644
index 8be86729..00000000
--- a/lnbits/extensions/watchonly/static/js/crypto/noble-secp256k1.js
+++ /dev/null
@@ -1,1177 +0,0 @@
-(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.nobleSecp256k1 = {}));
-})(this, (function (exports) { 'use strict';
-
- const _nodeResolve_empty = {};
-
- const nodeCrypto = /*#__PURE__*/Object.freeze({
- __proto__: null,
- 'default': _nodeResolve_empty
- });
-
- /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
- const _0n = BigInt(0);
- const _1n = BigInt(1);
- const _2n = BigInt(2);
- const _3n = BigInt(3);
- const _8n = BigInt(8);
- const POW_2_256 = _2n ** BigInt(256);
- const CURVE = {
- a: _0n,
- b: BigInt(7),
- P: POW_2_256 - _2n ** BigInt(32) - BigInt(977),
- n: POW_2_256 - BigInt('432420386565659656852420866394968145599'),
- h: _1n,
- Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
- Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
- beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
- };
- function weistrass(x) {
- const { a, b } = CURVE;
- const x2 = mod(x * x);
- const x3 = mod(x2 * x);
- return mod(x3 + a * x + b);
- }
- const USE_ENDOMORPHISM = CURVE.a === _0n;
- class JacobianPoint {
- constructor(x, y, z) {
- this.x = x;
- this.y = y;
- this.z = z;
- }
- static fromAffine(p) {
- if (!(p instanceof Point)) {
- throw new TypeError('JacobianPoint#fromAffine: expected Point');
- }
- return new JacobianPoint(p.x, p.y, _1n);
- }
- static toAffineBatch(points) {
- const toInv = invertBatch(points.map((p) => p.z));
- return points.map((p, i) => p.toAffine(toInv[i]));
- }
- static normalizeZ(points) {
- return JacobianPoint.toAffineBatch(points).map(JacobianPoint.fromAffine);
- }
- equals(other) {
- if (!(other instanceof JacobianPoint))
- throw new TypeError('JacobianPoint expected');
- const { x: X1, y: Y1, z: Z1 } = this;
- const { x: X2, y: Y2, z: Z2 } = other;
- const Z1Z1 = mod(Z1 ** _2n);
- const Z2Z2 = mod(Z2 ** _2n);
- const U1 = mod(X1 * Z2Z2);
- const U2 = mod(X2 * Z1Z1);
- const S1 = mod(mod(Y1 * Z2) * Z2Z2);
- const S2 = mod(mod(Y2 * Z1) * Z1Z1);
- return U1 === U2 && S1 === S2;
- }
- negate() {
- return new JacobianPoint(this.x, mod(-this.y), this.z);
- }
- double() {
- const { x: X1, y: Y1, z: Z1 } = this;
- const A = mod(X1 ** _2n);
- const B = mod(Y1 ** _2n);
- const C = mod(B ** _2n);
- const D = mod(_2n * (mod((X1 + B) ** _2n) - A - C));
- const E = mod(_3n * A);
- const F = mod(E ** _2n);
- const X3 = mod(F - _2n * D);
- const Y3 = mod(E * (D - X3) - _8n * C);
- const Z3 = mod(_2n * Y1 * Z1);
- return new JacobianPoint(X3, Y3, Z3);
- }
- add(other) {
- if (!(other instanceof JacobianPoint))
- throw new TypeError('JacobianPoint expected');
- const { x: X1, y: Y1, z: Z1 } = this;
- const { x: X2, y: Y2, z: Z2 } = other;
- if (X2 === _0n || Y2 === _0n)
- return this;
- if (X1 === _0n || Y1 === _0n)
- return other;
- const Z1Z1 = mod(Z1 ** _2n);
- const Z2Z2 = mod(Z2 ** _2n);
- const U1 = mod(X1 * Z2Z2);
- const U2 = mod(X2 * Z1Z1);
- const S1 = mod(mod(Y1 * Z2) * Z2Z2);
- const S2 = mod(mod(Y2 * Z1) * Z1Z1);
- const H = mod(U2 - U1);
- const r = mod(S2 - S1);
- if (H === _0n) {
- if (r === _0n) {
- return this.double();
- }
- else {
- return JacobianPoint.ZERO;
- }
- }
- const HH = mod(H ** _2n);
- const HHH = mod(H * HH);
- const V = mod(U1 * HH);
- const X3 = mod(r ** _2n - HHH - _2n * V);
- const Y3 = mod(r * (V - X3) - S1 * HHH);
- const Z3 = mod(Z1 * Z2 * H);
- return new JacobianPoint(X3, Y3, Z3);
- }
- subtract(other) {
- return this.add(other.negate());
- }
- multiplyUnsafe(scalar) {
- const P0 = JacobianPoint.ZERO;
- if (typeof scalar === 'bigint' && scalar === _0n)
- return P0;
- let n = normalizeScalar(scalar);
- if (n === _1n)
- return this;
- if (!USE_ENDOMORPHISM) {
- let p = P0;
- let d = this;
- while (n > _0n) {
- if (n & _1n)
- p = p.add(d);
- d = d.double();
- n >>= _1n;
- }
- return p;
- }
- let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
- let k1p = P0;
- let k2p = P0;
- let d = this;
- while (k1 > _0n || k2 > _0n) {
- if (k1 & _1n)
- k1p = k1p.add(d);
- if (k2 & _1n)
- k2p = k2p.add(d);
- d = d.double();
- k1 >>= _1n;
- k2 >>= _1n;
- }
- if (k1neg)
- k1p = k1p.negate();
- if (k2neg)
- k2p = k2p.negate();
- k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z);
- return k1p.add(k2p);
- }
- precomputeWindow(W) {
- const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1;
- const points = [];
- let p = this;
- let base = p;
- for (let window = 0; window < windows; window++) {
- base = p;
- points.push(base);
- for (let i = 1; i < 2 ** (W - 1); i++) {
- base = base.add(p);
- points.push(base);
- }
- p = base.double();
- }
- return points;
- }
- wNAF(n, affinePoint) {
- if (!affinePoint && this.equals(JacobianPoint.BASE))
- affinePoint = Point.BASE;
- const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
- if (256 % W) {
- throw new Error('Point#wNAF: Invalid precomputation window, must be power of 2');
- }
- let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
- if (!precomputes) {
- precomputes = this.precomputeWindow(W);
- if (affinePoint && W !== 1) {
- precomputes = JacobianPoint.normalizeZ(precomputes);
- pointPrecomputes.set(affinePoint, precomputes);
- }
- }
- let p = JacobianPoint.ZERO;
- let f = JacobianPoint.ZERO;
- const windows = 1 + (USE_ENDOMORPHISM ? 128 / W : 256 / W);
- const windowSize = 2 ** (W - 1);
- const mask = BigInt(2 ** W - 1);
- const maxNumber = 2 ** W;
- const shiftBy = BigInt(W);
- for (let window = 0; window < windows; window++) {
- const offset = window * windowSize;
- let wbits = Number(n & mask);
- n >>= shiftBy;
- if (wbits > windowSize) {
- wbits -= maxNumber;
- n += _1n;
- }
- if (wbits === 0) {
- let pr = precomputes[offset];
- if (window % 2)
- pr = pr.negate();
- f = f.add(pr);
- }
- else {
- let cached = precomputes[offset + Math.abs(wbits) - 1];
- if (wbits < 0)
- cached = cached.negate();
- p = p.add(cached);
- }
- }
- return { p, f };
- }
- multiply(scalar, affinePoint) {
- let n = normalizeScalar(scalar);
- let point;
- let fake;
- if (USE_ENDOMORPHISM) {
- const { k1neg, k1, k2neg, k2 } = splitScalarEndo(n);
- let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint);
- let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
- if (k1neg)
- k1p = k1p.negate();
- if (k2neg)
- k2p = k2p.negate();
- k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z);
- point = k1p.add(k2p);
- fake = f1p.add(f2p);
- }
- else {
- const { p, f } = this.wNAF(n, affinePoint);
- point = p;
- fake = f;
- }
- return JacobianPoint.normalizeZ([point, fake])[0];
- }
- toAffine(invZ = invert(this.z)) {
- const { x, y, z } = this;
- const iz1 = invZ;
- const iz2 = mod(iz1 * iz1);
- const iz3 = mod(iz2 * iz1);
- const ax = mod(x * iz2);
- const ay = mod(y * iz3);
- const zz = mod(z * iz1);
- if (zz !== _1n)
- throw new Error('invZ was invalid');
- return new Point(ax, ay);
- }
- }
- JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n);
- JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n);
- const pointPrecomputes = new WeakMap();
- class Point {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- }
- _setWindowSize(windowSize) {
- this._WINDOW_SIZE = windowSize;
- pointPrecomputes.delete(this);
- }
- static fromCompressedHex(bytes) {
- const isShort = bytes.length === 32;
- const x = bytesToNumber(isShort ? bytes : bytes.subarray(1));
- if (!isValidFieldElement(x))
- throw new Error('Point is not on curve');
- const y2 = weistrass(x);
- let y = sqrtMod(y2);
- const isYOdd = (y & _1n) === _1n;
- if (isShort) {
- if (isYOdd)
- y = mod(-y);
- }
- else {
- const isFirstByteOdd = (bytes[0] & 1) === 1;
- if (isFirstByteOdd !== isYOdd)
- y = mod(-y);
- }
- const point = new Point(x, y);
- point.assertValidity();
- return point;
- }
- static fromUncompressedHex(bytes) {
- const x = bytesToNumber(bytes.subarray(1, 33));
- const y = bytesToNumber(bytes.subarray(33, 65));
- const point = new Point(x, y);
- point.assertValidity();
- return point;
- }
- static fromHex(hex) {
- const bytes = ensureBytes(hex);
- const len = bytes.length;
- const header = bytes[0];
- if (len === 32 || (len === 33 && (header === 0x02 || header === 0x03))) {
- return this.fromCompressedHex(bytes);
- }
- if (len === 65 && header === 0x04)
- return this.fromUncompressedHex(bytes);
- throw new Error(`Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${len}`);
- }
- static fromPrivateKey(privateKey) {
- return Point.BASE.multiply(normalizePrivateKey(privateKey));
- }
- static fromSignature(msgHash, signature, recovery) {
- msgHash = ensureBytes(msgHash);
- const h = truncateHash(msgHash);
- const { r, s } = normalizeSignature(signature);
- if (recovery !== 0 && recovery !== 1) {
- throw new Error('Cannot recover signature: invalid recovery bit');
- }
- const prefix = recovery & 1 ? '03' : '02';
- const R = Point.fromHex(prefix + numTo32bStr(r));
- const { n } = CURVE;
- const rinv = invert(r, n);
- const u1 = mod(-h * rinv, n);
- const u2 = mod(s * rinv, n);
- const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2);
- if (!Q)
- throw new Error('Cannot recover signature: point at infinify');
- Q.assertValidity();
- return Q;
- }
- toRawBytes(isCompressed = false) {
- return hexToBytes(this.toHex(isCompressed));
- }
- toHex(isCompressed = false) {
- const x = numTo32bStr(this.x);
- if (isCompressed) {
- const prefix = this.y & _1n ? '03' : '02';
- return `${prefix}${x}`;
- }
- else {
- return `04${x}${numTo32bStr(this.y)}`;
- }
- }
- toHexX() {
- return this.toHex(true).slice(2);
- }
- toRawX() {
- return this.toRawBytes(true).slice(1);
- }
- assertValidity() {
- const msg = 'Point is not on elliptic curve';
- const { x, y } = this;
- if (!isValidFieldElement(x) || !isValidFieldElement(y))
- throw new Error(msg);
- const left = mod(y * y);
- const right = weistrass(x);
- if (mod(left - right) !== _0n)
- throw new Error(msg);
- }
- equals(other) {
- return this.x === other.x && this.y === other.y;
- }
- negate() {
- return new Point(this.x, mod(-this.y));
- }
- double() {
- return JacobianPoint.fromAffine(this).double().toAffine();
- }
- add(other) {
- return JacobianPoint.fromAffine(this).add(JacobianPoint.fromAffine(other)).toAffine();
- }
- subtract(other) {
- return this.add(other.negate());
- }
- multiply(scalar) {
- return JacobianPoint.fromAffine(this).multiply(scalar, this).toAffine();
- }
- multiplyAndAddUnsafe(Q, a, b) {
- const P = JacobianPoint.fromAffine(this);
- const aP = a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a);
- const bQ = JacobianPoint.fromAffine(Q).multiplyUnsafe(b);
- const sum = aP.add(bQ);
- return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine();
- }
- }
- Point.BASE = new Point(CURVE.Gx, CURVE.Gy);
- Point.ZERO = new Point(_0n, _0n);
- function sliceDER(s) {
- return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s;
- }
- function parseDERInt(data) {
- if (data.length < 2 || data[0] !== 0x02) {
- throw new Error(`Invalid signature integer tag: ${bytesToHex(data)}`);
- }
- const len = data[1];
- const res = data.subarray(2, len + 2);
- if (!len || res.length !== len) {
- throw new Error(`Invalid signature integer: wrong length`);
- }
- if (res[0] === 0x00 && res[1] <= 0x7f) {
- throw new Error('Invalid signature integer: trailing length');
- }
- return { data: bytesToNumber(res), left: data.subarray(len + 2) };
- }
- function parseDERSignature(data) {
- if (data.length < 2 || data[0] != 0x30) {
- throw new Error(`Invalid signature tag: ${bytesToHex(data)}`);
- }
- if (data[1] !== data.length - 2) {
- throw new Error('Invalid signature: incorrect length');
- }
- const { data: r, left: sBytes } = parseDERInt(data.subarray(2));
- const { data: s, left: rBytesLeft } = parseDERInt(sBytes);
- if (rBytesLeft.length) {
- throw new Error(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`);
- }
- return { r, s };
- }
- class Signature {
- constructor(r, s) {
- this.r = r;
- this.s = s;
- this.assertValidity();
- }
- static fromCompact(hex) {
- const arr = isUint8a(hex);
- const name = 'Signature.fromCompact';
- if (typeof hex !== 'string' && !arr)
- throw new TypeError(`${name}: Expected string or Uint8Array`);
- const str = arr ? bytesToHex(hex) : hex;
- if (str.length !== 128)
- throw new Error(`${name}: Expected 64-byte hex`);
- return new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128)));
- }
- static fromDER(hex) {
- const arr = isUint8a(hex);
- if (typeof hex !== 'string' && !arr)
- throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`);
- const { r, s } = parseDERSignature(arr ? hex : hexToBytes(hex));
- return new Signature(r, s);
- }
- static fromHex(hex) {
- return this.fromDER(hex);
- }
- assertValidity() {
- const { r, s } = this;
- if (!isWithinCurveOrder(r))
- throw new Error('Invalid Signature: r must be 0 < r < n');
- if (!isWithinCurveOrder(s))
- throw new Error('Invalid Signature: s must be 0 < s < n');
- }
- hasHighS() {
- const HALF = CURVE.n >> _1n;
- return this.s > HALF;
- }
- normalizeS() {
- return this.hasHighS() ? new Signature(this.r, CURVE.n - this.s) : this;
- }
- toDERRawBytes(isCompressed = false) {
- return hexToBytes(this.toDERHex(isCompressed));
- }
- toDERHex(isCompressed = false) {
- const sHex = sliceDER(numberToHexUnpadded(this.s));
- if (isCompressed)
- return sHex;
- const rHex = sliceDER(numberToHexUnpadded(this.r));
- const rLen = numberToHexUnpadded(rHex.length / 2);
- const sLen = numberToHexUnpadded(sHex.length / 2);
- const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4);
- return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;
- }
- toRawBytes() {
- return this.toDERRawBytes();
- }
- toHex() {
- return this.toDERHex();
- }
- toCompactRawBytes() {
- return hexToBytes(this.toCompactHex());
- }
- toCompactHex() {
- return numTo32bStr(this.r) + numTo32bStr(this.s);
- }
- }
- function concatBytes(...arrays) {
- if (!arrays.every(isUint8a))
- throw new Error('Uint8Array list expected');
- if (arrays.length === 1)
- return arrays[0];
- const length = arrays.reduce((a, arr) => a + arr.length, 0);
- const result = new Uint8Array(length);
- for (let i = 0, pad = 0; i < arrays.length; i++) {
- const arr = arrays[i];
- result.set(arr, pad);
- pad += arr.length;
- }
- return result;
- }
- function isUint8a(bytes) {
- return bytes instanceof Uint8Array;
- }
- const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
- function bytesToHex(uint8a) {
- if (!(uint8a instanceof Uint8Array))
- throw new Error('Expected Uint8Array');
- let hex = '';
- for (let i = 0; i < uint8a.length; i++) {
- hex += hexes[uint8a[i]];
- }
- return hex;
- }
- function numTo32bStr(num) {
- if (num > POW_2_256)
- throw new Error('Expected number < 2^256');
- return num.toString(16).padStart(64, '0');
- }
- function numTo32b(num) {
- return hexToBytes(numTo32bStr(num));
- }
- function numberToHexUnpadded(num) {
- const hex = num.toString(16);
- return hex.length & 1 ? `0${hex}` : hex;
- }
- function hexToNumber(hex) {
- if (typeof hex !== 'string') {
- throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
- }
- return BigInt(`0x${hex}`);
- }
- function hexToBytes(hex) {
- if (typeof hex !== 'string') {
- throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
- }
- if (hex.length % 2)
- throw new Error('hexToBytes: received invalid unpadded hex' + hex.length);
- const array = new Uint8Array(hex.length / 2);
- for (let i = 0; i < array.length; i++) {
- const j = i * 2;
- const hexByte = hex.slice(j, j + 2);
- const byte = Number.parseInt(hexByte, 16);
- if (Number.isNaN(byte) || byte < 0)
- throw new Error('Invalid byte sequence');
- array[i] = byte;
- }
- return array;
- }
- function bytesToNumber(bytes) {
- return hexToNumber(bytesToHex(bytes));
- }
- function ensureBytes(hex) {
- return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
- }
- function normalizeScalar(num) {
- if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0)
- return BigInt(num);
- if (typeof num === 'bigint' && isWithinCurveOrder(num))
- return num;
- throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n');
- }
- function mod(a, b = CURVE.P) {
- const result = a % b;
- return result >= _0n ? result : b + result;
- }
- function pow2(x, power) {
- const { P } = CURVE;
- let res = x;
- while (power-- > _0n) {
- res *= res;
- res %= P;
- }
- return res;
- }
- function sqrtMod(x) {
- const { P } = CURVE;
- const _6n = BigInt(6);
- const _11n = BigInt(11);
- const _22n = BigInt(22);
- const _23n = BigInt(23);
- const _44n = BigInt(44);
- const _88n = BigInt(88);
- const b2 = (x * x * x) % P;
- const b3 = (b2 * b2 * x) % P;
- const b6 = (pow2(b3, _3n) * b3) % P;
- const b9 = (pow2(b6, _3n) * b3) % P;
- const b11 = (pow2(b9, _2n) * b2) % P;
- const b22 = (pow2(b11, _11n) * b11) % P;
- const b44 = (pow2(b22, _22n) * b22) % P;
- const b88 = (pow2(b44, _44n) * b44) % P;
- const b176 = (pow2(b88, _88n) * b88) % P;
- const b220 = (pow2(b176, _44n) * b44) % P;
- const b223 = (pow2(b220, _3n) * b3) % P;
- const t1 = (pow2(b223, _23n) * b22) % P;
- const t2 = (pow2(t1, _6n) * b2) % P;
- return pow2(t2, _2n);
- }
- function invert(number, modulo = CURVE.P) {
- if (number === _0n || modulo <= _0n) {
- throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
- }
- let a = mod(number, modulo);
- let b = modulo;
- let x = _0n, u = _1n;
- while (a !== _0n) {
- const q = b / a;
- const r = b % a;
- const m = x - u * q;
- b = a, a = r, x = u, u = m;
- }
- const gcd = b;
- if (gcd !== _1n)
- throw new Error('invert: does not exist');
- return mod(x, modulo);
- }
- function invertBatch(nums, p = CURVE.P) {
- const scratch = new Array(nums.length);
- const lastMultiplied = nums.reduce((acc, num, i) => {
- if (num === _0n)
- return acc;
- scratch[i] = acc;
- return mod(acc * num, p);
- }, _1n);
- const inverted = invert(lastMultiplied, p);
- nums.reduceRight((acc, num, i) => {
- if (num === _0n)
- return acc;
- scratch[i] = mod(acc * scratch[i], p);
- return mod(acc * num, p);
- }, inverted);
- return scratch;
- }
- const divNearest = (a, b) => (a + b / _2n) / b;
- const POW_2_128 = _2n ** BigInt(128);
- function splitScalarEndo(k) {
- const { n } = CURVE;
- const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
- const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
- const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
- const b2 = a1;
- const c1 = divNearest(b2 * k, n);
- const c2 = divNearest(-b1 * k, n);
- let k1 = mod(k - c1 * a1 - c2 * a2, n);
- let k2 = mod(-c1 * b1 - c2 * b2, n);
- const k1neg = k1 > POW_2_128;
- const k2neg = k2 > POW_2_128;
- if (k1neg)
- k1 = n - k1;
- if (k2neg)
- k2 = n - k2;
- if (k1 > POW_2_128 || k2 > POW_2_128) {
- throw new Error('splitScalarEndo: Endomorphism failed, k=' + k);
- }
- return { k1neg, k1, k2neg, k2 };
- }
- function truncateHash(hash) {
- const { n } = CURVE;
- const byteLength = hash.length;
- const delta = byteLength * 8 - 256;
- let h = bytesToNumber(hash);
- if (delta > 0)
- h = h >> BigInt(delta);
- if (h >= n)
- h -= n;
- return h;
- }
- class HmacDrbg {
- constructor() {
- this.v = new Uint8Array(32).fill(1);
- this.k = new Uint8Array(32).fill(0);
- this.counter = 0;
- }
- hmac(...values) {
- return utils.hmacSha256(this.k, ...values);
- }
- hmacSync(...values) {
- if (typeof utils.hmacSha256Sync !== 'function')
- throw new Error('utils.hmacSha256Sync is undefined, you need to set it');
- const res = utils.hmacSha256Sync(this.k, ...values);
- if (res instanceof Promise)
- throw new Error('To use sync sign(), ensure utils.hmacSha256 is sync');
- return res;
- }
- incr() {
- if (this.counter >= 1000) {
- throw new Error('Tried 1,000 k values for sign(), all were invalid');
- }
- this.counter += 1;
- }
- async reseed(seed = new Uint8Array()) {
- this.k = await this.hmac(this.v, Uint8Array.from([0x00]), seed);
- this.v = await this.hmac(this.v);
- if (seed.length === 0)
- return;
- this.k = await this.hmac(this.v, Uint8Array.from([0x01]), seed);
- this.v = await this.hmac(this.v);
- }
- reseedSync(seed = new Uint8Array()) {
- this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed);
- this.v = this.hmacSync(this.v);
- if (seed.length === 0)
- return;
- this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed);
- this.v = this.hmacSync(this.v);
- }
- async generate() {
- this.incr();
- this.v = await this.hmac(this.v);
- return this.v;
- }
- generateSync() {
- this.incr();
- this.v = this.hmacSync(this.v);
- return this.v;
- }
- }
- function isWithinCurveOrder(num) {
- return _0n < num && num < CURVE.n;
- }
- function isValidFieldElement(num) {
- return _0n < num && num < CURVE.P;
- }
- function kmdToSig(kBytes, m, d) {
- const k = bytesToNumber(kBytes);
- if (!isWithinCurveOrder(k))
- return;
- const { n } = CURVE;
- const q = Point.BASE.multiply(k);
- const r = mod(q.x, n);
- if (r === _0n)
- return;
- const s = mod(invert(k, n) * mod(m + d * r, n), n);
- if (s === _0n)
- return;
- const sig = new Signature(r, s);
- const recovery = (q.x === sig.r ? 0 : 2) | Number(q.y & _1n);
- return { sig, recovery };
- }
- function normalizePrivateKey(key) {
- let num;
- if (typeof key === 'bigint') {
- num = key;
- }
- else if (typeof key === 'number' && Number.isSafeInteger(key) && key > 0) {
- num = BigInt(key);
- }
- else if (typeof key === 'string') {
- if (key.length !== 64)
- throw new Error('Expected 32 bytes of private key');
- num = hexToNumber(key);
- }
- else if (isUint8a(key)) {
- if (key.length !== 32)
- throw new Error('Expected 32 bytes of private key');
- num = bytesToNumber(key);
- }
- else {
- throw new TypeError('Expected valid private key');
- }
- if (!isWithinCurveOrder(num))
- throw new Error('Expected private key: 0 < key < n');
- return num;
- }
- function normalizePublicKey(publicKey) {
- if (publicKey instanceof Point) {
- publicKey.assertValidity();
- return publicKey;
- }
- else {
- return Point.fromHex(publicKey);
- }
- }
- function normalizeSignature(signature) {
- if (signature instanceof Signature) {
- signature.assertValidity();
- return signature;
- }
- try {
- return Signature.fromDER(signature);
- }
- catch (error) {
- return Signature.fromCompact(signature);
- }
- }
- function getPublicKey(privateKey, isCompressed = false) {
- return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
- }
- function recoverPublicKey(msgHash, signature, recovery, isCompressed = false) {
- return Point.fromSignature(msgHash, signature, recovery).toRawBytes(isCompressed);
- }
- function isPub(item) {
- const arr = isUint8a(item);
- const str = typeof item === 'string';
- const len = (arr || str) && item.length;
- if (arr)
- return len === 33 || len === 65;
- if (str)
- return len === 66 || len === 130;
- if (item instanceof Point)
- return true;
- return false;
- }
- function getSharedSecret(privateA, publicB, isCompressed = false) {
- if (isPub(privateA))
- throw new TypeError('getSharedSecret: first arg must be private key');
- if (!isPub(publicB))
- throw new TypeError('getSharedSecret: second arg must be public key');
- const b = normalizePublicKey(publicB);
- b.assertValidity();
- return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed);
- }
- function bits2int(bytes) {
- const slice = bytes.length > 32 ? bytes.slice(0, 32) : bytes;
- return bytesToNumber(slice);
- }
- function bits2octets(bytes) {
- const z1 = bits2int(bytes);
- const z2 = mod(z1, CURVE.n);
- return int2octets(z2 < _0n ? z1 : z2);
- }
- function int2octets(num) {
- if (typeof num !== 'bigint')
- throw new Error('Expected bigint');
- const hex = numTo32bStr(num);
- return hexToBytes(hex);
- }
- function initSigArgs(msgHash, privateKey, extraEntropy) {
- if (msgHash == null)
- throw new Error(`sign: expected valid message hash, not "${msgHash}"`);
- const h1 = ensureBytes(msgHash);
- const d = normalizePrivateKey(privateKey);
- const seedArgs = [int2octets(d), bits2octets(h1)];
- if (extraEntropy != null) {
- if (extraEntropy === true)
- extraEntropy = utils.randomBytes(32);
- const e = ensureBytes(extraEntropy);
- if (e.length !== 32)
- throw new Error('sign: Expected 32 bytes of extra data');
- seedArgs.push(e);
- }
- const seed = concatBytes(...seedArgs);
- const m = bits2int(h1);
- return { seed, m, d };
- }
- function finalizeSig(recSig, opts) {
- let { sig, recovery } = recSig;
- const { canonical, der, recovered } = Object.assign({ canonical: true, der: true }, opts);
- if (canonical && sig.hasHighS()) {
- sig = sig.normalizeS();
- recovery ^= 1;
- }
- const hashed = der ? sig.toDERRawBytes() : sig.toCompactRawBytes();
- return recovered ? [hashed, recovery] : hashed;
- }
- async function sign(msgHash, privKey, opts = {}) {
- const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy);
- let sig;
- const drbg = new HmacDrbg();
- await drbg.reseed(seed);
- while (!(sig = kmdToSig(await drbg.generate(), m, d)))
- await drbg.reseed();
- return finalizeSig(sig, opts);
- }
- function signSync(msgHash, privKey, opts = {}) {
- const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy);
- let sig;
- const drbg = new HmacDrbg();
- drbg.reseedSync(seed);
- while (!(sig = kmdToSig(drbg.generateSync(), m, d)))
- drbg.reseedSync();
- return finalizeSig(sig, opts);
- }
- const vopts = { strict: true };
- function verify(signature, msgHash, publicKey, opts = vopts) {
- let sig;
- try {
- sig = normalizeSignature(signature);
- msgHash = ensureBytes(msgHash);
- }
- catch (error) {
- return false;
- }
- const { r, s } = sig;
- if (opts.strict && sig.hasHighS())
- return false;
- const h = truncateHash(msgHash);
- let P;
- try {
- P = normalizePublicKey(publicKey);
- }
- catch (error) {
- return false;
- }
- const { n } = CURVE;
- const sinv = invert(s, n);
- const u1 = mod(h * sinv, n);
- const u2 = mod(r * sinv, n);
- const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2);
- if (!R)
- return false;
- const v = mod(R.x, n);
- return v === r;
- }
- function finalizeSchnorrChallenge(ch) {
- return mod(bytesToNumber(ch), CURVE.n);
- }
- function hasEvenY(point) {
- return (point.y & _1n) === _0n;
- }
- class SchnorrSignature {
- constructor(r, s) {
- this.r = r;
- this.s = s;
- this.assertValidity();
- }
- static fromHex(hex) {
- const bytes = ensureBytes(hex);
- if (bytes.length !== 64)
- throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`);
- const r = bytesToNumber(bytes.subarray(0, 32));
- const s = bytesToNumber(bytes.subarray(32, 64));
- return new SchnorrSignature(r, s);
- }
- assertValidity() {
- const { r, s } = this;
- if (!isValidFieldElement(r) || !isWithinCurveOrder(s))
- throw new Error('Invalid signature');
- }
- toHex() {
- return numTo32bStr(this.r) + numTo32bStr(this.s);
- }
- toRawBytes() {
- return hexToBytes(this.toHex());
- }
- }
- function schnorrGetPublicKey(privateKey) {
- return Point.fromPrivateKey(privateKey).toRawX();
- }
- function initSchnorrSigArgs(message, privateKey, auxRand) {
- if (message == null)
- throw new TypeError(`sign: Expected valid message, not "${message}"`);
- const m = ensureBytes(message);
- const d0 = normalizePrivateKey(privateKey);
- const rand = ensureBytes(auxRand);
- if (rand.length !== 32)
- throw new TypeError('sign: Expected 32 bytes of aux randomness');
- const P = Point.fromPrivateKey(d0);
- const px = P.toRawX();
- const d = hasEvenY(P) ? d0 : CURVE.n - d0;
- return { m, P, px, d, rand };
- }
- function initSchnorrNonce(d, t0h) {
- return numTo32b(d ^ bytesToNumber(t0h));
- }
- function finalizeSchnorrNonce(k0h) {
- const k0 = mod(bytesToNumber(k0h), CURVE.n);
- if (k0 === _0n)
- throw new Error('sign: Creation of signature failed. k is zero');
- const R = Point.fromPrivateKey(k0);
- const rx = R.toRawX();
- const k = hasEvenY(R) ? k0 : CURVE.n - k0;
- return { R, rx, k };
- }
- function finalizeSchnorrSig(R, k, e, d) {
- return new SchnorrSignature(R.x, mod(k + e * d, CURVE.n)).toRawBytes();
- }
- async function schnorrSign(message, privateKey, auxRand = utils.randomBytes()) {
- const { m, px, d, rand } = initSchnorrSigArgs(message, privateKey, auxRand);
- const t = initSchnorrNonce(d, await utils.taggedHash(TAGS.aux, rand));
- const { R, rx, k } = finalizeSchnorrNonce(await utils.taggedHash(TAGS.nonce, t, px, m));
- const e = finalizeSchnorrChallenge(await utils.taggedHash(TAGS.challenge, rx, px, m));
- const sig = finalizeSchnorrSig(R, k, e, d);
- const isValid = await schnorrVerify(sig, m, px);
- if (!isValid)
- throw new Error('sign: Invalid signature produced');
- return sig;
- }
- function schnorrSignSync(message, privateKey, auxRand = utils.randomBytes()) {
- const { m, px, d, rand } = initSchnorrSigArgs(message, privateKey, auxRand);
- const t = initSchnorrNonce(d, utils.taggedHashSync(TAGS.aux, rand));
- const { R, rx, k } = finalizeSchnorrNonce(utils.taggedHashSync(TAGS.nonce, t, px, m));
- const e = finalizeSchnorrChallenge(utils.taggedHashSync(TAGS.challenge, rx, px, m));
- const sig = finalizeSchnorrSig(R, k, e, d);
- const isValid = schnorrVerifySync(sig, m, px);
- if (!isValid)
- throw new Error('sign: Invalid signature produced');
- return sig;
- }
- function initSchnorrVerify(signature, message, publicKey) {
- const raw = signature instanceof SchnorrSignature;
- const sig = raw ? signature : SchnorrSignature.fromHex(signature);
- if (raw)
- sig.assertValidity();
- return {
- ...sig,
- m: ensureBytes(message),
- P: normalizePublicKey(publicKey),
- };
- }
- function finalizeSchnorrVerify(r, P, s, e) {
- const R = Point.BASE.multiplyAndAddUnsafe(P, normalizePrivateKey(s), mod(-e, CURVE.n));
- if (!R || !hasEvenY(R) || R.x !== r)
- return false;
- return true;
- }
- async function schnorrVerify(signature, message, publicKey) {
- try {
- const { r, s, m, P } = initSchnorrVerify(signature, message, publicKey);
- const e = finalizeSchnorrChallenge(await utils.taggedHash(TAGS.challenge, numTo32b(r), P.toRawX(), m));
- return finalizeSchnorrVerify(r, P, s, e);
- }
- catch (error) {
- return false;
- }
- }
- function schnorrVerifySync(signature, message, publicKey) {
- try {
- const { r, s, m, P } = initSchnorrVerify(signature, message, publicKey);
- const e = finalizeSchnorrChallenge(utils.taggedHashSync(TAGS.challenge, numTo32b(r), P.toRawX(), m));
- return finalizeSchnorrVerify(r, P, s, e);
- }
- catch (error) {
- return false;
- }
- }
- const schnorr = {
- Signature: SchnorrSignature,
- getPublicKey: schnorrGetPublicKey,
- sign: schnorrSign,
- verify: schnorrVerify,
- signSync: schnorrSignSync,
- verifySync: schnorrVerifySync,
- };
- Point.BASE._setWindowSize(8);
- const crypto = {
- node: nodeCrypto,
- web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined,
- };
- const TAGS = {
- challenge: 'BIP0340/challenge',
- aux: 'BIP0340/aux',
- nonce: 'BIP0340/nonce',
- };
- const TAGGED_HASH_PREFIXES = {};
- const utils = {
- isValidPrivateKey(privateKey) {
- try {
- normalizePrivateKey(privateKey);
- return true;
- }
- catch (error) {
- return false;
- }
- },
- privateAdd: (privateKey, tweak) => {
- const p = normalizePrivateKey(privateKey);
- const t = normalizePrivateKey(tweak);
- return numTo32b(mod(p + t, CURVE.n));
- },
- privateNegate: (privateKey) => {
- const p = normalizePrivateKey(privateKey);
- return numTo32b(CURVE.n - p);
- },
- pointAddScalar: (p, tweak, isCompressed) => {
- const P = Point.fromHex(p);
- const t = normalizePrivateKey(tweak);
- const Q = Point.BASE.multiplyAndAddUnsafe(P, t, _1n);
- if (!Q)
- throw new Error('Tweaked point at infinity');
- return Q.toRawBytes(isCompressed);
- },
- pointMultiply: (p, tweak, isCompressed) => {
- const P = Point.fromHex(p);
- const t = bytesToNumber(ensureBytes(tweak));
- return P.multiply(t).toRawBytes(isCompressed);
- },
- hashToPrivateKey: (hash) => {
- hash = ensureBytes(hash);
- if (hash.length < 40 || hash.length > 1024)
- throw new Error('Expected 40-1024 bytes of private key as per FIPS 186');
- const num = mod(bytesToNumber(hash), CURVE.n - _1n) + _1n;
- return numTo32b(num);
- },
- randomBytes: (bytesLength = 32) => {
- if (crypto.web) {
- return crypto.web.getRandomValues(new Uint8Array(bytesLength));
- }
- else if (crypto.node) {
- const { randomBytes } = crypto.node;
- return Uint8Array.from(randomBytes(bytesLength));
- }
- else {
- throw new Error("The environment doesn't have randomBytes function");
- }
- },
- randomPrivateKey: () => {
- return utils.hashToPrivateKey(utils.randomBytes(40));
- },
- bytesToHex,
- hexToBytes,
- concatBytes,
- mod,
- invert,
- sha256: async (...messages) => {
- if (crypto.web) {
- const buffer = await crypto.web.subtle.digest('SHA-256', concatBytes(...messages));
- return new Uint8Array(buffer);
- }
- else if (crypto.node) {
- const { createHash } = crypto.node;
- const hash = createHash('sha256');
- messages.forEach((m) => hash.update(m));
- return Uint8Array.from(hash.digest());
- }
- else {
- throw new Error("The environment doesn't have sha256 function");
- }
- },
- hmacSha256: async (key, ...messages) => {
- if (crypto.web) {
- const ckey = await crypto.web.subtle.importKey('raw', key, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign']);
- const message = concatBytes(...messages);
- const buffer = await crypto.web.subtle.sign('HMAC', ckey, message);
- return new Uint8Array(buffer);
- }
- else if (crypto.node) {
- const { createHmac } = crypto.node;
- const hash = createHmac('sha256', key);
- messages.forEach((m) => hash.update(m));
- return Uint8Array.from(hash.digest());
- }
- else {
- throw new Error("The environment doesn't have hmac-sha256 function");
- }
- },
- sha256Sync: undefined,
- hmacSha256Sync: undefined,
- taggedHash: async (tag, ...messages) => {
- let tagP = TAGGED_HASH_PREFIXES[tag];
- if (tagP === undefined) {
- const tagH = await utils.sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
- tagP = concatBytes(tagH, tagH);
- TAGGED_HASH_PREFIXES[tag] = tagP;
- }
- return utils.sha256(tagP, ...messages);
- },
- taggedHashSync: (tag, ...messages) => {
- if (typeof utils.sha256Sync !== 'function')
- throw new Error('utils.sha256Sync is undefined, you need to set it');
- let tagP = TAGGED_HASH_PREFIXES[tag];
- if (tagP === undefined) {
- const tagH = utils.sha256Sync(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
- tagP = concatBytes(tagH, tagH);
- TAGGED_HASH_PREFIXES[tag] = tagP;
- }
- return utils.sha256Sync(tagP, ...messages);
- },
- precompute(windowSize = 8, point = Point.BASE) {
- const cached = point === Point.BASE ? point : new Point(point.x, point.y);
- cached._setWindowSize(windowSize);
- cached.multiply(_3n);
- return cached;
- },
- };
-
- exports.CURVE = CURVE;
- exports.Point = Point;
- exports.Signature = Signature;
- exports.getPublicKey = getPublicKey;
- exports.getSharedSecret = getSharedSecret;
- exports.recoverPublicKey = recoverPublicKey;
- exports.schnorr = schnorr;
- exports.sign = sign;
- exports.signSync = signSync;
- exports.utils = utils;
- exports.verify = verify;
-
- Object.defineProperty(exports, '__esModule', { value: true });
-
-}));
diff --git a/lnbits/extensions/watchonly/static/js/index.js b/lnbits/extensions/watchonly/static/js/index.js
deleted file mode 100644
index 880d6b30..00000000
--- a/lnbits/extensions/watchonly/static/js/index.js
+++ /dev/null
@@ -1,435 +0,0 @@
-const watchOnly = async () => {
- Vue.component(VueQrcode.name, VueQrcode)
-
- await walletConfig('static/components/wallet-config/wallet-config.html')
- await walletList('static/components/wallet-list/wallet-list.html')
- await addressList('static/components/address-list/address-list.html')
- await history('static/components/history/history.html')
- await utxoList('static/components/utxo-list/utxo-list.html')
- await feeRate('static/components/fee-rate/fee-rate.html')
- await seedInput('static/components/seed-input/seed-input.html')
- await sendTo('static/components/send-to/send-to.html')
- await payment('static/components/payment/payment.html')
- await serialSigner('static/components/serial-signer/serial-signer.html')
- await serialPortConfig(
- 'static/components/serial-port-config/serial-port-config.html'
- )
-
- Vue.filter('reverse', function (value) {
- // slice to make a copy of array, then reverse the copy
- return value.slice().reverse()
- })
-
- new Vue({
- el: '#vue',
- mixins: [windowMixin],
- data: function () {
- return {
- scan: {
- scanning: false,
- scanCount: 0,
- scanIndex: 0
- },
-
- currentAddress: null,
-
- tab: 'addresses',
-
- config: {sats_denominated: true},
-
- qrCodeDialog: {
- show: false,
- data: null
- },
- ...tables,
- ...tableData,
-
- walletAccounts: [],
- addresses: [],
- history: [],
- historyFilter: '',
-
- showAddress: false,
- addressNote: '',
- showPayment: false,
- fetchedUtxos: false,
- utxosFilter: '',
- network: null,
-
- showEnterSignedPsbt: false,
- signedBase64Psbt: null
- }
- },
- computed: {
- mempoolHostname: function () {
- if (!this.config.isLoaded) return
- let hostname = new URL(this.config.mempool_endpoint).hostname
- if (this.config.network === 'Testnet') {
- hostname += '/testnet'
- }
- return hostname
- }
- },
-
- methods: {
- updateAmountForAddress: async function (addressData, amount = 0) {
- try {
- const wallet = this.g.user.wallets[0]
- addressData.amount = amount
- if (!addressData.isChange) {
- const addressWallet = this.walletAccounts.find(
- w => w.id === addressData.wallet
- )
- if (
- addressWallet &&
- addressWallet.address_no < addressData.addressIndex
- ) {
- addressWallet.address_no = addressData.addressIndex
- }
- }
-
- // todo: account deleted
- await LNbits.api.request(
- 'PUT',
- `/watchonly/api/v1/address/${addressData.id}`,
- wallet.adminkey,
- {amount}
- )
- } catch (err) {
- addressData.error = 'Failed to refresh amount for address'
- this.$q.notify({
- type: 'warning',
- message: `Failed to refresh amount for address ${addressData.address}`,
- timeout: 10000
- })
- LNbits.utils.notifyApiError(err)
- }
- },
- updateNoteForAddress: async function ({addressId, note}) {
- try {
- const wallet = this.g.user.wallets[0]
- await LNbits.api.request(
- 'PUT',
- `/watchonly/api/v1/address/${addressId}`,
- wallet.adminkey,
- {note}
- )
- const updatedAddress =
- this.addresses.find(a => a.id === addressId) || {}
- updatedAddress.note = note
- } catch (err) {
- LNbits.utils.notifyApiError(err)
- }
- },
-
- //################### ADDRESS HISTORY ###################
- addressHistoryFromTxs: function (addressData, txs) {
- const addressHistory = []
- txs.forEach(tx => {
- const sent = tx.vin
- .filter(
- vin => vin.prevout.scriptpubkey_address === addressData.address
- )
- .map(vin => mapInputToSentHistory(tx, addressData, vin))
-
- const received = tx.vout
- .filter(vout => vout.scriptpubkey_address === addressData.address)
- .map(vout => mapOutputToReceiveHistory(tx, addressData, vout))
- addressHistory.push(...sent, ...received)
- })
- return addressHistory
- },
-
- markSameTxAddressHistory: function () {
- this.history
- .filter(s => s.sent)
- .forEach((el, i, arr) => {
- if (el.isSubItem) return
-
- const sameTxItems = arr.slice(i + 1).filter(e => e.txId === el.txId)
- if (!sameTxItems.length) return
- sameTxItems.forEach(e => {
- e.isSubItem = true
- })
-
- el.totalAmount =
- el.amount + sameTxItems.reduce((t, e) => (t += e.amount || 0), 0)
- el.sameTxItems = sameTxItems
- })
- },
-
- //################### PAYMENT ###################
-
- initPaymentData: async function () {
- if (!this.payment.show) return
- await this.refreshAddresses()
- },
-
- goToPaymentView: async function () {
- this.showPayment = true
- await this.initPaymentData()
- },
-
- //################### PSBT ###################
-
- updateSignedPsbt: async function (psbtBase64) {
- this.$refs.paymentRef.updateSignedPsbt(psbtBase64)
- },
-
- showEnterSignedPsbtDialog: function () {
- this.signedBase64Psbt = ''
- this.showEnterSignedPsbt = true
- },
-
- checkPsbt: function () {
- this.$refs.paymentRef.updateSignedPsbt(this.signedBase64Psbt)
- },
-
- //################### UTXOs ###################
- scanAllAddresses: async function () {
- await this.refreshAddresses()
- this.history = []
- let addresses = this.addresses
- this.utxos.data = []
- this.utxos.total = 0
- // Loop while new funds are found on the gap adresses.
- // Use 1000 limit as a safety check (scan 20 000 addresses max)
- for (let i = 0; i < 1000 && addresses.length; i++) {
- await this.updateUtxosForAddresses(addresses)
- const oldAddresses = this.addresses.slice()
- await this.refreshAddresses()
- const newAddresses = this.addresses.slice()
- // check if gap addresses have been extended
- addresses = newAddresses.filter(
- newAddr => !oldAddresses.find(oldAddr => oldAddr.id === newAddr.id)
- )
- if (addresses.length) {
- this.$q.notify({
- type: 'positive',
- message: 'Funds found! Scanning for more...',
- timeout: 10000
- })
- }
- }
- },
- scanAddressWithAmount: async function () {
- this.utxos.data = []
- this.utxos.total = 0
- this.history = []
- const addresses = this.addresses.filter(a => a.hasActivity)
- await this.updateUtxosForAddresses(addresses)
- },
- scanAddress: async function (addressData) {
- this.updateUtxosForAddresses([addressData])
- this.$q.notify({
- type: 'positive',
- message: 'Address Rescanned',
- timeout: 10000
- })
- },
- refreshAddresses: async function () {
- if (!this.walletAccounts) return
- this.addresses = []
- for (const {id, type} of this.walletAccounts) {
- const newAddresses = await this.getAddressesForWallet(id)
- const uniqueAddresses = newAddresses.filter(
- newAddr => !this.addresses.find(a => a.address === newAddr.address)
- )
-
- const lastActiveAddress =
- uniqueAddresses.filter(a => !a.isChange && a.hasActivity).pop() ||
- {}
-
- uniqueAddresses.forEach(a => {
- a.expanded = false
- a.accountType = type
- a.gapLimitExceeded =
- !a.isChange &&
- a.addressIndex >
- lastActiveAddress.addressIndex + DEFAULT_RECEIVE_GAP_LIMIT
- })
- this.addresses.push(...uniqueAddresses)
- }
- this.$emit('update:addresses', this.addresses)
- },
- getAddressesForWallet: async function (walletId) {
- try {
- const {data} = await LNbits.api.request(
- 'GET',
- '/watchonly/api/v1/addresses/' + walletId,
- this.g.user.wallets[0].inkey
- )
- return data.map(mapAddressesData)
- } catch (error) {
- this.$q.notify({
- type: 'warning',
- message: `Failed to fetch addresses for wallet with id ${walletId}.`,
- timeout: 10000
- })
- LNbits.utils.notifyApiError(error)
- }
- return []
- },
- updateUtxosForAddresses: async function (addresses = []) {
- this.scan = {scanning: true, scanCount: addresses.length, scanIndex: 0}
-
- try {
- for (addrData of addresses) {
- const addressHistory = await this.getAddressTxsDelayed(addrData)
- // remove old entries
- this.history = this.history.filter(
- h => h.address !== addrData.address
- )
-
- // add new entries
- this.history.push(...addressHistory)
- this.history.sort((a, b) => (!a.height ? -1 : b.height - a.height))
- this.markSameTxAddressHistory()
-
- if (addressHistory.length) {
- // search only if it ever had any activity
- const utxos = await this.getAddressTxsUtxoDelayed(
- addrData.address
- )
- this.updateUtxosForAddress(addrData, utxos)
- }
-
- this.scan.scanIndex++
- }
- } catch (error) {
- console.error(error)
- this.$q.notify({
- type: 'warning',
- message: 'Failed to scan addresses',
- timeout: 10000
- })
- } finally {
- this.scan.scanning = false
- }
- },
- updateUtxosForAddress: function (addressData, utxos = []) {
- const wallet =
- this.walletAccounts.find(w => w.id === addressData.wallet) || {}
-
- const newUtxos = utxos.map(utxo =>
- mapAddressDataToUtxo(wallet, addressData, utxo)
- )
- // remove old utxos
- this.utxos.data = this.utxos.data.filter(
- u => u.address !== addressData.address
- )
- // add new utxos
- this.utxos.data.push(...newUtxos)
- if (utxos.length) {
- this.utxos.data.sort((a, b) => b.sort - a.sort)
- this.utxos.total = this.utxos.data.reduce(
- (total, y) => (total += y?.amount || 0),
- 0
- )
- }
- const addressTotal = utxos.reduce(
- (total, y) => (total += y?.value || 0),
- 0
- )
- this.updateAmountForAddress(addressData, addressTotal)
- },
-
- //################### MEMPOOL API ###################
- getAddressTxsDelayed: async function (addrData) {
- const accounts = this.walletAccounts
- const {
- bitcoin: {addresses: addressesAPI}
- } = mempoolJS({
- hostname: this.mempoolHostname
- })
- const fn = async () => {
- if (!accounts.find(w => w.id === addrData.wallet)) return []
- return addressesAPI.getAddressTxs({
- address: addrData.address
- })
- }
- const addressTxs = await retryWithDelay(fn)
- return this.addressHistoryFromTxs(addrData, addressTxs)
- },
-
- getAddressTxsUtxoDelayed: async function (address) {
- const endpoint = this.mempoolHostname
- const {
- bitcoin: {addresses: addressesAPI}
- } = mempoolJS({
- hostname: endpoint
- })
-
- const fn = async () => {
- if (endpoint !== this.mempoolHostname) return []
- return addressesAPI.getAddressTxsUtxo({
- address
- })
- }
- return retryWithDelay(fn)
- },
-
- //################### OTHER ###################
-
- openQrCodeDialog: function (addressData) {
- this.currentAddress = addressData
- this.addressNote = addressData.note || ''
- this.showAddress = true
- },
- searchInTab: function ({tab, value}) {
- this.tab = tab
- this[`${tab}Filter`] = value
- },
-
- updateAccounts: async function (accounts) {
- this.walletAccounts = accounts
- await this.refreshAddresses()
- await this.scanAddressWithAmount()
- },
- showAddressDetails: function (addressData) {
- this.openQrCodeDialog(addressData)
- },
- showAddressDetailsWithConfirmation: function ({addressData, wallet}) {
- this.showAddressDetails(addressData)
- if (this.$refs.serialSigner.isConnected()) {
- if (this.$refs.serialSigner.isAuthenticated()) {
- if (wallet.meta?.accountPath) {
- const branchIndex = addressData.isChange ? 1 : 0
- const path =
- wallet.meta.accountPath +
- `/${branchIndex}/${addressData.addressIndex}`
- this.$refs.serialSigner.hwwShowAddress(path, addressData.address)
- }
- } else {
- this.$q.notify({
- type: 'warning',
- message: 'Please login in order to confirm address on device',
- timeout: 10000
- })
- }
- }
- },
- initUtxos: function (addresses) {
- if (!this.fetchedUtxos && addresses.length) {
- this.fetchedUtxos = true
- this.addresses = addresses
- this.scanAddressWithAmount()
- }
- },
- handleBroadcastSuccess: async function (txId) {
- this.tab = 'history'
- this.searchInTab({tab: 'history', value: txId})
- this.showPayment = false
- await this.refreshAddresses()
- await this.scanAddressWithAmount()
- }
- },
- created: async function () {
- if (this.g.user.wallets.length) {
- await this.refreshAddresses()
- await this.scanAddressWithAmount()
- }
- }
- })
-}
-watchOnly()
diff --git a/lnbits/extensions/watchonly/static/js/map.js b/lnbits/extensions/watchonly/static/js/map.js
deleted file mode 100644
index 81093936..00000000
--- a/lnbits/extensions/watchonly/static/js/map.js
+++ /dev/null
@@ -1,81 +0,0 @@
-const mapAddressesData = a => ({
- id: a.id,
- address: a.address,
- amount: a.amount,
- wallet: a.wallet,
- note: a.note,
-
- isChange: a.branch_index === 1,
- addressIndex: a.address_index,
- hasActivity: a.has_activity
-})
-
-const mapInputToSentHistory = (tx, addressData, vin) => ({
- sent: true,
- txId: tx.txid,
- address: addressData.address,
- isChange: addressData.isChange,
- amount: vin.prevout.value,
- date: blockTimeToDate(tx.status.block_time),
- height: tx.status.block_height,
- confirmed: tx.status.confirmed,
- fee: tx.fee,
- expanded: false
-})
-
-const mapOutputToReceiveHistory = (tx, addressData, vout) => ({
- received: true,
- txId: tx.txid,
- address: addressData.address,
- isChange: addressData.isChange,
- amount: vout.value,
- date: blockTimeToDate(tx.status.block_time),
- height: tx.status.block_height,
- confirmed: tx.status.confirmed,
- fee: tx.fee,
- expanded: false
-})
-
-const mapUtxoToPsbtInput = utxo => ({
- tx_id: utxo.txId,
- vout: utxo.vout,
- amount: utxo.amount,
- address: utxo.address,
- branch_index: utxo.isChange ? 1 : 0,
- address_index: utxo.addressIndex,
- wallet: utxo.wallet,
- accountType: utxo.accountType,
- txHex: ''
-})
-
-const mapAddressDataToUtxo = (wallet, addressData, utxo) => ({
- id: addressData.id,
- address: addressData.address,
- isChange: addressData.isChange,
- addressIndex: addressData.addressIndex,
- wallet: addressData.wallet,
- accountType: addressData.accountType,
- masterpubFingerprint: wallet.fingerprint,
- txId: utxo.txid,
- vout: utxo.vout,
- confirmed: utxo.status.confirmed,
- amount: utxo.value,
- date: blockTimeToDate(utxo.status?.block_time),
- sort: utxo.status?.block_time,
- expanded: false,
- selected: false
-})
-
-const mapWalletAccount = function (o) {
- return Object.assign({}, o, {
- date: o.time
- ? Quasar.utils.date.formatDate(
- new Date(o.time * 1000),
- 'YYYY-MM-DD HH:mm'
- )
- : '',
- meta: o.meta ? JSON.parse(o.meta) : null,
- label: o.title,
- expanded: false
- })
-}
diff --git a/lnbits/extensions/watchonly/static/js/tables.js b/lnbits/extensions/watchonly/static/js/tables.js
deleted file mode 100644
index f437bcd5..00000000
--- a/lnbits/extensions/watchonly/static/js/tables.js
+++ /dev/null
@@ -1,61 +0,0 @@
-const tables = {
- summaryTable: {
- columns: [
- {
- name: 'totalInputs',
- align: 'center',
- label: 'Selected Amount'
- },
- {
- name: 'totalOutputs',
- align: 'center',
- label: 'Payed Amount'
- },
- {
- name: 'fees',
- align: 'center',
- label: 'Fees'
- },
- {
- name: 'change',
- align: 'center',
- label: 'Change'
- }
- ]
- }
-}
-
-const tableData = {
- utxos: {
- data: [],
- total: 0
- },
- payment: {
- fee: 0,
- txSize: 0,
- tx: null,
- psbtBase64: '',
- psbtBase64Signed: '',
- signedTx: null,
- signedTxHex: null,
- sentTxId: null,
-
- signModes: [
- {
- label: 'Serial Port Device',
- value: 'serial-port'
- },
- {
- label: 'Animated QR',
- value: 'animated-qr',
- disable: true
- }
- ],
- signMode: '',
- show: false,
- showAdvanced: false
- },
- summary: {
- data: [{totalInputs: 0, totalOutputs: 0, fees: 0, change: 0}]
- }
-}
diff --git a/lnbits/extensions/watchonly/static/js/utils.js b/lnbits/extensions/watchonly/static/js/utils.js
deleted file mode 100644
index c73dd9c0..00000000
--- a/lnbits/extensions/watchonly/static/js/utils.js
+++ /dev/null
@@ -1,210 +0,0 @@
-const PSBT_BASE64_PREFIX = 'cHNidP8'
-
-const COMMAND_PING = '/ping'
-const COMMAND_PASSWORD = '/password'
-const COMMAND_PASSWORD_CLEAR = '/password-clear'
-const COMMAND_ADDRESS = '/address'
-const COMMAND_SEND_PSBT = '/psbt'
-const COMMAND_SIGN_PSBT = '/sign'
-const COMMAND_HELP = '/help'
-const COMMAND_WIPE = '/wipe'
-const COMMAND_SEED = '/seed'
-const COMMAND_RESTORE = '/restore'
-const COMMAND_CONFIRM_NEXT = '/confirm-next'
-const COMMAND_CANCEL = '/cancel'
-const COMMAND_XPUB = '/xpub'
-const COMMAND_PAIR = '/pair'
-const COMMAND_LOG = '/log'
-const COMMAND_CHECK_PAIRING = '/check-pairing'
-
-const DEFAULT_RECEIVE_GAP_LIMIT = 20
-const PAIRING_CONTROL_TEXT = 'lnbits'
-
-const HWW_DEFAULT_CONFIG = Object.freeze({
- name: '',
- buttonOnePin: '',
- buttonTwoPin: '',
- baudRate: 9600,
- bufferSize: 255,
- dataBits: 8,
- flowControl: 'none',
- parity: 'none',
- stopBits: 1
-})
-
-const blockTimeToDate = blockTime =>
- blockTime ? moment(blockTime * 1000).format('LLL') : ''
-
-const currentDateTime = () => moment().format('LLL')
-
-const sleep = ms => new Promise(r => setTimeout(r, ms))
-
-const retryWithDelay = async function (fn, retryCount = 0) {
- try {
- await sleep(25)
- // Do not return the call directly, use result.
- // Otherwise the error will not be cought in this try-catch block.
- const result = await fn()
- return result
- } catch (err) {
- if (retryCount > 100) throw err
- await sleep((retryCount + 1) * 1000)
- return retryWithDelay(fn, retryCount + 1)
- }
-}
-
-const txSize = tx => {
- // https://bitcoinops.org/en/tools/calc-size/
- // overhead size
- const nVersion = 4
- const inCount = 1
- const outCount = 1
- const nlockTime = 4
- const hasSegwit = !!tx.inputs.find(inp =>
- ['p2wsh', 'p2wpkh', 'p2tr'].includes(inp.accountType)
- )
- const segwitFlag = hasSegwit ? 0.5 : 0
- const overheadSize = nVersion + inCount + outCount + nlockTime + segwitFlag
-
- // inputs size
- const outpoint = 36 // txId plus vout index number
- const scriptSigLength = 1
- const nSequence = 4
- const inputsSize = tx.inputs.reduce((t, inp) => {
- const scriptSig =
- inp.accountType === 'p2pkh' ? 107 : inp.accountType === 'p2sh' ? 254 : 0
- const witnessItemCount = hasSegwit ? 0.25 : 0
- const witnessItems =
- inp.accountType === 'p2wpkh'
- ? 27
- : inp.accountType === 'p2wsh'
- ? 63.5
- : inp.accountType === 'p2tr'
- ? 16.5
- : 0
- t +=
- outpoint +
- scriptSigLength +
- nSequence +
- scriptSig +
- witnessItemCount +
- witnessItems
- return t
- }, 0)
-
- // outputs size
- const nValue = 8
- const scriptPubKeyLength = 1
-
- const outputsSize = tx.outputs.reduce((t, out) => {
- const type = guessAddressType(out.address)
-
- const scriptPubKey =
- type === 'p2pkh'
- ? 25
- : type === 'p2wpkh'
- ? 22
- : type === 'p2sh'
- ? 23
- : type === 'p2wsh'
- ? 34
- : 34 // default to the largest size (p2tr included)
- t += nValue + scriptPubKeyLength + scriptPubKey
- return t
- }, 0)
-
- return overheadSize + inputsSize + outputsSize
-}
-const guessAddressType = (a = '') => {
- if (a.startsWith('1') || a.startsWith('n')) return 'p2pkh'
- if (a.startsWith('3') || a.startsWith('2')) return 'p2sh'
- if (a.startsWith('bc1q') || a.startsWith('tb1q'))
- return a.length === 42 ? 'p2wpkh' : 'p2wsh'
- if (a.startsWith('bc1p') || a.startsWith('tb1p')) return 'p2tr'
-}
-
-const ACCOUNT_TYPES = {
- p2tr: 'Taproot, BIP86, P2TR, Bech32m',
- p2wpkh: 'SegWit, BIP84, P2WPKH, Bech32',
- p2sh: 'BIP49, P2SH-P2WPKH, Base58',
- p2pkh: 'Legacy, BIP44, P2PKH, Base58'
-}
-
-const getAccountDescription = type => ACCOUNT_TYPES[type] || 'nonstandard'
-
-const readFromSerialPort = reader => {
- let partialChunk
- let fulliness = []
-
- const readStringUntil = async (separator = '\n') => {
- if (fulliness.length) return fulliness.shift().trim()
- const chunks = []
- if (partialChunk) {
- // leftovers from previous read
- chunks.push(partialChunk)
- partialChunk = undefined
- }
- while (true) {
- const {value, done} = await reader.read()
- if (value) {
- const values = value.split(separator)
- // found one or more separators
- if (values.length > 1) {
- chunks.push(values.shift()) // first element
- partialChunk = values.pop() // last element
- fulliness = values // full lines
- return {value: chunks.join('').trim(), done: false}
- }
- chunks.push(value)
- }
- if (done) return {value: chunks.join('').trim(), done: true}
- }
- }
- return readStringUntil
-}
-
-function satOrBtc(val, showUnit = true, showSats = false) {
- const value = showSats
- ? LNbits.utils.formatSat(val)
- : val == 0
- ? 0.0
- : (val / 100000000).toFixed(8)
- if (!showUnit) return value
- return showSats ? value + ' sat' : value + ' BTC'
-}
-
-function loadTemplateAsync(path) {
- const result = new Promise(resolve => {
- const xhttp = new XMLHttpRequest()
-
- xhttp.onreadystatechange = function () {
- if (this.readyState == 4) {
- if (this.status == 200) resolve(this.responseText)
-
- if (this.status == 404) resolve(`Page not found: ${path}
`)
- }
- }
-
- xhttp.open('GET', path, true)
- xhttp.send()
- })
-
- return result
-}
-
-function findAccountPathIssues(path = '') {
- const p = path.split('/')
- if (p[0] !== 'm') return "Path must start with 'm/'"
- for (let i = 1; i < p.length; i++) {
- if (p[i].endsWith('')) p[i] = p[i].substring(0, p[i].length - 1)
- if (isNaN(p[i])) return `${p[i]} is not a valid value`
- }
-}
-
-function asciiToUint8Array(str) {
- var chars = []
- for (var i = 0; i < str.length; ++i) {
- chars.push(str.charCodeAt(i))
- }
- return new Uint8Array(chars)
-}
diff --git a/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html b/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
deleted file mode 100644
index 0365df44..00000000
--- a/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
- Onchain Wallet (watch-only) extension uses mempool.space
- For use with "account Extended Public Key"
- https://iancoleman.io/bip39/
-
- Flash binaries
- directly from browser
-
-
Created by
- Ben Arc,
- Tiago Vasconcelos,
- motorina0
- (using,
- Embit)
-
-
- Swagger REST API Documentation
-
-
-
diff --git a/lnbits/extensions/watchonly/templates/watchonly/index.html b/lnbits/extensions/watchonly/templates/watchonly/index.html
deleted file mode 100644
index 84be5c81..00000000
--- a/lnbits/extensions/watchonly/templates/watchonly/index.html
+++ /dev/null
@@ -1,316 +0,0 @@
-{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
-%} {% block page %}
-
-
-
-
-
-
-
-
-
-
-
- {% raw %}
-
-
-
- Scan Blockchain
-
-
-
-
-
-
-
-
-
- New Payment
- Create a new payment by selecting Inputs and
- Outputs
-
-
-
-
- From Signed PSBT
- Paste a signed PSBT
-
-
-
-
-
- Back
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {% endraw %}
-
-
-
-
-
- {{SITE_TITLE}} Onchain Wallet (watch-only) Extension
- (v0.3)
-
-
-
-
- {% include "watchonly/_api_docs.html" %}
-
-
-
- {% raw %}
-
-
- Address Details
-
-
-
-
-
-
-
-
- {{ currentAddress.address }}
-
-
-
-
-
-
-
- Gap limit of 20 addresses exceeded. Other wallets might not detect
- funds at this address.
-
-
- Save Note
- Close
-
-
-
-
-
-
-
- Enter the Signed PSBT
-
-
-
-
-
-
-
- Check PSBT
- Close
-
-
-
-
-
- {% endraw %}
-
-
-{% endblock %} {% block scripts %} {{ window_vars(user) }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-{% endblock %}
diff --git a/lnbits/extensions/watchonly/views.py b/lnbits/extensions/watchonly/views.py
deleted file mode 100644
index 8cebc6cc..00000000
--- a/lnbits/extensions/watchonly/views.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from fastapi import Depends, Request
-from fastapi.templating import Jinja2Templates
-from starlette.responses import HTMLResponse
-
-from lnbits.core.models import User
-from lnbits.decorators import check_user_exists
-
-from . import watchonly_ext, watchonly_renderer
-
-templates = Jinja2Templates(directory="templates")
-
-
-@watchonly_ext.get("/", response_class=HTMLResponse)
-async def index(request: Request, user: User = Depends(check_user_exists)):
- return watchonly_renderer().TemplateResponse(
- "watchonly/index.html", {"request": request, "user": user.dict()}
- )
diff --git a/lnbits/extensions/watchonly/views_api.py b/lnbits/extensions/watchonly/views_api.py
deleted file mode 100644
index e0c427fe..00000000
--- a/lnbits/extensions/watchonly/views_api.py
+++ /dev/null
@@ -1,385 +0,0 @@
-import json
-from http import HTTPStatus
-from typing import List
-
-import httpx
-from embit import finalizer, script
-from embit.ec import PublicKey
-from embit.networks import NETWORKS
-from embit.psbt import PSBT, DerivationPath
-from embit.transaction import Transaction, TransactionInput, TransactionOutput
-from fastapi import Depends, HTTPException, Query, Request
-
-from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
-
-from . import watchonly_ext
-from .crud import (
- create_config,
- create_fresh_addresses,
- create_watch_wallet,
- delete_addresses_for_wallet,
- delete_watch_wallet,
- get_addresses,
- get_config,
- get_fresh_address,
- get_watch_wallet,
- get_watch_wallets,
- update_address,
- update_config,
- update_watch_wallet,
-)
-from .helpers import parse_key
-from .models import (
- Config,
- CreatePsbt,
- CreateWallet,
- ExtractPsbt,
- SerializedTransaction,
- SignedTransaction,
- WalletAccount,
-)
-
-###################WALLETS#############################
-
-
-@watchonly_ext.get("/api/v1/wallet")
-async def api_wallets_retrieve(
- network: str = Query("Mainnet"), wallet: WalletTypeInfo = Depends(get_key_type)
-):
-
- try:
- return [
- wallet.dict()
- for wallet in await get_watch_wallets(wallet.wallet.user, network)
- ]
- except:
- return []
-
-
-@watchonly_ext.get("/api/v1/wallet/{wallet_id}", dependencies=[Depends(get_key_type)])
-async def api_wallet_retrieve(wallet_id: str):
- w_wallet = await get_watch_wallet(wallet_id)
-
- if not w_wallet:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
- )
-
- return w_wallet.dict()
-
-
-@watchonly_ext.post("/api/v1/wallet")
-async def api_wallet_create_or_update(
- data: CreateWallet, w: WalletTypeInfo = Depends(require_admin_key)
-):
- try:
- descriptor, network = parse_key(data.masterpub)
- assert network
- if data.network != network["name"]:
- raise ValueError(
- "Account network error. This account is for '{}'".format(
- network["name"]
- )
- )
-
- new_wallet = WalletAccount(
- id="none",
- masterpub=data.masterpub,
- fingerprint=descriptor.keys[0].fingerprint.hex(),
- type=descriptor.scriptpubkey_type(),
- title=data.title,
- address_no=-1, # so fresh address on empty wallet can get address with index 0
- balance=0,
- network=network["name"],
- meta=data.meta,
- )
-
- wallets = await get_watch_wallets(w.wallet.user, network["name"])
- existing_wallet = next(
- (
- ew
- for ew in wallets
- if ew.fingerprint == new_wallet.fingerprint
- and ew.network == new_wallet.network
- and ew.masterpub == new_wallet.masterpub
- ),
- None,
- )
- if existing_wallet:
- raise ValueError(
- "Account '{}' has the same master pulic key".format(
- existing_wallet.title
- )
- )
-
- wallet = await create_watch_wallet(w.wallet.user, new_wallet)
-
- await api_get_addresses(wallet.id, w)
- except Exception as e:
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
-
- config = await get_config(w.wallet.user)
- if not config:
- await create_config(user=w.wallet.user)
- return wallet.dict()
-
-
-@watchonly_ext.delete(
- "/api/v1/wallet/{wallet_id}", dependencies=[Depends(require_admin_key)]
-)
-async def api_wallet_delete(wallet_id: str):
- wallet = await get_watch_wallet(wallet_id)
-
- if not wallet:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
- )
-
- await delete_watch_wallet(wallet_id)
- await delete_addresses_for_wallet(wallet_id)
-
- return "", HTTPStatus.NO_CONTENT
-
-
-#############################ADDRESSES##########################
-
-
-@watchonly_ext.get("/api/v1/address/{wallet_id}", dependencies=[Depends(get_key_type)])
-async def api_fresh_address(wallet_id: str):
- address = await get_fresh_address(wallet_id)
- assert address
- return address.dict()
-
-
-@watchonly_ext.put("/api/v1/address/{id}", dependencies=[Depends(require_admin_key)])
-async def api_update_address(id: str, req: Request):
- body = await req.json()
- params = {}
- # amout is only updated if the address has history
- if "amount" in body:
- params["amount"] = int(body["amount"])
- params["has_activity"] = True
-
- if "note" in body:
- params["note"] = body["note"]
-
- address = await update_address(**params, id=id)
- assert address
-
- wallet = (
- await get_watch_wallet(address.wallet)
- if address.branch_index == 0 and address.amount != 0
- else None
- )
-
- if wallet and wallet.address_no < address.address_index:
- await update_watch_wallet(
- address.wallet, **{"address_no": address.address_index}
- )
- return address
-
-
-@watchonly_ext.get("/api/v1/addresses/{wallet_id}")
-async def api_get_addresses(wallet_id, w: WalletTypeInfo = Depends(get_key_type)):
- wallet = await get_watch_wallet(wallet_id)
- if not wallet:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
- )
-
- addresses = await get_addresses(wallet_id)
- config = await get_config(w.wallet.user)
- assert config
-
- if not addresses:
- await create_fresh_addresses(wallet_id, 0, config.receive_gap_limit)
- await create_fresh_addresses(wallet_id, 0, config.change_gap_limit, True)
- addresses = await get_addresses(wallet_id)
-
- receive_addresses = list(filter(lambda addr: addr.branch_index == 0, addresses))
- change_addresses = list(filter(lambda addr: addr.branch_index == 1, addresses))
-
- last_receive_address = list(
- filter(lambda addr: addr.has_activity, receive_addresses)
- )[-1:]
- last_change_address = list(
- filter(lambda addr: addr.has_activity, change_addresses)
- )[-1:]
-
- if last_receive_address:
- current_index = receive_addresses[-1].address_index
- address_index = last_receive_address[0].address_index
- await create_fresh_addresses(
- wallet_id, current_index + 1, address_index + config.receive_gap_limit + 1
- )
-
- if last_change_address:
- current_index = change_addresses[-1].address_index
- address_index = last_change_address[0].address_index
- await create_fresh_addresses(
- wallet_id,
- current_index + 1,
- address_index + config.change_gap_limit + 1,
- True,
- )
-
- addresses = await get_addresses(wallet_id)
- return [address.dict() for address in addresses]
-
-
-#############################PSBT##########################
-
-
-@watchonly_ext.post("/api/v1/psbt", dependencies=[Depends(require_admin_key)])
-async def api_psbt_create(data: CreatePsbt):
- try:
- vin = [
- TransactionInput(bytes.fromhex(inp.tx_id), inp.vout) for inp in data.inputs
- ]
- vout = [
- TransactionOutput(out.amount, script.address_to_scriptpubkey(out.address))
- for out in data.outputs
- ]
-
- descriptors = {}
- for _, masterpub in enumerate(data.masterpubs):
- descriptors[masterpub.id] = parse_key(masterpub.public_key)
-
- inputs_extra: List[dict] = []
-
- for i, inp in enumerate(data.inputs):
- bip32_derivations = {}
- descriptor = descriptors[inp.wallet][0]
- d = descriptor.derive(inp.address_index, inp.branch_index)
- for k in d.keys:
- bip32_derivations[PublicKey.parse(k.sec())] = DerivationPath(
- k.origin.fingerprint, k.origin.derivation
- )
- inputs_extra.append(
- {
- "bip32_derivations": bip32_derivations,
- "non_witness_utxo": Transaction.from_string(inp.tx_hex),
- }
- )
-
- tx = Transaction(vin=vin, vout=vout)
- psbt = PSBT(tx)
-
- for i, inp_extra in enumerate(inputs_extra):
- psbt.inputs[i].bip32_derivations = inp_extra["bip32_derivations"]
- psbt.inputs[i].non_witness_utxo = inp_extra.get("non_witness_utxo", None)
-
- outputs_extra = []
- bip32_derivations = {}
- for i, out in enumerate(data.outputs):
- if out.branch_index == 1:
- assert out.wallet
- descriptor = descriptors[out.wallet][0]
- d = descriptor.derive(out.address_index, out.branch_index)
- for k in d.keys:
- bip32_derivations[PublicKey.parse(k.sec())] = DerivationPath(
- k.origin.fingerprint, k.origin.derivation
- )
- outputs_extra.append({"bip32_derivations": bip32_derivations})
-
- for i, out_extra in enumerate(outputs_extra):
- psbt.outputs[i].bip32_derivations = out_extra["bip32_derivations"]
-
- return psbt.to_string()
-
- except Exception as e:
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
-
-
-@watchonly_ext.put("/api/v1/psbt/utxos")
-async def api_psbt_utxos_tx(
- req: Request, w: WalletTypeInfo = Depends(require_admin_key)
-):
- """Extract previous unspent transaction outputs (tx_id, vout) from PSBT"""
-
- body = await req.json()
- try:
- psbt = PSBT.from_base64(body["psbtBase64"])
- res = []
- for _, inp in enumerate(psbt.inputs):
- res.append({"tx_id": inp.txid.hex(), "vout": inp.vout})
-
- return res
- except Exception as e:
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
-
-
-@watchonly_ext.put("/api/v1/psbt/extract", dependencies=[Depends(require_admin_key)])
-async def api_psbt_extract_tx(data: ExtractPsbt):
- network = NETWORKS["main"] if data.network == "Mainnet" else NETWORKS["test"]
- try:
- psbt = PSBT.from_base64(data.psbtBase64)
- for i, inp in enumerate(data.inputs):
- psbt.inputs[i].non_witness_utxo = Transaction.from_string(inp.tx_hex)
-
- final_psbt = finalizer.finalize_psbt(psbt)
- if not final_psbt:
- raise ValueError("PSBT cannot be finalized!")
-
- tx_hex = final_psbt.to_string()
- transaction = Transaction.from_string(tx_hex)
- tx = {
- "locktime": transaction.locktime,
- "version": transaction.version,
- "outputs": [],
- "fee": psbt.fee(),
- }
-
- for out in transaction.vout:
- tx["outputs"].append(
- {"amount": out.value, "address": out.script_pubkey.address(network)}
- )
- signed_tx = SignedTransaction(tx_hex=tx_hex, tx_json=json.dumps(tx))
- return signed_tx.dict()
- except Exception as e:
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
-
-
-@watchonly_ext.post("/api/v1/tx")
-async def api_tx_broadcast(
- data: SerializedTransaction, w: WalletTypeInfo = Depends(require_admin_key)
-):
- try:
- config = await get_config(w.wallet.user)
- if not config:
- raise ValueError(
- "Cannot broadcast transaction. Mempool endpoint not defined!"
- )
-
- endpoint = (
- config.mempool_endpoint
- if config.network == "Mainnet"
- else config.mempool_endpoint + "/testnet"
- )
- async with httpx.AsyncClient() as client:
- r = await client.post(endpoint + "/api/tx", content=data.tx_hex)
- r.raise_for_status()
- tx_id = r.text
- return tx_id
- except Exception as e:
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
-
-
-#############################CONFIG##########################
-
-
-@watchonly_ext.put("/api/v1/config")
-async def api_update_config(
- data: Config, w: WalletTypeInfo = Depends(require_admin_key)
-):
- config = await update_config(data, user=w.wallet.user)
- assert config
- return config.dict()
-
-
-@watchonly_ext.get("/api/v1/config")
-async def api_get_config(w: WalletTypeInfo = Depends(get_key_type)):
- config = await get_config(w.wallet.user)
- if not config:
- config = await create_config(user=w.wallet.user)
- return config.dict()