mirror of
https://github.com/fusion44/blitz_api.git
synced 2026-08-18 12:27:50 +02:00
parent
b9f4577d21
commit
86442dde89
4 changed files with 86 additions and 15 deletions
|
|
@ -99,6 +99,17 @@ If neither __target_conf__, or __sat_per_vbyte__ are set, then the internal wall
|
|||
* If __sat_per_vbyte__ is set then __target_conf__ is ignored and vbytes (sipabytes) will be used.
|
||||
|
||||
> 👉 See [https://lightning.readthedocs.io/lightning-txprepare.7.html](https://lightning.readthedocs.io/lightning-txprepare.7.html)
|
||||
|
||||
### Sending all onchain funds
|
||||
> ℹ️ Keep the following points in mind when sending all onchain funds:
|
||||
|
||||
* If __send_all__ is set to __true__, the __amount__ field must be set to __0__.
|
||||
* If the __amount__ field is greater than __0__, the __send_all__ field must be __false__.
|
||||
* The API will return an error if neither or both conditions are met at the same time.
|
||||
* If __send_all__ is set to __true__ the amount of satoshis to send will be calculated by subtracting the fee from the wallet balance.
|
||||
* If the wallet balance is not sufficient to cover the fee, the call will fail.
|
||||
* The call will __not__ close any channels.
|
||||
* The implementation may keep a reserve of funds if there are still open channels.
|
||||
"""
|
||||
|
||||
send_payment_desc = """
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import app.lightning.impl.protos.cln.node_pb2_grpc as clnrpc
|
|||
import app.lightning.impl.protos.cln.primitives_pb2 as lnp
|
||||
from app.api.utils import config_get_hex_str, next_push_id
|
||||
from app.bitcoind.utils import bitcoin_rpc_async
|
||||
from app.api.utils import SSE, broadcast_sse_msg
|
||||
from app.lightning.impl.ln_base import LightningNodeBase
|
||||
from app.lightning.models import (
|
||||
Channel,
|
||||
|
|
@ -352,7 +353,7 @@ class LnNodeCLNgRPC(LightningNodeBase):
|
|||
|
||||
# TODO: Improve this once CLN reports the block height in bkpr-listincome
|
||||
# see https://github.com/ElementsProject/lightning/issues/5694
|
||||
|
||||
|
||||
# now get the block height for each tx ...
|
||||
res = await _make_local_call("bkpr-listaccountevents")
|
||||
if not res:
|
||||
|
|
@ -567,7 +568,7 @@ class LnNodeCLNgRPC(LightningNodeBase):
|
|||
utxos.append(lnp.Outpoint(txid=o.txid, outnum=o.output))
|
||||
max_amt += o.amount_msat.msat
|
||||
|
||||
if max_amt <= input.amount:
|
||||
if not input.send_all and max_amt <= input.amount:
|
||||
raise HTTPException(
|
||||
status.HTTP_412_PRECONDITION_FAILED,
|
||||
detail=f"Could not afford {input.amount}sat. Not enough funds available",
|
||||
|
|
@ -576,14 +577,17 @@ class LnNodeCLNgRPC(LightningNodeBase):
|
|||
req = ln.WithdrawRequest(
|
||||
destination=input.address,
|
||||
satoshi=lnp.AmountOrAll(
|
||||
amount=lnp.Amount(msat=input.amount), all=False
|
||||
amount=lnp.Amount(msat=input.amount),
|
||||
all=input.send_all,
|
||||
),
|
||||
minconf=input.min_confs,
|
||||
feerate=fee_rate,
|
||||
utxos=utxos,
|
||||
)
|
||||
res = await self._cln_stub.Withdraw(req)
|
||||
return SendCoinsResponse.from_cln_grpc(res, input)
|
||||
response = await self._cln_stub.Withdraw(req)
|
||||
r = SendCoinsResponse.from_cln_grpc(response, input)
|
||||
await broadcast_sse_msg(SSE.LN_ONCHAIN_PAYMENT_STATUS, r.dict())
|
||||
return r
|
||||
except grpc.aio._call.AioRpcError as error:
|
||||
details = error.details()
|
||||
if details and details.find("Could not parse destination address") > -1:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from decouple import config as dconfig
|
|||
from fastapi.exceptions import HTTPException
|
||||
from starlette import status
|
||||
|
||||
import app.bitcoind.service as btc
|
||||
import app.lightning.impl.protos.lnd.lightning_pb2 as ln
|
||||
import app.lightning.impl.protos.lnd.lightning_pb2_grpc as lnrpc
|
||||
import app.lightning.impl.protos.lnd.router_pb2 as router
|
||||
|
|
@ -503,14 +504,26 @@ This will show more debug information.
|
|||
sat_per_vbyte=input.sat_per_vbyte,
|
||||
min_confs=input.min_confs,
|
||||
label=input.label,
|
||||
send_all=input.send_all,
|
||||
)
|
||||
|
||||
response = await self._lnd_stub.SendCoins(r)
|
||||
r = SendCoinsResponse.from_lnd_grpc(response, input)
|
||||
bi = await btc.get_blockchain_info()
|
||||
sendResponse = await self._lnd_stub.SendCoins(r)
|
||||
txResponse = await self._lnd_stub.GetTransactions(
|
||||
ln.GetTransactionsRequest(start_height=-1, end_height=bi.blocks)
|
||||
)
|
||||
|
||||
tx = None
|
||||
for t in txResponse.transactions:
|
||||
if t.tx_hash == sendResponse.txid:
|
||||
tx = t
|
||||
break
|
||||
|
||||
r = SendCoinsResponse.from_lnd_grpc(tx, input)
|
||||
await broadcast_sse_msg(SSE.LN_ONCHAIN_PAYMENT_STATUS, r.dict())
|
||||
return r
|
||||
except grpc.aio._call.AioRpcError as error:
|
||||
_check_if_locked()
|
||||
_check_if_locked(error)
|
||||
details = error.details()
|
||||
if details and details.find("invalid bech32 string") > -1:
|
||||
raise HTTPException(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from typing import List, Optional, Union
|
|||
|
||||
from deepdiff import DeepDiff
|
||||
from fastapi.param_functions import Query
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, validator
|
||||
from pydantic.types import conint
|
||||
|
||||
import app.lightning.docs as docs
|
||||
|
|
@ -1136,10 +1136,6 @@ class SendCoinsInput(BaseModel):
|
|||
...,
|
||||
description="The base58 or bech32 encoded bitcoin address to send coins to on-chain",
|
||||
)
|
||||
amount: conint(gt=0) = Query(
|
||||
...,
|
||||
description="The number of bitcoin denominated in satoshis to send",
|
||||
)
|
||||
target_conf: int = Query(
|
||||
None,
|
||||
description="The number of blocks that the transaction *should* confirm in, will be used for fee estimation",
|
||||
|
|
@ -1155,6 +1151,47 @@ class SendCoinsInput(BaseModel):
|
|||
label: str = Query(
|
||||
"", description="A label for the transaction. Ignored by CLN backend."
|
||||
)
|
||||
send_all: bool = Query(
|
||||
False,
|
||||
description="Send all available on-chain funds from the wallet. Will be executed `amount` is **0**",
|
||||
)
|
||||
amount: conint(ge=0) = Query(
|
||||
0,
|
||||
description="The number of bitcoin denominated in satoshis to send. Must not be set when `send_all` is true.",
|
||||
)
|
||||
|
||||
@validator("amount", pre=True, always=True)
|
||||
def check_amount_or_send_all(cls, amount, values):
|
||||
if amount == None:
|
||||
amount = 0
|
||||
|
||||
send_all = values.get("send_all") if "send_all" in values else False
|
||||
|
||||
if amount < 0:
|
||||
raise ValueError("Amount must not be negative")
|
||||
|
||||
if amount == 0 and not send_all:
|
||||
# neither amount nor send_all is set
|
||||
raise ValueError(
|
||||
"Either amount or send_all must be set. Please review the documentation."
|
||||
)
|
||||
|
||||
if amount > 0 and not send_all:
|
||||
# amount is set and send_all is false
|
||||
return amount
|
||||
|
||||
if amount > 0 and send_all:
|
||||
# amount is set and send_all is true
|
||||
raise ValueError(
|
||||
"Amount and send_all must not be set at the same time. Please review the documentation."
|
||||
)
|
||||
|
||||
if amount == 0 and send_all:
|
||||
# amount is not set and send_all is true
|
||||
return amount
|
||||
|
||||
# normally this should never be reached
|
||||
raise ValueError(f"Unknown input.")
|
||||
|
||||
|
||||
class SendCoinsResponse(BaseModel):
|
||||
|
|
@ -1167,16 +1204,22 @@ class SendCoinsResponse(BaseModel):
|
|||
...,
|
||||
description="The number of bitcoin denominated in satoshis which where sent",
|
||||
)
|
||||
fees: conint(ge=0) = Query(
|
||||
None,
|
||||
description="The number of bitcoin denominated in satoshis which where paid as fees",
|
||||
)
|
||||
label: str = Query(
|
||||
"", description="The label used for the transaction. Ignored by CLN backend."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_lnd_grpc(cls, r, input: SendCoinsInput):
|
||||
amount = input.amount if input.send_all == False else r.amount
|
||||
return cls(
|
||||
txid=r.txid,
|
||||
txid=r.tx_hash,
|
||||
address=input.address,
|
||||
amount=input.amount,
|
||||
amount=abs(amount),
|
||||
fees=r.total_fees,
|
||||
label=input.label,
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue