mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2024-11-20 10:39:59 +01:00
refactor: move serial port logic
This commit is contained in:
parent
4e9cfc0f74
commit
df7ebc6699
@ -0,0 +1,234 @@
|
||||
<div>
|
||||
<q-btn-dropdown
|
||||
split
|
||||
unelevated
|
||||
color="primary"
|
||||
icon="usb"
|
||||
:text-color="selectedPort ? 'orange' : 'white'"
|
||||
@click="openSerialPort"
|
||||
>
|
||||
<q-list>
|
||||
<q-item v-if="!selectedPort" clickable v-close-popup>
|
||||
<q-item-section >
|
||||
<q-item-label>Configure</q-item-label>
|
||||
<q-item-label caption
|
||||
>Set the Serial Port communication parameters</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item
|
||||
v-if="!hww.authenticated"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="hwwShowPasswordDialog()"
|
||||
>
|
||||
<q-item-section v-if="selectedPort">
|
||||
<q-item-label>Login</q-item-label>
|
||||
<q-item-label caption
|
||||
>Enter password for Hardware Wallet.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item
|
||||
v-if="hww.authenticated"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="hwwLogout()"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Logout</q-item-label>
|
||||
<q-item-label caption>Clear password for HWW.</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item
|
||||
v-if="selectedPort"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="hwwShowRestoreDialog()"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Restore</q-item-label>
|
||||
<q-item-label caption
|
||||
>Restore wallet from existing word list.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
v-if="hww.authenticated"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="hwwShowSeed()"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Show Seed</q-item-label>
|
||||
<q-item-label caption
|
||||
>Show seed on the Hardware Wallet display.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
v-if="selectedPort"
|
||||
@click="hwwShowWipeDialog()"
|
||||
clickable
|
||||
v-close-popup
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Wipe</q-item-label>
|
||||
<q-item-label caption
|
||||
>Clean-up the wallet. New random seed.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="selectedPort" @click="hwwHelp()" clickable v-close-popup>
|
||||
<q-item-section>
|
||||
<q-item-label>Help</q-item-label>
|
||||
<q-item-label caption>View available comands.</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="selectedPort" clickable v-close-popup>
|
||||
<q-item-section>
|
||||
<q-item-label>Console</q-item-label>
|
||||
<q-item-label caption
|
||||
>Show the serial port communication messages</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
<q-dialog v-model="hww.showPasswordDialog" position="top">
|
||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<q-form @submit="hwwLogin" class="q-gutter-md">
|
||||
<span>Enter password for Hardware Wallet (8 numbers/letters)</span>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="hww.password"
|
||||
type="password"
|
||||
label="Password"
|
||||
></q-input>
|
||||
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
:disable="!selectedPort"
|
||||
type="submit"
|
||||
>Login</q-btn
|
||||
>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||
>Cancel</q-btn
|
||||
>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
<q-dialog v-model="hww.showWipeDialog" position="top">
|
||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<q-form @submit="hwwWipe" class="q-gutter-md">
|
||||
<q-badge color="pink" text-color="black">
|
||||
This action will remove all data from the Hardware Wallet. Please
|
||||
create a back-up for the seed!
|
||||
</q-badge>
|
||||
<span>Enter new password for Hardware Wallet (8 numbers/letters)</span>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="hww.password"
|
||||
type="password"
|
||||
label="Password"
|
||||
></q-input>
|
||||
<q-badge color="pink" text-color="black">
|
||||
This action cannot be reversed!
|
||||
</q-badge>
|
||||
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
:disable="!selectedPort"
|
||||
type="submit"
|
||||
>Wipe</q-btn
|
||||
>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||
>Cancel</q-btn
|
||||
>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
<q-dialog v-model="hww.showRestoreDialog" position="top">
|
||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<q-form @submit="hwwRestore" class="q-gutter-md">
|
||||
<q-badge
|
||||
color="pink"
|
||||
text-color="black"
|
||||
class="text-subtitle2"
|
||||
multi-line
|
||||
>
|
||||
For test purposes only. Do not enter word list with real funds!!!
|
||||
</q-badge>
|
||||
<br /><br /><br />
|
||||
<span>Enter new word list separated by space</span>
|
||||
<q-input
|
||||
v-model.trim="hww.mnemonic"
|
||||
filled
|
||||
:type="hww.showMnemonic ? 'text' : 'password'"
|
||||
filled
|
||||
dense
|
||||
hint="Word List"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
:name="hww.showMnemonic ? 'visibility' : 'visibility_off'"
|
||||
class="cursor-pointer"
|
||||
@click="hww.showMnemonic = !hww.showMnemonic"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
<br />
|
||||
<span>Enter new password (8 numbers/letters)</span>
|
||||
<q-input
|
||||
v-model.trim="hww.password"
|
||||
filled
|
||||
:type="hww.showPassword ? 'text' : 'password'"
|
||||
filled
|
||||
dense
|
||||
hint="New Password"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
:name="hww.showPassword ? 'visibility' : 'visibility_off'"
|
||||
class="cursor-pointer"
|
||||
@click="hww.showPassword = !hww.showPassword"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
<br /><br />
|
||||
<q-badge
|
||||
color="pink"
|
||||
text-color="black"
|
||||
class="text-subtitle2"
|
||||
multi-line
|
||||
>
|
||||
For test purposes only. Do not enter word list with real funds.
|
||||
</q-badge>
|
||||
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
:disable="!selectedPort"
|
||||
type="submit"
|
||||
>Restore</q-btn
|
||||
>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||
>Cancel</q-btn
|
||||
>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</div>
|
@ -0,0 +1,380 @@
|
||||
async function serialSigner(path) {
|
||||
const t = await loadTemplateAsync(path)
|
||||
Vue.component('serial-signer', {
|
||||
name: 'serial-signer',
|
||||
template: t,
|
||||
|
||||
props: [],
|
||||
data: function () {
|
||||
return {
|
||||
selectedPort: null,
|
||||
writableStreamClosed: null,
|
||||
writer: null,
|
||||
readableStreamClosed: null,
|
||||
reader: null,
|
||||
showAdvancedConfig: false,
|
||||
receivedData: '',
|
||||
config: {},
|
||||
hww: {
|
||||
password: null,
|
||||
showPassword: false,
|
||||
mnemonic: null,
|
||||
showMnemonic: false,
|
||||
authenticated: false,
|
||||
showPasswordDialog: false,
|
||||
showWipeDialog: false,
|
||||
showRestoreDialog: false,
|
||||
showConsole: false,
|
||||
showSignedPsbt: false,
|
||||
sendingPsbt: false,
|
||||
signingPsbt: false,
|
||||
psbtSent: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openSerialPort: async function () {
|
||||
if (!this.checkSerialPortSupported()) return
|
||||
console.log('### openSerialPort', this.selectedPort)
|
||||
try {
|
||||
navigator.serial.addEventListener('connect', event => {
|
||||
console.log('### navigator.serial event: connected!', event)
|
||||
})
|
||||
|
||||
navigator.serial.addEventListener('disconnect', () => {
|
||||
this.hww.authenticated = false
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Disconnected from Serial Port!',
|
||||
timeout: 10000
|
||||
})
|
||||
})
|
||||
this.selectedPort = await navigator.serial.requestPort()
|
||||
// Wait for the serial port to open.
|
||||
await this.selectedPort.open({baudRate: 9600})
|
||||
this.startSerialPortReading()
|
||||
|
||||
const textEncoder = new TextEncoderStream()
|
||||
this.writableStreamClosed = textEncoder.readable.pipeTo(
|
||||
this.selectedPort.writable
|
||||
)
|
||||
|
||||
this.writer = textEncoder.writable.getWriter()
|
||||
} catch (error) {
|
||||
this.selectedPort = null
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Cannot open serial port!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
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')
|
||||
console.log('### value', value)
|
||||
if (value) {
|
||||
this.handleSerialPortResponse(value)
|
||||
this.updateSerialPortConsole(value)
|
||||
}
|
||||
console.log('### startSerialPortReading DONE', done)
|
||||
if (done) return
|
||||
}
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Serial port communication error!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log('### startSerialPortReading port', port)
|
||||
},
|
||||
handleSerialPortResponse: function (value) {
|
||||
const msg = value.split(' ')
|
||||
if (msg[0] == COMMAND_SIGN_PSBT) this.handleSignResponse(msg[1])
|
||||
else if (msg[0] == COMMAND_PASSWORD) this.handleLoginResponse(msg[1])
|
||||
else if (msg[0] == COMMAND_PASSWORD_CLEAR)
|
||||
this.handleLogoutResponse(msg[1])
|
||||
else if (msg[0] == COMMAND_SEND_PSBT)
|
||||
this.handleSendPsbtResponse(msg[1])
|
||||
else if (msg[0] == COMMAND_WIPE) this.handleWipeResponse(msg[1])
|
||||
else console.log('### handleSerialPortResponse', value)
|
||||
},
|
||||
updateSerialPortConsole: function (value) {
|
||||
this.receivedData += value + '\n'
|
||||
const textArea = document.getElementById(
|
||||
'watchonly-serial-port-data-input'
|
||||
)
|
||||
if (textArea) textArea.scrollTop = textArea.scrollHeight
|
||||
},
|
||||
hwwShowPasswordDialog: async function () {
|
||||
try {
|
||||
this.hww.showPasswordDialog = true
|
||||
await this.writer.write(COMMAND_PASSWORD + '\n')
|
||||
} catch (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.writer.write(COMMAND_WIPE + '\n')
|
||||
} 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.writer.write(COMMAND_WIPE + '\n')
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to connect to Hardware Wallet!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwLogin: async function () {
|
||||
try {
|
||||
await this.writer.write(
|
||||
COMMAND_PASSWORD + ' ' + this.hww.password + '\n'
|
||||
)
|
||||
} 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.showPassword = false
|
||||
}
|
||||
},
|
||||
handleLoginResponse: function (res = '') {
|
||||
this.hww.authenticated = res.trim() === '1'
|
||||
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.writer.write(COMMAND_PASSWORD_CLEAR + '\n')
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to logout from Hardware Wallet!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
handleLogoutResponse: function (res = '') {
|
||||
this.hww.authenticated = !(res.trim() === '1')
|
||||
if (this.hww.authenticated) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to logout from Hardware Wallet',
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwExecuteDefaultCommand: function () {
|
||||
if (this.hww.authenticated) {
|
||||
this.hwwSendPsbt()
|
||||
} else {
|
||||
this.hwwShowPasswordDialog()
|
||||
}
|
||||
},
|
||||
hwwSendPsbt: async function () {
|
||||
try {
|
||||
this.hww.sendingPsbt = true
|
||||
await this.writer.write(
|
||||
COMMAND_SEND_PSBT + ' ' + this.payment.psbtBase64 + '\n'
|
||||
)
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Data sent to serial port device!',
|
||||
timeout: 5000
|
||||
})
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to send data to serial port!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
handleSendPsbtResponse: function (res = '') {
|
||||
this.hww.psbtSent = true
|
||||
this.hww.sendingPsbt = false
|
||||
},
|
||||
hwwSignPsbt: async function () {
|
||||
try {
|
||||
this.hww.signingPsbt = true
|
||||
await this.writer.write(COMMAND_SIGN_PSBT + '\n')
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'PSBT signed!',
|
||||
timeout: 5000
|
||||
})
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to sign PSBT!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
handleSignResponse: function (res = '') {
|
||||
this.hww.signingPsbt = false
|
||||
this.updateSignedPsbt(res)
|
||||
if (this.hww.authenticated) {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Transaction Signed',
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwHelp: async function () {
|
||||
try {
|
||||
await this.writer.write(COMMAND_HELP + '\n')
|
||||
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.writer.write(COMMAND_WIPE + ' ' + this.hww.password + '\n')
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to ask for help!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
} finally {
|
||||
this.hww.password = null
|
||||
this.hww.showPassword = false
|
||||
}
|
||||
},
|
||||
handleWipeResponse: function (res = '') {
|
||||
const wiped = res.trim() === '1'
|
||||
console.log('### wiped', wiped)
|
||||
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
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwShowSeed: async function () {
|
||||
try {
|
||||
await this.writer.write(COMMAND_SEED + '\n')
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to show seed!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwRestore: async function () {
|
||||
try {
|
||||
await this.writer.write(
|
||||
COMMAND_RESTORE + ' ' + this.hww.mnemonic + '\n'
|
||||
)
|
||||
await this.writer.write(
|
||||
COMMAND_PASSWORD + ' ' + this.hww.password + '\n'
|
||||
)
|
||||
} 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.showPassword = false
|
||||
}
|
||||
}
|
||||
},
|
||||
created: async function () {}
|
||||
})
|
||||
}
|
@ -1,12 +1,7 @@
|
||||
<div>
|
||||
<q-card>
|
||||
<div class="row items-center no-wrap q-mb-md">
|
||||
<div class="col-11">
|
||||
<div class="row justify-center q-gutter-x-md items-center">
|
||||
<div class="text-h3 q-pa-sm">{{satBtc(total)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<div class="col-2 q-ml-lg">
|
||||
<q-btn
|
||||
unelevated
|
||||
@click="config.show = true"
|
||||
@ -15,6 +10,14 @@
|
||||
>
|
||||
</q-btn>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="row justify-center q-gutter-x-md items-center">
|
||||
<div class="text-h3">{{satBtc(total)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-2 float-right">
|
||||
<serial-signer></serial-signer>
|
||||
</div>
|
||||
</div>
|
||||
</q-card>
|
||||
|
||||
|
@ -9,9 +9,7 @@ const watchOnly = async () => {
|
||||
await feeRate('static/components/fee-rate/fee-rate.html')
|
||||
await sendTo('static/components/send-to/send-to.html')
|
||||
await payment('static/components/payment/payment.html')
|
||||
|
||||
//emplate static/components/payment/payment.html
|
||||
//lnbits/extensions/watchonly/static/components/payment/payment.html
|
||||
await serialSigner('static/components/serial-signer/serial-signer.html')
|
||||
|
||||
Vue.filter('reverse', function (value) {
|
||||
// slice to make a copy of array, then reverse the copy
|
||||
@ -274,56 +272,7 @@ const watchOnly = async () => {
|
||||
}
|
||||
},
|
||||
//################### SERIAL PORT ###################
|
||||
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
|
||||
},
|
||||
openSerialPort: async function () {
|
||||
if (!this.checkSerialPortSupported()) return
|
||||
console.log('### openSerialPort', this.serial.selectedPort)
|
||||
try {
|
||||
navigator.serial.addEventListener('connect', event => {
|
||||
console.log('### navigator.serial event: connected!', event)
|
||||
})
|
||||
|
||||
navigator.serial.addEventListener('disconnect', () => {
|
||||
this.hww.authenticated = false
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Disconnected from Serial Port!',
|
||||
timeout: 10000
|
||||
})
|
||||
})
|
||||
this.serial.selectedPort = await navigator.serial.requestPort()
|
||||
// Wait for the serial port to open.
|
||||
await this.serial.selectedPort.open({baudRate: 9600})
|
||||
this.startSerialPortReading()
|
||||
|
||||
const textEncoder = new TextEncoderStream()
|
||||
this.serial.writableStreamClosed = textEncoder.readable.pipeTo(
|
||||
this.serial.selectedPort.writable
|
||||
)
|
||||
|
||||
this.serial.writer = textEncoder.writable.getWriter()
|
||||
} catch (error) {
|
||||
this.serial.selectedPort = null
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Cannot open serial port!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
closeSerialPort: async function () {
|
||||
try {
|
||||
console.log('### closeSerialPort', this.serial.selectedPort)
|
||||
@ -354,305 +303,11 @@ const watchOnly = async () => {
|
||||
}
|
||||
},
|
||||
|
||||
startSerialPortReading: async function () {
|
||||
const port = this.serial.selectedPort
|
||||
|
||||
while (port && port.readable) {
|
||||
const textDecoder = new TextDecoderStream()
|
||||
this.serial.readableStreamClosed = port.readable.pipeTo(
|
||||
textDecoder.writable
|
||||
)
|
||||
this.serial.reader = textDecoder.readable.getReader()
|
||||
const readStringUntil = readFromSerialPort(this.serial)
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const {value, done} = await readStringUntil('\n')
|
||||
console.log('### value', value)
|
||||
if (value) {
|
||||
this.handleSerialPortResponse(value)
|
||||
this.updateSerialPortConsole(value)
|
||||
}
|
||||
console.log('### startSerialPortReading DONE', done)
|
||||
if (done) return
|
||||
}
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Serial port communication error!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log('### startSerialPortReading port', port)
|
||||
},
|
||||
|
||||
handleSerialPortResponse: function (value) {
|
||||
const msg = value.split(' ')
|
||||
if (msg[0] == COMMAND_SIGN_PSBT) this.handleSignResponse(msg[1])
|
||||
else if (msg[0] == COMMAND_PASSWORD) this.handleLoginResponse(msg[1])
|
||||
else if (msg[0] == COMMAND_PASSWORD_CLEAR)
|
||||
this.handleLogoutResponse(msg[1])
|
||||
else if (msg[0] == COMMAND_SEND_PSBT)
|
||||
this.handleSendPsbtResponse(msg[1])
|
||||
else if (msg[0] == COMMAND_WIPE) this.handleWipeResponse(msg[1])
|
||||
else console.log('### handleSerialPortResponse', value)
|
||||
},
|
||||
updateSerialPortConsole: function (value) {
|
||||
this.serial.receivedData += value + '\n'
|
||||
const textArea = document.getElementById(
|
||||
'watchonly-serial-port-data-input'
|
||||
)
|
||||
if (textArea) textArea.scrollTop = textArea.scrollHeight
|
||||
},
|
||||
sharePsbtWithAnimatedQRCode: async function () {
|
||||
console.log('### sharePsbtWithAnimatedQRCode')
|
||||
},
|
||||
//################### HARDWARE WALLET ###################
|
||||
hwwShowPasswordDialog: async function () {
|
||||
try {
|
||||
this.hww.showPasswordDialog = true
|
||||
await this.serial.writer.write(COMMAND_PASSWORD + '\n')
|
||||
} catch (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.serial.writer.write(COMMAND_WIPE + '\n')
|
||||
} 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.serial.writer.write(COMMAND_WIPE + '\n')
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to connect to Hardware Wallet!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwLogin: async function () {
|
||||
try {
|
||||
await this.serial.writer.write(
|
||||
COMMAND_PASSWORD + ' ' + this.hww.password + '\n'
|
||||
)
|
||||
} 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.showPassword = false
|
||||
}
|
||||
},
|
||||
handleLoginResponse: function (res = '') {
|
||||
this.hww.authenticated = res.trim() === '1'
|
||||
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.serial.writer.write(COMMAND_PASSWORD_CLEAR + '\n')
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to logout from Hardware Wallet!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
handleLogoutResponse: function (res = '') {
|
||||
this.hww.authenticated = !(res.trim() === '1')
|
||||
if (this.hww.authenticated) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to logout from Hardware Wallet',
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwExecuteDefaultCommand: function () {
|
||||
if (this.hww.authenticated) {
|
||||
this.hwwSendPsbt()
|
||||
} else {
|
||||
this.hwwShowPasswordDialog()
|
||||
}
|
||||
},
|
||||
hwwSendPsbt: async function () {
|
||||
try {
|
||||
this.hww.sendingPsbt = true
|
||||
await this.serial.writer.write(
|
||||
COMMAND_SEND_PSBT + ' ' + this.payment.psbtBase64 + '\n'
|
||||
)
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Data sent to serial port device!',
|
||||
timeout: 5000
|
||||
})
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to send data to serial port!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
handleSendPsbtResponse: function (res = '') {
|
||||
this.hww.psbtSent = true
|
||||
this.hww.sendingPsbt = false
|
||||
},
|
||||
hwwSignPsbt: async function () {
|
||||
try {
|
||||
this.hww.signingPsbt = true
|
||||
await this.serial.writer.write(COMMAND_SIGN_PSBT + '\n')
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'PSBT signed!',
|
||||
timeout: 5000
|
||||
})
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to sign PSBT!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
handleSignResponse: function (res = '') {
|
||||
this.hww.signingPsbt = false
|
||||
this.updateSignedPsbt(res)
|
||||
if (this.hww.authenticated) {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Transaction Signed',
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwHelp: async function () {
|
||||
try {
|
||||
await this.serial.writer.write(COMMAND_HELP + '\n')
|
||||
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.serial.writer.write(
|
||||
COMMAND_WIPE + ' ' + this.hww.password + '\n'
|
||||
)
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to ask for help!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
} finally {
|
||||
this.hww.password = null
|
||||
this.hww.showPassword = false
|
||||
}
|
||||
},
|
||||
handleWipeResponse: function (res = '') {
|
||||
const wiped = res.trim() === '1'
|
||||
console.log('### wiped', wiped)
|
||||
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
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwShowSeed: async function () {
|
||||
try {
|
||||
await this.serial.writer.write(COMMAND_SEED + '\n')
|
||||
} catch (error) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Failed to show seed!',
|
||||
caption: `${error}`,
|
||||
timeout: 10000
|
||||
})
|
||||
}
|
||||
},
|
||||
hwwRestore: async function () {
|
||||
try {
|
||||
await this.serial.writer.write(
|
||||
COMMAND_RESTORE + ' ' + this.hww.mnemonic + '\n'
|
||||
)
|
||||
await this.serial.writer.write(
|
||||
COMMAND_PASSWORD + ' ' + this.hww.password + '\n'
|
||||
)
|
||||
} 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.showPassword = false
|
||||
}
|
||||
},
|
||||
|
||||
//################### UTXOs ###################
|
||||
scanAllAddresses: async function () {
|
||||
await this.$refs.addressList.refreshAddresses()
|
||||
|
@ -110,7 +110,7 @@ const ACCOUNT_TYPES = {
|
||||
|
||||
const getAccountDescription = type => ACCOUNT_TYPES[type] || 'nonstandard'
|
||||
|
||||
const readFromSerialPort = serial => {
|
||||
const readFromSerialPort = reader => {
|
||||
let partialChunk
|
||||
let fulliness = []
|
||||
|
||||
@ -123,7 +123,7 @@ const readFromSerialPort = serial => {
|
||||
partialChunk = undefined
|
||||
}
|
||||
while (true) {
|
||||
const {value, done} = await serial.reader.read()
|
||||
const {value, done} = await reader.read()
|
||||
console.log('### serial read', value)
|
||||
if (value) {
|
||||
const values = value.split(separator)
|
||||
|
@ -230,96 +230,6 @@
|
||||
:label="hww.authenticated ? 'Sign Tx' : 'Login'"
|
||||
@click="hwwExecuteDefaultCommand()"
|
||||
>
|
||||
<q-list>
|
||||
<q-item
|
||||
v-if="!hww.authenticated"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="hwwShowPasswordDialog()"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Login</q-item-label>
|
||||
<q-item-label caption
|
||||
>Enter password for Hardware Wallet.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item
|
||||
v-if="hww.authenticated"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="hwwLogout()"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Logout</q-item-label>
|
||||
<q-item-label caption
|
||||
>Clear password for HWW.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
:disabled="!hww.authenticated"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="hwwSendPsbt()"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Sign</q-item-label>
|
||||
<q-item-label caption
|
||||
>Sign transaction on Hardware Wallet</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="hwwShowRestoreDialog()"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Restore</q-item-label>
|
||||
<q-item-label caption
|
||||
>Restore wallet from existing word
|
||||
list.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
:disabled="!hww.authenticated"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="hwwShowSeed()"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Show Seed</q-item-label>
|
||||
<q-item-label caption
|
||||
>Show seed on the Hardware Wallet
|
||||
display.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
@click="hwwShowWipeDialog()"
|
||||
clickable
|
||||
v-close-popup
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>Wipe</q-item-label>
|
||||
<q-item-label caption
|
||||
>Clean-up the wallet. New random
|
||||
seed.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item @click="hwwHelp()" clickable v-close-popup>
|
||||
<q-item-section>
|
||||
<q-item-label>Help</q-item-label>
|
||||
<q-item-label caption
|
||||
>View available comands.</q-item-label
|
||||
>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
@ -577,139 +487,6 @@
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog v-model="hww.showPasswordDialog" position="top">
|
||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<q-form @submit="hwwLogin" class="q-gutter-md">
|
||||
<span>Enter password for Hardware Wallet (8 numbers/letters)</span>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="hww.password"
|
||||
type="password"
|
||||
label="Password"
|
||||
></q-input>
|
||||
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
:disable="!serial.selectedPort"
|
||||
type="submit"
|
||||
>Login</q-btn
|
||||
>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||
>Cancel</q-btn
|
||||
>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
<q-dialog v-model="hww.showWipeDialog" position="top">
|
||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<q-form @submit="hwwWipe" class="q-gutter-md">
|
||||
<q-badge color="pink" text-color="black">
|
||||
This action will remove all data from the Hardware Wallet. Please
|
||||
create a back-up for the seed!
|
||||
</q-badge>
|
||||
<span>Enter new password for Hardware Wallet (8 numbers/letters)</span>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="hww.password"
|
||||
type="password"
|
||||
label="Password"
|
||||
></q-input>
|
||||
<q-badge color="pink" text-color="black">
|
||||
This action cannot be reversed!
|
||||
</q-badge>
|
||||
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
:disable="!serial.selectedPort"
|
||||
type="submit"
|
||||
>Wipe</q-btn
|
||||
>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||
>Cancel</q-btn
|
||||
>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
<q-dialog v-model="hww.showRestoreDialog" position="top">
|
||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<q-form @submit="hwwRestore" class="q-gutter-md">
|
||||
<q-badge
|
||||
color="pink"
|
||||
text-color="black"
|
||||
class="text-subtitle2"
|
||||
multi-line
|
||||
>
|
||||
For test purposes only. Do not enter word list with real funds!!!
|
||||
</q-badge>
|
||||
<br /><br /><br />
|
||||
<span>Enter new word list separated by space</span>
|
||||
<q-input
|
||||
v-model.trim="hww.mnemonic"
|
||||
filled
|
||||
:type="hww.showMnemonic ? 'text' : 'password'"
|
||||
filled
|
||||
dense
|
||||
hint="Word List"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
:name="hww.showMnemonic ? 'visibility' : 'visibility_off'"
|
||||
class="cursor-pointer"
|
||||
@click="hww.showMnemonic = !hww.showMnemonic"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
<br />
|
||||
<span>Enter new password (8 numbers/letters)</span>
|
||||
<q-input
|
||||
v-model.trim="hww.password"
|
||||
filled
|
||||
:type="hww.showPassword ? 'text' : 'password'"
|
||||
filled
|
||||
dense
|
||||
hint="New Password"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
:name="hww.showPassword ? 'visibility' : 'visibility_off'"
|
||||
class="cursor-pointer"
|
||||
@click="hww.showPassword = !hww.showPassword"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
<br /><br />
|
||||
<q-badge
|
||||
color="pink"
|
||||
text-color="black"
|
||||
class="text-subtitle2"
|
||||
multi-line
|
||||
>
|
||||
For test purposes only. Do not enter word list with real funds.
|
||||
</q-badge>
|
||||
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
:disable="!serial.selectedPort"
|
||||
type="submit"
|
||||
>Restore</q-btn
|
||||
>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||
>Cancel</q-btn
|
||||
>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
{% endraw %}
|
||||
</div>
|
||||
|
||||
@ -733,5 +510,6 @@
|
||||
<script src="{{ url_for('watchonly_static', path='components/fee-rate/fee-rate.js') }}"></script>
|
||||
<script src="{{ url_for('watchonly_static', path='components/send-to/send-to.js') }}"></script>
|
||||
<script src="{{ url_for('watchonly_static', path='components/payment/payment.js') }}"></script>
|
||||
<script src="{{ url_for('watchonly_static', path='components/serial-signer/serial-signer.js') }}"></script>
|
||||
<script src="{{ url_for('watchonly_static', path='js/index.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
Loading…
Reference in New Issue
Block a user