mirror of
https://github.com/yzernik/squeaknode.git
synced 2026-08-19 13:18:26 +02:00
Thread ancestors db query (#153)
* Add replyto field to squeak display response in rpc method * Got thread ancestors query working mostyly * Got thread ancestors query working * Fix docstring for ancestor query * Add check for ancestor query to itest
This commit is contained in:
parent
e5fd3cd066
commit
a37296565c
8 changed files with 129 additions and 9 deletions
4
init.sql
4
init.sql
|
|
@ -14,13 +14,13 @@ CREATE TABLE IF NOT EXISTS squeak (
|
|||
n_nonce BIGINT NOT NULL,
|
||||
enc_content CHAR(2272) NOT NULL, -- Encrypted content length is always 1136 bytes (2272 hex characters).
|
||||
vch_script_sig bytea NOT NULL,
|
||||
address VARCHAR(35) NOT NULL, -- Maximum length of a bitcoin address is 35.
|
||||
author_address VARCHAR(35) NOT NULL, -- Maximum length of a bitcoin address is 35.
|
||||
vch_decryption_key bytea NOT NULL,
|
||||
block_header bytea
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_squeak_address
|
||||
ON squeak(address);
|
||||
ON squeak(author_address);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS profile (
|
||||
profile_id SERIAL PRIMARY KEY,
|
||||
|
|
|
|||
|
|
@ -403,6 +403,53 @@ def run():
|
|||
get_profile_by_address_response.squeak_profile.profile_name == "bob"
|
||||
)
|
||||
|
||||
# Make another squeak as a reply
|
||||
reply_1_squeak_response = admin_stub.MakeSqueak(
|
||||
squeak_admin_pb2.MakeSqueakRequest(
|
||||
profile_id=profile_id,
|
||||
content="Reply #1",
|
||||
replyto=make_squeak_hash,
|
||||
)
|
||||
)
|
||||
reply_1_squeak_hash = reply_1_squeak_response.squeak_hash
|
||||
print("Get reply #1 squeak hash: " + str(reply_1_squeak_hash))
|
||||
|
||||
# Make a second squeak as a reply
|
||||
reply_2_squeak_response = admin_stub.MakeSqueak(
|
||||
squeak_admin_pb2.MakeSqueakRequest(
|
||||
profile_id=profile_id,
|
||||
content="Reply #2",
|
||||
replyto=reply_1_squeak_hash,
|
||||
)
|
||||
)
|
||||
reply_2_squeak_hash = reply_2_squeak_response.squeak_hash
|
||||
print("Get make reply squeak response: " + str(reply_2_squeak_response))
|
||||
|
||||
# Get the squeak and check that the reply field is correct
|
||||
get_reply_squeak_display_response = admin_stub.GetSqueakDisplay(
|
||||
squeak_admin_pb2.GetSqueakDisplayRequest(
|
||||
squeak_hash=reply_2_squeak_hash,
|
||||
)
|
||||
)
|
||||
print("Get reply squeak display entry response: " + str(get_reply_squeak_display_response))
|
||||
assert (
|
||||
get_reply_squeak_display_response.squeak_display_entry.squeak_hash == reply_2_squeak_hash
|
||||
)
|
||||
assert (
|
||||
get_reply_squeak_display_response.squeak_display_entry.reply_to == reply_1_squeak_hash
|
||||
)
|
||||
|
||||
# Get the ancestors of the latest reply squeak
|
||||
get_ancestors_response = admin_stub.GetAncestorSqueakDisplays(
|
||||
squeak_admin_pb2.GetAncestorSqueakDisplaysRequest(
|
||||
squeak_hash=reply_2_squeak_hash,
|
||||
)
|
||||
)
|
||||
print("Get ancestor squeak display entries response: " + str(get_ancestors_response))
|
||||
assert (
|
||||
len(get_ancestors_response.squeak_display_entries) == 3
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig()
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ service SqueakAdmin {
|
|||
*/
|
||||
rpc GetAddressSqueakDisplays (GetAddressSqueakDisplaysRequest) returns (GetAddressSqueakDisplaysReply) {}
|
||||
|
||||
/** sqkadmin: `getaddresssqueakdisplays`
|
||||
*/
|
||||
rpc GetAncestorSqueakDisplays (GetAncestorSqueakDisplaysRequest) returns (GetAncestorSqueakDisplaysReply) {}
|
||||
|
||||
}
|
||||
|
||||
message HelloRequest {
|
||||
|
|
@ -226,3 +230,13 @@ message GetAddressSqueakDisplaysReply {
|
|||
/// Multiple squeak display entries
|
||||
repeated SqueakDisplayEntry squeak_display_entries = 1;
|
||||
}
|
||||
|
||||
message GetAncestorSqueakDisplaysRequest {
|
||||
/// Hash of the squeak.
|
||||
string squeak_hash = 1;
|
||||
}
|
||||
|
||||
message GetAncestorSqueakDisplaysReply {
|
||||
/// Multiple squeak display entries
|
||||
repeated SqueakDisplayEntry squeak_display_entries = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,3 +96,14 @@ class SqueakAdminServerHandler(object):
|
|||
max_block,
|
||||
)
|
||||
return squeak_entries_with_profile
|
||||
|
||||
def handle_get_ancestor_squeak_display_entries(self, squeak_hash_str):
|
||||
logger.info("Handle get ancestor squeak display entries for squeak hash: {}".format(squeak_hash_str))
|
||||
squeak_entries_with_profile = self.squeak_node.get_ancestor_squeak_entries_with_profile(
|
||||
squeak_hash_str,
|
||||
)
|
||||
logger.info("Got number of ancestor squeak entries: {}".format(len(squeak_entries_with_profile)))
|
||||
logger.info("Got ancestor squeak display entries:")
|
||||
for entry in squeak_entries_with_profile:
|
||||
logger.info("Entry: {}".format(entry))
|
||||
return squeak_entries_with_profile
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import grpc
|
|||
|
||||
from proto import squeak_admin_pb2, squeak_admin_pb2_grpc
|
||||
|
||||
from squeakserver.server.util import get_hash
|
||||
from squeakserver.server.util import get_hash, get_replyto
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -132,6 +132,20 @@ class SqueakAdminServerServicer(squeak_admin_pb2_grpc.SqueakAdminServicer):
|
|||
squeak_display_entries=squeak_display_msgs
|
||||
)
|
||||
|
||||
def GetAncestorSqueakDisplays(self, request, context):
|
||||
squeak_hash_str = request.squeak_hash
|
||||
squeak_entries_with_profile = self.handler.handle_get_ancestor_squeak_display_entries(
|
||||
squeak_hash_str,
|
||||
)
|
||||
squeak_display_msgs = [
|
||||
self._squeak_entry_to_message(entry)
|
||||
for entry in
|
||||
squeak_entries_with_profile
|
||||
]
|
||||
return squeak_admin_pb2.GetFollowedSqueakDisplaysReply(
|
||||
squeak_display_entries=squeak_display_msgs
|
||||
)
|
||||
|
||||
def _squeak_entry_to_message(self, squeak_entry_with_profile):
|
||||
if squeak_entry_with_profile is None:
|
||||
return None
|
||||
|
|
@ -144,6 +158,8 @@ class SqueakAdminServerServicer(squeak_admin_pb2_grpc.SqueakAdminServicer):
|
|||
is_author_known = squeak_profile is not None
|
||||
author_name = squeak_profile.profile_name if squeak_profile else None
|
||||
author_address = str(squeak.GetAddress())
|
||||
is_reply = squeak.is_reply
|
||||
reply_to = get_replyto(squeak).hex() if is_reply else None
|
||||
return squeak_admin_pb2.SqueakDisplayEntry(
|
||||
squeak_hash=get_hash(squeak).hex(),
|
||||
is_unlocked=squeak.HasDecryptionKey(),
|
||||
|
|
@ -153,6 +169,8 @@ class SqueakAdminServerServicer(squeak_admin_pb2_grpc.SqueakAdminServicer):
|
|||
is_author_known=is_author_known,
|
||||
author_name=author_name,
|
||||
author_address=author_address,
|
||||
is_reply=is_reply,
|
||||
reply_to=reply_to,
|
||||
)
|
||||
|
||||
def _squeak_profile_to_message(self, squeak_profile):
|
||||
|
|
|
|||
|
|
@ -161,3 +161,8 @@ class SqueakNode:
|
|||
min_block,
|
||||
max_block,
|
||||
)
|
||||
|
||||
def get_ancestor_squeak_entries_with_profile(self, squeak_hash_str):
|
||||
return self.postgres_db.get_thread_ancestor_squeak_entries_with_profile(
|
||||
squeak_hash_str,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class PostgresDb:
|
|||
def insert_squeak(self, squeak):
|
||||
""" Insert a new squeak. """
|
||||
sql = """
|
||||
INSERT INTO squeak(hash, n_version, hash_enc_content, hash_reply_sqk, hash_block, n_block_height, vch_script_pub_key, vch_encryption_key, enc_data_key, iv, n_time, n_nonce, enc_content, vch_script_sig, address, vch_decryption_key)
|
||||
INSERT INTO squeak(hash, n_version, hash_enc_content, hash_reply_sqk, hash_block, n_block_height, vch_script_pub_key, vch_encryption_key, enc_data_key, iv, n_time, n_nonce, enc_content, vch_script_sig, author_address, vch_decryption_key)
|
||||
VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
RETURNING hash;"""
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ class PostgresDb:
|
|||
sql = """
|
||||
SELECT * FROM squeak
|
||||
LEFT JOIN profile
|
||||
ON squeak.address=profile.address
|
||||
ON squeak.author_address=profile.address
|
||||
WHERE squeak.hash=%s
|
||||
"""
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ class PostgresDb:
|
|||
sql = """
|
||||
SELECT * FROM squeak
|
||||
JOIN profile
|
||||
ON squeak.address=profile.address
|
||||
ON squeak.author_address=profile.address
|
||||
WHERE squeak.block_header IS NOT NULL
|
||||
AND profile.following=False
|
||||
ORDER BY n_block_height DESC, n_time DESC;
|
||||
|
|
@ -129,9 +129,9 @@ class PostgresDb:
|
|||
sql = """
|
||||
SELECT * FROM squeak
|
||||
JOIN profile
|
||||
ON squeak.address=profile.address
|
||||
ON squeak.author_address=profile.address
|
||||
WHERE squeak.block_header IS NOT NULL
|
||||
AND squeak.address=%s
|
||||
AND squeak.author_address=%s
|
||||
AND n_block_height >= %s
|
||||
AND n_block_height <= %s
|
||||
ORDER BY n_block_height DESC, n_time DESC;
|
||||
|
|
@ -141,11 +141,32 @@ class PostgresDb:
|
|||
rows = curs.fetchall()
|
||||
return [self._parse_squeak_entry_with_profile(row) for row in rows]
|
||||
|
||||
def get_thread_ancestor_squeak_entries_with_profile(self, squeak_hash_str):
|
||||
""" Get all reply ancestors of squeak hash. """
|
||||
sql = """
|
||||
WITH RECURSIVE is_thread_ancestor(n) AS (
|
||||
VALUES(%s)
|
||||
UNION\n
|
||||
SELECT hash_reply_sqk FROM squeak, is_thread_ancestor
|
||||
WHERE squeak.hash=is_thread_ancestor.n
|
||||
)
|
||||
SELECT * FROM squeak
|
||||
JOIN is_thread_ancestor
|
||||
ON squeak.hash=is_thread_ancestor.n
|
||||
LEFT JOIN profile
|
||||
ON squeak.author_address=profile.address
|
||||
WHERE squeak.block_header IS NOT NULL;
|
||||
"""
|
||||
with self.get_cursor() as curs:
|
||||
curs.execute(sql, (squeak_hash_str,))
|
||||
rows = curs.fetchall()
|
||||
return [self._parse_squeak_entry_with_profile(row) for row in rows]
|
||||
|
||||
def lookup_squeaks(self, addresses, min_block, max_block):
|
||||
""" Lookup squeaks. """
|
||||
sql = """
|
||||
SELECT hash FROM squeak
|
||||
WHERE address IN %s
|
||||
WHERE author_address IN %s
|
||||
AND n_block_height >= %s
|
||||
AND n_block_height <= %s
|
||||
AND vch_decryption_key IS NOT NULL
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ def get_hash(squeak):
|
|||
return squeak.GetHash()[::-1]
|
||||
|
||||
|
||||
def get_replyto(squeak):
|
||||
return squeak.hashReplySqk
|
||||
|
||||
|
||||
def generate_offer_nonce():
|
||||
return os.urandom(DATA_KEY_LENGTH)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue