feat: broadcast transaction (partial solution)

This commit is contained in:
Vlad Stan 2022-07-15 09:02:50 +03:00
parent 6098b8308e
commit 367faf437a
5 changed files with 101 additions and 17 deletions

View file

@ -82,7 +82,7 @@ class CreatePsbt(BaseModel):
class ExtractPsbt(BaseModel): class ExtractPsbt(BaseModel):
psbtBase64 = "" psbtBase64 = "" # // todo snake case
class SignedTransaction(BaseModel): class SignedTransaction(BaseModel):
@ -90,6 +90,10 @@ class SignedTransaction(BaseModel):
tx_json: Optional[str] tx_json: Optional[str]
class BroadcastTransaction(BaseModel):
tx_hex: str
class Config(BaseModel): class Config(BaseModel):
mempool_endpoint = "https://mempool.space" mempool_endpoint = "https://mempool.space"
receive_gap_limit = 20 receive_gap_limit = 20

View file

@ -639,8 +639,10 @@ new Vue({
) )
if (data) { if (data) {
this.payment.signedTx = JSON.parse(data.tx_json) this.payment.signedTx = JSON.parse(data.tx_json)
this.payment.signedTxHex = data.tx_hex
} else { } else {
this.payment.signedTx = null this.payment.signedTx = null
his.payment.signedTxHex = null
} }
} }
} else { } else {
@ -688,6 +690,27 @@ new Vue({
console.log('### sharePsbtWithAnimatedQRCode') console.log('### sharePsbtWithAnimatedQRCode')
}, },
broadcastTransaction: async function () {
console.log('### broadcastTransaction', this.payment.signedTxHex)
try {
const wallet = this.g.user.wallets[0]
await LNbits.api.request(
'POST',
'/watchonly/api/v1/tx',
wallet.adminkey,
{tx_hex: this.payment.signedTxHex}
)
} catch (error) {
this.$q.notify({
type: 'warning',
message: 'Failed to broadcast!',
caption: `${error}`,
timeout: 10000
})
}
},
//################### UTXOs ################### //################### UTXOs ###################
scanAllAddresses: async function () { scanAllAddresses: async function () {
await this.refreshAddresses() await this.refreshAddresses()

View file

@ -262,6 +262,8 @@ const tableData = {
psbtBase64: '', psbtBase64: '',
psbtBase64Signed: '', psbtBase64Signed: '',
signedTx: null, signedTx: null,
signedTxHex: null,
sentTxId: null,
utxoSelectionModes: [ utxoSelectionModes: [
'Manual', 'Manual',
'Random', 'Random',

View file

@ -182,7 +182,6 @@
:disabled="scan.scanning == true" :disabled="scan.scanning == true"
>Scan Blockchain</q-btn >Scan Blockchain</q-btn
> >
</div> </div>
<div class="col-6"> <div class="col-6">
<q-spinner <q-spinner
@ -997,8 +996,8 @@
class="text-subtitle2" class="text-subtitle2"
color="green" color="green"
> >
{{payment.changeAmount ? satBtc(payment.changeAmount): {{payment.changeAmount ?
'no change'}} satBtc(payment.changeAmount): 'no change'}}
</q-badge> </q-badge>
<q-badge <q-badge
v-if="payment.changeAmount > 0 && payment.changeAmount < DUST_LIMIT" v-if="payment.changeAmount > 0 && payment.changeAmount < DUST_LIMIT"
@ -1065,7 +1064,7 @@
>Disconnect</q-btn >Disconnect</q-btn
> >
</div> </div>
<div class="col-5"> <div class="col-6">
<q-toggle <q-toggle
label="Advanced Config" label="Advanced Config"
color="secodary float-left" color="secodary float-left"
@ -1073,7 +1072,7 @@
v-model="serial.showAdvancedConfig" v-model="serial.showAdvancedConfig"
></q-toggle> ></q-toggle>
</div> </div>
<div class="col-3 "> <div class="col-3">
<q-btn <q-btn
v-if="serial.selectedPort" v-if="serial.selectedPort"
@click="sendPsbtToSerialPort()" @click="sendPsbtToSerialPort()"
@ -1113,16 +1112,50 @@
<div class="row items-center no-wrap q-mb-sm"> <div class="row items-center no-wrap q-mb-sm">
<div class="col-3 q-pr-lg">Fee</div> <div class="col-3 q-pr-lg">Fee</div>
<div class="col-9"> <div class="col-9">
<q-badge color="orange">{{satBtc(payment.signedTx.fee)}} </q-badge></div> <q-badge color="orange"
>{{satBtc(payment.signedTx.fee)}}
</q-badge>
</div>
</div> </div>
<q-separator class="q-mb-lg"></q-separator> <q-separator class="q-mb-lg"></q-separator>
<div v-for="out in payment.signedTx.outputs" class="row items-center no-wrap q-mb-sm"> <div
<div class="col-3 q-pr-lg"> <q-badge color="orange">{{satBtc(out.amount)}}</div> v-for="out in payment.signedTx.outputs"
class="row items-center no-wrap q-mb-sm"
<div class="col-9"> >
<q-badge outline color="blue">{{out.address}}</q-badge></div> <div class="col-3 q-pr-lg">
<q-badge color="orange"
>{{satBtc(out.amount)}}</q-badge
>
</div> </div>
<div class="col-9">
<q-badge outline color="blue"
>{{out.address}}</q-badge
>
</div>
</div>
</div>
</div>
<div
v-if="payment.psbtBase64Signed"
class="row items-center no-wrap q-mb-md"
>
<div class="col-3 q-pr-lg">
<q-btn
v-if="payment.signedTx"
@click="broadcastTransaction()"
unelevated
color="secondary"
>Send Payment</q-btn
>
</div>
<div class="col-9">
<q-input
v-if="payment.sentTxId"
v-model="payment.sentTxId"
filled
readonly
/>
</div> </div>
</div> </div>
</q-card-section> </q-card-section>
@ -1301,7 +1334,6 @@
{% endblock %} {% block scripts %} {{ window_vars(user) }} {% endblock %} {% block scripts %} {{ window_vars(user) }}
<style> <style>
.btn-full { .btn-full {
width: 100%; width: 100%;
} }

View file

@ -1,4 +1,5 @@
from http import HTTPStatus from http import HTTPStatus
import httpx
import json import json
from fastapi import Query, Request from fastapi import Query, Request
@ -33,6 +34,7 @@ from .crud import (
update_config, update_config,
) )
from .models import ( from .models import (
BroadcastTransaction,
SignedTransaction, SignedTransaction,
CreateWallet, CreateWallet,
CreatePsbt, CreatePsbt,
@ -304,6 +306,27 @@ async def api_psbt_extract_tx(
return res.dict() return res.dict()
@watchonly_ext.post("/api/v1/tx")
async def api_psbt_extract_tx(
data: BroadcastTransaction, w: WalletTypeInfo = Depends(require_admin_key)
):
print("### data", data)
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
)
tx_id = r.json()
return tx_id
except Exception as e:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
#############################CONFIG########################## #############################CONFIG##########################