feat: extract signed transaction

This commit is contained in:
Vlad Stan 2022-07-27 11:32:03 +03:00
parent d5cc8d9187
commit 32486d62bf
7 changed files with 133 additions and 72 deletions

View file

@ -186,8 +186,13 @@
</q-list> </q-list>
</q-btn-dropdown> </q-btn-dropdown>
</div> </div>
<div class="col-9"> <div class="col-9">
<q-spinner v-if="showChecking" color="primary"></q-spinner> <q-spinner
v-if="showChecking"
size="2.55em"
color="primary"
></q-spinner>
<q-badge <q-badge
v-if="changeAmount < 0" v-if="changeAmount < 0"
class="text-subtitle2 float-right" class="text-subtitle2 float-right"

View file

@ -92,6 +92,7 @@ async function payment(path) {
if (this.psbtBase64) { if (this.psbtBase64) {
await this.serialSignerRef.hwwSendPsbt(this.psbtBase64) await this.serialSignerRef.hwwSendPsbt(this.psbtBase64)
await this.serialSignerRef.isSendingPsbt()
} }
console.log('### hwwSendPsbt') console.log('### hwwSendPsbt')
@ -189,6 +190,77 @@ async function payment(path) {
} }
this.selectChangeAddress(this.changeWallet) this.selectChangeAddress(this.changeWallet)
}, },
updateSignedPsbt: async function (psbtBase64) {
console.log('### payment updateSignedPsbt psbtBase64', psbtBase64)
const data = await this.extractTxFromPsbt(psbtBase64)
if (data) {
this.signedTx = JSON.parse(data.tx_json)
this.signedTxHex = data.tx_hex
} else {
this.signedTx = null
this.signedTxHex = null
}
},
extractTxFromPsbt: async function (psbtBase64) {
console.log('### extractTxFromPsbt psbtBase64', psbtBase64)
try {
const {data} = await LNbits.api.request(
'PUT',
'/watchonly/api/v1/psbt/extract',
this.adminkey,
{
psbtBase64,
inputs: this.tx.inputs
}
)
console.log('### extractTxFromPsbt data', data)
return data
} catch (error) {
console.log('### error', error)
this.$q.notify({
type: 'warning',
message: 'Cannot finalize PSBT!',
timeout: 10000
})
LNbits.utils.notifyApiError(error)
}
},
broadcastTransaction: async function () {
try {
const wallet = this.g.user.wallets[0]
const {data} = await LNbits.api.request(
'POST',
'/watchonly/api/v1/tx',
wallet.adminkey,
{tx_hex: this.payment.signedTxHex}
)
this.payment.sentTxId = data
this.$q.notify({
type: 'positive',
message: 'Transaction broadcasted!',
caption: `${data}`,
timeout: 10000
})
this.hww.psbtSent = false
this.payment.psbtBase64Signed = null
this.payment.signedTxHex = null
this.payment.signedTx = null
this.payment.psbtBase64 = null
await this.scanAddressWithAmount()
} catch (error) {
this.payment.sentTxId = null
this.$q.notify({
type: 'warning',
message: 'Failed to broadcast!',
caption: `${error}`,
timeout: 10000
})
}
},
fetchTxHex: async function (txId) { fetchTxHex: async function (txId) {
const { const {
bitcoin: {transactions: transactionsAPI} bitcoin: {transactions: transactionsAPI}

View file

@ -136,6 +136,35 @@
</q-form> </q-form>
</q-card> </q-card>
</q-dialog> </q-dialog>
<q-dialog v-model="hww.psbtSent" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form @submit="hwwSignPsbt" class="q-gutter-md">
<div class="row q-mt-lg">
<div class="col-12">
<span>Check transaction on your hardware device</span>
</div>
</div>
<div class="row q-mt-lg">
<!-- todo: disable until all data is confirmed -->
<q-btn
unelevated
color="green"
:disable="!selectedPort"
type="submit"
label="Confirm"
>
<q-spinner v-if="hww.signingPsbt" color="primary"></q-spinner>
</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-dialog v-model="hww.showWipeDialog" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card"> <q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form @submit="hwwWipe" class="q-gutter-md"> <q-form @submit="hwwWipe" class="q-gutter-md">

View file

@ -24,11 +24,13 @@ async function serialSigner(path) {
showPasswordDialog: false, showPasswordDialog: false,
showWipeDialog: false, showWipeDialog: false,
showRestoreDialog: false, showRestoreDialog: false,
showConfirmationDialog: false,
showConsole: false, showConsole: false,
showSignedPsbt: false, showSignedPsbt: false,
sendingPsbt: false, sendingPsbt: false,
signingPsbt: false, signingPsbt: false,
psbtSent: false psbtSent: false,
psbtSentResolve: null
} }
} }
}, },
@ -113,6 +115,12 @@ async function serialSigner(path) {
isAuthenticated: function () { isAuthenticated: function () {
return this.hww.authenticated return this.hww.authenticated
}, },
isSendingPsbt: async function () {
if (!this.hww.sendingPsbt) return false
return new Promise(resolve => {
this.psbtSentResolve = resolve
})
},
checkSerialPortSupported: function () { checkSerialPortSupported: function () {
if (!navigator.serial) { if (!navigator.serial) {
@ -281,6 +289,7 @@ async function serialSigner(path) {
timeout: 5000 timeout: 5000
}) })
} catch (error) { } catch (error) {
this.hww.sendingPsbt = false
this.$q.notify({ this.$q.notify({
type: 'warning', type: 'warning',
message: 'Failed to send data to serial port!', message: 'Failed to send data to serial port!',
@ -292,9 +301,11 @@ async function serialSigner(path) {
handleSendPsbtResponse: function (res = '') { handleSendPsbtResponse: function (res = '') {
this.hww.psbtSent = true this.hww.psbtSent = true
this.hww.sendingPsbt = false this.hww.sendingPsbt = false
this.psbtSentResolve()
}, },
hwwSignPsbt: async function () { hwwSignPsbt: async function () {
try { try {
this.hww.psbtSent = false
this.hww.signingPsbt = true this.hww.signingPsbt = true
await this.writer.write(COMMAND_SIGN_PSBT + '\n') await this.writer.write(COMMAND_SIGN_PSBT + '\n')
this.$q.notify({ this.$q.notify({
@ -407,6 +418,9 @@ async function serialSigner(path) {
this.hww.password = null this.hww.password = null
this.hww.showPassword = false this.hww.showPassword = false
} }
},
updateSignedPsbt: async function (value) {
this.$emit('signed:psbt', value)
} }
}, },
created: async function () {} created: async function () {}

View file

@ -84,7 +84,9 @@ const watchOnly = async () => {
showAddress: false, showAddress: false,
addressNote: '', addressNote: '',
showPayment: false, showPayment: false,
fetchedUtxos: false fetchedUtxos: false,
signedTx: null,
signedTxHex: null
} }
}, },
@ -202,75 +204,11 @@ const watchOnly = async () => {
//################### PSBT ################### //################### PSBT ###################
extractTxFromPsbt: async function (psbtBase64) { updateSignedPsbt: async function (psbtBase64) {
const wallet = this.g.user.wallets[0] console.log('### updateSignedPsbt psbtBase64', psbtBase64)
try { this.$refs.paymentRef.updateSignedPsbt(psbtBase64)
const {data} = await LNbits.api.request(
'PUT',
'/watchonly/api/v1/psbt/extract',
wallet.adminkey,
{
psbtBase64,
inputs: this.payment.tx.inputs
}
)
return data
} catch (error) {
this.$q.notify({
type: 'warning',
message: 'Cannot finalize PSBT!',
timeout: 10000
})
LNbits.utils.notifyApiError(error)
}
}, },
updateSignedPsbt: async function (value) {
this.payment.psbtBase64Signed = value
const data = await this.extractTxFromPsbt(this.payment.psbtBase64Signed)
if (data) {
this.payment.signedTx = JSON.parse(data.tx_json)
this.payment.signedTxHex = data.tx_hex
} else {
this.payment.signedTx = null
this.payment.signedTxHex = null
}
},
broadcastTransaction: async function () {
try {
const wallet = this.g.user.wallets[0]
const {data} = await LNbits.api.request(
'POST',
'/watchonly/api/v1/tx',
wallet.adminkey,
{tx_hex: this.payment.signedTxHex}
)
this.payment.sentTxId = data
this.$q.notify({
type: 'positive',
message: 'Transaction broadcasted!',
caption: `${data}`,
timeout: 10000
})
this.hww.psbtSent = false
this.payment.psbtBase64Signed = null
this.payment.signedTxHex = null
this.payment.signedTx = null
this.payment.psbtBase64 = null
await this.scanAddressWithAmount()
} catch (error) {
this.payment.sentTxId = null
this.$q.notify({
type: 'warning',
message: 'Failed to broadcast!',
caption: `${error}`,
timeout: 10000
})
}
},
//################### SERIAL PORT ################### //################### SERIAL PORT ###################
//################### HARDWARE WALLET ################### //################### HARDWARE WALLET ###################

View file

@ -124,7 +124,6 @@ const readFromSerialPort = reader => {
} }
while (true) { while (true) {
const {value, done} = await reader.read() const {value, done} = await reader.read()
console.log('### serial read', value)
if (value) { if (value) {
const values = value.split(separator) const values = value.split(separator)
// found one or more separators // found one or more separators

View file

@ -8,7 +8,10 @@
:adminkey="g.user.wallets[0].adminkey" :adminkey="g.user.wallets[0].adminkey"
> >
<template v-slot:serial> <template v-slot:serial>
<serial-signer ref="serialSigner"></serial-signer> <serial-signer
ref="serialSigner"
@signed:psbt="updateSignedPsbt"
></serial-signer>
</template> </template>
</wallet-config> </wallet-config>
@ -113,6 +116,7 @@
</q-card> </q-card>
<div class="q-pt-sm"> <div class="q-pt-sm">
<payment <payment
ref="paymentRef"
v-show="showPayment" v-show="showPayment"
:accounts="walletAccounts" :accounts="walletAccounts"
:addresses="addresses" :addresses="addresses"