lnbits-legend/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js

423 lines
13 KiB
JavaScript
Raw Normal View History

2022-07-26 20:06:07 +03:00
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,
receivedData: '',
config: {},
2022-07-27 12:26:07 +03:00
2022-07-26 20:06:07 +03:00
hww: {
password: null,
showPassword: false,
mnemonic: null,
showMnemonic: false,
authenticated: false,
showPasswordDialog: false,
showWipeDialog: false,
showRestoreDialog: false,
2022-07-27 11:32:03 +03:00
showConfirmationDialog: false,
2022-07-26 20:06:07 +03:00
showSignedPsbt: false,
sendingPsbt: false,
signingPsbt: false,
2022-07-27 11:32:03 +03:00
psbtSent: false,
psbtSentResolve: null
2022-07-27 12:26:07 +03:00
},
showConsole: false
2022-07-26 20:06:07 +03:00
}
},
methods: {
openSerialPort: async function () {
if (!this.checkSerialPortSupported()) return
if (this.selectedPort) return
2022-07-26 20:06:07 +03:00
console.log('### openSerialPort', this.selectedPort)
try {
navigator.serial.addEventListener('connect', event => {
console.log('### navigator.serial event: connected!', event)
})
navigator.serial.addEventListener('disconnect', () => {
console.log('### navigator.serial event: disconnected!', event)
2022-07-26 20:06:07 +03:00
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
})
}
},
2022-07-27 09:16:36 +03:00
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.selectedPort = null
this.$q.notify({
type: 'positive',
message: 'Serial port disconnected!',
timeout: 5000
})
} catch (error) {
this.selectedPort = null
console.log('### error', error)
this.$q.notify({
type: 'warning',
message: 'Cannot close serial port!',
caption: `${error}`,
timeout: 10000
})
2022-07-27 09:27:51 +03:00
} finally {
this.hww.authenticated = false
2022-07-27 09:16:36 +03:00
}
},
isConnected: function () {
return !!this.selectedPort
},
isAuthenticated: function () {
return this.hww.authenticated
},
2022-07-27 11:32:03 +03:00
isSendingPsbt: async function () {
if (!this.hww.sendingPsbt) return false
return new Promise(resolve => {
this.psbtSentResolve = resolve
})
},
2022-07-26 20:06:07 +03:00
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'
2022-07-27 12:26:07 +03:00
const textArea = document.getElementById('serial-port-console')
2022-07-26 20:06:07 +03:00
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
})
}
},
2022-07-27 10:10:09 +03:00
hwwSendPsbt: async function (psbtBase64) {
2022-07-26 20:06:07 +03:00
try {
this.hww.sendingPsbt = true
2022-07-27 10:10:09 +03:00
await this.writer.write(COMMAND_SEND_PSBT + ' ' + psbtBase64 + '\n')
2022-07-26 20:06:07 +03:00
this.$q.notify({
type: 'positive',
message: 'Data sent to serial port device!',
timeout: 5000
})
} catch (error) {
2022-07-27 11:32:03 +03:00
this.hww.sendingPsbt = false
2022-07-26 20:06:07 +03:00
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
2022-07-27 11:32:03 +03:00
this.psbtSentResolve()
2022-07-26 20:06:07 +03:00
},
hwwSignPsbt: async function () {
try {
2022-07-27 11:32:03 +03:00
this.hww.psbtSent = false
2022-07-26 20:06:07 +03:00
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
}
2022-07-27 11:32:03 +03:00
},
updateSignedPsbt: async function (value) {
this.$emit('signed:psbt', value)
2022-07-26 20:06:07 +03:00
}
},
created: async function () {}
})
}