1
1
import json
2
2
import time
3
+ from copy import deepcopy
3
4
from datetime import datetime , timezone
4
5
from enum import Enum
5
6
from typing import Any , Dict , List , Optional , Tuple , Union
6
7
7
8
import cbor2
8
9
import requests
9
10
import websocket
11
+ from cachetools import Cache , LRUCache , TTLCache
10
12
11
13
from pycardano .address import Address
12
14
from pycardano .backend .base import (
@@ -49,6 +51,8 @@ class OgmiosChainContext(ChainContext):
49
51
_last_chain_tip_fetch : float
50
52
_genesis_param : Optional [GenesisParameters ]
51
53
_protocol_param : Optional [ProtocolParameters ]
54
+ _utxo_cache : Cache
55
+ _datum_cache : Cache
52
56
53
57
def __init__ (
54
58
self ,
@@ -57,6 +61,8 @@ def __init__(
57
61
compact_result = True ,
58
62
kupo_url = None ,
59
63
refetch_chain_tip_interval : Optional [float ] = None ,
64
+ utxo_cache_size : int = 10000 ,
65
+ datum_cache_size : int = 10000 ,
60
66
):
61
67
self ._ws_url = ws_url
62
68
self ._network = network
@@ -77,6 +83,11 @@ def __init__(
77
83
/ self .genesis_param .active_slots_coefficient
78
84
)
79
85
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
+
80
91
def _request (self , method : OgmiosQueryType , args : JsonDict ) -> Any :
81
92
ws = websocket .WebSocket ()
82
93
ws .connect (self ._ws_url )
@@ -253,13 +264,45 @@ def _utxos(self, address: str) -> List[UTxO]:
253
264
Returns:
254
265
List[UTxO]: A list of UTxOs.
255
266
"""
267
+ if (self .last_block_slot , address ) in self ._utxo_cache :
268
+ return deepcopy (self ._utxo_cache [(self .last_block_slot , address )])
269
+
256
270
if self ._kupo_url :
257
271
utxos = self ._utxos_kupo (address )
258
272
else :
259
273
utxos = self ._utxos_ogmios (address )
260
274
275
+ self ._utxo_cache [(self .last_block_slot , address )] = deepcopy (utxos )
276
+
261
277
return utxos
262
278
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
+
263
306
def _utxos_kupo (self , address : str ) -> List [UTxO ]:
264
307
"""Get all UTxOs associated with an address with Kupo.
265
308
Since UTxO querying will be deprecated from Ogmios in next
@@ -313,10 +356,8 @@ def _utxos_kupo(self, address: str) -> List[UTxO]:
313
356
else None
314
357
)
315
358
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 :
320
361
datum_hash = None
321
362
322
363
if not result ["value" ]["assets" ]:
0 commit comments