Add download replies button (#965)

* Add download replies rpc command

* Add download replies button to squeak page

* Use reply to hash in lookup query

* Update frontend build
This commit is contained in:
Jonathan Zernik 2021-08-17 23:00:58 -07:00 committed by GitHub
parent 95176be9c0
commit bf6fbc0ab7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 179 additions and 71 deletions

View file

@ -19,6 +19,7 @@ import TimelineDot from '@material-ui/lab/TimelineDot';
import Paper from '@material-ui/core/Paper';
import FaceIcon from '@material-ui/icons/Face';
import GetAppIcon from '@material-ui/icons/GetApp';
// styles
import useStyles from "./styles";
@ -38,6 +39,7 @@ import {
getAncestorSqueakDisplaysRequest,
getReplySqueakDisplaysRequest,
getNetworkRequest,
downloadRepliesRequest,
} from "../../squeakclient/requests"
import {
goToSqueakAddressPage,
@ -70,6 +72,14 @@ export default function SqueakPage() {
getSqueak(hash);
};
const onDownloadRepliesClick = (event) => {
event.preventDefault();
console.log("Handling download replies click...");
downloadRepliesRequest(hash, (response) => {
// Do nothing.
});
}
useEffect(()=>{
getSqueak(hash)
},[hash]);
@ -148,6 +158,7 @@ export default function SqueakPage() {
{AncestorsContent()}
</Timeline>
{CurrentSqueakContent()}
{DownloadRepliesButtonContent()}
{RepliesContent()}
</>
)
@ -169,6 +180,22 @@ export default function SqueakPage() {
)
}
function DownloadRepliesButtonContent() {
return (
<>
<Box p={1}>
<Button
variant="contained"
onClick={onDownloadRepliesClick}
>
<GetAppIcon />
Download replies
</Button>
</Box>
</>
)
}
return (
<>
{GridContent()}

View file

@ -118,6 +118,8 @@ import {
DisconnectPeerReply as DisconnectSqueakPeerReply,
DownloadOffersRequest,
DownloadOffersReply,
DownloadRepliesRequest,
DownloadRepliesReply,
} from "../proto/squeak_admin_pb"
console.log('The value of REACT_APP_SERVER_PORT is:', process.env.REACT_APP_SERVER_PORT);
@ -702,6 +704,17 @@ export function downloadOffersRequest(squeakHash, handleResponse) {
);
}
export function downloadRepliesRequest(squeakHash, handleResponse) {
var request = new DownloadRepliesRequest();
request.setSqueakHash(squeakHash);
makeRequest(
'downloadreplies',
request,
DownloadRepliesReply.deserializeBinary,
handleResponse,
);
}
export function getSqueakDetailsRequest(hash, handleResponse) {
var request = new GetSqueakDetailsRequest();
request.setSqueakHash(hash);

View file

@ -200,6 +200,10 @@ service SqueakAdmin {
*/
rpc DownloadOffers (DownloadOffersRequest) returns (DownloadOffersReply) {}
/** sqkadmin: `downloadreplies`
*/
rpc DownloadReplies (DownloadRepliesRequest) returns (DownloadRepliesReply) {}
/** sqkadmin: `payoffer`
*/
rpc PayOffer (PayOfferRequest) returns (PayOfferReply) {}
@ -792,6 +796,14 @@ message DownloadOffersRequest {
message DownloadOffersReply {
}
message DownloadRepliesRequest {
/// The squeak hash.
string squeak_hash = 1;
}
message DownloadRepliesReply {
}
message GetSqueakDetailsRequest {
/// Hash of the squeak.
string squeak_hash = 1;

View file

@ -536,6 +536,14 @@ class SqueakAdminServerHandler(object):
self.squeak_controller.download_offers(squeak_hash)
return squeak_admin_pb2.DownloadOffersReply()
def handle_download_replies(self, request):
squeak_hash_str = request.squeak_hash
squeak_hash = bytes.fromhex(squeak_hash_str)
logger.info(
"Handle download replies for hash: {}".format(squeak_hash_str))
self.squeak_controller.download_replies(squeak_hash)
return squeak_admin_pb2.DownloadRepliesReply()
def handle_pay_offer(self, request):
offer_id = request.offer_id
logger.info("Handle pay offer for offer id: {}".format(offer_id))

View file

@ -184,6 +184,9 @@ class SqueakAdminServerServicer(squeak_admin_pb2_grpc.SqueakAdminServicer):
def DownloadOffers(self, request, context):
return self.handler.handle_download_offers(request)
def DownloadReplies(self, request, context):
return self.handler.handle_download_replies(request)
def PayOffer(self, request, context):
return self.handler.handle_pay_offer(request)

View file

@ -451,6 +451,14 @@ def create_app(handler, username, password):
handler.handle_download_offers,
)
@app.route("/downloadreplies", methods=["POST"])
@login_required
def downloadreplies():
return handle_request(
squeak_admin_pb2.DownloadRepliesRequest(),
handler.handle_download_replies,
)
@app.route("/getsqueakdetails", methods=["POST"])
@login_required
def getsqueakdetails():

View file

@ -1,17 +1,17 @@
{
"files": {
"main.js": "/static/js/main.8e06516c.chunk.js",
"main.js.map": "/static/js/main.8e06516c.chunk.js.map",
"main.js": "/static/js/main.8eee83b3.chunk.js",
"main.js.map": "/static/js/main.8eee83b3.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.15110fae.chunk.css": "/static/css/2.15110fae.chunk.css",
"static/js/2.0bf951c4.chunk.js": "/static/js/2.0bf951c4.chunk.js",
"static/js/2.0bf951c4.chunk.js.map": "/static/js/2.0bf951c4.chunk.js.map",
"static/js/2.e761e728.chunk.js": "/static/js/2.e761e728.chunk.js",
"static/js/2.e761e728.chunk.js.map": "/static/js/2.e761e728.chunk.js.map",
"index.html": "/index.html",
"precache-manifest.d7723da04e3e0a7a8f1b66c43f1f21d4.js": "/precache-manifest.d7723da04e3e0a7a8f1b66c43f1f21d4.js",
"precache-manifest.b3c33283266462810bfc5d9d3abcb68e.js": "/precache-manifest.b3c33283266462810bfc5d9d3abcb68e.js",
"service-worker.js": "/service-worker.js",
"static/css/2.15110fae.chunk.css.map": "/static/css/2.15110fae.chunk.css.map",
"static/js/2.0bf951c4.chunk.js.LICENSE.txt": "/static/js/2.0bf951c4.chunk.js.LICENSE.txt",
"static/js/2.e761e728.chunk.js.LICENSE.txt": "/static/js/2.e761e728.chunk.js.LICENSE.txt",
"static/media/font-awesome.min.css": "/static/media/fontawesome-webfont.fee66e71.woff",
"static/media/google.svg": "/static/media/google.695a3160.svg",
"static/media/logo.svg": "/static/media/logo.a0185b04.svg"
@ -19,7 +19,7 @@
"entrypoints": [
"static/js/runtime-main.9f0ba400.js",
"static/css/2.15110fae.chunk.css",
"static/js/2.0bf951c4.chunk.js",
"static/js/main.8e06516c.chunk.js"
"static/js/2.e761e728.chunk.js",
"static/js/main.8eee83b3.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.15110fae.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.0bf951c4.chunk.js"></script><script src="/static/js/main.8e06516c.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.15110fae.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.e761e728.chunk.js"></script><script src="/static/js/main.8eee83b3.chunk.js"></script></body></html>

View file

@ -1,23 +1,23 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "246f49675c81dab180475f86be05d500",
"revision": "48165ff5c237731663a8ef1e46176f0c",
"url": "/index.html"
},
{
"revision": "f27cc370bf4b79bdb6eb",
"revision": "38f2816b4209a7ec0cec",
"url": "/static/css/2.15110fae.chunk.css"
},
{
"revision": "f27cc370bf4b79bdb6eb",
"url": "/static/js/2.0bf951c4.chunk.js"
"revision": "38f2816b4209a7ec0cec",
"url": "/static/js/2.e761e728.chunk.js"
},
{
"revision": "7156b2c9571000777b9be03b3c6006ee",
"url": "/static/js/2.0bf951c4.chunk.js.LICENSE.txt"
"url": "/static/js/2.e761e728.chunk.js.LICENSE.txt"
},
{
"revision": "8c0f62e63eeca801cb58",
"url": "/static/js/main.8e06516c.chunk.js"
"revision": "4688282489da4f5094ee",
"url": "/static/js/main.8eee83b3.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.d7723da04e3e0a7a8f1b66c43f1f21d4.js"
"/precache-manifest.b3c33283266462810bfc5d9d3abcb68e.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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -11,8 +11,6 @@ import sqlalchemy
from bitcoin.core import CBlockHeader
from sqlalchemy import func
from sqlalchemy import literal
from sqlalchemy.sql import and_
from sqlalchemy.sql import or_
from sqlalchemy.sql import select
from squeak.core import CSqueak
@ -377,24 +375,40 @@ class SqueakDb:
addresses: List[str],
min_block: int,
max_block: int,
reply_to_hash: bytes,
include_locked: bool = False,
) -> List[bytes]:
""" Lookup squeaks. """
if not addresses:
return []
s = (
select([self.squeaks.c.hash])
.where(self.squeaks.c.author_address.in_(addresses))
.where(self.squeaks.c.n_block_height >= min_block)
.where(self.squeaks.c.n_block_height <= max_block)
.where(
or_(
self.squeak_has_secret_key,
include_locked,
)
)
)
logger.info("""Running lookup query with
addresses: {},
min_block: {}
max_block: {}
reply_to_hash: {!r}
include_locked: {}""".format(
addresses,
min_block,
max_block,
reply_to_hash,
include_locked,
))
s = select([self.squeaks.c.hash])
if addresses:
s = s.where(self.squeaks.c.author_address.in_(addresses))
if min_block:
s = s.where(self.squeaks.c.n_block_height >= min_block)
if max_block:
s = s.where(self.squeaks.c.n_block_height <= max_block)
if reply_to_hash:
s = s.where(self.squeaks.c.hash_reply_sqk == reply_to_hash.hex())
if not include_locked:
s = s.where(self.squeak_has_secret_key)
logger.info("Query s: {}".format(s))
with self.get_connection() as connection:
# logger.info("Mogrify lookup_squeaks: {}.".format(
# connection.connection.cursor().mogrify(s),
@ -424,40 +438,40 @@ class SqueakDb:
num_squeaks = row["num_squeaks"]
return num_squeaks
def lookup_squeaks_needing_offer(
self,
addresses: List[str],
min_block: int,
max_block: int,
peer_address: PeerAddress,
) -> List[bytes]:
""" Lookup squeaks that are locked and don't have an offer. """
if not addresses:
return []
# def lookup_squeaks_needing_offer(
# self,
# addresses: List[str],
# min_block: int,
# max_block: int,
# peer_address: PeerAddress,
# ) -> List[bytes]:
# """ Lookup squeaks that are locked and don't have an offer. """
# if not addresses:
# return []
s = (
select([self.squeaks.c.hash])
.select_from(
self.squeaks.outerjoin(
self.received_offers,
and_(
self.received_offers.c.squeak_hash == self.squeaks.c.hash,
self.received_offers.c.peer_host == peer_address.host,
self.received_offers.c.peer_port == peer_address.port,
),
)
)
.where(self.squeaks.c.author_address.in_(addresses))
.where(self.squeaks.c.n_block_height >= min_block)
.where(self.squeaks.c.n_block_height <= max_block)
.where(self.squeak_has_no_secret_key)
.where(self.received_offer_does_not_exist)
)
with self.get_connection() as connection:
result = connection.execute(s)
rows = result.fetchall()
hashes = [bytes.fromhex(row["hash"]) for row in rows]
return hashes
# s = (
# select([self.squeaks.c.hash])
# .select_from(
# self.squeaks.outerjoin(
# self.received_offers,
# and_(
# self.received_offers.c.squeak_hash == self.squeaks.c.hash,
# self.received_offers.c.peer_host == peer_address.host,
# self.received_offers.c.peer_port == peer_address.port,
# ),
# )
# )
# .where(self.squeaks.c.author_address.in_(addresses))
# .where(self.squeaks.c.n_block_height >= min_block)
# .where(self.squeaks.c.n_block_height <= max_block)
# .where(self.squeak_has_no_secret_key)
# .where(self.received_offer_does_not_exist)
# )
# with self.get_connection() as connection:
# result = connection.execute(s)
# rows = result.fetchall()
# hashes = [bytes.fromhex(row["hash"]) for row in rows]
# return hashes
def get_old_squeaks_to_delete(
self,

View file

@ -17,6 +17,8 @@ from squeaknode.node.squeak_controller import SqueakController
logger = logging.getLogger(__name__)
EMPTY_HASH = b'\x00' * 32
class PeerMessageHandler:
"""Handles incoming messages from peers.
@ -153,10 +155,12 @@ class PeerMessageHandler:
def handle_getsqueaks(self, msg):
# TODO: Maybe combine all invs into a single send_msg.
for interest in msg.locator.vInterested:
reply_to_hash = interest.hashReplySqk if interest.hashReplySqk != EMPTY_HASH else None
squeak_hashes = self.squeak_controller.lookup_squeaks_for_interest(
address=str(interest.address),
min_block=interest.nMinBlockHeight,
max_block=interest.nMaxBlockHeight,
reply_to_hash=reply_to_hash,
)
invs = [
CInv(type=1, hash=squeak_hash)

View file

@ -510,11 +510,13 @@ class SqueakController:
address: str,
min_block: int,
max_block: int,
reply_to_hash: bytes,
):
return self.squeak_db.lookup_squeaks(
[address],
min_block,
max_block,
reply_to_hash,
)
def filter_known_invs(self, invs):
@ -583,6 +585,23 @@ class SqueakController:
getdata_msg = msg_getdata(inv=invs)
self.broadcast_msg(getdata_msg)
def download_replies(self, squeak_hash: bytes):
logger.info("Downloading replies for squeak: {}".format(
squeak_hash.hex(),
))
interests = [
CInterested(
hashReplySqk=squeak_hash,
)
]
locator = CSqueakLocator(
vInterested=interests,
)
getsqueaks_msg = msg_getsqueaks(
locator=locator,
)
self.broadcast_msg(getsqueaks_msg)
def filter_shared_squeak_locator(self, interests: List[CInterested]):
ret = []
block_range = self.get_block_range()