eth-monitor/eth_monitor/rules.py

202 lines
5.9 KiB
Python
Raw Normal View History

# standard imports
import logging
import uuid
# external imports
from chainlib.eth.address import is_same_address
2023-08-17 13:48:33 +02:00
from .error import RuleFail
logg = logging.getLogger()
2022-04-05 16:02:18 +02:00
class RuleData:
2023-08-17 13:48:33 +02:00
def __init__(self, fragments, description=None, match_all=False):
2022-04-05 16:02:18 +02:00
self.fragments = fragments
self.description = description
if self.description == None:
self.description = str(uuid.uuid4())
2023-08-17 13:48:33 +02:00
self.match_all = match_all
2022-04-05 16:02:18 +02:00
def check(self, sender, recipient, data, tx_hash):
2023-08-17 13:48:33 +02:00
have_fail = False
have_match = False
2022-04-05 16:02:18 +02:00
if len(self.fragments) == 0:
return False
for fragment in self.fragments:
l = len(fragment)
if len(fragment) > len(data):
continue
if fragment in data:
logg.debug('tx {} rule {} match in DATA FRAGMENT {}'.format(tx_hash, self.description, fragment))
2023-08-17 13:48:33 +02:00
if not self.match_all:
return True
have_match = True
else:
logg.debug('data match all {}'.format(self.match_all))
if self.match_all:
return False
have_fail = True
2022-04-05 16:02:18 +02:00
2023-08-17 13:48:33 +02:00
return have_match
2022-04-05 16:02:18 +02:00
def __str__(self):
return 'Fragment ' + self.description + ' {}'.format(
self.fragments,
)
2022-04-05 13:44:15 +02:00
class RuleMethod:
2023-08-17 13:48:33 +02:00
def __init__(self, methods, description=None, match_all=False):
2022-04-05 13:44:15 +02:00
self.methods = methods
self.description = description
if self.description == None:
self.description = str(uuid.uuid4())
2023-08-17 13:48:33 +02:00
if match_all:
logg.warning('match_all ignord for RuleMethod rule')
2022-04-05 13:44:15 +02:00
def check(self, sender, recipient, data, tx_hash):
if len(self.methods) == 0:
return False
for method in self.methods:
l = len(method)
if len(method) > len(data):
continue
if data[:l] == method:
logg.debug('tx {} rule {} match in DATA {}'.format(tx_hash, self.description, method))
return True
return False
def __str__(self):
return 'Method ' + self.description + ' {}'.format(
self.methods,
)
class RuleSimple:
2023-08-17 11:28:27 +02:00
def __init__(self, outputs, inputs, executables, description=None, match_all=False):
self.description = description
if self.description == None:
self.description = str(uuid.uuid4())
self.outputs = outputs
self.inputs = inputs
self.executables = executables
2023-08-17 11:28:27 +02:00
self.match_all = match_all
2023-08-17 11:28:27 +02:00
2022-04-05 13:44:15 +02:00
def check(self, sender, recipient, data, tx_hash):
2023-08-17 13:48:33 +02:00
r = None
try:
r = self.__check(sender, recipient, data, tx_hash)
except RuleFail:
return False
return r
def __check(self, sender, recipient, data, tx_hash):
2023-08-17 11:28:27 +02:00
have_fail = False
have_match = False
for rule in self.outputs:
if rule != None and is_same_address(sender, rule):
2022-04-05 13:44:15 +02:00
logg.debug('tx {} rule {} match in SENDER {}'.format(tx_hash, self.description, sender))
2023-08-17 13:48:33 +02:00
if not self.match_all:
return True
have_match = True
else:
if self.match_all:
raise RuleFail(rule)
have_fail = True
if recipient == None:
return False
for rule in self.inputs:
if rule != None and is_same_address(recipient, rule):
2022-04-05 13:44:15 +02:00
logg.debug('tx {} rule {} match in RECIPIENT {}'.format(tx_hash, self.description, recipient))
2023-08-17 13:48:33 +02:00
if not self.match_all:
return True
have_match = True
else:
if self.match_all:
raise RuleFail(rule)
have_fail = True
for rule in self.executables:
if rule != None and is_same_address(recipient, rule):
2022-04-05 13:44:15 +02:00
logg.debug('tx {} rule {} match in EXECUTABLE {}'.format(tx_hash, self.description, recipient))
2023-08-17 13:48:33 +02:00
if not self.match_all:
return True
have_match = True
else:
if self.match_all:
raise RuleFail(rule)
have_fail = True
return have_match
def __str__(self):
return 'Simple ' + self.description + ' outputs {} inputs {} execs {}'.format(
self.outputs,
self.inputs,
self.executables,
)
class AddressRules:
2023-08-17 11:28:27 +02:00
def __init__(self, include_by_default=False, match_all=False):
self.excludes = []
self.includes = []
self.include_by_default = include_by_default
2023-08-17 11:28:27 +02:00
self.match_all = match_all
def exclude(self, rule):
self.excludes.append(rule)
logg.info('cache filter added EXCLUDE rule {}'.format(rule))
def include(self, rule):
self.includes.append(rule)
2022-04-05 13:44:15 +02:00
logg.info('cache filter added INCLUDE rule {}'.format(rule))
def apply_rules(self, tx):
2022-04-05 13:44:15 +02:00
return self.apply_rules_addresses(tx.outputs[0], tx.inputs[0], tx.payload, tx.hash)
2022-04-05 13:44:15 +02:00
def apply_rules_addresses(self, sender, recipient, data, tx_hash):
v = self.include_by_default
2023-08-17 11:28:27 +02:00
have_fail = False
have_match = False
for rule in self.includes:
2022-04-05 13:44:15 +02:00
if rule.check(sender, recipient, data, tx_hash):
v = True
logg.info('match in includes rule: {}'.format(rule))
2023-08-17 11:28:27 +02:00
if not self.match_all:
break
elif self.match_all:
v = False
break
2023-08-17 11:28:27 +02:00
if not v:
return v
for rule in self.excludes:
2022-04-05 13:44:15 +02:00
if rule.check(sender, recipient, data, tx_hash):
v = False
logg.info('match in excludes rule: {}'.format(rule))
2023-08-17 11:28:27 +02:00
if not self.match_all:
break
return v