Skip to content

Commit cc16bbe

Browse files
authored
Get UTxO from Transaction ID and Index (#186)
* utxo_by_tx_id * utxo_by_tx_id ogmios test
1 parent b432e22 commit cc16bbe

File tree

2 files changed

+91
-50
lines changed

2 files changed

+91
-50
lines changed

pycardano/backend/ogmios.py

Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -366,61 +366,76 @@ def _utxos_ogmios(self, address: str) -> List[UTxO]:
366366

367367
utxos = []
368368
for result in results:
369-
in_ref = result[0]
370-
output = result[1]
371-
tx_in = TransactionInput.from_primitive([in_ref["txId"], in_ref["index"]])
369+
utxos.append(self._utxo_from_ogmios_result(result))
372370

373-
lovelace_amount = output["value"]["coins"]
374-
375-
script = output.get("script", None)
376-
if script:
377-
if "plutus:v2" in script:
378-
script = PlutusV2Script(
379-
cbor2.loads(bytes.fromhex(script["plutus:v2"]))
380-
)
381-
elif "plutus:v1" in script:
382-
script = PlutusV1Script(
383-
cbor2.loads(bytes.fromhex(script["plutus:v1"]))
384-
)
385-
else:
386-
raise ValueError("Unknown plutus script type")
371+
return utxos
387372

388-
datum_hash = (
389-
DatumHash.from_primitive(output["datumHash"])
390-
if output.get("datumHash", None)
391-
else None
392-
)
373+
def utxo_by_tx_id(self, tx_id: str, index: int) -> Optional[UTxO]:
374+
"""Get a UTxO associated with a tx_id and index.
393375
394-
datum = None
376+
Args:
377+
tx_id (str): The transaction id.
378+
index (int): The index for the UTxO at the specified transaction.
395379
396-
if output["datum"] and output["datum"] != output["datumHash"]:
397-
datum = RawCBOR(bytes.fromhex(output["datum"]))
380+
Returns:
381+
Optional[UTxO]: Return a UTxO if exists at the tx_id and index.
382+
"""
383+
results = self._query_utxos_by_tx_id(tx_id, index)
384+
assert len(results) < 2, f"Query for UTxO {tx_id}#{index} should be unique!"
398385

399-
if not output["value"]["assets"]:
400-
tx_out = TransactionOutput(
401-
Address.from_primitive(address),
402-
amount=lovelace_amount,
403-
datum_hash=datum_hash,
404-
datum=datum,
405-
script=script,
406-
)
386+
utxos = []
387+
for result in results:
388+
utxos.append(self._utxo_from_ogmios_result(result))
389+
390+
if len(utxos) > 0:
391+
return utxos[0]
392+
return None
393+
394+
def _utxo_from_ogmios_result(self, result) -> UTxO:
395+
in_ref = result[0]
396+
output = result[1]
397+
tx_in = TransactionInput.from_primitive([in_ref["txId"], in_ref["index"]])
398+
lovelace_amount = output["value"]["coins"]
399+
script = output.get("script", None)
400+
if script:
401+
if "plutus:v2" in script:
402+
script = PlutusV2Script(cbor2.loads(bytes.fromhex(script["plutus:v2"])))
403+
elif "plutus:v1" in script:
404+
script = PlutusV1Script(cbor2.loads(bytes.fromhex(script["plutus:v1"])))
407405
else:
408-
multi_assets = MultiAsset()
409-
410-
for asset, quantity in output["value"]["assets"].items():
411-
policy_hex, policy, asset_name_hex = self._extract_asset_info(asset)
412-
multi_assets.setdefault(policy, Asset())[asset_name_hex] = quantity
413-
414-
tx_out = TransactionOutput(
415-
Address.from_primitive(address),
416-
amount=Value(lovelace_amount, multi_assets),
417-
datum_hash=datum_hash,
418-
datum=datum,
419-
script=script,
420-
)
421-
utxos.append(UTxO(tx_in, tx_out))
422-
423-
return utxos
406+
raise ValueError("Unknown plutus script type")
407+
datum_hash = (
408+
DatumHash.from_primitive(output["datumHash"])
409+
if output.get("datumHash", None)
410+
else None
411+
)
412+
datum = None
413+
if output["datum"] and output["datum"] != output["datumHash"]:
414+
datum = RawCBOR(bytes.fromhex(output["datum"]))
415+
if not output["value"]["assets"]:
416+
tx_out = TransactionOutput(
417+
Address.from_primitive(output["address"]),
418+
amount=lovelace_amount,
419+
datum_hash=datum_hash,
420+
datum=datum,
421+
script=script,
422+
)
423+
else:
424+
multi_assets = MultiAsset()
425+
426+
for asset, quantity in output["value"]["assets"].items():
427+
policy_hex, policy, asset_name_hex = self._extract_asset_info(asset)
428+
multi_assets.setdefault(policy, Asset())[asset_name_hex] = quantity
429+
430+
tx_out = TransactionOutput(
431+
Address.from_primitive(output["address"]),
432+
amount=Value(lovelace_amount, multi_assets),
433+
datum_hash=datum_hash,
434+
datum=datum,
435+
script=script,
436+
)
437+
utxo = UTxO(tx_in, tx_out)
438+
return utxo
424439

425440
def submit_tx(self, cbor: Union[bytes, str]):
426441
"""Submit a transaction to the blockchain.

test/pycardano/backend/test_ogmios.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,17 @@ def override_request(method, args):
8989
elif args["query"] == "genesisConfig":
9090
return GENESIS_RESULT
9191
elif "utxo" in args["query"]:
92-
return UTXOS
92+
query = args["query"]["utxo"][0]
93+
if isinstance(query, dict):
94+
for utxo in UTXOS:
95+
if (
96+
utxo[0]["txId"] == query["txId"]
97+
and utxo[0]["index"] == query["index"]
98+
):
99+
return [utxo]
100+
return []
101+
else:
102+
return UTXOS
93103
else:
94104
return None
95105

@@ -174,3 +184,19 @@ def test_utxo(self):
174184
},
175185
}
176186
)
187+
188+
def test_utxo_by_tx_id(self):
189+
utxo = self.chain_context.utxo_by_tx_id(
190+
"3a42f652bd8dee788577e8c39b6217db3df659c33b10a2814c20fb66089ca167",
191+
1,
192+
)
193+
assert utxo.input == TransactionInput.from_primitive(
194+
["3a42f652bd8dee788577e8c39b6217db3df659c33b10a2814c20fb66089ca167", 1]
195+
)
196+
assert utxo.output.amount == 764295183
197+
198+
not_utxo = self.chain_context.utxo_by_tx_id(
199+
"3a42f652bd8dee788577e8c39b6217db3df659c33b10a2814c20fb66089ca167",
200+
2,
201+
)
202+
assert not_utxo is None

0 commit comments

Comments
 (0)