diff --git a/pycardano/serialization.py b/pycardano/serialization.py index fe1b5f75..b25f597b 100644 --- a/pycardano/serialization.py +++ b/pycardano/serialization.py @@ -870,6 +870,13 @@ def __copy__(self): def __deepcopy__(self, memodict={}): return self.__class__(deepcopy(self.data)) + def validate(self): + for key, value in self.data.items(): + if isinstance(key, CBORSerializable): + key.validate() + if isinstance(value, CBORSerializable): + value.validate() + def to_shallow_primitive(self) -> dict: # Sort keys in a map according to https://datatracker.ietf.org/doc/html/rfc7049#section-3.9 def _get_sortable_val(key): diff --git a/pycardano/transaction.py b/pycardano/transaction.py index cf6ca814..79b7ce01 100644 --- a/pycardano/transaction.py +++ b/pycardano/transaction.py @@ -54,6 +54,9 @@ "Withdrawals", ] +_MAX_INT64 = (1 << 63) - 1 +_MIN_INT64 = -(1 << 63) + @dataclass(repr=False) class TransactionInput(ArrayCBORSerializable): @@ -561,6 +564,15 @@ class TransactionBody(MapCBORSerializable): }, ) + def validate(self): + if ( + self.mint + and self.mint.count(lambda p, n, v: v < _MIN_INT64 or v > _MAX_INT64) > 0 + ): + raise InvalidDataException( + f"Mint amount must be between {_MIN_INT64} and {_MAX_INT64}. \n Mint amount: {self.mint}" + ) + def hash(self) -> bytes: return blake2b(self.to_cbor(), TRANSACTION_HASH_SIZE, encoder=RawEncoder) # type: ignore diff --git a/test/pycardano/test_transaction.py b/test/pycardano/test_transaction.py index 8d2b7539..d414bb32 100644 --- a/test/pycardano/test_transaction.py +++ b/test/pycardano/test_transaction.py @@ -469,3 +469,15 @@ class TestDatum(PlutusData): cbor = output.to_cbor_hex() assert cbor == TransactionOutput.from_cbor(cbor).to_cbor_hex() + + +def test_out_of_bound_asset(): + a = Asset({AssetName(b"abc"): 1 << 64}) + + a.to_cbor_hex() # okay to have out of bound asset + + tx = TransactionBody(mint=MultiAsset({ScriptHash(b"1" * SCRIPT_HASH_SIZE): a})) + + # Not okay only when minting + with pytest.raises(InvalidDataException): + tx.to_cbor_hex()