Skip to content

Commit a2c3a8e

Browse files
Refactor ogmios.py module maintainability (#114)
* REFACTOR. pull out current protocol param query * REFACTOR. pull out genesis config query * REFACTOR. pull out current epoch query * UPDATE. running black formatter UPDATE. including black format check in ci/cd static analyses part * REFACTOR. pulling out chain tip query * FIX. return JSON result from query chain tip * REFACTOR. pull out utxo ogmios query methods with explicit return type REFACTOR. re-order methods so that helper functions are right beneath the main function for readability * REFACTOR. pull out genesis and protocol param fetching logic to be independently testable REFACTOR. renaming chain tip status checking helper method
1 parent 7d1cbc4 commit a2c3a8e

File tree

8 files changed

+143
-122
lines changed

8 files changed

+143
-122
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ test-single: ## runs tests with "single" markers
6363
qa: ## runs static analyses
6464
poetry run flake8 pycardano
6565
poetry run mypy --install-types --non-interactive pycardano
66+
poetry run black --check .
6667

6768
format: ## runs code style and formatter
6869
poetry run isort .

pycardano/address.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from __future__ import annotations
1010

1111
from enum import Enum
12-
from typing import Union, Type
12+
from typing import Type, Union
1313

1414
from pycardano.crypto.bech32 import decode, encode
1515
from pycardano.exception import (

pycardano/backend/ogmios.py

+130-114
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import json
33
import time
44
from enum import Enum
5-
from typing import Any, Dict, List, Optional, Union, Tuple
5+
from typing import Any, Dict, List, Optional, Tuple, Union
66

77
import cbor2
88
import requests
@@ -88,7 +88,31 @@ def _request(self, method: OgmiosQueryType, args: JSON) -> Any:
8888
)
8989
return json.loads(response)["result"]
9090

91-
def _check_chain_tip_and_update(self):
91+
def _query_current_protocol_params(self) -> JSON:
92+
args = {"query": "currentProtocolParameters"}
93+
return self._request(OgmiosQueryType.Query, args)
94+
95+
def _query_genesis_config(self) -> JSON:
96+
args = {"query": "genesisConfig"}
97+
return self._request(OgmiosQueryType.Query, args)
98+
99+
def _query_current_epoch(self) -> int:
100+
args = {"query": "currentEpoch"}
101+
return self._request(OgmiosQueryType.Query, args)
102+
103+
def _query_chain_tip(self) -> JSON:
104+
args = {"query": "chainTip"}
105+
return self._request(OgmiosQueryType.Query, args)
106+
107+
def _query_utxos_by_address(self, address: str) -> List[List[JSON]]:
108+
args = {"query": {"utxo": [address]}}
109+
return self._request(OgmiosQueryType.Query, args)
110+
111+
def _query_utxos_by_tx_id(self, tx_id: str, index: int) -> List[List[JSON]]:
112+
args = {"query": {"utxo": [{"txId": tx_id, "index": index}]}}
113+
return self._request(OgmiosQueryType.Query, args)
114+
115+
def _is_chain_tip_updated(self):
92116
slot = self.last_block_slot
93117
if self._last_known_block_slot != slot:
94118
self._last_known_block_slot = slot
@@ -104,84 +128,84 @@ def _fraction_parser(fraction: str) -> float:
104128
@property
105129
def protocol_param(self) -> ProtocolParameters:
106130
"""Get current protocol parameters"""
107-
args = {"query": "currentProtocolParameters"}
108-
if not self._protocol_param or self._check_chain_tip_and_update():
109-
result = self._request(OgmiosQueryType.Query, args)
110-
param = ProtocolParameters(
111-
min_fee_constant=result["minFeeConstant"],
112-
min_fee_coefficient=result["minFeeCoefficient"],
113-
max_block_size=result["maxBlockBodySize"],
114-
max_tx_size=result["maxTxSize"],
115-
max_block_header_size=result["maxBlockHeaderSize"],
116-
key_deposit=result["stakeKeyDeposit"],
117-
pool_deposit=result["poolDeposit"],
118-
pool_influence=self._fraction_parser(result["poolInfluence"]),
119-
monetary_expansion=self._fraction_parser(result["monetaryExpansion"]),
120-
treasury_expansion=self._fraction_parser(result["treasuryExpansion"]),
121-
decentralization_param=self._fraction_parser(
122-
result.get("decentralizationParameter", "0/1")
123-
),
124-
extra_entropy=result.get("extraEntropy", ""),
125-
protocol_major_version=result["protocolVersion"]["major"],
126-
protocol_minor_version=result["protocolVersion"]["minor"],
127-
min_pool_cost=result["minPoolCost"],
128-
price_mem=self._fraction_parser(result["prices"]["memory"]),
129-
price_step=self._fraction_parser(result["prices"]["steps"]),
130-
max_tx_ex_mem=result["maxExecutionUnitsPerTransaction"]["memory"],
131-
max_tx_ex_steps=result["maxExecutionUnitsPerTransaction"]["steps"],
132-
max_block_ex_mem=result["maxExecutionUnitsPerBlock"]["memory"],
133-
max_block_ex_steps=result["maxExecutionUnitsPerBlock"]["steps"],
134-
max_val_size=result["maxValueSize"],
135-
collateral_percent=result["collateralPercentage"],
136-
max_collateral_inputs=result["maxCollateralInputs"],
137-
coins_per_utxo_word=result.get(
138-
"coinsPerUtxoWord", ALONZO_COINS_PER_UTXO_WORD
139-
),
140-
coins_per_utxo_byte=result.get("coinsPerUtxoByte", 0),
141-
cost_models=result.get("costModels", {}),
142-
)
131+
if not self._protocol_param or self._is_chain_tip_updated():
132+
self._protocol_param = self._fetch_protocol_param()
133+
return self._protocol_param
143134

144-
if "plutus:v1" in param.cost_models:
145-
param.cost_models["PlutusV1"] = param.cost_models.pop("plutus:v1")
146-
if "plutus:v2" in param.cost_models:
147-
param.cost_models["PlutusV2"] = param.cost_models.pop("plutus:v2")
135+
def _fetch_protocol_param(self) -> ProtocolParameters:
136+
result = self._query_current_protocol_params()
137+
param = ProtocolParameters(
138+
min_fee_constant=result["minFeeConstant"],
139+
min_fee_coefficient=result["minFeeCoefficient"],
140+
max_block_size=result["maxBlockBodySize"],
141+
max_tx_size=result["maxTxSize"],
142+
max_block_header_size=result["maxBlockHeaderSize"],
143+
key_deposit=result["stakeKeyDeposit"],
144+
pool_deposit=result["poolDeposit"],
145+
pool_influence=self._fraction_parser(result["poolInfluence"]),
146+
monetary_expansion=self._fraction_parser(result["monetaryExpansion"]),
147+
treasury_expansion=self._fraction_parser(result["treasuryExpansion"]),
148+
decentralization_param=self._fraction_parser(
149+
result.get("decentralizationParameter", "0/1")
150+
),
151+
extra_entropy=result.get("extraEntropy", ""),
152+
protocol_major_version=result["protocolVersion"]["major"],
153+
protocol_minor_version=result["protocolVersion"]["minor"],
154+
min_pool_cost=result["minPoolCost"],
155+
price_mem=self._fraction_parser(result["prices"]["memory"]),
156+
price_step=self._fraction_parser(result["prices"]["steps"]),
157+
max_tx_ex_mem=result["maxExecutionUnitsPerTransaction"]["memory"],
158+
max_tx_ex_steps=result["maxExecutionUnitsPerTransaction"]["steps"],
159+
max_block_ex_mem=result["maxExecutionUnitsPerBlock"]["memory"],
160+
max_block_ex_steps=result["maxExecutionUnitsPerBlock"]["steps"],
161+
max_val_size=result["maxValueSize"],
162+
collateral_percent=result["collateralPercentage"],
163+
max_collateral_inputs=result["maxCollateralInputs"],
164+
coins_per_utxo_word=result.get(
165+
"coinsPerUtxoWord", ALONZO_COINS_PER_UTXO_WORD
166+
),
167+
coins_per_utxo_byte=result.get("coinsPerUtxoByte", 0),
168+
cost_models=result.get("costModels", {}),
169+
)
148170

149-
args = {"query": "genesisConfig"}
150-
result = self._request(OgmiosQueryType.Query, args)
151-
param.min_utxo = result["protocolParameters"]["minUtxoValue"]
171+
if "plutus:v1" in param.cost_models:
172+
param.cost_models["PlutusV1"] = param.cost_models.pop("plutus:v1")
173+
if "plutus:v2" in param.cost_models:
174+
param.cost_models["PlutusV2"] = param.cost_models.pop("plutus:v2")
152175

153-
self._protocol_param = param
154-
return self._protocol_param
176+
result = self._query_genesis_config()
177+
param.min_utxo = result["protocolParameters"]["minUtxoValue"]
178+
return param
155179

156180
@property
157181
def genesis_param(self) -> GenesisParameters:
158182
"""Get chain genesis parameters"""
159-
args = {"query": "genesisConfig"}
160-
if not self._genesis_param or self._check_chain_tip_and_update():
161-
result = self._request(OgmiosQueryType.Query, args)
162-
system_start_unix = int(
163-
calendar.timegm(
164-
time.strptime(
165-
result["systemStart"].split(".")[0], "%Y-%m-%dT%H:%M:%S"
166-
),
167-
)
168-
)
169-
self._genesis_param = GenesisParameters(
170-
active_slots_coefficient=self._fraction_parser(
171-
result["activeSlotsCoefficient"]
172-
),
173-
update_quorum=result["updateQuorum"],
174-
max_lovelace_supply=result["maxLovelaceSupply"],
175-
network_magic=result["networkMagic"],
176-
epoch_length=result["epochLength"],
177-
system_start=system_start_unix,
178-
slots_per_kes_period=result["slotsPerKesPeriod"],
179-
slot_length=result["slotLength"],
180-
max_kes_evolutions=result["maxKesEvolutions"],
181-
security_param=result["securityParameter"],
182-
)
183+
if not self._genesis_param or self._is_chain_tip_updated():
184+
self._genesis_param = self._fetch_genesis_param()
183185
return self._genesis_param
184186

187+
def _fetch_genesis_param(self) -> GenesisParameters:
188+
result = self._query_genesis_config()
189+
system_start_unix = int(
190+
calendar.timegm(
191+
time.strptime(result["systemStart"].split(".")[0], "%Y-%m-%dT%H:%M:%S"),
192+
)
193+
)
194+
return GenesisParameters(
195+
active_slots_coefficient=self._fraction_parser(
196+
result["activeSlotsCoefficient"]
197+
),
198+
update_quorum=result["updateQuorum"],
199+
max_lovelace_supply=result["maxLovelaceSupply"],
200+
network_magic=result["networkMagic"],
201+
epoch_length=result["epochLength"],
202+
system_start=system_start_unix,
203+
slots_per_kes_period=result["slotsPerKesPeriod"],
204+
slot_length=result["slotLength"],
205+
max_kes_evolutions=result["maxKesEvolutions"],
206+
security_param=result["securityParameter"],
207+
)
208+
185209
@property
186210
def network(self) -> Network:
187211
"""Get current network"""
@@ -190,37 +214,29 @@ def network(self) -> Network:
190214
@property
191215
def epoch(self) -> int:
192216
"""Current epoch number"""
193-
args = {"query": "currentEpoch"}
194-
return self._request(OgmiosQueryType.Query, args)
217+
return self._query_current_epoch()
195218

196219
@property
197220
def last_block_slot(self) -> int:
198221
"""Slot number of last block"""
199-
args = {"query": "chainTip"}
200-
return self._request(OgmiosQueryType.Query, args)["slot"]
201-
202-
def _extract_asset_info(self, asset_hash: str) -> Tuple[str, ScriptHash, AssetName]:
203-
policy_hex, asset_name_hex = asset_hash.split(".")
204-
policy = ScriptHash.from_primitive(policy_hex)
205-
asset_name = AssetName.from_primitive(asset_name_hex)
222+
result = self._query_chain_tip()
223+
return result["slot"]
206224

207-
return policy_hex, policy, asset_name
208-
209-
def _check_utxo_unspent(self, tx_id: str, index: int) -> bool:
210-
"""Check whether an UTxO is unspent with Ogmios.
225+
def utxos(self, address: str) -> List[UTxO]:
226+
"""Get all UTxOs associated with an address.
211227
212228
Args:
213-
tx_id (str): transaction id.
214-
index (int): transaction index.
215-
"""
216-
217-
args = {"query": {"utxo": [{"txId": tx_id, "index": index}]}}
218-
results = self._request(OgmiosQueryType.Query, args)
229+
address (str): An address encoded with bech32.
219230
220-
if results:
221-
return True
231+
Returns:
232+
List[UTxO]: A list of UTxOs.
233+
"""
234+
if self._kupo_url:
235+
utxos = self._utxos_kupo(address)
222236
else:
223-
return False
237+
utxos = self._utxos_ogmios(address)
238+
239+
return utxos
224240

225241
def _utxos_kupo(self, address: str) -> List[UTxO]:
226242
"""Get all UTxOs associated with an address with Kupo.
@@ -234,7 +250,9 @@ def _utxos_kupo(self, address: str) -> List[UTxO]:
234250
List[UTxO]: A list of UTxOs.
235251
"""
236252
if self._kupo_url is None:
237-
raise AssertionError("kupo_url object attribute has not been assigned properly.")
253+
raise AssertionError(
254+
"kupo_url object attribute has not been assigned properly."
255+
)
238256

239257
address_url = self._kupo_url + "/" + address
240258
results = requests.get(address_url).json()
@@ -288,6 +306,23 @@ def _utxos_kupo(self, address: str) -> List[UTxO]:
288306

289307
return utxos
290308

309+
def _check_utxo_unspent(self, tx_id: str, index: int) -> bool:
310+
"""Check whether an UTxO is unspent with Ogmios.
311+
312+
Args:
313+
tx_id (str): transaction id.
314+
index (int): transaction index.
315+
"""
316+
results = self._query_utxos_by_tx_id(tx_id, index)
317+
return len(results) > 0
318+
319+
def _extract_asset_info(self, asset_hash: str) -> Tuple[str, ScriptHash, AssetName]:
320+
policy_hex, asset_name_hex = asset_hash.split(".")
321+
policy = ScriptHash.from_primitive(policy_hex)
322+
asset_name = AssetName.from_primitive(asset_name_hex)
323+
324+
return policy_hex, policy, asset_name
325+
291326
def _utxos_ogmios(self, address: str) -> List[UTxO]:
292327
"""Get all UTxOs associated with an address with Ogmios.
293328
@@ -297,12 +332,9 @@ def _utxos_ogmios(self, address: str) -> List[UTxO]:
297332
Returns:
298333
List[UTxO]: A list of UTxOs.
299334
"""
300-
301-
args = {"query": {"utxo": [address]}}
302-
results = self._request(OgmiosQueryType.Query, args)
335+
results = self._query_utxos_by_address(address)
303336

304337
utxos = []
305-
306338
for result in results:
307339
in_ref = result[0]
308340
output = result[1]
@@ -360,22 +392,6 @@ def _utxos_ogmios(self, address: str) -> List[UTxO]:
360392

361393
return utxos
362394

363-
def utxos(self, address: str) -> List[UTxO]:
364-
"""Get all UTxOs associated with an address.
365-
366-
Args:
367-
address (str): An address encoded with bech32.
368-
369-
Returns:
370-
List[UTxO]: A list of UTxOs.
371-
"""
372-
if self._kupo_url:
373-
utxos = self._utxos_kupo(address)
374-
else:
375-
utxos = self._utxos_ogmios(address)
376-
377-
return utxos
378-
379395
def submit_tx(self, cbor: Union[bytes, str]):
380396
"""Submit a transaction to the blockchain.
381397

pycardano/hash.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""All type of hashes in Cardano ledger spec."""
22

3-
from typing import TypeVar, Union, Type
3+
from typing import Type, TypeVar, Union
44

55
from pycardano.serialization import CBORSerializable
66

pycardano/metadata.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass, field
4-
from typing import Any, ClassVar, List, Union, Type
4+
from typing import Any, ClassVar, List, Type, Union
55

66
from cbor2 import CBORTag
77
from nacl.encoding import RawEncoder

pycardano/nativescript.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
from dataclasses import dataclass, field
6-
from typing import ClassVar, List, Union, Type
6+
from typing import ClassVar, List, Type, Union
77

88
from nacl.encoding import RawEncoder
99
from nacl.hash import blake2b

pycardano/plutus.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import json
77
from dataclasses import dataclass, field, fields
88
from enum import Enum
9-
from typing import Any, ClassVar, List, Optional, Union, Type
9+
from typing import Any, ClassVar, List, Optional, Type, Union
1010

1111
import cbor2
1212
from cbor2 import CBORTag

0 commit comments

Comments
 (0)