From 0daa16ed69487786a588df13aeba40ad65da88ab Mon Sep 17 00:00:00 2001 From: Jerry Date: Sat, 20 Apr 2024 12:55:01 -0700 Subject: [PATCH 1/2] Add boundary check for asset value --- pycardano/serialization.py | 7 +++++++ pycardano/transaction.py | 10 ++++++++++ test/pycardano/test_transaction.py | 7 +++++++ 3 files changed, 24 insertions(+) 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..b097a27d 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): @@ -78,6 +81,13 @@ class Asset(DictCBORSerializable): VALUE_TYPE = int + def validate(self): + for n in self: + if self[n] < _MIN_INT64 or self[n] > _MAX_INT64: + raise InvalidDataException( + f"Asset amount must be between {_MIN_INT64} and {_MAX_INT64}: \n {self[n]}" + ) + def union(self, other: Asset) -> Asset: return self + other diff --git a/test/pycardano/test_transaction.py b/test/pycardano/test_transaction.py index 8d2b7539..59f9779a 100644 --- a/test/pycardano/test_transaction.py +++ b/test/pycardano/test_transaction.py @@ -469,3 +469,10 @@ class TestDatum(PlutusData): cbor = output.to_cbor_hex() assert cbor == TransactionOutput.from_cbor(cbor).to_cbor_hex() + + +def test_out_of_bound_asset(): + bad_asset = Asset({AssetName(b"abc"): 1 << 64}) + + with pytest.raises(InvalidDataException): + bad_asset.to_cbor_hex() From e0cbb1a566dbdeea514c8b2de8e32a995aef1409 Mon Sep 17 00:00:00 2001 From: Jerry Date: Sat, 27 Apr 2024 16:57:59 -0700 Subject: [PATCH 2/2] Only raise exception when minting assets whose values are out of bound --- pycardano/transaction.py | 16 +++++++++------- test/pycardano/test_transaction.py | 9 +++++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/pycardano/transaction.py b/pycardano/transaction.py index b097a27d..79b7ce01 100644 --- a/pycardano/transaction.py +++ b/pycardano/transaction.py @@ -81,13 +81,6 @@ class Asset(DictCBORSerializable): VALUE_TYPE = int - def validate(self): - for n in self: - if self[n] < _MIN_INT64 or self[n] > _MAX_INT64: - raise InvalidDataException( - f"Asset amount must be between {_MIN_INT64} and {_MAX_INT64}: \n {self[n]}" - ) - def union(self, other: Asset) -> Asset: return self + other @@ -571,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 59f9779a..d414bb32 100644 --- a/test/pycardano/test_transaction.py +++ b/test/pycardano/test_transaction.py @@ -472,7 +472,12 @@ class TestDatum(PlutusData): def test_out_of_bound_asset(): - bad_asset = Asset({AssetName(b"abc"): 1 << 64}) + 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): - bad_asset.to_cbor_hex() + tx.to_cbor_hex()