11import json
22import time
3+ from copy import deepcopy
34from datetime import datetime , timezone
45from enum import Enum
56from typing import Any , Dict , List , Optional , Tuple , Union
67
78import cbor2
89import requests
910import websocket
11+ from cachetools import Cache , LRUCache , TTLCache
1012
1113from pycardano .address import Address
1214from pycardano .backend .base import (
@@ -49,6 +51,8 @@ class OgmiosChainContext(ChainContext):
4951 _last_chain_tip_fetch : float
5052 _genesis_param : Optional [GenesisParameters ]
5153 _protocol_param : Optional [ProtocolParameters ]
54+ _utxo_cache : Cache
55+ _datum_cache : Cache
5256
5357 def __init__ (
5458 self ,
@@ -57,6 +61,8 @@ def __init__(
5761 compact_result = True ,
5862 kupo_url = None ,
5963 refetch_chain_tip_interval : Optional [float ] = None ,
64+ utxo_cache_size : int = 10000 ,
65+ datum_cache_size : int = 10000 ,
6066 ):
6167 self ._ws_url = ws_url
6268 self ._network = network
@@ -77,6 +83,11 @@ def __init__(
7783 / self .genesis_param .active_slots_coefficient
7884 )
7985
86+ self ._utxo_cache = TTLCache (
87+ ttl = self ._refetch_chain_tip_interval , maxsize = utxo_cache_size
88+ )
89+ self ._datum_cache = LRUCache (maxsize = datum_cache_size )
90+
8091 def _request (self , method : OgmiosQueryType , args : JsonDict ) -> Any :
8192 ws = websocket .WebSocket ()
8293 ws .connect (self ._ws_url )
@@ -253,13 +264,45 @@ def _utxos(self, address: str) -> List[UTxO]:
253264 Returns:
254265 List[UTxO]: A list of UTxOs.
255266 """
267+ if (self .last_block_slot , address ) in self ._utxo_cache :
268+ return deepcopy (self ._utxo_cache [(self .last_block_slot , address )])
269+
256270 if self ._kupo_url :
257271 utxos = self ._utxos_kupo (address )
258272 else :
259273 utxos = self ._utxos_ogmios (address )
260274
275+ self ._utxo_cache [(self .last_block_slot , address )] = deepcopy (utxos )
276+
261277 return utxos
262278
279+ def _get_datum_from_kupo (self , datum_hash : str ) -> Optional [RawCBOR ]:
280+ """Get datum from Kupo.
281+
282+ Args:
283+ datum_hash (str): A datum hash.
284+
285+ Returns:
286+ Optional[RawCBOR]: A datum.
287+ """
288+ datum = self ._datum_cache .get (datum_hash , None )
289+
290+ if datum is not None or (datum is None and not self ._is_chain_tip_updated ()):
291+ return datum
292+
293+ if self ._kupo_url is None :
294+ raise AssertionError (
295+ "kupo_url object attribute has not been assigned properly."
296+ )
297+
298+ kupo_datum_url = self ._kupo_url + "/datums/" + datum_hash
299+ datum_result = requests .get (kupo_datum_url ).json ()
300+ if datum_result and datum_result ["datum" ] != datum_hash :
301+ datum = RawCBOR (bytes .fromhex (datum_result ["datum" ]))
302+
303+ self ._datum_cache [datum_hash ] = datum
304+ return datum
305+
263306 def _utxos_kupo (self , address : str ) -> List [UTxO ]:
264307 """Get all UTxOs associated with an address with Kupo.
265308 Since UTxO querying will be deprecated from Ogmios in next
@@ -313,10 +356,8 @@ def _utxos_kupo(self, address: str) -> List[UTxO]:
313356 else None
314357 )
315358 if datum_hash and result .get ("datum_type" , "inline" ):
316- kupo_datum_url = self ._kupo_url + "/datums/" + result ["datum_hash" ]
317- datum_result = requests .get (kupo_datum_url ).json ()
318- if datum_result and datum_result ["datum" ] != datum_hash :
319- datum = RawCBOR (bytes .fromhex (datum_result ["datum" ]))
359+ datum = self ._get_datum_from_kupo (result ["datum_hash" ])
360+ if datum is not None :
320361 datum_hash = None
321362
322363 if not result ["value" ]["assets" ]:
0 commit comments