From f9a02b2245bdbca009615f11fe60eae549aa109e Mon Sep 17 00:00:00 2001 From: Jonathan Zernik Date: Mon, 11 Jan 2021 19:43:33 -0800 Subject: [PATCH] Add docstrings for core classes and methods (#611) --- squeaknode/core/buy_offer.py | 4 +- squeaknode/core/received_offer.py | 2 +- squeaknode/core/received_payment.py | 1 + squeaknode/core/sent_offer.py | 1 + squeaknode/core/sent_payment.py | 1 + squeaknode/core/squeak_controller.py | 4 +- squeaknode/core/squeak_core.py | 170 +++++++++++++++++++-------- squeaknode/core/squeak_peer.py | 1 + squeaknode/core/squeak_profile.py | 1 + 9 files changed, 135 insertions(+), 50 deletions(-) diff --git a/squeaknode/core/buy_offer.py b/squeaknode/core/buy_offer.py index 731154c9..0b472be0 100644 --- a/squeaknode/core/buy_offer.py +++ b/squeaknode/core/buy_offer.py @@ -2,7 +2,9 @@ from typing import NamedTuple class BuyOffer(NamedTuple): - """Class for representing a generated offer on a seller node.""" + """Represents the offer details that are sent from seller + to buyer. + """ squeak_hash: bytes nonce: bytes payment_request: str diff --git a/squeaknode/core/received_offer.py b/squeaknode/core/received_offer.py index 37a65f6a..88bcb848 100644 --- a/squeaknode/core/received_offer.py +++ b/squeaknode/core/received_offer.py @@ -3,7 +3,7 @@ from typing import Optional class ReceivedOffer(NamedTuple): - """Class for saving an offer from a remote peer.""" + """Represents an offer received by a buyer.""" received_offer_id: Optional[int] squeak_hash: bytes price_msat: int diff --git a/squeaknode/core/received_payment.py b/squeaknode/core/received_payment.py index c2dd930b..214f7bff 100644 --- a/squeaknode/core/received_payment.py +++ b/squeaknode/core/received_payment.py @@ -4,6 +4,7 @@ from typing import Optional class ReceivedPayment(NamedTuple): + """Represents an payment received by a seller.""" received_payment_id: Optional[int] created: Optional[datetime] squeak_hash: bytes diff --git a/squeaknode/core/sent_offer.py b/squeaknode/core/sent_offer.py index b856f232..90b4bb0c 100644 --- a/squeaknode/core/sent_offer.py +++ b/squeaknode/core/sent_offer.py @@ -3,6 +3,7 @@ from typing import Optional class SentOffer(NamedTuple): + """Represents an offer generated by a seller to be sent.""" sent_offer_id: Optional[int] squeak_hash: bytes payment_hash: bytes diff --git a/squeaknode/core/sent_payment.py b/squeaknode/core/sent_payment.py index 5ebd829f..99bd1679 100644 --- a/squeaknode/core/sent_payment.py +++ b/squeaknode/core/sent_payment.py @@ -4,6 +4,7 @@ from typing import Optional class SentPayment(NamedTuple): + """Represents a payment made by a buyer.""" sent_payment_id: Optional[int] created: Optional[datetime] peer_id: int diff --git a/squeaknode/core/squeak_controller.py b/squeaknode/core/squeak_controller.py index 8005a049..60359d8a 100644 --- a/squeaknode/core/squeak_controller.py +++ b/squeaknode/core/squeak_controller.py @@ -74,7 +74,7 @@ class SqueakController: def get_buy_offer(self, squeak_hash: bytes, client_addr: str): # Check if there is an existing offer for the hash/client_addr combination sent_offer = self.get_saved_sent_offer(squeak_hash, client_addr) - return self.squeak_core.create_buy_offer( + return self.squeak_core.package_offer( sent_offer, self.config.lnd.external_host, self.config.lnd.port, @@ -298,7 +298,7 @@ class SqueakController: return self.config.core.network def get_offer(self, squeak: CSqueak, offer_msg: squeak_server_pb2.SqueakBuyOffer, peer: SqueakPeer) -> ReceivedOffer: - return self.squeak_core.get_offer(squeak, offer_msg, peer) + return self.squeak_core.unpack_offer(squeak, offer_msg, peer) def get_squeak_entry_with_profile(self, squeak_hash: bytes): return self.squeak_db.get_squeak_entry_with_profile(squeak_hash) diff --git a/squeaknode/core/squeak_core.py b/squeaknode/core/squeak_core.py index 591b9ca8..c04da8b2 100644 --- a/squeaknode/core/squeak_core.py +++ b/squeaknode/core/squeak_core.py @@ -37,6 +37,19 @@ class SqueakCore: self.lightning_client = lightning_client def make_squeak(self, signing_profile: SqueakProfile, content_str: str, replyto_hash: Optional[bytes] = None) -> SqueakEntry: + """Create a new squeak. + + Args: + signing_profile: The profile of the author of the squeak. + content_str: The content of the squeak as a string. + replyto_hash: The hash of the squeak to which this one is replying. + + Returns: + SqueakEntry: containing the squeak together with its block header. + + Raises: + Exception: If the profile does not have a signing key. + """ if signing_profile.private_key is None: raise Exception("Can't make squeak with a contact profile.") signing_key_str = signing_profile.private_key.decode() @@ -59,6 +72,18 @@ class SqueakCore: ) def validate_squeak(self, squeak: CSqueak) -> SqueakEntry: + """Checks if the embedded block hash in the squeak is valid for its + block height. + + Args: + squeak: The squeak to be validated. + + Returns: + SqueakEntry: containing the squeak together with its block header. + + Raises: + Exception: If the block hash is not valid. + """ block_info = self.blockchain_client.get_block_info_by_height( squeak.nBlockHeight) if squeak.hashBlock != block_info.block_hash: @@ -69,10 +94,25 @@ class SqueakCore: ) def get_best_block_height(self) -> int: + """Get the current height of the latest block in the blockchain. + + Returns: + int: the current latest block height. + """ block_info = self.blockchain_client.get_best_block_info() return block_info.block_height def create_offer(self, squeak: CSqueak, client_addr: str, 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. + price_msat: The price in msats. + + Returns: + SentOffer: A record of the details of the offer for the seller. + """ # Get the squeak hash squeak_hash = get_hash(squeak) # Generate a new random nonce @@ -108,7 +148,18 @@ class SqueakCore: client_addr=client_addr, ) - def create_buy_offer(self, sent_offer: SentOffer, lnd_external_host: str, lnd_port: int) -> BuyOffer: + def package_offer(self, sent_offer: SentOffer, lnd_external_host: str, lnd_port: int) -> BuyOffer: + """Package the offer details into a message that will be sent from + seller to buyer. + + Args: + sent_offer: The offer that was already generated by the seller. + lnd_external_host: The host of the lnd node. + lnd_port: The port of the lnd node. + + Returns: + SentOffer: A record of the details of the offer for the seller. + """ return BuyOffer( squeak_hash=sent_offer.squeak_hash, nonce=sent_offer.nonce, @@ -117,53 +168,18 @@ class SqueakCore: port=lnd_port, ) - def pay_offer(self, received_offer: ReceivedOffer) -> SentPayment: - if received_offer.received_offer_id is None: - raise Exception("Received offer must have a non-null offer_id.") - # Pay the invoice - payment = self.lightning_client.pay_invoice_sync( - received_offer.payment_request) - preimage = payment.payment_preimage - if not preimage: - raise Exception( - "Payment failed with error: {}".format(payment.payment_error) - ) - # Calculate the secret key - nonce = received_offer.nonce - # secret_key = bxor(nonce, preimage) - secret_key = subtract_tweak(preimage, nonce) - # Save the preimage of the sent payment - return SentPayment( - sent_payment_id=None, - created=None, - peer_id=received_offer.peer_id, - squeak_hash=received_offer.squeak_hash, - payment_hash=received_offer.payment_hash, - secret_key=secret_key, - price_msat=received_offer.price_msat, - node_pubkey=received_offer.destination, - ) + def unpack_offer(self, squeak: CSqueak, offer: BuyOffer, peer: SqueakPeer) -> ReceivedOffer: + """Get the offer details from the message that the buyer + receives from the seller. - def get_received_payments(self, get_sent_offer_fn, latest_settle_index) -> Iterator[ReceivedPayment]: - for invoice in self.lightning_client.subscribe_invoices( - settle_index=latest_settle_index, - ): - if invoice.settled: - payment_hash = invoice.r_hash - settle_index = invoice.settle_index - sent_offer = get_sent_offer_fn(payment_hash) - received_payment = ReceivedPayment( - received_payment_id=None, - created=None, - squeak_hash=sent_offer.squeak_hash, - payment_hash=sent_offer.payment_hash, - price_msat=sent_offer.price_msat, - settle_index=settle_index, - client_addr=sent_offer.client_addr, - ) - yield received_payment + Args: + squeak: The squeak that will be unlocked upon payment. + offer: The offer details received from the seller. + peer: The peer that sent the offer. - def get_offer(self, squeak: CSqueak, offer: BuyOffer, peer: SqueakPeer) -> ReceivedOffer: + Returns: + ReceivedOffer: A record of the details of the offer for the buyer. + """ if peer.peer_id is None: raise Exception("Peer must have a non-null peer_id.") # Get the squeak hash @@ -212,3 +228,65 @@ class SqueakCore: # ) # ) return decoded_offer + + def pay_offer(self, received_offer: ReceivedOffer) -> SentPayment: + """Pay the offer that the buyer received from the seller. + + Args: + received_offer: The details of the offer received by the buyer. + + Returns: + SentPayment: A record of the sent payment. + """ + # Pay the invoice + payment = self.lightning_client.pay_invoice_sync( + received_offer.payment_request) + preimage = payment.payment_preimage + if not preimage: + raise Exception( + "Payment failed with error: {}".format(payment.payment_error) + ) + # Calculate the secret key + nonce = received_offer.nonce + # secret_key = bxor(nonce, preimage) + secret_key = subtract_tweak(preimage, nonce) + # Save the preimage of the sent payment + return SentPayment( + sent_payment_id=None, + created=None, + peer_id=received_offer.peer_id, + squeak_hash=received_offer.squeak_hash, + payment_hash=received_offer.payment_hash, + secret_key=secret_key, + price_msat=received_offer.price_msat, + node_pubkey=received_offer.destination, + ) + + def get_received_payments(self, get_sent_offer_fn, latest_settle_index) -> Iterator[ReceivedPayment]: + """Get an iterator of received payments. + + Args: + get_sent_offer_fn: Function that takes a payment hash and returns + the corresponding SentOffer. + latest_settle_index: The latest settle index of the lnd invoice database. + + Returns: + Iterator[ReceivedPayment]: An iterator of received payments. + """ + for invoice in self.lightning_client.subscribe_invoices( + settle_index=latest_settle_index, + ): + if invoice.settled: + payment_hash = invoice.r_hash + settle_index = invoice.settle_index + sent_offer = get_sent_offer_fn(payment_hash) + received_payment = ReceivedPayment( + received_payment_id=None, + created=None, + squeak_hash=sent_offer.squeak_hash, + payment_hash=sent_offer.payment_hash, + price_msat=sent_offer.price_msat, + settle_index=settle_index, + client_addr=sent_offer.client_addr, + ) + yield received_payment diff --git a/squeaknode/core/squeak_peer.py b/squeaknode/core/squeak_peer.py index 75b2456b..96d8530b 100644 --- a/squeaknode/core/squeak_peer.py +++ b/squeaknode/core/squeak_peer.py @@ -3,6 +3,7 @@ from typing import Optional class SqueakPeer(NamedTuple): + """Represents another node in the network.""" peer_id: Optional[int] peer_name: str host: str diff --git a/squeaknode/core/squeak_profile.py b/squeaknode/core/squeak_profile.py index 9d83fc1f..7f87532d 100644 --- a/squeaknode/core/squeak_profile.py +++ b/squeaknode/core/squeak_profile.py @@ -3,6 +3,7 @@ from typing import Optional class SqueakProfile(NamedTuple): + """Represents a user who can author squeaks.""" profile_id: Optional[int] profile_name: str private_key: Optional[bytes]