diff --git a/squeakserver/blockchain/bitcoin_blockchain_client.py b/squeakserver/blockchain/bitcoin_blockchain_client.py index 484ac0db..436c19f7 100644 --- a/squeakserver/blockchain/bitcoin_blockchain_client.py +++ b/squeakserver/blockchain/bitcoin_blockchain_client.py @@ -4,18 +4,46 @@ from typing import Optional import requests +from squeakserver.blockchain.block_info import BlockInfo +from squeakserver.blockchain.blockchain_client import BlockchainClient + logger = logging.getLogger(__name__) -class BitcoinBlockchainClient: +class BitcoinBlockchainClient(BlockchainClient): """Access a bitcoin daemon using RPC.""" def __init__(self, host: str, port: int, rpc_user: str, rpc_password: str,) -> None: self.url = f"https://{rpc_user}:{rpc_password}@{host}:{port}" self.headers = {"content-type": "application/json"} - def get_block_hash(self, block_height: int) -> Optional[bytes]: - # return self.access.getblockhash(block_height) + def get_best_block_info(self) -> BlockInfo: + block_height = self.get_block_count() - 1 + return self.get_block_info_by_height(block_height) + + def get_block_info_by_height(self, block_height: int) -> BlockInfo: + block_hash = self.get_block_hash(block_height) + block_header = self.get_block_header(block_hash, False) + return BlockInfo(block_height, block_hash, block_header) + + def get_block_count(self) -> Optional[int]: + payload = { + "method": "getblockcount", + "params": [], + "jsonrpc": "2.0", + "id": 0, + } + response = requests.post( + self.url, data=json.dumps(payload), headers=self.headers, + ).json() + + logger.info("Got response for get_block_count: {}".format(response)) + result = response["result"] + block_count = int(result) + logger.info("Got block_count: {}".format(block_count)) + return block_count + + def get_block_hash(self, block_height: int) -> Optional[str]: payload = { "method": "getblockhash", "params": [block_height], @@ -26,11 +54,28 @@ class BitcoinBlockchainClient: self.url, data=json.dumps(payload), headers=self.headers, ).json() + logger.info("Got response for get_block_hash: {}".format(response)) result = response["result"] - block_hash = bytes.fromhex(result) + block_hash = result + logger.info("Got block_hash: {}".format(block_hash)) return block_hash - def get_block_header(self, block_hash: int, verbose: bool) -> Optional[bytes]: + # def get_best_block_hash(self) -> Optional[bytes]: + # payload = { + # "method": "getblockhash", + # "params": [], + # "jsonrpc": "2.0", + # "id": 0, + # } + # response = requests.post( + # self.url, data=json.dumps(payload), headers=self.headers, + # ).json() + + # result = response["result"] + # block_hash = bytes.fromhex(result) + # return block_hash + + def get_block_header(self, block_hash: str, verbose: bool) -> Optional[bytes]: payload = { "method": "getblockheader", "params": [block_hash, verbose], @@ -41,6 +86,7 @@ class BitcoinBlockchainClient: self.url, data=json.dumps(payload), headers=self.headers, ).json() - logger.info("Got header request result: {}".format(response)) + logger.info("Got response for get_block_header: {}".format(response)) result = response["result"] + logger.info("Got block_header: {}".format(result)) return result diff --git a/squeakserver/blockchain/block_info.py b/squeakserver/blockchain/block_info.py new file mode 100644 index 00000000..41f26238 --- /dev/null +++ b/squeakserver/blockchain/block_info.py @@ -0,0 +1,6 @@ +from collections import namedtuple + +BlockInfo = namedtuple( + "BlockInfo", + "block_height, block_hash, block_header", +) diff --git a/squeakserver/blockchain/blockchain_client.py b/squeakserver/blockchain/blockchain_client.py new file mode 100644 index 00000000..76ef6a5c --- /dev/null +++ b/squeakserver/blockchain/blockchain_client.py @@ -0,0 +1,22 @@ +import json +import logging +from typing import Optional + +from abc import ABC, abstractmethod + +from squeakserver.blockchain.block_info import BlockInfo + +import requests + +logger = logging.getLogger(__name__) + + +class BlockchainClient(ABC): + + @abstractmethod + def get_best_block_info(self) -> BlockInfo: + pass + + @abstractmethod + def get_block_info_by_height(self) -> BlockInfo: + pass diff --git a/squeakserver/node/squeak_block_verifier.py b/squeakserver/node/squeak_block_verifier.py index 77628214..25b130cc 100644 --- a/squeakserver/node/squeak_block_verifier.py +++ b/squeakserver/node/squeak_block_verifier.py @@ -13,13 +13,16 @@ class SqueakBlockVerifier: def verify_squeak_block(self, squeak_hash): logger.info("Verifying squeak hash: {}".format(squeak_hash)) squeak = self._get_squeak(squeak_hash) - valid_block_info = self._check_block_info(squeak) - logger.info("Is block hash correct: {}".format(valid_block_info)) - if valid_block_info: - block_header = self._get_block_header(squeak) - self._mark_squeak_verified(squeak_hash, block_header) + block_info = self._get_block_info_for_height(squeak.nBlockHeight) + + logger.info("Checking block height: {}".format(squeak.nBlockHeight)) + logger.info("Checking block hash: {}".format(squeak.hashBlock.hex())) + if squeak.hashBlock.hex() == block_info.block_hash: + logger.info("block hash correct: {}".format(block_info)) + self._mark_squeak_verified(squeak_hash, block_info) else: - self._delete_squeak + logger.info("block hash incorrect: {}".format(block_info)) + self._delete_squeak(squeak_hash) def verify_all_unverified_squeaks(self): logger.info("Calling verify_squeaks.") @@ -42,24 +45,12 @@ class SqueakBlockVerifier: squeak_entry = self.postgres_db.get_squeak_entry(squeak_hash) return squeak_entry.squeak - def _mark_squeak_verified(self, squeak_hash, block_header): - self.postgres_db.mark_squeak_block_valid(squeak_hash, block_header) + def _mark_squeak_verified(self, squeak_hash, block_info): + block_header_bytes = bytes.fromhex(block_info.block_header) + self.postgres_db.mark_squeak_block_valid(squeak_hash, block_header_bytes) def _delete_squeak(self, squeak_hash): self.postgres_db.delete_squeak(squeak_hash) - def _check_block_info(self, squeak): - block_height = squeak.nBlockHeight - block_hash = self._get_block_hash(block_height) - return squeak.hashBlock == block_hash - - def _get_block_header(self, squeak): - block_hash_str = squeak.hashBlock.hex() - block_header = self.blockchain_client.get_block_header(block_hash_str, False) - logger.info("Got block header from blockchain: {}".format(block_header)) - return bytes.fromhex(block_header) - - def _get_block_hash(self, block_height): - block_hash = self.blockchain_client.get_block_hash(block_height) - logger.info("Got block hash from blockchain: {}".format(block_hash)) - return block_hash + def _get_block_info_for_height(self, block_height): + return self.blockchain_client.get_block_info_by_height(block_height) diff --git a/squeakserver/node/squeak_maker.py b/squeakserver/node/squeak_maker.py index 8965c7bb..6f75ce54 100644 --- a/squeakserver/node/squeak_maker.py +++ b/squeakserver/node/squeak_maker.py @@ -10,17 +10,17 @@ logger = logging.getLogger(__name__) class SqueakMaker: - def __init__(self, lightning_client): - self.lightning_client = lightning_client + def __init__(self, blockchain_client): + self.blockchain_client = blockchain_client def make_squeak(self, signing_profile, content_str, replyto_hash=None): signing_key_str = signing_profile.private_key.decode() signing_key = CSigningKey(signing_key_str) logger.info("Creating squeak with signing key: {}".format(signing_key)) logger.info("Creating squeak with replyto_hash: {}".format(replyto_hash)) - latest_block = self._get_latest_block() - block_height = latest_block.block_height - block_hash = latest_block.block_hash + block_info = self._get_latest_block_info() + block_height = block_info.block_height + block_hash = bytes.fromhex(block_info.block_hash) timestamp = self._get_current_time_s() logger.info("Creating squeak with block height: {}".format(block_height)) logger.info("Creating squeak with block hash: {}".format(block_hash)) @@ -38,11 +38,14 @@ class SqueakMaker: replyto_hash, ) - def _get_latest_block(self): - get_info_response = self.lightning_client.get_info() - block_hash = bytes.fromhex(get_info_response.block_hash) - block_height = get_info_response.block_height - return BlockInfo(block_hash, block_height) + # def _get_latest_block(self): + # get_info_response = self.lightning_client.get_info() + # block_hash = bytes.fromhex(get_info_response.block_hash) + # block_height = get_info_response.block_height + # return BlockInfo(block_hash, block_height) + + def _get_latest_block_info(self): + return self.blockchain_client.get_best_block_info() def _get_current_time_s(self): return int(time.time()) diff --git a/squeakserver/node/squeak_node.py b/squeakserver/node/squeak_node.py index 123a73f5..32151533 100644 --- a/squeakserver/node/squeak_node.py +++ b/squeakserver/node/squeak_node.py @@ -57,6 +57,7 @@ class SqueakNode: self.squeak_subscription_downloader = SqueakSubscriptionDownloader( postgres_db, self.squeak_store, + self.blockchain_client, ) def start_running(self): @@ -173,7 +174,7 @@ class SqueakNode: def make_squeak(self, profile_id, content_str, replyto_hash): squeak_profile = self.postgres_db.get_profile(profile_id) - squeak_maker = SqueakMaker(self.lightning_client) + squeak_maker = SqueakMaker(self.blockchain_client) squeak = squeak_maker.make_squeak(squeak_profile, content_str, replyto_hash) return self.save_created_squeak(squeak) diff --git a/squeakserver/node/squeak_subscription_downloader.py b/squeakserver/node/squeak_subscription_downloader.py index ec665740..da0c4678 100644 --- a/squeakserver/node/squeak_subscription_downloader.py +++ b/squeakserver/node/squeak_subscription_downloader.py @@ -9,9 +9,15 @@ SUBSCRIBE_UPDATE_INTERVAL_S = 10.0 class SqueakSubscriptionDownloader: - def __init__(self, postgres_db, squeak_store, update_interval_s=SUBSCRIBE_UPDATE_INTERVAL_S): + def __init__(self, + postgres_db, + squeak_store, + blockchain_client, + update_interval_s=SUBSCRIBE_UPDATE_INTERVAL_S, + ): self.postgres_db = postgres_db self.squeak_store = squeak_store + self.blockchain_client = blockchain_client self.update_interval_s = update_interval_s def sync_subscriptions(self):