feat: integrate psbt response from HWW
This commit is contained in:
parent
367faf437a
commit
b7c4a411b1
5 changed files with 110 additions and 60 deletions
|
|
@ -83,6 +83,7 @@ class CreatePsbt(BaseModel):
|
|||
|
||||
class ExtractPsbt(BaseModel):
|
||||
psbtBase64 = "" # // todo snake case
|
||||
inputs: List[TransactionInput]
|
||||
|
||||
|
||||
class SignedTransaction(BaseModel):
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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##########################
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue