Merge pull request #1010 from motorina0/fix_usb_coonect

Fix USB connect
This commit is contained in:
Arc 2022-09-28 16:24:53 +01:00 committed by GitHub
commit 8fbaee37fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 70 additions and 18 deletions

View File

@ -80,6 +80,7 @@ class CreatePsbt(BaseModel):
class ExtractPsbt(BaseModel): class ExtractPsbt(BaseModel):
psbtBase64 = "" # // todo snake case psbtBase64 = "" # // todo snake case
inputs: List[TransactionInput] inputs: List[TransactionInput]
network = "Mainnet"
class SignedTransaction(BaseModel): class SignedTransaction(BaseModel):

View File

@ -5,7 +5,7 @@
<send-to <send-to
:data.sync="sendToList" :data.sync="sendToList"
:fee-rate="feeRate" :fee-rate="feeRate"
:tx-size="txSizeNoChange" :tx-size="txSize"
:selected-amount="selectedAmount" :selected-amount="selectedAmount"
:sats-denominated="satsDenominated" :sats-denominated="satsDenominated"
@update:outputs="handleOutputsChange" @update:outputs="handleOutputsChange"

View File

@ -11,7 +11,8 @@ async function payment(path) {
'mempool-endpoint', 'mempool-endpoint',
'sats-denominated', 'sats-denominated',
'serial-signer-ref', 'serial-signer-ref',
'adminkey' 'adminkey',
'network'
], ],
watch: { watch: {
immediate: true, immediate: true,
@ -279,7 +280,8 @@ async function payment(path) {
this.adminkey, this.adminkey,
{ {
psbtBase64, psbtBase64,
inputs: this.tx.inputs inputs: this.tx.inputs,
network: this.network
} }
) )
return data return data

View File

@ -49,7 +49,7 @@
<q-item <q-item
v-for="device in pairedDevices" v-for="device in pairedDevices"
:key="device.id" :key="device.id"
v-if="!selectedPort" v-if="!selectedPort && showPairedDevices"
clickable clickable
v-close-popup v-close-popup
> >

View File

@ -15,7 +15,7 @@ async function serialSigner(path) {
receivedData: '', receivedData: '',
config: {}, config: {},
decryptionKey: null, decryptionKey: null,
sharedSecret: null, // todo: store in secure local storage sharedSecret: null,
hww: { hww: {
password: null, password: null,
@ -51,12 +51,14 @@ async function serialSigner(path) {
}, },
tx: null, // todo: move to hww tx: null, // todo: move to hww
showConsole: false showConsole: false,
showPairedDevices: true
} }
}, },
computed: { computed: {
pairedDevices: { pairedDevices: {
cache: false,
get: function () { get: function () {
return ( return (
JSON.parse(window.localStorage.getItem('lnbits-paired-devices')) || JSON.parse(window.localStorage.getItem('lnbits-paired-devices')) ||
@ -109,7 +111,10 @@ async function serialSigner(path) {
// Wait for the serial port to open. // Wait for the serial port to open.
await this.selectedPort.open(config) await this.selectedPort.open(config)
// do not await
this.startSerialPortReading() this.startSerialPortReading()
// wait to init
sleep(1000)
const textEncoder = new TextEncoderStream() const textEncoder = new TextEncoderStream()
this.writableStreamClosed = textEncoder.readable.pipeTo( this.writableStreamClosed = textEncoder.readable.pipeTo(
@ -225,8 +230,9 @@ async function serialSigner(path) {
while (true) { while (true) {
const {value, done} = await readStringUntil('\n') const {value, done} = await readStringUntil('\n')
if (value) { if (value) {
this.handleSerialPortResponse(value) const {command, commandData} = await this.extractCommand(value)
this.updateSerialPortConsole(value) this.handleSerialPortResponse(command, commandData)
this.updateSerialPortConsole(command)
} }
if (done) return if (done) return
} }
@ -240,8 +246,7 @@ async function serialSigner(path) {
} }
} }
}, },
handleSerialPortResponse: async function (value) { handleSerialPortResponse: async function (command, commandData) {
const {command, commandData} = await this.extractCommand(value)
this.logPublicCommandsResponse(command, commandData) this.logPublicCommandsResponse(command, commandData)
switch (command) { switch (command) {
@ -282,7 +287,7 @@ async function serialSigner(path) {
) )
break break
default: default:
console.log(` %c${value}`, 'background: #222; color: red') console.log(` %c${command}`, 'background: #222; color: red')
} }
}, },
logPublicCommandsResponse: function (command, commandData) { logPublicCommandsResponse: function (command, commandData) {
@ -307,6 +312,8 @@ async function serialSigner(path) {
}, },
hwwPing: async function () { hwwPing: async function () {
try { 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]) await this.sendCommandClearText(COMMAND_PING, [window.location.host])
} catch (error) { } catch (error) {
this.$q.notify({ this.$q.notify({
@ -582,7 +589,7 @@ async function serialSigner(path) {
hwwCheckPairing: async function () { hwwCheckPairing: async function () {
const iv = window.crypto.getRandomValues(new Uint8Array(16)) const iv = window.crypto.getRandomValues(new Uint8Array(16))
const encrypted = await this.encryptMessage( const encrypted = await this.encryptMessage(
this.sharedSecret, this.sharedSecret, // todo: revisit
iv, iv,
PAIRING_CONTROL_TEXT.length + ' ' + PAIRING_CONTROL_TEXT PAIRING_CONTROL_TEXT.length + ' ' + PAIRING_CONTROL_TEXT
) )
@ -603,10 +610,10 @@ async function serialSigner(path) {
} }
}, },
handleCheckPairingResponse: async function (res = '') { handleCheckPairingResponse: async function (res = '') {
const [statusCode, encryptedMessage] = res.split(' ') const [statusCode, message] = res.split(' ')
switch (statusCode) { switch (statusCode) {
case '0': case '0':
const controlText = await this.decryptData(encryptedMessage) const controlText = await this.decryptData(message)
if (controlText == PAIRING_CONTROL_TEXT) { if (controlText == PAIRING_CONTROL_TEXT) {
this.$q.notify({ this.$q.notify({
type: 'positive', type: 'positive',
@ -622,6 +629,16 @@ async function serialSigner(path) {
}) })
} }
break 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: default:
// noting to do here yet // noting to do here yet
break break
@ -746,7 +763,7 @@ async function serialSigner(path) {
} catch (error) { } catch (error) {
this.$q.notify({ this.$q.notify({
type: 'warning', type: 'warning',
message: 'Failed to ask for help!', message: 'Failed to wipe!',
caption: `${error}`, caption: `${error}`,
timeout: 10000 timeout: 10000
}) })
@ -862,6 +879,11 @@ async function serialSigner(path) {
sendCommandSecure: async function (command, attrs = []) { sendCommandSecure: async function (command, attrs = []) {
const message = [command].concat(attrs).join(' ') const message = [command].concat(attrs).join(' ')
const iv = window.crypto.getRandomValues(new Uint8Array(16)) 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( const encrypted = await this.encryptMessage(
this.sharedSecret, this.sharedSecret,
iv, iv,
@ -901,6 +923,7 @@ async function serialSigner(path) {
}, },
decryptData: async function (value) { decryptData: async function (value) {
if (!this.sharedSecret) { if (!this.sharedSecret) {
console.log('/error Secure session not established!')
return '/error Secure session not established!' return '/error Secure session not established!'
} }
try { try {
@ -921,6 +944,7 @@ async function serialSigner(path) {
.trim() .trim()
return command return command
} catch (error) { } catch (error) {
console.log('/error Failed to decrypt message from device!')
return '/error Failed to decrypt message from device!' return '/error Failed to decrypt message from device!'
} }
}, },
@ -949,6 +973,11 @@ async function serialSigner(path) {
devices.splice(deviceIndex, 1) devices.splice(deviceIndex, 1)
} }
this.pairedDevices = devices this.pairedDevices = devices
this.showPairedDevices = false
setTimeout(() => {
// force UI refresh
this.showPairedDevices = true
})
}, },
addPairedDevice: function (deviceId, sharedSecretHex, config) { addPairedDevice: function (deviceId, sharedSecretHex, config) {
const devices = this.pairedDevices const devices = this.pairedDevices
@ -960,6 +989,11 @@ async function serialSigner(path) {
config config
}) })
this.pairedDevices = devices this.pairedDevices = devices
this.showPairedDevices = false
setTimeout(() => {
// force UI refresh
this.showPairedDevices = true
})
}, },
updatePairedDeviceConfig(deviceId, config) { updatePairedDeviceConfig(deviceId, config) {
const device = this.getPairedDevice(deviceId) const device = this.getPairedDevice(deviceId)

View File

@ -18,9 +18,21 @@
>directly from browser</a >directly from browser</a
> >
<small> <small>
<br />Created by, <br />Created by
<a target="_blank" style="color: unset" href="https://github.com/arcbtc" <a target="_blank" style="color: unset" href="https://github.com/arcbtc"
>Ben Arc</a >Ben Arc</a
>,
<a
target="_blank"
style="color: unset"
href="https://github.com/talvasconcelos"
>Tiago Vasconcelos</a
>,
<a
target="_blank"
style="color: unset"
href="https://github.com/motorina0"
>motorina0</a
> >
(using, (using,
<a <a

View File

@ -136,6 +136,7 @@
:adminkey="g.user.wallets[0].adminkey" :adminkey="g.user.wallets[0].adminkey"
:serial-signer-ref="$refs.serialSigner" :serial-signer-ref="$refs.serialSigner"
:sats-denominated="config.sats_denominated" :sats-denominated="config.sats_denominated"
:network="config.network"
@broadcast-done="handleBroadcastSuccess" @broadcast-done="handleBroadcastSuccess"
></payment> ></payment>
<!-- todo: no more utxos.data --> <!-- todo: no more utxos.data -->
@ -149,7 +150,7 @@
<q-card-section> <q-card-section>
<h6 class="text-subtitle1 q-my-none"> <h6 class="text-subtitle1 q-my-none">
{{SITE_TITLE}} Onchain Wallet (watch-only) Extension {{SITE_TITLE}} Onchain Wallet (watch-only) Extension
<small>(v0.2)</small> <small>(v0.3)</small>
</h6> </h6>
</q-card-section> </q-card-section>
<q-card-section class="q-pa-none"> <q-card-section class="q-pa-none">

View File

@ -4,6 +4,7 @@ from http import HTTPStatus
import httpx import httpx
from embit import finalizer, script from embit import finalizer, script
from embit.ec import PublicKey from embit.ec import PublicKey
from embit.networks import NETWORKS
from embit.psbt import PSBT, DerivationPath from embit.psbt import PSBT, DerivationPath
from embit.transaction import Transaction, TransactionInput, TransactionOutput from embit.transaction import Transaction, TransactionInput, TransactionOutput
from fastapi import Query, Request from fastapi import Query, Request
@ -295,6 +296,7 @@ async def api_psbt_create(
async def api_psbt_extract_tx( async def api_psbt_extract_tx(
data: ExtractPsbt, w: WalletTypeInfo = Depends(require_admin_key) data: ExtractPsbt, w: WalletTypeInfo = Depends(require_admin_key)
): ):
network = NETWORKS["main"] if data.network == "Mainnet" else NETWORKS["test"]
res = SignedTransaction() res = SignedTransaction()
try: try:
psbt = PSBT.from_base64(data.psbtBase64) psbt = PSBT.from_base64(data.psbtBase64)
@ -316,7 +318,7 @@ async def api_psbt_extract_tx(
for out in transaction.vout: for out in transaction.vout:
tx["outputs"].append( tx["outputs"].append(
{"amount": out.value, "address": out.script_pubkey.address()} {"amount": out.value, "address": out.script_pubkey.address(network)}
) )
res.tx_json = json.dumps(tx) res.tx_json = json.dumps(tx)
except Exception as e: except Exception as e: