From 7c4b84056bfb2c94d65bb4b7249fa8c112c2096c Mon Sep 17 00:00:00 2001 From: Jonathan Zernik Date: Thu, 30 Jul 2020 03:05:21 -0700 Subject: [PATCH] Use created time for rate limiter (#178) * Use created time to rate limit based on last hour * Delete commented lines in rate limiter --- docker/config.ini | 2 +- itests/config.ini | 2 +- squeakserver/node/squeak_node.py | 4 +-- squeakserver/node/squeak_rate_limiter.py | 31 ++++++++++++------------ squeakserver/server/main.py | 8 +++--- squeakserver/server/postgres_db.py | 22 +++++++++++++++++ 6 files changed, 45 insertions(+), 24 deletions(-) diff --git a/docker/config.ini b/docker/config.ini index d7de1483..f80b586d 100644 --- a/docker/config.ini +++ b/docker/config.ini @@ -21,7 +21,7 @@ rpc_port=8774 [squeaknode] price=1000 -max_squeaks_per_block_per_address=2 +max_squeaks_per_address_per_hour=2 [admin] rpc_host=0.0.0.0 diff --git a/itests/config.ini b/itests/config.ini index 95a7f43b..6f2239f4 100644 --- a/itests/config.ini +++ b/itests/config.ini @@ -21,7 +21,7 @@ rpc_port=8774 [squeaknode] price=1000 -max_squeaks_per_block_per_address=5 +max_squeaks_per_address_per_hour=5 [admin] rpc_host=0.0.0.0 diff --git a/squeakserver/node/squeak_node.py b/squeakserver/node/squeak_node.py index b092fa1e..01d4e3e9 100644 --- a/squeakserver/node/squeak_node.py +++ b/squeakserver/node/squeak_node.py @@ -24,7 +24,7 @@ class SqueakNode: lightning_client, lightning_host_port, price, - max_squeaks_per_block_per_address, + max_squeaks_per_address_per_hour, ): self.postgres_db = postgres_db self.blockchain_client = blockchain_client @@ -42,7 +42,7 @@ class SqueakNode: postgres_db, blockchain_client, lightning_client, - max_squeaks_per_block_per_address, + max_squeaks_per_address_per_hour, ) self.squeak_whitelist = SqueakWhitelist( postgres_db, diff --git a/squeakserver/node/squeak_rate_limiter.py b/squeakserver/node/squeak_rate_limiter.py index 88b4b6ab..ad7bb34e 100644 --- a/squeakserver/node/squeak_rate_limiter.py +++ b/squeakserver/node/squeak_rate_limiter.py @@ -7,35 +7,34 @@ from squeakserver.node.block_info import BlockInfo logger = logging.getLogger(__name__) +HOUR_IN_SECONDS = 3600 + + class SqueakRateLimiter: - def __init__(self, postgres_db, blockchain_client, lightning_client, max_squeaks_per_block_per_address): + def __init__(self, postgres_db, blockchain_client, lightning_client, max_squeaks_per_address_per_hour): self.postgres_db = postgres_db self.blockchain_client = blockchain_client self.lightning_client = lightning_client - self.max_squeaks_per_block_per_address = max_squeaks_per_block_per_address + self.max_squeaks_per_address_per_hour = max_squeaks_per_address_per_hour def should_rate_limit_allow(self, squeak): squeak_hash = get_hash(squeak) logger.info("Checking rate limit for squeak: {}".format(squeak_hash)) current_squeak_count = self._get_current_squeak_count(squeak) - logger.info("Current squeak count: {}, limit: {}".format(current_squeak_count, self.max_squeaks_per_block_per_address)) - return current_squeak_count < self.max_squeaks_per_block_per_address + logger.info("Current squeak count: {}, limit: {}".format(current_squeak_count, self.max_squeaks_per_address_per_hour)) + return current_squeak_count < self.max_squeaks_per_address_per_hour def _get_current_squeak_count(self, squeak): - current_block_info = self._get_latest_block() - current_block_height = current_block_info.block_height squeak_address = self._get_squeak_address(squeak) - return self._get_num_squeaks_in_block(current_block_height, squeak_address) + return self._get_num_squeaks_in_last_hour(squeak_address) - 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_num_squeaks_in_block(self, block_height, squeak_address): - logger.info("Getting squeak count for block height: {}, squeak address: {}".format(block_height, squeak_address)) - hashes = self.postgres_db.lookup_squeaks([squeak_address], block_height, block_height, include_unverified=True) + def _get_num_squeaks_in_last_hour(self, squeak_address): + logger.info("Getting squeak count for last hour for squeak address: {}".format(squeak_address)) + hashes = self.postgres_db.lookup_squeaks_by_time( + [squeak_address], + HOUR_IN_SECONDS, + include_unverified=True, + ) return len(hashes) def _get_squeak_address(self, squeak): diff --git a/squeakserver/server/main.py b/squeakserver/server/main.py index 3b388145..6bcdf469 100644 --- a/squeakserver/server/main.py +++ b/squeakserver/server/main.py @@ -55,8 +55,8 @@ def load_price(config): return int(config["squeaknode"]["price"]) -def load_max_squeaks_per_block_per_address(config): - return int(config["squeaknode"]["max_squeaks_per_block_per_address"]) +def load_max_squeaks_per_address_per_hour(config): + return int(config["squeaknode"]["max_squeaks_per_address_per_hour"]) def load_handler(squeak_node): @@ -152,7 +152,7 @@ def run_server(config): price = load_price(config) # load the max squeaks per block per address - max_squeaks_per_block_per_address = load_max_squeaks_per_block_per_address(config) + max_squeaks_per_address_per_hour = load_max_squeaks_per_address_per_hour(config) # load the lightning client lightning_client = load_lightning_client(config) @@ -163,7 +163,7 @@ def run_server(config): # Create and start the squeak node squeak_node = SqueakNode( - postgres_db, blockchain_client, lightning_client, lightning_host_port, price, max_squeaks_per_block_per_address + postgres_db, blockchain_client, lightning_client, lightning_host_port, price, max_squeaks_per_address_per_hour ) squeak_node.start_running() diff --git a/squeakserver/server/postgres_db.py b/squeakserver/server/postgres_db.py index 5cd23438..a1b3d177 100644 --- a/squeakserver/server/postgres_db.py +++ b/squeakserver/server/postgres_db.py @@ -186,6 +186,28 @@ class PostgresDb: hashes = [bytes.fromhex(row["hash"]) for row in rows] return hashes + def lookup_squeaks_by_time(self, addresses, interval_seconds, include_unverified=False): + """ Lookup squeaks. """ + sql = """ + SELECT hash FROM squeak + WHERE author_address IN %s + AND created > now() - interval '%s seconds' + AND vch_decryption_key IS NOT NULL + AND ((block_header IS NOT NULL) OR %s); + """ + addresses_tuple = tuple(addresses) + + if not addresses: + return [] + + with self.get_cursor() as curs: + # mogrify to debug. + # logger.info(curs.mogrify(sql, (addresses_tuple, min_block, max_block))) + curs.execute(sql, (addresses_tuple, interval_seconds, include_unverified)) + rows = curs.fetchall() + hashes = [bytes.fromhex(row["hash"]) for row in rows] + return hashes + def insert_profile(self, squeak_profile): """ Insert a new squeak profile. """ sql = """