mirror of
https://github.com/yzernik/squeaknode.git
synced 2026-08-15 12:50:47 +02:00
Add squeak core module (#587)
* Add squeak_core module * Remove unused import * Delete squeakblockverifier * Use squeak_core to make squeak * Remove blockchain client from controller * Move create offer method to squeak core * Add some type hints * Move get buy offer method to squeak_core * Delete commented old get buy offer block * Add pay_offer to squeak_core * Add get received payments generator function to squak core * Add get_offer method to squeak_core * Remove lightning_client property from peer_task * Remove old comments from payment verifier class * Use bytes internally for payment_hash * Represent secret_key as bytes internally
This commit is contained in:
parent
ae61726053
commit
138ef7d984
14 changed files with 443 additions and 279 deletions
|
|
@ -81,6 +81,17 @@ services:
|
|||
- ../createdb.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
- squeaknode_pgdata:/var/lib/postgresql
|
||||
|
||||
squeaknode_other_db:
|
||||
image: postgres
|
||||
container_name: test_squeaknode_other_db
|
||||
environment:
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
volumes:
|
||||
- ../createdb.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
- squeaknode_other_pgdata:/var/lib/postgresql
|
||||
|
||||
squeaknode:
|
||||
image: squeaknode
|
||||
container_name: test_squeaknode
|
||||
|
|
@ -110,6 +121,8 @@ services:
|
|||
build:
|
||||
context: ../
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
- SQUEAKNODE_DB_CONNECTION_STRING=postgresql://postgres:postgres@squeaknode_other_db/squeaknode
|
||||
volumes:
|
||||
- test_shared:/rpc
|
||||
- test_lnd_client_dir:/root/.lnd
|
||||
|
|
@ -117,6 +130,7 @@ services:
|
|||
links:
|
||||
- "btcd:btcd"
|
||||
- "lnd_client:lnd"
|
||||
- "squeaknode_other_db:squeaknode_other_db"
|
||||
sysctls:
|
||||
- net.ipv6.conf.all.disable_ipv6=0
|
||||
entrypoint: ["./start-squeaknode.sh"]
|
||||
|
|
@ -155,3 +169,5 @@ volumes:
|
|||
# postgres db data
|
||||
squeaknode_pgdata:
|
||||
driver: local
|
||||
squeaknode_other_pgdata:
|
||||
driver: local
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ def sent_payment_with_peer_to_message(sent_payment_with_peer):
|
|||
peer_name=peer.peer_name,
|
||||
squeak_hash=sent_payment.squeak_hash.hex(),
|
||||
payment_hash=sent_payment.payment_hash.hex(),
|
||||
secret_key=sent_payment.secret_key,
|
||||
secret_key=sent_payment.secret_key.hex(),
|
||||
price_msat=sent_payment.price_msat,
|
||||
node_pubkey=sent_payment.node_pubkey,
|
||||
time_ms=int(sent_payment.created.timestamp()) * 1000,
|
||||
|
|
@ -136,8 +136,8 @@ def sent_offer_to_message(sent_offer):
|
|||
return squeak_admin_pb2.SentOffer(
|
||||
sent_offer_id=sent_offer.sent_offer_id,
|
||||
squeak_hash=sent_offer.squeak_hash.hex(),
|
||||
payment_hash=sent_offer.payment_hash,
|
||||
secret_key=sent_offer.secret_key,
|
||||
payment_hash=sent_offer.payment_hash.hex(),
|
||||
secret_key=sent_offer.secret_key.hex(),
|
||||
nonce=sent_offer.nonce.hex(),
|
||||
price_msat=sent_offer.price_msat,
|
||||
)
|
||||
|
|
@ -149,7 +149,7 @@ def received_payments_to_message(received_payment):
|
|||
return squeak_admin_pb2.ReceivedPayment(
|
||||
received_payment_id=received_payment.received_payment_id,
|
||||
squeak_hash=received_payment.squeak_hash.hex(),
|
||||
payment_hash=received_payment.payment_hash,
|
||||
payment_hash=received_payment.payment_hash.hex(),
|
||||
price_msat=received_payment.price_msat,
|
||||
payment_time_ms=int(received_payment.created.timestamp()) * 1000,
|
||||
client_addr=received_payment.client_addr,
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ class Offer(NamedTuple):
|
|||
"""Class for saving an offer from a remote peer."""
|
||||
offer_id: Optional[int]
|
||||
squeak_hash: bytes
|
||||
price_msat: bytes
|
||||
price_msat: int
|
||||
payment_hash: bytes
|
||||
nonce: str
|
||||
nonce: bytes
|
||||
payment_point: bytes
|
||||
invoice_timestamp: int
|
||||
invoice_expiry: int
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ class SentPayment(NamedTuple):
|
|||
payment_hash: bytes
|
||||
secret_key: bytes
|
||||
price_msat: int
|
||||
node_pubkey: bytes
|
||||
node_pubkey: str
|
||||
|
|
|
|||
|
|
@ -6,21 +6,15 @@ from squeak.core import CSqueak
|
|||
from squeak.core.signing import CSigningKey
|
||||
from squeak.core.signing import CSqueakAddress
|
||||
|
||||
from squeaknode.core.buy_offer import BuyOffer
|
||||
from proto import squeak_server_pb2
|
||||
from squeaknode.core.offer import Offer
|
||||
from squeaknode.core.sent_offer import SentOffer
|
||||
from squeaknode.core.sent_payment import SentPayment
|
||||
from squeaknode.core.squeak_address_validator import SqueakAddressValidator
|
||||
from squeaknode.core.squeak_peer import SqueakPeer
|
||||
from squeaknode.core.squeak_profile import SqueakProfile
|
||||
from squeaknode.core.util import add_tweak
|
||||
from squeaknode.core.util import generate_tweak
|
||||
from squeaknode.core.util import subtract_tweak
|
||||
from squeaknode.node.received_payments_subscription_client import (
|
||||
OpenReceivedPaymentsSubscriptionClient,
|
||||
)
|
||||
from squeaknode.node.sent_offers_verifier import SentOffersVerifier
|
||||
from squeaknode.node.squeak_maker import SqueakMaker
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -29,20 +23,18 @@ class SqueakController:
|
|||
def __init__(
|
||||
self,
|
||||
squeak_db,
|
||||
blockchain_client,
|
||||
lightning_client,
|
||||
squeak_core,
|
||||
squeak_store,
|
||||
squeak_whitelist,
|
||||
config,
|
||||
):
|
||||
self.squeak_db = squeak_db
|
||||
self.blockchain_client = blockchain_client
|
||||
self.lightning_client = lightning_client
|
||||
self.squeak_core = squeak_core
|
||||
self.squeak_store = squeak_store
|
||||
self.squeak_whitelist = squeak_whitelist
|
||||
self.sent_offers_verifier = SentOffersVerifier(
|
||||
self.squeak_db,
|
||||
self.lightning_client,
|
||||
self.squeak_core,
|
||||
)
|
||||
self.config = config
|
||||
|
||||
|
|
@ -64,59 +56,10 @@ 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)
|
||||
# Get the lightning network node pubkey
|
||||
get_info_response = self.lightning_client.get_info()
|
||||
pubkey = get_info_response.identity_pubkey
|
||||
# Return the buy offer
|
||||
return BuyOffer(
|
||||
squeak_hash=squeak_hash,
|
||||
price_msat=self.config.core.price_msat,
|
||||
nonce=sent_offer.nonce,
|
||||
payment_request=sent_offer.payment_request,
|
||||
pubkey=pubkey,
|
||||
host=self.config.lnd.external_host,
|
||||
port=self.config.lnd.port,
|
||||
)
|
||||
|
||||
def create_offer(self, squeak_hash: bytes, client_addr: str):
|
||||
# Generate a new random nonce
|
||||
nonce = generate_tweak()
|
||||
# Get the squeak secret key
|
||||
squeak = self.squeak_store.get_squeak(squeak_hash)
|
||||
secret_key = squeak.GetDecryptionKey()
|
||||
# Calculate the preimage
|
||||
# preimage = bxor(nonce, secret_key)
|
||||
preimage = add_tweak(secret_key, nonce)
|
||||
logger.info(
|
||||
"Create offer with secret key: {} nonce: {} preimage: {}".format(
|
||||
secret_key, nonce, preimage
|
||||
)
|
||||
)
|
||||
# Create the lightning invoice
|
||||
add_invoice_response = self.lightning_client.add_invoice(
|
||||
preimage, self.config.core.price_msat
|
||||
)
|
||||
logger.info("add_invoice_response: {}".format(add_invoice_response))
|
||||
payment_hash = add_invoice_response.r_hash
|
||||
invoice_payment_request = add_invoice_response.payment_request
|
||||
# invoice_expiry = add_invoice_response.expiry
|
||||
lookup_invoice_response = self.lightning_client.lookup_invoice(
|
||||
payment_hash.hex()
|
||||
)
|
||||
invoice_time = lookup_invoice_response.creation_date
|
||||
invoice_expiry = lookup_invoice_response.expiry
|
||||
# Save the incoming potential payment in the databse.
|
||||
return SentOffer(
|
||||
sent_offer_id=None,
|
||||
squeak_hash=squeak_hash,
|
||||
payment_hash=payment_hash.hex(),
|
||||
secret_key=preimage.hex(),
|
||||
nonce=nonce,
|
||||
price_msat=self.config.core.price_msat,
|
||||
payment_request=invoice_payment_request,
|
||||
invoice_time=invoice_time,
|
||||
invoice_expiry=invoice_expiry,
|
||||
client_addr=client_addr,
|
||||
return self.squeak_core.create_buy_offer(
|
||||
sent_offer,
|
||||
self.config.lnd.external_host,
|
||||
self.config.lnd.port,
|
||||
)
|
||||
|
||||
def get_saved_sent_offer(self, squeak_hash: bytes, client_addr: str):
|
||||
|
|
@ -127,7 +70,14 @@ class SqueakController:
|
|||
)
|
||||
if sent_offer:
|
||||
return sent_offer
|
||||
sent_offer = self.create_offer(squeak_hash, client_addr)
|
||||
squeak = self.squeak_store.get_squeak(squeak_hash)
|
||||
# sent_offer = self.create_offer(
|
||||
# squeak, client_addr, self.config.core.price_msat)
|
||||
sent_offer = self.squeak_core.create_offer(
|
||||
squeak,
|
||||
client_addr,
|
||||
self.config.core.price_msat,
|
||||
)
|
||||
self.squeak_db.insert_sent_offer(sent_offer)
|
||||
return sent_offer
|
||||
|
||||
|
|
@ -189,10 +139,9 @@ class SqueakController:
|
|||
|
||||
def make_squeak(self, profile_id: int, content_str: str, replyto_hash: bytes):
|
||||
squeak_profile = self.squeak_db.get_profile(profile_id)
|
||||
squeak_maker = SqueakMaker(self.blockchain_client)
|
||||
squeak = squeak_maker.make_squeak(
|
||||
squeak_entry = self.squeak_core.make_squeak(
|
||||
squeak_profile, content_str, replyto_hash)
|
||||
return self.save_created_squeak(squeak)
|
||||
return self.save_created_squeak(squeak_entry.squeak)
|
||||
|
||||
def get_squeak_entry_with_profile(self, squeak_hash: bytes):
|
||||
return self.squeak_store.get_squeak_entry_with_profile(squeak_hash)
|
||||
|
|
@ -258,42 +207,10 @@ class SqueakController:
|
|||
offer_with_peer = self.squeak_db.get_offer_with_peer(offer_id)
|
||||
offer = offer_with_peer.offer
|
||||
logger.info("Paying offer: {}".format(offer))
|
||||
|
||||
# Pay the invoice
|
||||
payment = self.lightning_client.pay_invoice_sync(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 = offer.nonce
|
||||
# secret_key = bxor(nonce, preimage)
|
||||
secret_key = subtract_tweak(preimage, nonce)
|
||||
logger.info(
|
||||
"Pay offer with secret key: {} nonce: {} preimage: {}".format(
|
||||
secret_key, nonce, preimage
|
||||
)
|
||||
)
|
||||
|
||||
# Save the preimage of the sent payment
|
||||
sent_payment = SentPayment(
|
||||
sent_payment_id=None,
|
||||
created=None,
|
||||
offer_id=offer_id,
|
||||
peer_id=offer.peer_id,
|
||||
squeak_hash=offer.squeak_hash,
|
||||
payment_hash=offer.payment_hash,
|
||||
secret_key=secret_key.hex(),
|
||||
price_msat=offer.price_msat,
|
||||
node_pubkey=offer.destination,
|
||||
)
|
||||
sent_payment = self.squeak_core.pay_offer(offer)
|
||||
sent_payment_id = self.squeak_db.insert_sent_payment(sent_payment)
|
||||
|
||||
secret_key = sent_payment.secret_key
|
||||
self.unlock_squeak(offer, secret_key)
|
||||
|
||||
return sent_payment_id
|
||||
|
||||
def unlock_squeak(self, offer: Offer, secret_key: bytes):
|
||||
|
|
@ -350,10 +267,10 @@ class SqueakController:
|
|||
yield payment
|
||||
|
||||
def get_best_block_height(self):
|
||||
block_info = self.blockchain_client.get_best_block_info()
|
||||
return block_info.block_height
|
||||
return self.squeak_core.get_best_block_height()
|
||||
|
||||
def get_network(self):
|
||||
print(self.config)
|
||||
print(self.config.core)
|
||||
return self.config.core.network
|
||||
|
||||
def get_offer(self, squeak: CSqueak, offer_msg: squeak_server_pb2.SqueakBuyOffer, peer: SqueakPeer) -> Offer:
|
||||
return self.squeak_core.get_offer(squeak, offer_msg, peer)
|
||||
|
|
|
|||
251
squeaknode/core/squeak_core.py
Normal file
251
squeaknode/core/squeak_core.py
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
import logging
|
||||
import time
|
||||
from typing import Iterator
|
||||
from typing import Optional
|
||||
|
||||
from squeak.core import CSqueak
|
||||
from squeak.core import MakeSqueakFromStr
|
||||
from squeak.core.signing import CSigningKey
|
||||
|
||||
from proto import squeak_server_pb2
|
||||
from squeaknode.core.buy_offer import BuyOffer
|
||||
from squeaknode.core.offer import Offer
|
||||
from squeaknode.core.received_payment import ReceivedPayment
|
||||
from squeaknode.core.sent_offer import SentOffer
|
||||
from squeaknode.core.sent_payment import SentPayment
|
||||
from squeaknode.core.squeak_entry import SqueakEntry
|
||||
from squeaknode.core.squeak_peer import SqueakPeer
|
||||
from squeaknode.core.squeak_profile import SqueakProfile
|
||||
from squeaknode.core.util import add_tweak
|
||||
from squeaknode.core.util import generate_tweak
|
||||
from squeaknode.core.util import get_hash
|
||||
from squeaknode.core.util import subtract_tweak
|
||||
from squeaknode.node.received_payments_subscription_client import (
|
||||
OpenReceivedPaymentsSubscriptionClient,
|
||||
)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SqueakCore:
|
||||
def __init__(
|
||||
self,
|
||||
blockchain_client,
|
||||
lightning_client,
|
||||
):
|
||||
self.blockchain_client = blockchain_client
|
||||
self.lightning_client = lightning_client
|
||||
|
||||
def make_squeak(self, signing_profile: SqueakProfile, content_str: str, replyto_hash: Optional[bytes] = None) -> SqueakEntry:
|
||||
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()
|
||||
signing_key = CSigningKey(signing_key_str)
|
||||
block_info = self.blockchain_client.get_best_block_info()
|
||||
block_height = block_info.block_height
|
||||
block_hash = block_info.block_hash
|
||||
timestamp = int(time.time())
|
||||
if replyto_hash is None or len(replyto_hash) == 0:
|
||||
squeak = MakeSqueakFromStr(
|
||||
signing_key,
|
||||
content_str,
|
||||
block_height,
|
||||
block_hash,
|
||||
timestamp,
|
||||
)
|
||||
else:
|
||||
squeak = MakeSqueakFromStr(
|
||||
signing_key,
|
||||
content_str,
|
||||
block_height,
|
||||
block_hash,
|
||||
timestamp,
|
||||
replyto_hash,
|
||||
)
|
||||
return SqueakEntry(
|
||||
squeak=squeak,
|
||||
block_header=block_info.block_header,
|
||||
)
|
||||
|
||||
def validate_squeak(self, squeak: CSqueak) -> SqueakEntry:
|
||||
block_info = self.blockchain_client.get_block_info_by_height(
|
||||
squeak.nBlockHeight)
|
||||
if squeak.hashBlock != block_info.block_hash:
|
||||
raise Exception("Block hash incorrect.")
|
||||
return SqueakEntry(
|
||||
squeak=squeak,
|
||||
block_header=block_info.block_header,
|
||||
)
|
||||
|
||||
def get_best_block_height(self) -> int:
|
||||
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:
|
||||
# Get the squeak hash
|
||||
squeak_hash = get_hash(squeak)
|
||||
# Generate a new random nonce
|
||||
nonce = generate_tweak()
|
||||
# Get the squeak secret key
|
||||
secret_key = squeak.GetDecryptionKey()
|
||||
# Calculate the preimage
|
||||
# preimage = bxor(nonce, secret_key)
|
||||
preimage = add_tweak(secret_key, nonce)
|
||||
# Create the lightning invoice
|
||||
add_invoice_response = self.lightning_client.add_invoice(
|
||||
preimage, price_msat
|
||||
)
|
||||
payment_hash = add_invoice_response.r_hash
|
||||
invoice_payment_request = add_invoice_response.payment_request
|
||||
# invoice_expiry = add_invoice_response.expiry
|
||||
lookup_invoice_response = self.lightning_client.lookup_invoice(
|
||||
payment_hash.hex()
|
||||
)
|
||||
invoice_time = lookup_invoice_response.creation_date
|
||||
invoice_expiry = lookup_invoice_response.expiry
|
||||
# Save the incoming potential payment in the databse.
|
||||
return SentOffer(
|
||||
sent_offer_id=None,
|
||||
squeak_hash=squeak_hash,
|
||||
payment_hash=payment_hash,
|
||||
secret_key=preimage,
|
||||
nonce=nonce,
|
||||
price_msat=price_msat,
|
||||
payment_request=invoice_payment_request,
|
||||
invoice_time=invoice_time,
|
||||
invoice_expiry=invoice_expiry,
|
||||
client_addr=client_addr,
|
||||
)
|
||||
|
||||
def create_buy_offer(self, sent_offer: SentOffer, lnd_external_host: str, lnd_port: int) -> BuyOffer:
|
||||
# Get the lightning network node pubkey
|
||||
get_info_response = self.lightning_client.get_info()
|
||||
pubkey = get_info_response.identity_pubkey
|
||||
# Return the buy offer
|
||||
return BuyOffer(
|
||||
squeak_hash=sent_offer.squeak_hash,
|
||||
price_msat=sent_offer.price_msat,
|
||||
nonce=sent_offer.nonce,
|
||||
payment_request=sent_offer.payment_request,
|
||||
pubkey=pubkey,
|
||||
host=lnd_external_host,
|
||||
port=lnd_port,
|
||||
)
|
||||
|
||||
def pay_offer(self, offer: Offer) -> SentPayment:
|
||||
if offer.offer_id is None:
|
||||
raise Exception("Offer must have a non-null offer_id.")
|
||||
# Pay the invoice
|
||||
payment = self.lightning_client.pay_invoice_sync(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 = 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,
|
||||
offer_id=offer.offer_id,
|
||||
peer_id=offer.peer_id,
|
||||
squeak_hash=offer.squeak_hash,
|
||||
payment_hash=offer.payment_hash,
|
||||
secret_key=secret_key,
|
||||
price_msat=offer.price_msat,
|
||||
node_pubkey=offer.destination,
|
||||
)
|
||||
|
||||
def get_received_payments(self, get_sent_offer_fn, latest_settle_index, retry_s) -> Iterator[ReceivedPayment]:
|
||||
try:
|
||||
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 = self.squeak_db.get_sent_offer_by_payment_hash(
|
||||
# payment_hash)
|
||||
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,
|
||||
)
|
||||
# self.squeak_db.insert_received_payment(received_payment)
|
||||
yield received_payment
|
||||
|
||||
except Exception:
|
||||
logger.info(
|
||||
"Unable to subscribe invoices from lnd. Retrying in "
|
||||
"{} seconds.".format(retry_s),
|
||||
)
|
||||
time.sleep(retry_s)
|
||||
|
||||
def get_offer(self, squeak: CSqueak, offer_msg: squeak_server_pb2.SqueakBuyOffer, peer: SqueakPeer) -> Offer:
|
||||
if peer.peer_id is None:
|
||||
raise Exception("Peer must have a non-null peer_id.")
|
||||
|
||||
# Get the squeak hash
|
||||
squeak_hash = get_hash(squeak)
|
||||
|
||||
# Decode the payment request
|
||||
pay_req = self.lightning_client.decode_pay_req(
|
||||
offer_msg.payment_request)
|
||||
logger.info("Decoded payment request: {}".format(pay_req))
|
||||
|
||||
squeak_payment_point = squeak.paymentPoint
|
||||
payment_hash = bytes.fromhex(pay_req.payment_hash)
|
||||
price_msat = pay_req.num_msat
|
||||
destination = pay_req.destination
|
||||
invoice_timestamp = pay_req.timestamp
|
||||
invoice_expiry = pay_req.expiry
|
||||
node_host = offer_msg.host or peer.host
|
||||
node_port = offer_msg.port
|
||||
|
||||
logger.info("price_msat: {}".format(price_msat))
|
||||
logger.info("destination: {}".format(destination))
|
||||
logger.info("invoice_timestamp: {}".format(invoice_timestamp))
|
||||
logger.info("invoice_expiry: {}".format(invoice_expiry))
|
||||
logger.info("node_host: {}".format(node_host))
|
||||
logger.info("node_port: {}".format(node_port))
|
||||
|
||||
decoded_offer = Offer(
|
||||
offer_id=None,
|
||||
squeak_hash=squeak_hash,
|
||||
price_msat=price_msat,
|
||||
payment_hash=payment_hash,
|
||||
nonce=offer_msg.nonce,
|
||||
payment_point=squeak_payment_point,
|
||||
invoice_timestamp=invoice_timestamp,
|
||||
invoice_expiry=invoice_expiry,
|
||||
payment_request=offer_msg.payment_request,
|
||||
destination=destination,
|
||||
node_host=node_host,
|
||||
node_port=node_port,
|
||||
peer_id=peer.peer_id,
|
||||
)
|
||||
|
||||
# TODO: Check the payment point
|
||||
# payment_point = offer.payment_point
|
||||
# logger.info("Payment point: {}".format(payment_point.hex()))
|
||||
# expected_payment_point = squeak.paymentPoint
|
||||
# logger.info("Expected payment point: {}".format(expected_payment_point.hex()))
|
||||
# if payment_point != expected_payment_point:
|
||||
# raise Exception(
|
||||
# "Invalid offer payment point: {}, expected: {}".format(
|
||||
# payment_point.hex(),
|
||||
# expected_payment_point.hex(),
|
||||
# )
|
||||
# )
|
||||
|
||||
return decoded_offer
|
||||
|
|
@ -732,9 +732,9 @@ class SqueakDb:
|
|||
""" Insert a new offer. """
|
||||
ins = self.offers.insert().values(
|
||||
squeak_hash=offer.squeak_hash.hex(),
|
||||
payment_hash=offer.payment_hash,
|
||||
payment_hash=offer.payment_hash.hex(),
|
||||
nonce=offer.nonce.hex(),
|
||||
payment_point=offer.payment_point,
|
||||
payment_point=offer.payment_point.hex(),
|
||||
invoice_timestamp=offer.invoice_timestamp,
|
||||
invoice_expiry=offer.invoice_expiry,
|
||||
price_msat=offer.price_msat,
|
||||
|
|
@ -881,8 +881,8 @@ class SqueakDb:
|
|||
offer_id=sent_payment.offer_id,
|
||||
peer_id=sent_payment.peer_id,
|
||||
squeak_hash=sent_payment.squeak_hash.hex(),
|
||||
payment_hash=sent_payment.payment_hash,
|
||||
secret_key=sent_payment.secret_key,
|
||||
payment_hash=sent_payment.payment_hash.hex(),
|
||||
secret_key=sent_payment.secret_key.hex(),
|
||||
price_msat=sent_payment.price_msat,
|
||||
node_pubkey=sent_payment.node_pubkey,
|
||||
)
|
||||
|
|
@ -933,8 +933,8 @@ class SqueakDb:
|
|||
""" Insert a new sent offer. """
|
||||
ins = self.sent_offers.insert().values(
|
||||
squeak_hash=sent_offer.squeak_hash.hex(),
|
||||
payment_hash=sent_offer.payment_hash,
|
||||
secret_key=sent_offer.secret_key,
|
||||
payment_hash=sent_offer.payment_hash.hex(),
|
||||
secret_key=sent_offer.secret_key.hex(),
|
||||
nonce=sent_offer.nonce.hex(),
|
||||
price_msat=sent_offer.price_msat,
|
||||
payment_request=sent_offer.payment_request,
|
||||
|
|
@ -958,10 +958,10 @@ class SqueakDb:
|
|||
sent_offers = [self._parse_sent_offer(row) for row in rows]
|
||||
return sent_offers
|
||||
|
||||
def get_sent_offer_by_payment_hash(self, payment_hash):
|
||||
def get_sent_offer_by_payment_hash(self, payment_hash: bytes):
|
||||
""" Get a sent offer by preimage hash. """
|
||||
s = select([self.sent_offers]).where(
|
||||
self.sent_offers.c.payment_hash == payment_hash
|
||||
self.sent_offers.c.payment_hash == payment_hash.hex()
|
||||
)
|
||||
with self.get_connection() as connection:
|
||||
result = connection.execute(s)
|
||||
|
|
@ -1009,7 +1009,7 @@ class SqueakDb:
|
|||
""" Insert a new received payment. """
|
||||
ins = self.received_payments.insert().values(
|
||||
squeak_hash=received_payment.squeak_hash.hex(),
|
||||
payment_hash=received_payment.payment_hash,
|
||||
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,
|
||||
|
|
@ -1108,9 +1108,9 @@ class SqueakDb:
|
|||
return Offer(
|
||||
offer_id=row["offer_id"],
|
||||
squeak_hash=bytes.fromhex(row["squeak_hash"]),
|
||||
payment_hash=row["payment_hash"],
|
||||
payment_hash=bytes.fromhex(row["payment_hash"]),
|
||||
nonce=bytes.fromhex(row["nonce"]),
|
||||
payment_point=row["payment_point"],
|
||||
payment_point=bytes.fromhex(row["payment_point"]),
|
||||
invoice_timestamp=row["invoice_timestamp"],
|
||||
invoice_expiry=row["invoice_expiry"],
|
||||
price_msat=row["price_msat"],
|
||||
|
|
@ -1140,8 +1140,8 @@ class SqueakDb:
|
|||
offer_id=row["offer_id"],
|
||||
peer_id=row["peer_id"],
|
||||
squeak_hash=bytes.fromhex(row["squeak_hash"]),
|
||||
payment_hash=row["payment_hash"],
|
||||
secret_key=row["secret_key"],
|
||||
payment_hash=bytes.fromhex(row["payment_hash"]),
|
||||
secret_key=bytes.fromhex(row["secret_key"]),
|
||||
price_msat=row["price_msat"],
|
||||
node_pubkey=row["node_pubkey"],
|
||||
)
|
||||
|
|
@ -1162,8 +1162,8 @@ class SqueakDb:
|
|||
return SentOffer(
|
||||
sent_offer_id=row["sent_offer_id"],
|
||||
squeak_hash=bytes.fromhex(row["squeak_hash"]),
|
||||
payment_hash=row["payment_hash"],
|
||||
secret_key=row["secret_key"],
|
||||
payment_hash=bytes.fromhex(row["payment_hash"]),
|
||||
secret_key=bytes.fromhex(row["secret_key"]),
|
||||
nonce=bytes.fromhex(row["nonce"]),
|
||||
price_msat=row["price_msat"],
|
||||
payment_request=row["payment_request"],
|
||||
|
|
@ -1179,7 +1179,7 @@ class SqueakDb:
|
|||
received_payment_id=row["received_payment_id"],
|
||||
created=row["created"],
|
||||
squeak_hash=bytes.fromhex(row["squeak_hash"]),
|
||||
payment_hash=row["payment_hash"],
|
||||
payment_hash=bytes.fromhex(row["payment_hash"]),
|
||||
price_msat=row["price_msat"],
|
||||
settle_index=row["settle_index"],
|
||||
client_addr=row["client_addr"],
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from squeaknode.admin.webapp.app import SqueakAdminWebServer
|
|||
from squeaknode.bitcoin.bitcoin_blockchain_client import BitcoinBlockchainClient
|
||||
from squeaknode.config.config import SqueaknodeConfig
|
||||
from squeaknode.core.squeak_controller import SqueakController
|
||||
from squeaknode.core.squeak_core import SqueakCore
|
||||
from squeaknode.db.db_engine import get_engine
|
||||
from squeaknode.db.db_engine import get_sqlite_connection_string
|
||||
from squeaknode.db.squeak_db import SqueakDb
|
||||
|
|
@ -19,7 +20,6 @@ from squeaknode.lightning.lnd_lightning_client import LNDLightningClient
|
|||
from squeaknode.node.received_payments_subscription_client import (
|
||||
OpenReceivedPaymentsSubscriptionClient,
|
||||
)
|
||||
from squeaknode.node.squeak_block_verifier import SqueakBlockVerifier
|
||||
from squeaknode.node.squeak_memory_whitelist import SqueakMemoryWhitelist
|
||||
from squeaknode.node.squeak_node import SqueakNode
|
||||
from squeaknode.node.squeak_rate_limiter import SqueakRateLimiter
|
||||
|
|
@ -233,7 +233,10 @@ def run_node(config):
|
|||
# load the blockchain client
|
||||
blockchain_client = load_blockchain_client(config)
|
||||
|
||||
squeak_block_verifier = SqueakBlockVerifier(blockchain_client)
|
||||
squeak_core = SqueakCore(
|
||||
blockchain_client,
|
||||
lightning_client,
|
||||
)
|
||||
squeak_rate_limiter = SqueakRateLimiter(
|
||||
squeak_db,
|
||||
blockchain_client,
|
||||
|
|
@ -245,15 +248,14 @@ def run_node(config):
|
|||
)
|
||||
squeak_store = SqueakStore(
|
||||
squeak_db,
|
||||
squeak_block_verifier,
|
||||
squeak_core,
|
||||
squeak_rate_limiter,
|
||||
squeak_whitelist,
|
||||
)
|
||||
|
||||
squeak_controller = SqueakController(
|
||||
squeak_db,
|
||||
blockchain_client,
|
||||
lightning_client,
|
||||
squeak_core,
|
||||
squeak_store,
|
||||
squeak_whitelist,
|
||||
config,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import logging
|
||||
import time
|
||||
|
||||
from squeaknode.core.received_payment import ReceivedPayment
|
||||
from squeaknode.core.sent_offer import SentOffer
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -9,16 +9,9 @@ LND_CONNECT_RETRY_S = 10
|
|||
|
||||
|
||||
class SentOffersVerifier:
|
||||
def __init__(self, squeak_db, lightning_client):
|
||||
def __init__(self, squeak_db, squeak_core):
|
||||
self.squeak_db = squeak_db
|
||||
self.lightning_client = lightning_client
|
||||
|
||||
def verify_sent_offer(self, invoice):
|
||||
logger.info("Verifying invoice: {}".format(invoice))
|
||||
if invoice.settled:
|
||||
payment_hash = invoice.r_hash.hex()
|
||||
settle_index = invoice.settle_index
|
||||
self._record_payment(payment_hash, settle_index)
|
||||
self.squeak_core = squeak_core
|
||||
|
||||
def process_subscribed_invoices(self):
|
||||
while True:
|
||||
|
|
@ -26,39 +19,19 @@ class SentOffersVerifier:
|
|||
|
||||
def try_processing(self):
|
||||
latest_settle_index = self._get_latest_settle_index() or 0
|
||||
logger.info("latest settle index: {}".format(latest_settle_index))
|
||||
try:
|
||||
for invoice in self.lightning_client.subscribe_invoices(
|
||||
settle_index=latest_settle_index,
|
||||
):
|
||||
self.verify_sent_offer(invoice)
|
||||
except Exception:
|
||||
logger.info(
|
||||
"Unable to subscribe invoices from lnd. Retrying in "
|
||||
"{} seconds.".format(LND_CONNECT_RETRY_S),
|
||||
|
||||
def get_sent_offer_for_payment_hash(payment_hash: bytes) -> SentOffer:
|
||||
return self.squeak_db.get_sent_offer_by_payment_hash(
|
||||
payment_hash
|
||||
)
|
||||
time.sleep(LND_CONNECT_RETRY_S)
|
||||
|
||||
for received_payment in self.squeak_core.get_received_payments(
|
||||
get_sent_offer_for_payment_hash,
|
||||
latest_settle_index,
|
||||
LND_CONNECT_RETRY_S,
|
||||
):
|
||||
self.squeak_db.insert_received_payment(received_payment)
|
||||
|
||||
def _get_latest_settle_index(self):
|
||||
logger.info("Getting latest settle index from db...")
|
||||
return self.squeak_db.get_latest_settle_index()
|
||||
|
||||
def _record_payment(self, payment_hash, settle_index):
|
||||
logger.info(
|
||||
"Saving received payment for payment_hash: {} with settle_index: {}".format(
|
||||
payment_hash,
|
||||
settle_index,
|
||||
)
|
||||
)
|
||||
sent_offer = self.squeak_db.get_sent_offer_by_payment_hash(
|
||||
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,
|
||||
)
|
||||
self.squeak_db.insert_received_payment(received_payment)
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SqueakBlockVerifier:
|
||||
def __init__(self, blockchain_client):
|
||||
self.blockchain_client = blockchain_client
|
||||
|
||||
def _get_block_info_for_height(self, block_height):
|
||||
return self.blockchain_client.get_block_info_by_height(block_height)
|
||||
|
||||
def get_block_header(self, squeak):
|
||||
try:
|
||||
block_info = self._get_block_info_for_height(squeak.nBlockHeight)
|
||||
except Exception:
|
||||
logger.error("Failed to get block info for squeak.",
|
||||
exc_info=False)
|
||||
return None
|
||||
if squeak.hashBlock != block_info.block_hash:
|
||||
logger.info("block hash incorrect: {}".format(block_info))
|
||||
return None
|
||||
return block_info.block_header
|
||||
|
|
@ -8,10 +8,10 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
class SqueakStore:
|
||||
def __init__(
|
||||
self, squeak_db, squeak_block_verifier, squeak_rate_limiter, squeak_whitelist
|
||||
self, squeak_db, squeak_core, squeak_rate_limiter, squeak_whitelist
|
||||
):
|
||||
self.squeak_db = squeak_db
|
||||
self.squeak_block_verifier = squeak_block_verifier
|
||||
self.squeak_core = squeak_core
|
||||
self.squeak_rate_limiter = squeak_rate_limiter
|
||||
self.squeak_whitelist = squeak_whitelist
|
||||
|
||||
|
|
@ -23,10 +23,9 @@ class SqueakStore:
|
|||
if not self.squeak_rate_limiter.should_rate_limit_allow(squeak):
|
||||
raise Exception(
|
||||
"Excedeed allowed number of squeaks per block.")
|
||||
block_header_bytes = self.squeak_block_verifier.get_block_header(
|
||||
squeak)
|
||||
block_info = self.squeak_core.validate_squeak(squeak)
|
||||
inserted_squeak_hash = self.squeak_db.insert_squeak(
|
||||
squeak, block_header_bytes)
|
||||
squeak, block_info.block_header)
|
||||
return inserted_squeak_hash
|
||||
|
||||
def get_squeak(self, squeak_hash: bytes, clear_decryption_key: bool = False):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import logging
|
||||
|
||||
from squeaknode.core.offer import Offer
|
||||
from squeaknode.core.util import get_hash
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -27,10 +26,6 @@ class PeerSyncTask:
|
|||
def squeak_db(self):
|
||||
return self.squeak_controller.squeak_db
|
||||
|
||||
@property
|
||||
def lightning_client(self):
|
||||
return self.squeak_controller.lightning_client
|
||||
|
||||
@property
|
||||
def peer_client(self):
|
||||
return self.peer_connection.peer_client
|
||||
|
|
@ -167,56 +162,8 @@ class PeerSyncTask:
|
|||
# Download the buy offer
|
||||
offer_msg = self._download_offer_msg(squeak_hash)
|
||||
|
||||
# Decode the payment request
|
||||
pay_req = self._decode_payment_request(offer_msg.payment_request)
|
||||
logger.info("Decoded payment request: {}".format(pay_req))
|
||||
|
||||
# TODO: Use the real payment point, not a fake value.
|
||||
squeak_payment_point = squeak.paymentPoint
|
||||
# payment_point = b""
|
||||
payment_hash = bytes.fromhex(pay_req.payment_hash)
|
||||
price_msat = pay_req.num_msat
|
||||
destination = pay_req.destination
|
||||
invoice_timestamp = pay_req.timestamp
|
||||
invoice_expiry = pay_req.expiry
|
||||
node_host = offer_msg.host or self.peer.host
|
||||
node_port = offer_msg.port
|
||||
|
||||
logger.info("price_msat: {}".format(price_msat))
|
||||
logger.info("destination: {}".format(destination))
|
||||
logger.info("invoice_timestamp: {}".format(invoice_timestamp))
|
||||
logger.info("invoice_expiry: {}".format(invoice_expiry))
|
||||
logger.info("node_host: {}".format(node_host))
|
||||
logger.info("node_port: {}".format(node_port))
|
||||
|
||||
decoded_offer = Offer(
|
||||
offer_id=None,
|
||||
squeak_hash=squeak_hash,
|
||||
price_msat=price_msat,
|
||||
payment_hash=payment_hash,
|
||||
nonce=offer_msg.nonce,
|
||||
payment_point=squeak_payment_point,
|
||||
invoice_timestamp=invoice_timestamp,
|
||||
invoice_expiry=invoice_expiry,
|
||||
payment_request=offer_msg.payment_request,
|
||||
destination=destination,
|
||||
node_host=node_host,
|
||||
node_port=node_port,
|
||||
peer_id=self.peer.peer_id,
|
||||
)
|
||||
|
||||
# TODO: Check the payment point
|
||||
# payment_point = offer.payment_point
|
||||
# logger.info("Payment point: {}".format(payment_point.hex()))
|
||||
# expected_payment_point = squeak.paymentPoint
|
||||
# logger.info("Expected payment point: {}".format(expected_payment_point.hex()))
|
||||
# if payment_point != expected_payment_point:
|
||||
# raise Exception(
|
||||
# "Invalid offer payment point: {}, expected: {}".format(
|
||||
# payment_point.hex(),
|
||||
# expected_payment_point.hex(),
|
||||
# )
|
||||
# )
|
||||
decoded_offer = self.squeak_controller.get_offer(
|
||||
squeak, offer_msg, self.peer)
|
||||
|
||||
# Save the offer
|
||||
self._save_offer(decoded_offer)
|
||||
|
|
@ -297,6 +244,3 @@ class PeerSyncTask:
|
|||
def _save_offer(self, offer):
|
||||
logger.info("Saving offer: {}".format(offer))
|
||||
self.squeak_db.insert_offer(offer)
|
||||
|
||||
def _decode_payment_request(self, payment_request):
|
||||
return self.lightning_client.decode_pay_req(payment_request)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import mock
|
||||
import pytest
|
||||
|
||||
from squeaknode.bitcoin.blockchain_client import BlockchainClient
|
||||
from squeaknode.config.config import SqueaknodeConfig
|
||||
from squeaknode.core.lightning_address import LightningAddressHostPort
|
||||
from squeaknode.core.squeak_controller import SqueakController
|
||||
from squeaknode.core.squeak_core import SqueakCore
|
||||
from squeaknode.core.squeak_peer import SqueakPeer
|
||||
from squeaknode.db.squeak_db import SqueakDb
|
||||
from squeaknode.node.squeak_store import SqueakStore
|
||||
|
|
@ -34,13 +34,8 @@ def squeak_db():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def blockchain_client():
|
||||
return mock.Mock(spec=BlockchainClient)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lightning_client():
|
||||
return mock.Mock()
|
||||
def squeak_core():
|
||||
return mock.Mock(spec=SqueakCore)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -71,16 +66,14 @@ def squeak_store():
|
|||
@pytest.fixture
|
||||
def squeak_controller(
|
||||
squeak_db,
|
||||
blockchain_client,
|
||||
lightning_client,
|
||||
squeak_core,
|
||||
squeak_store,
|
||||
squeak_whitelist,
|
||||
config,
|
||||
):
|
||||
return SqueakController(
|
||||
squeak_db,
|
||||
blockchain_client,
|
||||
lightning_client,
|
||||
squeak_core,
|
||||
squeak_store,
|
||||
squeak_whitelist,
|
||||
config,
|
||||
|
|
@ -90,16 +83,14 @@ def squeak_controller(
|
|||
@pytest.fixture
|
||||
def regtest_squeak_controller(
|
||||
squeak_db,
|
||||
blockchain_client,
|
||||
lightning_client,
|
||||
squeak_core,
|
||||
squeak_store,
|
||||
squeak_whitelist,
|
||||
regtest_config,
|
||||
):
|
||||
return SqueakController(
|
||||
squeak_db,
|
||||
blockchain_client,
|
||||
lightning_client,
|
||||
squeak_core,
|
||||
squeak_store,
|
||||
squeak_whitelist,
|
||||
regtest_config,
|
||||
|
|
|
|||
94
tests/core/test_squeak_core.py
Normal file
94
tests/core/test_squeak_core.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import mock
|
||||
import pytest
|
||||
from bitcoin.core import CoreMainParams
|
||||
from squeak.core.signing import CSigningKey
|
||||
from squeak.core.signing import CSqueakAddress
|
||||
|
||||
from squeaknode.bitcoin.block_info import BlockInfo
|
||||
from squeaknode.bitcoin.blockchain_client import BlockchainClient
|
||||
from squeaknode.core.lightning_address import LightningAddressHostPort
|
||||
from squeaknode.core.squeak_core import SqueakCore
|
||||
from squeaknode.core.squeak_profile import SqueakProfile
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lightning_client():
|
||||
return mock.Mock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lightning_host_port():
|
||||
return LightningAddressHostPort(host="my_lightning_host", port=8765)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def price_msat():
|
||||
return 777
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def max_squeaks_per_address_per_hour():
|
||||
return 5000
|
||||
|
||||
|
||||
class MockBitcoinClient(BlockchainClient):
|
||||
genesis_block_info = BlockInfo(
|
||||
block_height=0,
|
||||
block_hash=CoreMainParams.GENESIS_BLOCK.GetHash(),
|
||||
block_header=CoreMainParams.GENESIS_BLOCK.serialize(),
|
||||
)
|
||||
|
||||
def get_best_block_info(self) -> BlockInfo:
|
||||
return self.genesis_block_info
|
||||
|
||||
def get_block_info_by_height(self, block_height: int) -> BlockInfo:
|
||||
if block_height == 0:
|
||||
return self.genesis_block_info
|
||||
else:
|
||||
raise Exception("Invalid block height")
|
||||
|
||||
def get_block_hash(self, block_height: int) -> bytes:
|
||||
if block_height == 0:
|
||||
return self.genesis_block_info.block_hash
|
||||
else:
|
||||
raise Exception("Invalid block height")
|
||||
|
||||
def get_block_header(self, block_hash: bytes, verbose: bool) -> bytes:
|
||||
if block_hash == self.genesis_block_info.block_hash:
|
||||
return self.genesis_block_info.block_header
|
||||
else:
|
||||
raise Exception("Invalid block hash")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def signing_profile():
|
||||
profile_name = "fake_name"
|
||||
signing_key = CSigningKey.generate()
|
||||
verifying_key = signing_key.get_verifying_key()
|
||||
address = CSqueakAddress.from_verifying_key(verifying_key)
|
||||
signing_key_str = str(signing_key)
|
||||
signing_key_bytes = signing_key_str.encode()
|
||||
return SqueakProfile(
|
||||
profile_id=None,
|
||||
profile_name=profile_name,
|
||||
private_key=signing_key_bytes,
|
||||
address=str(address),
|
||||
sharing=False,
|
||||
following=False,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bitcoin_client():
|
||||
return MockBitcoinClient()
|
||||
|
||||
|
||||
def test_make_squeak(bitcoin_client, lightning_client, signing_profile):
|
||||
squeak_core = SqueakCore(bitcoin_client, lightning_client)
|
||||
squeak_entry = squeak_core.make_squeak(signing_profile, "hello")
|
||||
|
||||
assert squeak_entry.squeak.GetDecryptedContentStr() == "hello"
|
||||
|
||||
validated_squeak_entry = squeak_core.validate_squeak(squeak_entry.squeak)
|
||||
|
||||
assert validated_squeak_entry == squeak_entry
|
||||
Loading…
Add table
Add a link
Reference in a new issue