taint.py (5932B)
1 # standard imports 2 import logging 3 4 # external imports 5 from hexathon import strip_0x 6 7 # local imports 8 from .cache import Cache 9 from .account import Account 10 from .crypto import Salter 11 12 logg = logging.getLogger().getChild(__name__) 13 14 15 def noop_process_actors(self, inputs, outputs): 16 return (inputs, outputs,) 17 18 19 class Tainter(Cache): 20 """Frontend object containing code to load and save state of a cache, aswell as chain sync handling. 21 22 :param chain_spec: Chain spec context for the cache 23 :type chain_spec: chainlib.chain.ChainSpec 24 :param bits_size: Bitsize of bloom filter used for cache 25 :type bits_size: int 26 :param result_handler: Callback called once for each registered account found in a transaction. 27 :type result_handler: function 28 :param store: State storage for cache 29 :type store: taint.store.base.BaseStore 30 :param cache_bloom: Cache bloom filter to instantiate 31 :type cache_bloom: taint.cache.CacheBloom 32 """ 33 def __init__(self, chain_spec, bits_size, result_handler=None, store=None, cache_bloom=None): 34 super(Tainter, self).__init__(chain_spec, bits_size, cache_bloom=cache_bloom) 35 self.store = store 36 self.result_handler = result_handler 37 self.processors = [noop_process_actors] 38 39 40 def register_actor_processor(self, processor): 41 self.processors.insert(0, processor) 42 43 44 def add_account(self, account, label): 45 """Add account to be tracked in cache. 46 47 If registered, the result handler will be called with the initial state of the added account. 48 49 The label will only be stored in memory for the given session, and will not be part of state storage. 50 51 :param account: Account to add 52 :type account: taint.account.Account 53 :param label: Filter section to add account to 54 :type label: taint.cache.CacheAccountEnum 55 """ 56 super(Tainter, self).add_account(account, label) 57 if self.result_handler != None: 58 self.result_handler.register(account) 59 60 61 def filter(self, conn, block, tx, storer): 62 """Transaction callback for chainsyncer. 63 64 :param conn: RPC connection object 65 :type conn: chainlib.connection.RPCConnection 66 :param block: Block object 67 :type block: chainlib.block.Block 68 :param tx: Transaction object 69 :type tx: chainlib.tx.Tx 70 :param storer: State storage object (e.g. a sql database session) 71 :type storer: any 72 """ 73 outputs = None 74 inputs = None 75 76 for p in self.processors: 77 r = p(tx.outputs, tx.inputs) 78 if r != None: 79 outputs = r[0] 80 inputs = r[1] 81 break 82 83 for output in outputs: 84 for inpt in inputs: 85 sender = bytes.fromhex(strip_0x(output)) 86 recipient = bytes.fromhex(strip_0x(inpt)) 87 r = None 88 try: 89 r = self.add_tx( 90 self.sprinkle(sender), 91 self.sprinkle(recipient), 92 block.number, 93 tx.index, 94 block_hash=block.hash, 95 tx_hash=tx.hash, 96 ) 97 except KeyError: 98 logg.debug('false positive match tx {}'.format(tx.hash)) 99 continue 100 101 if r == None: 102 continue 103 104 subjects = r[0] 105 objects = r[1] 106 107 if self.result_handler != None: 108 for account in subjects: 109 self.result_handler.register(account) 110 for account in objects: 111 self.result_handler.register(account) 112 113 114 def save(self): 115 """Save state of all accounts and the salt used for the session to the cache store. 116 """ 117 for account in self.subjects.values(): 118 self.store.put(account.account, account.serialize()) 119 self.store.put(self.root_key(), self.serialize()) 120 121 122 def load_account(self, k, label=None): 123 """Load state for an accounts from a cache store. 124 125 :param k: Account to load, by obfuscated value. 126 :type k: bytes 127 :param label: Label to associate with account, for display use. 128 """ 129 try: 130 b = self.store.get(k) 131 except FileNotFoundError: 132 return None 133 return Account.from_serialized(b, self.chain_spec, label) 134 135 136 def load_subject(self, k, label=None): 137 """Load state for an account as subject from the cache store. 138 139 A subject will always merge tags with any other subject or object in the same transaction. 140 141 :param k: Account to load, by obfuscated value. 142 :type k: bytes 143 :param label: Label to associate with account, for display use. 144 """ 145 a = self.load_account(k, label) 146 if a == None: 147 return False 148 self.add_subject(a) 149 return True 150 151 152 def load_object(self, k, label=None): 153 """Load state for an account as object from the cache store. 154 155 An object will only merge tags with other subjects in the same transaction. 156 157 :param k: Account to load, by obfuscated value. 158 :type k: bytes 159 :param label: Label to associate with account, for display use. 160 """ 161 a = self.load_account(k, label) 162 if a == None: 163 return False 164 self.add_object(a) 165 return True 166 167 168 @staticmethod 169 def load(store, chain_spec, result_handler=None): 170 """Instantiate new Tainter object with salt stored from previous session. 171 """ 172 a = Salter(chain_spec) 173 b = store.get(a.root_key()) 174 c = Tainter.from_serialized(chain_spec, b) 175 c.store = store 176 c.result_handler = result_handler 177 return c 178