feat: integrate psbt response from HWW

This commit is contained in:
Vlad Stan 2022-07-19 18:48:39 +03:00
parent 367faf437a
commit b7c4a411b1
5 changed files with 110 additions and 60 deletions

View file

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

View file

@ -40,6 +40,7 @@ new Vue({
readableStreamClosed: null,
reader: null,
showAdvancedConfig: false,
receivedData: '',
config: {}
},
@ -498,6 +499,7 @@ new Vue({
input.tx_hex = await this.fetchTxHex(input.tx_id)
}
this.payment.tx = tx
const {data} = await LNbits.api.request(
'POST',
'/watchonly/api/v1/psbt',
@ -611,53 +613,19 @@ new Vue({
textDecoder.writable
)
this.serial.reader = textDecoder.readable.getReader()
let psbtChunks = []
const readStringUntil = readFromSerialPort(this.serial)
try {
while (true) {
console.log('### reader.read()')
const {value, done} = await this.serial.reader.read()
const {value, done} = await readStringUntil('\n')
console.log('### value', value)
if (value) {
const data = value.split('\n')
console.log('### xxx', data)
const isPsbtStartChunk = data[0].startsWith(PSBT_BASE64_PREFIX)
if (isPsbtStartChunk) {
psbtChunks = [data[0]]
} else if (psbtChunks.length) {
psbtChunks.push(data[0])
if (data.length > 1) {
console.log('### psbtChunks', psbtChunks)
this.payment.psbtBase64Signed = psbtChunks.join('')
this.$q.notify({
type: 'positive',
message: 'PSBT received from serial port device!',
timeout: 10000
})
const data = await this.etractTxFromPsbt(
this.payment.psbtBase64Signed
)
if (data) {
this.payment.signedTx = JSON.parse(data.tx_json)
this.payment.signedTxHex = data.tx_hex
} else {
this.payment.signedTx = null
his.payment.signedTxHex = null
}
}
} else {
psbtChunks = []
this.$q.notify({
type: 'warning',
message: 'Received data from serial port (not psbt)',
caption: value.slice(0, 80) + '...',
timeout: 5000
})
}
}
if (done) {
return
const isPsbt = value.startsWith(PSBT_BASE64_PREFIX)
if (isPsbt) this.updateSignedPsbt(value)
this.updateSerialPortConsole(value)
}
console.log('### startSerialPortReading DONE', done)
if (done) return
}
} catch (error) {
this.$q.notify({
@ -668,9 +636,9 @@ new Vue({
})
}
}
console.log('### startSerialPortReading DONE')
console.log('### startSerialPortReading port', port)
},
etractTxFromPsbt: async function (psbtBase64) {
extractTxFromPsbt: async function (psbtBase64) {
const wallet = this.g.user.wallets[0]
try {
const {data} = await LNbits.api.request(
@ -678,14 +646,43 @@ new Vue({
'/watchonly/api/v1/psbt/extract',
wallet.adminkey,
{
psbtBase64
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
this.$q.notify({
type: 'positive',
message: 'PSBT received from serial port device!',
timeout: 10000
})
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
}
},
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')
},
@ -695,12 +692,18 @@ new Vue({
try {
const wallet = this.g.user.wallets[0]
await LNbits.api.request(
const {data} = await LNbits.api.request(
'POST',
'/watchonly/api/v1/tx',
wallet.adminkey,
{tx_hex: this.payment.signedTxHex}
)
this.$q.notify({
type: 'positive',
message: 'Transaction broadcasted!',
caption: `${data}`,
timeout: 10000
})
} catch (error) {
this.$q.notify({
type: 'warning',

View file

@ -99,3 +99,36 @@ const ACCOUNT_TYPES = {
}
const getAccountDescription = type => ACCOUNT_TYPES[type] || 'nonstandard'
const readFromSerialPort = serial => {
let partialChunk
let fulliness = []
const readStringUntil = async (separator = '\n') => {
console.log('### fulliness', fulliness)
if (fulliness.length) return fulliness.shift()
const chunks = []
if (partialChunk) {
// leftovers from previous read
chunks.push(partialChunk)
partialChunk = undefined
}
while (true) {
const {value, done} = await serial.reader.read()
console.log('### value 1', value)
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(''), done: false}
}
chunks.push(value)
}
if (done) return {value: chunks.join(''), done: true}
}
}
return readStringUntil
}

View file

@ -1019,12 +1019,7 @@
>
</div>
<div class="col-9">
<q-input
v-if="payment.psbtBase64"
v-model="payment.psbtBase64"
filled
readonly
/>
<q-input v-model="payment.psbtBase64" filled />
</div>
</div>
<div
@ -1066,7 +1061,7 @@
</div>
<div class="col-6">
<q-toggle
label="Advanced Config"
label="Advanced"
color="secodary float-left"
class="q-ml-lg"
v-model="serial.showAdvancedConfig"
@ -1082,6 +1077,20 @@
>
</div>
</div>
<div
v-if="serial.showAdvancedConfig"
class="row items-center no-wrap q-mb-md"
>
<div class="col-3 q-pr-lg">Message from device</div>
<div class="col-9">
<q-input
for="watchonly-serial-port-data-input"
v-model="serial.receivedData"
filled
type="textarea"
/>
</div>
</div>
<div
v-if="payment.psbtBase64Signed"
class="row items-center no-wrap q-mb-md"

View file

@ -252,6 +252,7 @@ async def api_psbt_create(
psbt = PSBT(tx)
for i, inp in enumerate(inputs_extra):
print("### ", psbt.inputs[i].bip32_derivations)
psbt.inputs[i].bip32_derivations = inp["bip32_derivations"]
psbt.inputs[i].non_witness_utxo = inp.get("non_witness_utxo", None)
@ -283,6 +284,9 @@ async def api_psbt_extract_tx(
res = SignedTransaction()
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!")
@ -314,19 +318,19 @@ async def api_psbt_extract_tx(
try:
config = await get_config(w.wallet.user)
if not config:
raise ValueError("Cannot broadcast transaction. Mempool endpoint not defined!")
x = bytes.fromhex(data.tx_hex)
print('### x', x)
async with httpx.AsyncClient() as client:
r = await client.post(
config.mempool_endpoint + "/api/tx",
data=x
raise ValueError(
"Cannot broadcast transaction. Mempool endpoint not defined!"
)
tx_id = r.json()
x = bytes.fromhex(data.tx_hex)
print("### x", x)
async with httpx.AsyncClient() as client:
r = await client.post(config.mempool_endpoint + "/api/tx", data=data.tx_hex)
tx_id = r.text
return tx_id
except Exception as e:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
#############################CONFIG##########################