Save host and port of buyer client (#868)

* Save host and port of buyer client

* Show buyer port in frontend for received payments
This commit is contained in:
Jonathan Zernik 2021-02-21 20:01:02 -08:00 committed by GitHub
parent 55b9c61b60
commit a93025a026
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 89 additions and 53 deletions

View file

@ -92,7 +92,17 @@ export default function ReceivedPayment({
alignItems="flex-start"
>
<Grid item>
Buyer address:<span> </span>{receivedPayment.getClientAddr()}
Buyer host:<span> </span>{receivedPayment.getClientHost()}
</Grid>
</Grid>
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<Grid item>
Buyer port:<span> </span>{receivedPayment.getClientPort()}
</Grid>
</Grid>
</Box>

View file

@ -1212,7 +1212,7 @@ def test_connect_other_node(
five_minutes = datetime.timedelta(minutes=5)
assert received_payment_time > datetime.datetime.now() - five_minutes
assert received_payment_time < datetime.datetime.now()
assert len(received_payment.client_addr) > 4
assert len(received_payment.client_host) > 4
# Subscribe to received payments starting from index zero
subscribe_received_payments_response = admin_stub.SubscribeReceivedPayments(

View file

@ -810,8 +810,11 @@ message ReceivedPayment {
/// Time of payment
int64 time_s = 5;
/// The address of the buyer client
string client_addr = 6;
/// The host of the buyer client
string client_host = 6;
/// The port of the buyer client
int32 client_port = 7;
}
message SubscribeReceivedPaymentsRequest {

View file

@ -163,7 +163,8 @@ def received_payments_to_message(received_payment: ReceivedPayment) -> squeak_ad
payment_hash=received_payment.payment_hash.hex(),
price_msat=received_payment.price_msat,
time_s=int(received_payment.created.timestamp()),
client_addr=received_payment.client_addr,
client_host=received_payment.client_addr.host,
client_port=received_payment.client_addr.port,
)

View file

@ -1,14 +1,14 @@
{
"files": {
"main.js": "/static/js/main.3d1a3f73.chunk.js",
"main.js.map": "/static/js/main.3d1a3f73.chunk.js.map",
"main.js": "/static/js/main.0de375a2.chunk.js",
"main.js.map": "/static/js/main.0de375a2.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.9f0ba400.js",
"runtime-main.js.map": "/static/js/runtime-main.9f0ba400.js.map",
"static/css/2.ea4ba2f0.chunk.css": "/static/css/2.ea4ba2f0.chunk.css",
"static/js/2.c2e186c2.chunk.js": "/static/js/2.c2e186c2.chunk.js",
"static/js/2.c2e186c2.chunk.js.map": "/static/js/2.c2e186c2.chunk.js.map",
"index.html": "/index.html",
"precache-manifest.224463fd4137d8feb8ecb501115807e3.js": "/precache-manifest.224463fd4137d8feb8ecb501115807e3.js",
"precache-manifest.64bccad812e22e0ac14c4020841ec65b.js": "/precache-manifest.64bccad812e22e0ac14c4020841ec65b.js",
"service-worker.js": "/service-worker.js",
"static/css/2.ea4ba2f0.chunk.css.map": "/static/css/2.ea4ba2f0.chunk.css.map",
"static/js/2.c2e186c2.chunk.js.LICENSE.txt": "/static/js/2.c2e186c2.chunk.js.LICENSE.txt",
@ -20,6 +20,6 @@
"static/js/runtime-main.9f0ba400.js",
"static/css/2.ea4ba2f0.chunk.css",
"static/js/2.c2e186c2.chunk.js",
"static/js/main.3d1a3f73.chunk.js"
"static/js/main.0de375a2.chunk.js"
]
}

View file

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Squeak Node</title><meta name="description" content="Squeak Node is a frontend for accessing a squeak node"><meta name="keywords" content="squeak, bitcoin, lightning"><meta name="author" content="Flatlogic LLC."><link href="/static/css/2.ea4ba2f0.chunk.css" rel="stylesheet"></head><body style="font-family:Roboto,sans-serif"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,f,l=r[0],a=r[1],i=r[2],c=0,s=[];c<l.length;c++)f=l[c],Object.prototype.hasOwnProperty.call(o,f)&&o[f]&&s.push(o[f][0]),o[f]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var a=t[l];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var l=this["webpackJsonpsqueak-node-frontend"]=this["webpackJsonpsqueak-node-frontend"]||[],a=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var p=a;t()}([])</script><script src="/static/js/2.c2e186c2.chunk.js"></script><script src="/static/js/main.3d1a3f73.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Squeak Node</title><meta name="description" content="Squeak Node is a frontend for accessing a squeak node"><meta name="keywords" content="squeak, bitcoin, lightning"><meta name="author" content="Flatlogic LLC."><link href="/static/css/2.ea4ba2f0.chunk.css" rel="stylesheet"></head><body style="font-family:Roboto,sans-serif"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,f,l=r[0],a=r[1],i=r[2],c=0,s=[];c<l.length;c++)f=l[c],Object.prototype.hasOwnProperty.call(o,f)&&o[f]&&s.push(o[f][0]),o[f]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var a=t[l];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var l=this["webpackJsonpsqueak-node-frontend"]=this["webpackJsonpsqueak-node-frontend"]||[],a=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var p=a;t()}([])</script><script src="/static/js/2.c2e186c2.chunk.js"></script><script src="/static/js/main.0de375a2.chunk.js"></script></body></html>

View file

@ -1,6 +1,6 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "5672130e7d9cee3d5f1297d43dd8562d",
"revision": "3b5466d119bb648e7ac82d07adb4fc79",
"url": "/index.html"
},
{
@ -16,8 +16,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/js/2.c2e186c2.chunk.js.LICENSE.txt"
},
{
"revision": "d2ee60b5c892192d4417",
"url": "/static/js/main.3d1a3f73.chunk.js"
"revision": "0d15fde7ac5b43bf673b",
"url": "/static/js/main.0de375a2.chunk.js"
},
{
"revision": "cc9816de5a8639d377ea",

View file

@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"/precache-manifest.224463fd4137d8feb8ecb501115807e3.js"
"/precache-manifest.64bccad812e22e0ac14c4020841ec65b.js"
);
self.addEventListener('message', (event) => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -2,6 +2,8 @@ from datetime import datetime
from typing import NamedTuple
from typing import Optional
from squeaknode.core.peer_address import PeerAddress
class ReceivedPayment(NamedTuple):
"""Represents an payment received by a seller."""
@ -11,4 +13,4 @@ class ReceivedPayment(NamedTuple):
payment_hash: bytes
price_msat: int
settle_index: int
client_addr: str
client_addr: PeerAddress

View file

@ -1,6 +1,8 @@
from typing import NamedTuple
from typing import Optional
from squeaknode.core.peer_address import PeerAddress
class SentOffer(NamedTuple):
"""Represents an offer generated by a seller to be sent."""
@ -13,4 +15,4 @@ class SentOffer(NamedTuple):
payment_request: str
invoice_time: int
invoice_expiry: int
client_addr: str
client_addr: PeerAddress

View file

@ -116,12 +116,12 @@ class SqueakCore:
block_info = self.bitcoin_client.get_best_block_info()
return block_info.block_height
def create_offer(self, squeak: CSqueak, client_addr: str, price_msat: int) -> SentOffer:
def create_offer(self, squeak: CSqueak, client_address: PeerAddress, price_msat: int) -> SentOffer:
"""Creates an offer to sell a squeak key to another node.
Args:
squeak: The squeak to be sold.
client_addr: The IP address of the buyer.
client_address: The address of the buyer.
price_msat: The price in msats.
Returns:
@ -159,7 +159,7 @@ class SqueakCore:
payment_request=invoice_payment_request,
invoice_time=invoice_time,
invoice_expiry=invoice_expiry,
client_addr=client_addr,
client_addr=client_address,
)
def package_offer(self, sent_offer: SentOffer, lnd_external_host: str, lnd_port: int) -> Offer:

View file

@ -1,8 +1,8 @@
"""Initialize all
Revision ID: d0b60e721ff7
Revision ID: 0676aadc35c6
Revises:
Create Date: 2021-02-21 16:00:52.473818
Create Date: 2021-02-21 17:28:39.366082
"""
import sqlalchemy as sa
@ -12,7 +12,7 @@ import squeaknode.db.models
# revision identifiers, used by Alembic.
revision = 'd0b60e721ff7'
revision = '0676aadc35c6'
down_revision = None
branch_labels = None
depends_on = None
@ -89,8 +89,8 @@ def upgrade():
sa.Column('price_msat', sa.Integer(), nullable=False),
sa.Column('settle_index',
squeaknode.db.models.SLBigInteger(), nullable=False),
sa.Column('client_addr', sa.String(
length=64), nullable=False),
sa.Column('client_host', sa.String(), nullable=False),
sa.Column('client_port', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('received_payment_id'),
sa.UniqueConstraint('payment_hash'),
sqlite_autoincrement=True
@ -112,8 +112,8 @@ def upgrade():
sa.Column('invoice_timestamp',
sa.Integer(), nullable=False),
sa.Column('invoice_expiry', sa.Integer(), nullable=False),
sa.Column('client_addr', sa.String(
length=64), nullable=False),
sa.Column('client_host', sa.String(), nullable=False),
sa.Column('client_port', sa.Integer(), nullable=False),
sa.Column('paid', sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint('sent_offer_id'),
sa.UniqueConstraint('payment_hash'),

View file

@ -157,7 +157,8 @@ class Models:
Column("payment_request", String, nullable=False),
Column("invoice_timestamp", Integer, nullable=False),
Column("invoice_expiry", Integer, nullable=False),
Column("client_addr", String(64), nullable=False),
Column("client_host", String, nullable=False),
Column("client_port", Integer, nullable=False),
Column("paid", Boolean, nullable=False, default=False),
sqlite_autoincrement=True,
)
@ -172,6 +173,7 @@ class Models:
Column("payment_hash", String(64), unique=True, nullable=False),
Column("price_msat", Integer, nullable=False),
Column("settle_index", SLBigInteger, nullable=False),
Column("client_addr", String(64), nullable=False),
Column("client_host", String, nullable=False),
Column("client_port", Integer, nullable=False),
sqlite_autoincrement=True,
)

View file

@ -844,7 +844,8 @@ class SqueakDb:
payment_request=sent_offer.payment_request,
invoice_timestamp=sent_offer.invoice_time,
invoice_expiry=sent_offer.invoice_expiry,
client_addr=sent_offer.client_addr,
client_host=sent_offer.client_addr.host,
client_port=sent_offer.client_addr.port,
)
with self.get_connection() as connection:
res = connection.execute(ins)
@ -875,15 +876,15 @@ class SqueakDb:
sent_offer = self._parse_sent_offer(row)
return sent_offer
def get_sent_offer_by_squeak_hash_and_client_addr(self, squeak_hash: bytes, client_addr: str) -> Optional[SentOffer]:
def get_sent_offer_by_squeak_hash_and_client(self, squeak_hash: bytes, client_address: PeerAddress) -> Optional[SentOffer]:
"""
Get a sent offer by squeak hash and client addr. Only
Get a sent offer by squeak hash and client address host. Only
return sent offer if it's not expired and not paid.
"""
s = (
select([self.sent_offers])
.where(self.sent_offers.c.squeak_hash == squeak_hash.hex())
.where(self.sent_offers.c.client_addr == client_addr)
.where(self.sent_offers.c.client_host == client_address.host)
.where(self.sent_offer_is_not_paid)
.where(self.sent_offer_is_not_expired)
)
@ -944,7 +945,8 @@ class SqueakDb:
payment_hash=received_payment.payment_hash.hex(),
price_msat=received_payment.price_msat,
settle_index=received_payment.settle_index,
client_addr=received_payment.client_addr,
client_host=received_payment.client_addr.host,
client_port=received_payment.client_addr.port,
)
with self.get_connection() as connection:
try:
@ -1147,7 +1149,10 @@ class SqueakDb:
payment_request=row["payment_request"],
invoice_time=row["invoice_timestamp"],
invoice_expiry=row["invoice_expiry"],
client_addr=row["client_addr"],
client_addr=PeerAddress(
host=row["client_host"],
port=row["client_port"],
),
)
def _parse_received_payment(self, row) -> ReceivedPayment:
@ -1158,7 +1163,10 @@ class SqueakDb:
payment_hash=bytes.fromhex(row["payment_hash"]),
price_msat=row["price_msat"],
settle_index=row["settle_index"],
client_addr=row["client_addr"],
client_addr=PeerAddress(
host=row["client_host"],
port=row["client_port"],
),
)
def _parse_received_payment_summary(self, row) -> ReceivedPaymentSummary:

View file

@ -133,27 +133,27 @@ class SqueakController:
followed_addresses = self.get_followed_addresses()
return set(followed_addresses) & set(addresses)
def get_buy_offer(self, squeak_hash: bytes, client_addr: str) -> Offer:
def get_buy_offer(self, squeak_hash: bytes, client_address: PeerAddress) -> Offer:
# Check if there is an existing offer for the hash/client_addr combination
sent_offer = self.get_saved_sent_offer(squeak_hash, client_addr)
sent_offer = self.get_saved_sent_offer(squeak_hash, client_address)
return self.squeak_core.package_offer(
sent_offer,
self.config.lnd.external_host,
self.config.lnd.port,
)
def get_saved_sent_offer(self, squeak_hash: bytes, client_addr: str) -> SentOffer:
def get_saved_sent_offer(self, squeak_hash: bytes, client_address: PeerAddress) -> SentOffer:
# Check if there is an existing offer for the hash/client_addr combination
sent_offer = self.squeak_db.get_sent_offer_by_squeak_hash_and_client_addr(
sent_offer = self.squeak_db.get_sent_offer_by_squeak_hash_and_client(
squeak_hash,
client_addr,
client_address,
)
if sent_offer:
return sent_offer
squeak = self.get_squeak(squeak_hash)
sent_offer = self.squeak_core.create_offer(
squeak,
client_addr,
client_address,
self.config.core.price_msat,
)
self.squeak_db.insert_sent_offer(sent_offer)

View file

@ -3,6 +3,7 @@ import logging
from squeak.core import CSqueak
from proto import squeak_server_pb2
from squeaknode.core.peer_address import PeerAddress
from squeaknode.core.util import get_hash
from squeaknode.node.squeak_controller import SqueakController
@ -80,12 +81,14 @@ class SqueakServerHandler(object):
max_block=block_range.max_block,
)
def handle_get_offer(self, squeak_hash: bytes, client_addr: str):
def handle_get_offer(self, squeak_hash: bytes, client_address: PeerAddress):
logger.info(
"Handle get offer by hash: {} from client_addr: {}".format(
squeak_hash.hex(), client_addr
squeak_hash.hex(), client_address,
)
)
buy_offer = self.squeak_controller.get_buy_offer(
squeak_hash, client_addr)
squeak_hash,
client_address,
)
return buy_offer

View file

@ -8,7 +8,7 @@ from proto import squeak_server_pb2_grpc
from squeaknode.network.messages import offer_to_msg
from squeaknode.network.messages import squeak_from_msg
from squeaknode.network.messages import squeak_to_msg
from squeaknode.server.util import parse_ip_address
from squeaknode.server.util import parse_client_address
logger = logging.getLogger(__name__)
@ -51,9 +51,9 @@ class SqueakServerServicer(squeak_server_pb2_grpc.SqueakServerServicer):
def DownloadOffer(self, request, context):
squeak_hash = request.hash
# TODO: check if hash is valid
client_addr = context.peer()
ip_addr = parse_ip_address(client_addr)
offer = self.handler.handle_get_offer(squeak_hash, ip_addr)
grpc_address = context.peer()
client_addr = parse_client_address(grpc_address)
offer = self.handler.handle_get_offer(squeak_hash, client_addr)
if offer is None:
raise Exception("Offer not found.")
logger.info("Sending offer: {}".format(offer))

View file

@ -1,4 +1,9 @@
from squeaknode.core.peer_address import PeerAddress
def parse_ip_address(grpc_addr_str: str) -> str:
return grpc_addr_str.split(':')[1]
def parse_client_address(grpc_addr_str: str) -> PeerAddress:
_, host, port = grpc_addr_str.split(':')
return PeerAddress(
host=host,
port=int(port),
)