Add draft LND gRCP mocks

This commit is contained in:
Reckless_Satoshi 2023-11-07 16:12:53 +00:00 committed by Reckless_Satoshi
parent 4270f2d0a2
commit 89ae6cd4a6
3 changed files with 144 additions and 11 deletions

View File

@ -45,6 +45,17 @@ DISABLE_ONCHAIN = config("DISABLE_ONCHAIN", cast=bool, default=True)
MAX_SWAP_AMOUNT = config("MAX_SWAP_AMOUNT", cast=int, default=500_000)
# Logger function used to build tests/mocks/lnd.py
def log(name, request, response):
if not config("LOG_LND", cast=bool, default=True):
return
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_message = f"######################################\nEvent: {name}\nTime: {current_time}\nRequest:\n{request}\nResponse:\n{response}\nType: {type(response)}\n"
with open("lnd_log.txt", "a") as file:
file.write(log_message)
class LNDNode:
os.environ["GRPC_SSL_CIPHER_SUITES"] = "HIGH+ECDSA"
@ -76,6 +87,7 @@ class LNDNode:
try:
request = verrpc.VersionRequest()
response = cls.verstub.GetVersion(request)
log("verstub.GetVersion", request, response)
return "v" + response.version
except Exception as e:
print(e)
@ -86,17 +98,21 @@ class LNDNode:
"""Decodes a lightning payment request (invoice)"""
request = lnrpc.PayReqString(pay_req=invoice)
response = cls.lightningstub.DecodePayReq(request)
log("lightningstub.DecodePayReq", request, response)
return response
@classmethod
def estimate_fee(cls, amount_sats, target_conf=2, min_confs=1):
"""Returns estimated fee for onchain payouts"""
is_testnet = lightningstub.GetInfo(lnrpc.GetInfoRequest()).testnet
if is_testnet:
request = lnrpc.GetInfoRequest()
response = lightningstub.GetInfo(request)
log("lightningstub.GetInfo", request, response)
if response.testnet:
dummy_address = "tb1qehyqhruxwl2p5pt52k6nxj4v8wwc3f3pg7377x"
else:
dummy_address = "bc1qgxwaqe4m9mypd7ltww53yv3lyxhcfnhzzvy5j3"
# We assume segwit. Use hardcoded address as shortcut so there is no need of user inputs yet.
request = lnrpc.EstimateFeeRequest(
AddrToAmount={dummy_address: amount_sats},
@ -106,6 +122,7 @@ class LNDNode:
)
response = cls.lightningstub.EstimateFee(request)
log("lightningstub.EstimateFee", request, response)
return {
"mining_fee_sats": response.fee_sat,
@ -120,6 +137,7 @@ class LNDNode:
"""Returns onchain balance"""
request = lnrpc.WalletBalanceRequest()
response = cls.lightningstub.WalletBalance(request)
log("lightningstub.WalletBalance", request, response)
return {
"total_balance": response.total_balance,
@ -135,6 +153,7 @@ class LNDNode:
"""Returns channels balance"""
request = lnrpc.ChannelBalanceRequest()
response = cls.lightningstub.ChannelBalance(request)
log("lightningstub.ChannelBalance", request, response)
return {
"local_balance": response.local_balance.sat,
@ -169,6 +188,7 @@ class LNDNode:
onchainpayment.status = on_mempool_code
onchainpayment.save(update_fields=["status"])
response = cls.lightningstub.SendCoins(request)
log("lightningstub.SendCoins", request, response)
if response.txid:
onchainpayment.txid = response.txid
@ -192,6 +212,7 @@ class LNDNode:
"""Cancels or returns a hold invoice"""
request = invoicesrpc.CancelInvoiceMsg(payment_hash=bytes.fromhex(payment_hash))
response = cls.invoicesstub.CancelInvoice(request)
log("invoicesstub.CancelInvoice", request, response)
# Fix this: tricky because canceling sucessfully an invoice has no response. TODO
return str(response) == "" # True if no response, false otherwise.
@ -200,6 +221,7 @@ class LNDNode:
"""settles a hold invoice"""
request = invoicesrpc.SettleInvoiceMsg(preimage=bytes.fromhex(preimage))
response = cls.invoicesstub.SettleInvoice(request)
log("invoicesstub.SettleInvoice", request, response)
# Fix this: tricky because settling sucessfully an invoice has None response. TODO
return str(response) == "" # True if no response, false otherwise.
@ -215,7 +237,6 @@ class LNDNode:
time,
):
"""Generates hold invoice"""
hold_payment = {}
# The preimage is a random hash of 256 bits entropy
preimage = hashlib.sha256(secrets.token_bytes(nbytes=32)).digest()
@ -233,6 +254,7 @@ class LNDNode:
cltv_expiry=cltv_expiry_blocks,
)
response = cls.invoicesstub.AddHoldInvoice(request)
log("invoicesstub.AddHoldInvoice", request, response)
hold_payment["invoice"] = response.payment_request
payreq_decoded = cls.decode_payreq(hold_payment["invoice"])
@ -257,6 +279,7 @@ class LNDNode:
payment_hash=bytes.fromhex(lnpayment.payment_hash)
)
response = cls.invoicesstub.LookupInvoiceV2(request)
log("invoicesstub.LookupInvoiceV2", request, response)
# Will fail if 'unable to locate invoice'. Happens if invoice expiry
# time has passed (but these are 15% padded at the moment). Should catch it
@ -297,6 +320,7 @@ class LNDNode:
payment_hash=bytes.fromhex(lnpayment.payment_hash)
)
response = cls.invoicesstub.LookupInvoiceV2(request)
log("invoicesstub.LookupInvoiceV2", request, response)
status = lnd_response_state_to_lnpayment_status[response.state]
@ -442,6 +466,7 @@ class LNDNode:
)
for response in cls.routerstub.SendPaymentV2(request):
log("routerstub.SendPaymentV2", request, response)
if (
response.status == lnrpc.Payment.PaymentStatus.UNKNOWN
): # Status 0 'UNKNOWN'
@ -597,6 +622,7 @@ class LNDNode:
try:
for response in cls.routerstub.SendPaymentV2(request):
log("routerstub.SendPaymentV2", request, response)
handle_response(response)
except Exception as e:
@ -609,6 +635,7 @@ class LNDNode:
)
for response in cls.routerstub.TrackPaymentV2(request):
log("routerstub.TrackPaymentV2", request, response)
handle_response(response, was_in_transit=True)
except Exception as e:
@ -648,6 +675,7 @@ class LNDNode:
)
for response in cls.routerstub.TrackPaymentV2(request):
log("routerstub.TrackPaymentV2", request, response)
handle_response(response, was_in_transit=True)
elif "invoice is already paid" in str(e):
@ -658,6 +686,7 @@ class LNDNode:
)
for response in cls.routerstub.TrackPaymentV2(request):
log("routerstub.TrackPaymentV2", request, response)
handle_response(response)
else:
@ -721,6 +750,7 @@ class LNDNode:
allow_self_payment=ALLOW_SELF_KEYSEND,
)
for response in cls.routerstub.SendPaymentV2(request):
log("routerstub.SendPaymentV2", request, response)
if response.status == lnrpc.Payment.PaymentStatus.IN_FLIGHT:
keysend_payment["status"] = LNPayment.Status.FLIGHT
if response.status == lnrpc.Payment.PaymentStatus.SUCCEEDED:
@ -744,6 +774,7 @@ class LNDNode:
"""Just as it sounds. Better safe than sorry!"""
request = invoicesrpc.LookupInvoiceMsg(payment_hash=bytes.fromhex(payment_hash))
response = cls.invoicesstub.LookupInvoiceV2(request)
log("invoicesstub.LookupInvoiceV2", request, response)
return (
response.state == lnrpc.Invoice.InvoiceState.SETTLED

View File

@ -1,4 +1,4 @@
FROM lightninglabs/lnd:v0.16.3-beta
FROM lightninglabs/lnd:v0.17.0-beta
ARG LOCAL_USER_ID=9999
ARG LOCAL_GROUP_ID=9999

View File

@ -1,11 +1,11 @@
from unittest.mock import MagicMock
# Mock up of LND gRPC responses
class MockLightningStub:
def GetInfo(self, request):
response = MagicMock()
# Set the testnet attribute to True for testing purposes
response.testnet = True
return response
@ -15,18 +15,120 @@ class MockLightningStub:
response.sat_per_vbyte = 13
return response
def DecodePayReq(self, request):
response = MagicMock()
if request.pay_req == "lntb17314....x":
response.destination = "00000000"
response.payment_hash = "00000000"
response.num_satoshis = 1731
response.timestamp = 1699359597
response.expiry = 450
response.description = "Payment reference: xxxxxxxxxxxxxxxxxxxxxxx. This payment WILL FREEZE IN YOUR WALLET, check on RoboSats if the lock was successful. It will be unlocked (fail) unless you cheat or cancel unilaterally."
response.cltv_expiry = 650
response.payment_addr = "\275\205\224\002\036h\322"
response.num_msat = 1731000
def CancelInvoice(self, request):
response = MagicMock()
if request == b"xU\305\212\306":
response = {}
return response
def WalletBalance(self, request):
response = MagicMock()
response.total_balance = 10_000_000
response.confirmed_balance = 9_000_000
response.unconfirmed_balance = 1_000_000
response.reserved_balance_anchor_chan = 30_000
response.account_balance = {}
return response
def ChannelBalance(self, request):
response = MagicMock()
response.balance: 10_000_000
response.local_balance.sat = 10_000_000
response.local_balance.msat = 10_000_000_000
response.remote_balance.sat = 30_000_000
response.remote_balance.msat = 30_000_000_000
response.unsettled_local_balance.sat = 500_000
response.unsettled_local_balance.msat = 500_000_000
response.unsettled_remote_balance.sat = 100_000
response.unsettled_remote_balance.msat = 100_000_000
response.pending_open_local_balance = 2_000_000
response.pending_open_local_balance = 2_000_000_000
response.pending_open_remote_balance = 5_000_000
response.pending_open_remote_balance = 5_000_000_000
return response
def SendCoins(self, request):
response = MagicMock()
return response
class MockInvoicesStub:
pass
def AddHoldInvoice(self, request):
response = MagicMock()
if request.value == 1731:
response.payment_request = "lntb17314....x"
response.add_index = 1
response.payment_addr = b"\275\205\322"
def CancelInvoice(self, request):
response = MagicMock()
return response
def SettleInvoice(self, request):
response = MagicMock()
return response
def LookupInvoiceV2(self, request):
response = MagicMock()
return response
class MockRouterStub:
pass
def ResetMissionControl(self, request):
response = MagicMock()
return response
def SendPaymentV2(self, request):
response = MagicMock()
return response
def TrackPaymentV2(self, request):
response = MagicMock()
return response
class MockSignerStub:
pass
def SignMessage(self, request):
response = MagicMock()
return response
class MockVersionerStub:
pass
def GetVersion(self, request):
response = MagicMock()
response.commit = "v0.17.0-beta"
response.commit_hash = "2fb150c8fe827df9df0520ef9916b3afb7b03a8d"
response.version = "0.17.0-beta"
response.app_minor = 17
response.app_patch = 0
response.app_pre_release = "beta"
response.build_tags = [
"autopilotrpc",
"signrpc",
"walletrpc",
"chainrpc",
"invoicesrpc",
"watchtowerrpc",
"neutrinorpc",
"monitoring",
"peersrpc",
"kvdb_postgres",
"kvdb_etcd",
"kvdb_sqlite",
"go1.20.3",
]
response.go_version = "go1.21.0"
return response