From dd4ddabb5a6e31e65ec2f0a09dfc4887afb30dc4 Mon Sep 17 00:00:00 2001 From: Jonathan Zernik Date: Tue, 24 Aug 2021 05:09:00 -0700 Subject: [PATCH] Load squeak display entry without deserializing squeak object (#1052) * Load squeak display entry without deserializing squeak object * Use block time column instead of block header in squeak table * Include block time in squeak display response --- itests/tests/test_squeak_node.py | 8 ++++ squeaknode/admin/messages.py | 38 +++++++++---------- .../admin/squeak_admin_server_handler.py | 11 +++--- squeaknode/core/squeak_entry.py | 12 +++--- ...0_add_block_time_column_to_squeak_table.py | 36 ++++++++++++++++++ squeaknode/db/models.py | 2 +- squeaknode/db/squeak_db.py | 32 +++++----------- squeaknode/node/squeak_controller.py | 3 +- 8 files changed, 85 insertions(+), 57 deletions(-) create mode 100644 squeaknode/db/alembic/versions/a35d180bf020_add_block_time_column_to_squeak_table.py diff --git a/itests/tests/test_squeak_node.py b/itests/tests/test_squeak_node.py index b089f147..44151d54 100644 --- a/itests/tests/test_squeak_node.py +++ b/itests/tests/test_squeak_node.py @@ -91,6 +91,14 @@ def test_make_squeak(admin_stub, signing_profile_id): assert len( get_squeak_display_entry.author.profile_image) > 0 + # Block time should be within the past hour + block_time = datetime.datetime.fromtimestamp( + get_squeak_display_entry.block_time, + ) + one_hour = datetime.timedelta(hours=1) + assert block_time > datetime.datetime.now() - one_hour + assert block_time < datetime.datetime.now() + one_hour + # Get the squeak profile squeak_profile = get_squeak_profile(admin_stub, signing_profile_id) squeak_profile_address = squeak_profile.address diff --git a/squeaknode/admin/messages.py b/squeaknode/admin/messages.py index 23760a9e..93ad1bf9 100644 --- a/squeaknode/admin/messages.py +++ b/squeaknode/admin/messages.py @@ -1,5 +1,7 @@ import logging +from squeak.core import CSqueak + from proto import squeak_admin_pb2 from squeaknode.admin.profile_image_util import bytes_to_base64_string from squeaknode.admin.profile_image_util import load_default_profile_image @@ -13,7 +15,6 @@ from squeaknode.core.sent_payment_summary import SentPaymentSummary from squeaknode.core.squeak_entry_with_profile import SqueakEntryWithProfile from squeaknode.core.squeak_peer import SqueakPeer from squeaknode.core.squeak_profile import SqueakProfile -from squeaknode.core.util import get_hash from squeaknode.network.peer import Peer logger = logging.getLogger(__name__) @@ -21,36 +22,33 @@ logger = logging.getLogger(__name__) DEFAULT_PROFILE_IMAGE = load_default_profile_image() +# TODO: Remove this after reply is nullable column +EMPTY_HASH = b'\x00' * 32 + def squeak_entry_to_message(squeak_entry_with_profile: SqueakEntryWithProfile) -> squeak_admin_pb2.SqueakDisplayEntry: squeak_entry = squeak_entry_with_profile.squeak_entry - squeak = squeak_entry.squeak - block_header = squeak_entry.block_header - liked_time_s = squeak_entry.liked_time or 0 - is_unlocked = squeak.HasDecryptionKey() - content_str = squeak_entry.content if is_unlocked else None - is_reply = squeak.is_reply - reply_to = squeak.hashReplySqk.hex() if is_reply else None - author_address = str(squeak.GetAddress()) squeak_profile = squeak_entry_with_profile.squeak_profile + is_reply = squeak_entry.reply_to != EMPTY_HASH + reply_to = squeak_entry.reply_to.hex() if squeak_entry.reply_to else None is_author_known = False profile_msg = None if squeak_profile is not None: is_author_known = True profile_msg = squeak_profile_to_message(squeak_profile) return squeak_admin_pb2.SqueakDisplayEntry( - squeak_hash=get_hash(squeak).hex(), - is_unlocked=squeak.HasDecryptionKey(), - content_str=content_str, # type: ignore - block_height=squeak.nBlockHeight, - block_hash=squeak.hashBlock.hex(), - block_time=block_header.nTime, + squeak_hash=squeak_entry.squeak_hash.hex(), + is_unlocked=squeak_entry.is_unlocked, + content_str=squeak_entry.content, # type: ignore + block_height=squeak_entry.block_height, + block_hash=squeak_entry.block_hash.hex(), + block_time=squeak_entry.block_time, is_reply=is_reply, - reply_to=reply_to, - author_address=author_address, + reply_to=reply_to, # type: ignore + author_address=squeak_entry.address, is_author_known=is_author_known, author=profile_msg, - liked_time_s=liked_time_s, + liked_time_s=squeak_entry.liked_time, # type: ignore ) @@ -116,9 +114,7 @@ def sent_payment_to_message(sent_payment: SentPayment) -> squeak_admin_pb2.SentP ) -def squeak_entry_to_detail_message(squeak_entry_with_profile: SqueakEntryWithProfile) -> squeak_admin_pb2.SqueakDetailEntry: - squeak_entry = squeak_entry_with_profile.squeak_entry - squeak = squeak_entry.squeak +def squeak_to_detail_message(squeak: CSqueak) -> squeak_admin_pb2.SqueakDetailEntry: serialized_squeak = squeak.serialize() return squeak_admin_pb2.SqueakDetailEntry( serialized_squeak_hex=serialized_squeak.hex(), diff --git a/squeaknode/admin/squeak_admin_server_handler.py b/squeaknode/admin/squeak_admin_server_handler.py index 5000bec4..a897f6b3 100644 --- a/squeaknode/admin/squeak_admin_server_handler.py +++ b/squeaknode/admin/squeak_admin_server_handler.py @@ -9,10 +9,10 @@ from squeaknode.admin.messages import payment_summary_to_message from squeaknode.admin.messages import received_payments_to_message from squeaknode.admin.messages import sent_offer_to_message from squeaknode.admin.messages import sent_payment_to_message -from squeaknode.admin.messages import squeak_entry_to_detail_message from squeaknode.admin.messages import squeak_entry_to_message from squeaknode.admin.messages import squeak_peer_to_message from squeaknode.admin.messages import squeak_profile_to_message +from squeaknode.admin.messages import squeak_to_detail_message from squeaknode.admin.profile_image_util import base64_string_to_bytes from squeaknode.lightning.lnd_lightning_client import LNDLightningClient from squeaknode.node.squeak_controller import SqueakController @@ -547,17 +547,16 @@ class SqueakAdminServerHandler(object): squeak_hash = bytes.fromhex(squeak_hash_str) logger.info( "Handle get squeak details for hash: {}".format(squeak_hash_str)) - squeak_entry_with_profile = ( - self.squeak_controller.get_squeak_entry_with_profile( + squeak = ( + self.squeak_controller.get_squeak( squeak_hash ) ) - if squeak_entry_with_profile is None: + if squeak is None: return squeak_admin_pb2.GetSqueakDetailsReply( squeak_detail_entry=None ) - detail_message = squeak_entry_to_detail_message( - squeak_entry_with_profile) + detail_message = squeak_to_detail_message(squeak) return squeak_admin_pb2.GetSqueakDetailsReply( squeak_detail_entry=detail_message ) diff --git a/squeaknode/core/squeak_entry.py b/squeaknode/core/squeak_entry.py index d52a8730..e6d527a8 100644 --- a/squeaknode/core/squeak_entry.py +++ b/squeaknode/core/squeak_entry.py @@ -1,12 +1,14 @@ from typing import NamedTuple from typing import Optional -from bitcoin.core import CBlockHeader -from squeak.core import CSqueak - class SqueakEntry(NamedTuple): - squeak: CSqueak - block_header: CBlockHeader + squeak_hash: bytes + address: str + block_height: int + block_hash: bytes + block_time: int + reply_to: Optional[bytes] + is_unlocked: bool liked_time: Optional[int] = None content: Optional[str] = None diff --git a/squeaknode/db/alembic/versions/a35d180bf020_add_block_time_column_to_squeak_table.py b/squeaknode/db/alembic/versions/a35d180bf020_add_block_time_column_to_squeak_table.py new file mode 100644 index 00000000..c9b39c11 --- /dev/null +++ b/squeaknode/db/alembic/versions/a35d180bf020_add_block_time_column_to_squeak_table.py @@ -0,0 +1,36 @@ +"""Add block time column to squeak table + +Revision ID: a35d180bf020 +Revises: c0316d4931a5 +Create Date: 2021-08-24 04:27:45.463418 + +""" +import sqlalchemy as sa +from alembic import op + + +# revision identifiers, used by Alembic. +revision = 'a35d180bf020' +down_revision = 'c0316d4931a5' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('squeak', schema=None) as batch_op: + batch_op.add_column( + sa.Column('block_time', sa.Integer(), nullable=False, server_default=sa.text('0'))) + batch_op.drop_column('block_header') + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('squeak', schema=None) as batch_op: + batch_op.add_column( + sa.Column('block_header', sa.BLOB(), nullable=False, server_default=sa.literal(''))) + batch_op.drop_column('block_time') + + # ### end Alembic commands ### diff --git a/squeaknode/db/models.py b/squeaknode/db/models.py index 7366c5b5..5d687d48 100644 --- a/squeaknode/db/models.py +++ b/squeaknode/db/models.py @@ -69,7 +69,7 @@ class Models: Column("n_time", Integer, nullable=False), Column("author_address", String(35), index=True, nullable=False), Column("secret_key", String(64), nullable=True), - Column("block_header", Binary, nullable=False), + Column("block_time", Integer, nullable=False), Column("liked_time", TZDateTime, default=None, nullable=True), Column("content", String(280), nullable=True), diff --git a/squeaknode/db/squeak_db.py b/squeaknode/db/squeak_db.py index 7ee34a8d..1685274d 100644 --- a/squeaknode/db/squeak_db.py +++ b/squeaknode/db/squeak_db.py @@ -14,7 +14,6 @@ from sqlalchemy import literal from sqlalchemy.sql import select from squeak.core import CSqueak -from squeaknode.bitcoin.util import parse_block_header from squeaknode.core.lightning_address import LightningAddressHostPort from squeaknode.core.peer_address import PeerAddress from squeaknode.core.received_offer import ReceivedOffer @@ -174,7 +173,7 @@ class SqueakDb: n_time=squeak.nTime, author_address=str(squeak.GetAddress()), secret_key=None, - block_header=block_header.serialize(), + block_time=block_header.nTime, ) with self.get_connection() as connection: try: @@ -1103,30 +1102,19 @@ class SqueakDb: def _parse_squeak_entry(self, row) -> SqueakEntry: secret_key_column = row["secret_key"] - secret_key = ( - bytes.fromhex(secret_key_column) if - secret_key_column else b"" - ) - squeak = CSqueak.deserialize(row["squeak"]) - if secret_key: - squeak.SetDecryptionKey(secret_key) - block_header_column = row["block_header"] - block_header_bytes = ( - bytes(block_header_column) if - block_header_column else None - ) - block_header = ( - parse_block_header(block_header_bytes) if - block_header_bytes else None - ) + is_locked = bool(secret_key_column) liked_time = row["liked_time"] liked_time_s = int(liked_time.timestamp()) if liked_time else None - content = row["content"] return SqueakEntry( - squeak=squeak, - block_header=block_header, + squeak_hash=bytes.fromhex(row["hash"]), + address=row["author_address"], + block_height=row["n_block_height"], + block_hash=bytes.fromhex(row["hash_block"]), + block_time=row["block_time"], + reply_to=bytes.fromhex(row["hash_reply_sqk"]), + is_unlocked=is_locked, liked_time=liked_time_s, - content=content, + content=row["content"], ) def _parse_squeak_profile(self, row) -> SqueakProfile: diff --git a/squeaknode/node/squeak_controller.py b/squeaknode/node/squeak_controller.py index 142edce5..4cfca2a5 100644 --- a/squeaknode/node/squeak_controller.py +++ b/squeaknode/node/squeak_controller.py @@ -75,8 +75,7 @@ class SqueakController: return inserted_squeak_hash def unlock_squeak(self, squeak_hash: bytes, secret_key: bytes): - squeak_entry = self.squeak_db.get_squeak_entry(squeak_hash) - squeak = squeak_entry.squeak + squeak = self.squeak_db.get_squeak(squeak_hash) decrypted_content = self.squeak_core.get_decrypted_content( squeak, secret_key,