diff --git a/.gitignore b/.gitignore
index 51480282..07c2094f 100755
--- a/.gitignore
+++ b/.gitignore
@@ -643,4 +643,5 @@ frontend/static/frontend/main*
frontend/static/assets/avatars*
api/lightning/lightning*
api/lightning/invoices*
+api/lightning/router*
api/lightning/googleapis*
diff --git a/api/lightning/node.py b/api/lightning/node.py
index 11ec9b34..a78c72a3 100644
--- a/api/lightning/node.py
+++ b/api/lightning/node.py
@@ -1,6 +1,7 @@
import grpc, os, hashlib, secrets, json
from . import lightning_pb2 as lnrpc, lightning_pb2_grpc as lightningstub
from . import invoices_pb2 as invoicesrpc, invoices_pb2_grpc as invoicesstub
+from . import router_pb2 as routerrpc, router_pb2_grpc as routerstub
from decouple import config
from base64 import b64decode
@@ -19,10 +20,13 @@ LND_GRPC_HOST = config('LND_GRPC_HOST')
class LNNode():
os.environ["GRPC_SSL_CIPHER_SUITES"] = 'HIGH+ECDSA'
+
creds = grpc.ssl_channel_credentials(CERT)
channel = grpc.secure_channel(LND_GRPC_HOST, creds)
+
lightningstub = lightningstub.LightningStub(channel)
invoicesstub = invoicesstub.InvoicesStub(channel)
+ routerstub = routerstub.RouterStub(channel)
@classmethod
def decode_payreq(cls, invoice):
@@ -46,8 +50,8 @@ class LNNode():
@classmethod
def settle_hold_invoice(cls, preimage):
'''settles a hold invoice'''
- request = invoicesrpc.SettleInvoiceMsg(preimage=preimage)
- response = invoicesstub.SettleInvoice(request, metadata=[('macaroon', MACAROON.hex())])
+ request = invoicesrpc.SettleInvoiceMsg(preimage=bytes.fromhex(preimage))
+ response = cls.invoicesstub.SettleInvoice(request, metadata=[('macaroon', MACAROON.hex())])
# Fix this: tricky because settling sucessfully an invoice has no response. TODO
if response == None:
return True
@@ -84,32 +88,30 @@ class LNNode():
@classmethod
def validate_hold_invoice_locked(cls, payment_hash):
'''Checks if hold invoice is locked'''
-
- request = invoicesrpc.LookupInvoiceMsg(payment_hash=payment_hash)
- response = invoicesstub.LookupInvoiceV2(request, metadata=[('macaroon', MACAROON.hex())])
-
- # What is the state for locked ???
- if response.state == 'OPEN' or response.state == 'SETTLED':
- return False
- else:
- return True
-
+ request = invoicesrpc.LookupInvoiceMsg(payment_hash=bytes.fromhex(payment_hash))
+ response = cls.invoicesstub.LookupInvoiceV2(request, metadata=[('macaroon', MACAROON.hex())])
+ print('status here')
+ print(response.state) # LND states: 0 OPEN, 1 SETTLED, 3 ACCEPTED, GRPC_ERROR status 5 when cancelled
+ return response.state == 3 # True if hold invoice is accepted.
@classmethod
def check_until_invoice_locked(cls, payment_hash, expiration):
'''Checks until hold invoice is locked.
When invoice is locked, returns true.
If time expires, return False.'''
-
- request = invoicesrpc.SubscribeSingleInvoiceRequest(r_hash=payment_hash)
- for invoice in invoicesstub.SubscribeSingleInvoice(request):
+ # Experimental, needs asyncio
+ # Maybe best to pass LNpayment object and change status live.
+
+ request = cls.invoicesrpc.SubscribeSingleInvoiceRequest(r_hash=payment_hash)
+ for invoice in cls.invoicesstub.SubscribeSingleInvoice(request):
+ print(invoice)
if timezone.now > expiration:
break
- if invoice.state == 'LOCKED':
+ if invoice.state == 'ACCEPTED':
return True
-
return False
+
@classmethod
def validate_ln_invoice(cls, invoice, num_satoshis):
'''Checks if the submited LN invoice comforms to expectations'''
@@ -125,10 +127,15 @@ class LNNode():
try:
payreq_decoded = cls.decode_payreq(invoice)
+ print(payreq_decoded)
except:
buyer_invoice['context'] = {'bad_invoice':'Does not look like a valid lightning invoice'}
return buyer_invoice
+ if payreq_decoded.num_satoshis == 0:
+ buyer_invoice['context'] = {'bad_invoice':'The invoice provided has no explicit amount'}
+ return buyer_invoice
+
if not payreq_decoded.num_satoshis == num_satoshis:
buyer_invoice['context'] = {'bad_invoice':'The invoice provided is not for '+'{:,}'.format(num_satoshis)+ ' Sats'}
return buyer_invoice
@@ -147,11 +154,28 @@ class LNNode():
return buyer_invoice
@classmethod
- def pay_invoice(cls, invoice):
+ def pay_invoice(cls, invoice, num_satoshis):
'''Sends sats to buyer'''
+ # Needs router subservice
+ # Maybe best to pass order and change status live.
+ fee_limit_sat = max(num_satoshis * 0.0002, 10) # 200 ppm or 10 sats
- return True
+ request = routerrpc.SendPaymentRequest(
+ payment_request=invoice,
+ amt_msat=num_satoshis,
+ fee_limit_sat=fee_limit_sat,
+ timeout_seconds=60,
+ )
+
+ for response in routerstub.SendPaymentV2(request, metadata=[('macaroon', MACAROON.hex())]):
+ print(response)
+ print(response.status)
+
+ if response.status == True:
+ return True
+
+ return False
@classmethod
def double_check_htlc_is_settled(cls, payment_hash):
diff --git a/api/logics.py b/api/logics.py
index 1041f502..bf3e8851 100644
--- a/api/logics.py
+++ b/api/logics.py
@@ -149,7 +149,6 @@ class Logics():
# If the order status is 'Waiting for both'. Move forward to 'waiting for escrow'
if order.status == Order.Status.WF2:
- print(order.trade_escrow)
if order.trade_escrow:
if order.trade_escrow.status == LNPayment.Status.LOCKED:
order.status = Order.Status.CHA
@@ -159,27 +158,36 @@ class Logics():
order.save()
return True, None
+ def add_profile_rating(profile, rating):
+ ''' adds a new rating to a user profile'''
+
+ profile.total_ratings = profile.total_ratings + 1
+ latest_ratings = profile.latest_ratings
+ if len(latest_ratings) <= 1:
+ profile.latest_ratings = [rating]
+ profile.avg_rating = rating
+
+ else:
+ latest_ratings = list(latest_ratings).append(rating)
+ profile.latest_ratings = latest_ratings
+ profile.avg_rating = sum(latest_ratings) / len(latest_ratings)
+
+ profile.save()
+
@classmethod
def rate_counterparty(cls, order, user, rating):
# If the trade is finished
if order.status > Order.Status.PAY:
-
# if maker, rates taker
if order.maker == user:
- order.taker.profile.total_ratings = order.taker.profile.total_ratings + 1
- last_ratings = list(order.taker.profile.last_ratings).append(rating)
- order.taker.profile.total_ratings = sum(last_ratings) / len(last_ratings)
-
+ cls.add_profile_rating(order.taker.profile, rating)
# if taker, rates maker
if order.taker == user:
- order.maker.profile.total_ratings = order.maker.profile.total_ratings + 1
- last_ratings = list(order.maker.profile.last_ratings).append(rating)
- order.maker.profile.total_ratings = sum(last_ratings) / len(last_ratings)
+ cls.add_profile_rating(order.maker.profile, rating)
else:
return False, {'bad_request':'You cannot rate your counterparty yet.'}
- order.save()
return True, None
def is_penalized(user):
@@ -204,7 +212,7 @@ class Logics():
order.maker = None
order.status = Order.Status.UCA
order.save()
- return True, None
+ return True, {}
# 2) When maker cancels after bond
'''The order dissapears from book and goes to cancelled.
@@ -213,12 +221,14 @@ class Logics():
of the bond (requires maker submitting an invoice)'''
elif order.status == Order.Status.PUB and order.maker == user:
#Settle the maker bond (Maker loses the bond for a public order)
- valid = cls.settle_maker_bond(order)
- if valid:
+ if cls.settle_maker_bond(order):
+ order.maker_bond.status = LNPayment.Status.SETLED
+ order.maker_bond.save()
+
order.maker = None
order.status = Order.Status.UCA
order.save()
- return True, None
+ return True, {}
# 3) When taker cancels before bond
''' The order goes back to the book as public.
@@ -226,13 +236,13 @@ class Logics():
elif order.status == Order.Status.TAK and order.taker == user:
# adds a timeout penalty
user.profile.penalty_expiration = timezone.now() + timedelta(seconds=PENALTY_TIMEOUT)
- user.save()
+ user.profile.save()
order.taker = None
order.status = Order.Status.PUB
order.save()
- return True, None
+ return True, {}
# 4) When taker or maker cancel after bond (before escrow)
'''The order goes into cancelled status if maker cancels.
@@ -248,19 +258,19 @@ class Logics():
order.maker = None
order.status = Order.Status.UCA
order.save()
- return True, None
+ return True, {}
# 4.b) When taker cancel after bond (before escrow)
'''The order into cancelled status if maker cancels.'''
elif order.status > Order.Status.TAK and order.status < Order.Status.CHA and order.taker == user:
- #Settle the maker bond (Maker loses the bond for canceling an ongoing trade)
+ # Settle the maker bond (Maker loses the bond for canceling an ongoing trade)
valid = cls.settle_taker_bond(order)
if valid:
order.taker = None
order.status = Order.Status.PUB
# order.taker_bond = None # TODO fix this, it overrides the information about the settled taker bond. Might make admin tasks hard.
order.save()
- return True, None
+ return True, {}
# 5) When trade collateral has been posted (after escrow)
'''Always goes to cancelled status. Collaboration is needed.
@@ -281,6 +291,7 @@ class Logics():
# Return the previous invoice if there was one and is still unpaid
if order.maker_bond:
+ cls.check_maker_bond_locked(order)
if order.maker_bond.status == LNPayment.Status.INVGEN:
return True, {'bond_invoice':order.maker_bond.invoice,'bond_satoshis':order.maker_bond.num_satoshis}
else:
@@ -289,7 +300,7 @@ class Logics():
order.last_satoshis = cls.satoshis_now(order)
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
- description = f"RoboSats - Publishing '{str(order)}' - This is a maker bond. It will automatically return if you do not cancel or cheat"
+ description = f"RoboSats - Publishing '{str(order)}' - This is a maker bond, it will freeze in your wallet. It automatically returns. It will be charged if you cheat or cancel."
# Gen hold Invoice
hold_payment = LNNode.gen_hold_invoice(bond_satoshis, description, BOND_EXPIRY*3600)
@@ -311,6 +322,29 @@ class Logics():
order.save()
return True, {'bond_invoice':hold_payment['invoice'], 'bond_satoshis':bond_satoshis}
+ @classmethod
+ def check_until_maker_bond_locked(cls, order):
+ expiration = order.maker_bond.created_at + timedelta(seconds=EXP_MAKER_BOND_INVOICE)
+ is_locked = LNNode.check_until_invoice_locked(order.payment_hash, expiration)
+
+ if is_locked:
+ order.maker_bond.status = LNPayment.Status.LOCKED
+ order.maker_bond.save()
+ order.status = Order.Status.PUB
+
+ order.save()
+ return is_locked
+
+ @classmethod
+ def check_maker_bond_locked(cls, order):
+ if LNNode.validate_hold_invoice_locked(order.maker_bond.payment_hash):
+ order.maker_bond.status = LNPayment.Status.LOCKED
+ order.maker_bond.save()
+ order.status = Order.Status.PUB
+ order.save()
+ return True
+ return False
+
@classmethod
def gen_taker_hold_invoice(cls, order, user):
@@ -330,7 +364,7 @@ class Logics():
order.last_satoshis = cls.satoshis_now(order) # LOCKS THE AMOUNT OF SATOSHIS FOR THE TRADE
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
- description = f"RoboSats - Taking '{str(order)}' - This is a taker bond. It will automatically return if you do not cancel or cheat"
+ description = f"RoboSats - Taking '{str(order)}' - This is a taker bond, it will freeze in your wallet. It automatically returns. It will be charged if you cheat or cancel."
# Gen hold Invoice
hold_payment = LNNode.gen_hold_invoice(bond_satoshis, description, BOND_EXPIRY*3600)
@@ -407,12 +441,10 @@ class Logics():
def settle_maker_bond(order):
''' Settles the maker bond hold invoice'''
# TODO ERROR HANDLING
- valid = LNNode.settle_hold_invoice(order.maker_bond.preimage)
- if valid:
+ if LNNode.settle_hold_invoice(order.maker_bond.preimage):
order.maker_bond.status = LNPayment.Status.SETLED
order.save()
-
- return valid
+ return True
def settle_taker_bond(order):
''' Settles the taker bond hold invoice'''
diff --git a/api/models.py b/api/models.py
index e1707561..b5b5bb28 100644
--- a/api/models.py
+++ b/api/models.py
@@ -34,8 +34,8 @@ class LNPayment(models.Model):
RETNED = 3, 'Returned'
MISSNG = 4, 'Missing'
VALIDI = 5, 'Valid'
- PAYING = 6, 'Paying ongoing'
- FAILRO = 7, 'Failed routing'
+ FLIGHT = 6, 'On flight'
+ FAILRO = 7, 'Routing failed'
# payment use details
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False, default=Types.HOLD)
diff --git a/api/views.py b/api/views.py
index ae891419..b4d3ec70 100644
--- a/api/views.py
+++ b/api/views.py
@@ -60,7 +60,7 @@ class MakerView(CreateAPIView):
premium=premium,
satoshis=satoshis,
is_explicit=is_explicit,
- expires_at=timezone.now()+timedelta(minutes=EXP_MAKER_BOND_INVOICE), # TODO Move to class method
+ expires_at=timezone.now()+timedelta(seconds=EXP_MAKER_BOND_INVOICE), # TODO Move to class method
maker=request.user)
# TODO move to Order class method when new instance is created!
@@ -95,11 +95,11 @@ class OrderView(viewsets.ViewSet):
# This is our order.
order = order[0]
- # 1) If order expired
+ # 1) If order has expired
if order.status == Order.Status.EXP:
return Response({'bad_request':'This order has expired'},status.HTTP_400_BAD_REQUEST)
- # 2) If order cancelled
+ # 2) If order has been cancelled
if order.status == Order.Status.UCA:
return Response({'bad_request':'This order has been cancelled by the maker'},status.HTTP_400_BAD_REQUEST)
if order.status == Order.Status.CCA:
@@ -107,7 +107,7 @@ class OrderView(viewsets.ViewSet):
data = ListOrderSerializer(order).data
- # if user is under a limit (penalty), inform him
+ # if user is under a limit (penalty), inform him.
is_penalized, time_out = Logics.is_penalized(request.user)
if is_penalized:
data['penalty'] = time_out
@@ -125,7 +125,7 @@ class OrderView(viewsets.ViewSet):
elif not data['is_participant'] and order.status != Order.Status.PUB:
return Response(data, status=status.HTTP_200_OK)
- # For participants add position side, nicks and status as message
+ # For participants add positions, nicks and status as a message
data['is_buyer'] = Logics.is_buyer(order,request.user)
data['is_seller'] = Logics.is_seller(order,request.user)
data['maker_nick'] = str(order.maker)
@@ -134,7 +134,7 @@ class OrderView(viewsets.ViewSet):
data['is_fiat_sent'] = order.is_fiat_sent
data['is_disputed'] = order.is_disputed
- # If both bonds are locked, participants can see the trade in sats is also final.
+ # If both bonds are locked, participants can see the final trade amount in sats.
if order.taker_bond:
if order.maker_bond.status == order.taker_bond.status == LNPayment.Status.LOCKED:
# Seller sees the amount he pays
@@ -182,8 +182,10 @@ class OrderView(viewsets.ViewSet):
else:
return Response(context, status.HTTP_400_BAD_REQUEST)
- # 8) If status is 'CHA'or '' or '' and all HTLCS are in LOCKED
- elif order.status == Order.Status.CHA: # TODO Add the other status
+ # 8) If status is 'CHA' or 'FSE' and all HTLCS are in LOCKED
+ elif order.status == Order.Status.CHA or order.status == Order.Status.FSE: # TODO Add the other status
+
+ # If all bonds are locked.
if order.maker_bond.status == order.taker_bond.status == order.trade_escrow.status == LNPayment.Status.LOCKED:
# add whether a collaborative cancel is pending
data['pending_cancel'] = order.is_pending_cancel
@@ -193,7 +195,7 @@ class OrderView(viewsets.ViewSet):
def take_update_confirm_dispute_cancel(self, request, format=None):
'''
- Here take place all of the user updates to the order object.
+ Here takes place all of updatesto the order object.
That is: take, confim, cancel, dispute, update_invoice or rate.
'''
order_id = request.GET.get(self.lookup_url_kwarg)
@@ -208,7 +210,7 @@ class OrderView(viewsets.ViewSet):
invoice = serializer.data.get('invoice')
rating = serializer.data.get('rating')
- # 1) If action is take, it is be taker request!
+ # 1) If action is take, it is a taker request!
if action == 'take':
if order.status == Order.Status.PUB:
valid, context = Logics.validate_already_maker_or_taker(request.user)
@@ -253,7 +255,7 @@ class OrderView(viewsets.ViewSet):
return Response(
{'bad_request':
'The Robotic Satoshis working in the warehouse did not understand you. ' +
- 'Please, fill a Bug Issue in Github https://github.com/Reckless-Satoshi/robosats/issues'},
+ 'Please, fill a Bug Issue in Github https://github.com/reckless-satoshi/robosats/issues'},
status.HTTP_501_NOT_IMPLEMENTED)
return self.get(request)
@@ -277,6 +279,16 @@ class UserView(APIView):
- Creates login credentials (new User object)
Response with Avatar and Nickname.
'''
+
+ # if request.user.id:
+ # context = {}
+ # context['nickname'] = request.user.username
+ # participant = not Logics.validate_already_maker_or_taker(request.user)
+ # context['bad_request'] = f'You are already logged in as {request.user}'
+ # if participant:
+ # context['bad_request'] = f'You are already logged in as as {request.user} and have an active order'
+ # return Response(context,status.HTTP_200_OK)
+
token = request.GET.get(self.lookup_url_kwarg)
# Compute token entropy
diff --git a/frontend/src/components/OrderPage.js b/frontend/src/components/OrderPage.js
index 65340f0d..5fb3c490 100644
--- a/frontend/src/components/OrderPage.js
+++ b/frontend/src/components/OrderPage.js
@@ -67,7 +67,7 @@ export default class OrderPage extends Component {
super(props);
this.state = {
isExplicit: false,
- delay: 10000, // Refresh every 10 seconds
+ delay: 2000, // Refresh every 2 seconds by default
currencies_dict: {"1":"USD"}
};
this.orderId = this.props.match.params.orderId;
@@ -109,7 +109,7 @@ export default class OrderPage extends Component {
escrowInvoice: data.escrow_invoice,
escrowSatoshis: data.escrow_satoshis,
invoiceAmount: data.invoice_amount,
- });
+ })
});
}
@@ -129,9 +129,6 @@ export default class OrderPage extends Component {
tick = () => {
this.getOrderDetails();
}
- handleDelayChange = (e) => {
- this.setState({ delay: Number(e.target.value) });
- }
// Fix to use proper react props
handleClickBackButton=()=>{
@@ -149,7 +146,9 @@ export default class OrderPage extends Component {
};
fetch('/api/order/' + '?order_id=' + this.orderId, requestOptions)
.then((response) => response.json())
- .then((data) => (console.log(data) & this.getOrderDetails(data.id)));
+ .then((data) => (this.setState({badRequest:data.bad_request})
+ & console.log(data)
+ & this.getOrderDetails(data.id)));
}
getCurrencyDict() {
fetch('/static/assets/currencies.json')
@@ -278,8 +277,9 @@ export default class OrderPage extends Component {
>
}
- {/* Makers can cancel before commiting the bond (status 0)*/}
- {this.state.isMaker & this.state.statusCode == 0 ?
+ {/* Makers can cancel before trade escrow deposited (status <9)*/}
+ {/* Only free cancel before bond locked (status 0)*/}
+ {this.state.isMaker & this.state.statusCode < 9 ?
diff --git a/frontend/src/components/TradeBox.js b/frontend/src/components/TradeBox.js
index a7ec4e88..f27ef3e3 100644
--- a/frontend/src/components/TradeBox.js
+++ b/frontend/src/components/TradeBox.js
@@ -1,5 +1,5 @@
import React, { Component } from "react";
-import { Paper, Button, Grid, Typography, TextField, List, ListItem, ListItemText, Divider} from "@mui/material"
+import { Link, Paper, Rating, Button, Grid, Typography, TextField, List, ListItem, ListItemText, Divider} from "@mui/material"
import QRCode from "react-qr-code";
function getCookie(name) {
@@ -294,6 +294,19 @@ handleClickOpenDisputeButton=()=>{
.then((response) => response.json())
.then((data) => (this.props.data = data));
}
+handleRatingChange=(e)=>{
+ const requestOptions = {
+ method: 'POST',
+ headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
+ body: JSON.stringify({
+ 'action': "rate",
+ 'rating': e.target.value,
+ }),
+ };
+ fetch('/api/order/' + '?order_id=' + this.props.data.id, requestOptions)
+ .then((response) => response.json())
+ .then((data) => (this.props.data = data));
+}
showFiatSentButton(){
return(
@@ -359,6 +372,7 @@ handleClickOpenDisputeButton=()=>{
)
}
+
// showFiatReceivedButton(){
// }
@@ -367,9 +381,28 @@ handleClickOpenDisputeButton=()=>{
// }
- // showRateSelect(){
-
- // }
+ showRateSelect(){
+ return(
+
+
+
+ 🎉Trade finished!🥳
+
+
+
+
+ What do you think of {this.props.data.isMaker ? this.props.data.takerNick : this.props.data.makerNick}?
+
+
+
+
+
+
+
+
+
+ )
+ }
render() {
@@ -393,14 +426,25 @@ handleClickOpenDisputeButton=()=>{
{this.props.data.isBuyer & this.props.data.statusCode == 7 ? this.showWaitingForEscrow() : ""}
{this.props.data.isSeller & this.props.data.statusCode == 8 ? this.showWaitingForBuyerInvoice() : ""}
- {/* In Chatroom - showChat(showSendButton, showReveiceButton, showDisputeButton) */}
+ {/* In Chatroom - No fiat sent - showChat(showSendButton, showReveiceButton, showDisputeButton) */}
{this.props.data.isBuyer & this.props.data.statusCode == 9 ? this.showChat(true,false,true) : ""}
{this.props.data.isSeller & this.props.data.statusCode == 9 ? this.showChat(false,false,true) : ""}
+
+ {/* In Chatroom - Fiat sent - showChat(showSendButton, showReveiceButton, showDisputeButton) */}
{this.props.data.isBuyer & this.props.data.statusCode == 10 ? this.showChat(false,false,true) : ""}
{this.props.data.isSeller & this.props.data.statusCode == 10 ? this.showChat(false,true,true) : ""}
{/* Trade Finished */}
{this.props.data.isSeller & this.props.data.statusCode > 12 & this.props.data.statusCode < 15 ? this.showRateSelect() : ""}
+ {this.props.data.isBuyer & this.props.data.statusCode == 14 ? this.showRateSelect() : ""}
+
+ {/* Trade Finished - Payment Routing Failed */}
+ {this.props.data.isBuyer & this.props.data.statusCode == 15 ? this.showUpdateInvoice() : ""}
+
+ {/* Trade Finished - Payment Routing Failed - TODO Needs more planning */}
+ {this.props.data.statusCode == 11 ? this.showInDispute() : ""}
+
+
{/* TODO */}
{/* */}
{/* */}
diff --git a/setup.md b/setup.md
index 26307531..73c95a9d 100644
--- a/setup.md
+++ b/setup.md
@@ -58,15 +58,17 @@ git clone https://github.com/googleapis/googleapis.git
curl -o lightning.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/lightning.proto
python3 -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. lightning.proto
```
-We also use the *Invoices* subservice for invoice validation.
+We also use the *Invoices* and *Router* subservices for invoice validation and payment routing.
```
curl -o invoices.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/invoicesrpc/invoices.proto
python3 -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. invoices.proto
+curl -o router.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/routerrpc/router.proto
+python3 -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. router.proto
```
Relative imports are not working at the moment, so some editing is needed in
-`api/lightning` files `lightning_pb2_grpc.py`, `invoices_pb2_grpc.py` and `invoices_pb2.py`.
+`api/lightning` files `lightning_pb2_grpc.py`, `invoices_pb2_grpc.py`, `invoices_pb2.py`, `router_pb2_grpc.py` and `router_pb2.py`.
-Example, change line :
+For example in `lightning_pb2_grpc.py` , add "from . " :
`import lightning_pb2 as lightning__pb2`
@@ -74,6 +76,8 @@ to
`from . import lightning_pb2 as lightning__pb2`
+Same for every other file
+
## React development environment
### Install npm
`sudo apt install npm`
@@ -96,7 +100,7 @@ npm install react-native-svg
npm install react-qr-code
npm install @mui/material
```
-Note we are using mostly MaterialUI V5, but Image loading from V4 extentions (so both V4 and V5 are needed)
+Note we are using mostly MaterialUI V5 (@mui/material) but Image loading from V4 (@material-ui/core) extentions (so both V4 and V5 are needed)
### Launch the React render
from frontend/ directory