diff --git a/stellar_sdk/scval.py b/stellar_sdk/scval.py index 60d737f7..e37542a3 100644 --- a/stellar_sdk/scval.py +++ b/stellar_sdk/scval.py @@ -1,10 +1,46 @@ -from typing import Union, List +from typing import Union, List, Dict from . import xdr as stellar_xdr from .address import Address -from collections import OrderedDict +__all__ = [ + "to_address", + "from_address", + "to_bool", + "from_bool", + "to_bytes", + "from_bytes", + "to_duration", + "from_duration", + "to_int32", + "from_int32", + "to_int64", + "from_int64", + "to_int128", + "from_int128", + "to_int256", + "from_int256", + "to_map", + "from_map", + "to_string", + "from_string", + "to_symbol", + "from_symbol", + "to_timepoint", + "from_timepoint", + "to_uint32", + "from_uint32", + "to_uint64", + "from_uint64", + "to_uint128", + "from_uint128", + "to_uint256", + "from_uint256", + "to_vec", + "from_vec" +] + def to_address(value: Union[Address, str]) -> stellar_xdr.SCVal: """Creates a new :class:`stellar_sdk.xdr.SCVal` XDR object from an :class:`stellar_sdk.address.Address` object. @@ -95,8 +131,7 @@ def from_duration(sc_val: stellar_xdr.SCVal) -> int: """ if sc_val.type != stellar_xdr.SCValType.SCV_DURATION: raise ValueError(f"Invalid sc_val type, must be SCV_DURATION, got {sc_val.type}") - assert sc_val.timepoint is not None - return sc_val.timepoint.time_point.uint64 + return sc_val.duration.duration.uint64 def to_int32(value: int) -> stellar_xdr.SCVal: @@ -234,27 +269,27 @@ def from_int256(sc_val: stellar_xdr.SCVal) -> int: return int.from_bytes(value_bytes, "big", signed=True) -def to_map(value: OrderedDict[stellar_xdr.SCVal, stellar_xdr.SCVal]) -> stellar_xdr.SCVal: +def to_map(value: Dict[stellar_xdr.SCVal, stellar_xdr.SCVal]) -> stellar_xdr.SCVal: """Creates a new :class:`stellar_sdk.xdr.SCVal` XDR object from an OrderedDict value. - :param value: The OrderedDict value. + :param value: The value. :return: A new :class:`stellar_sdk.xdr.SCVal` XDR object with type :class:`stellar_sdk.xdr.SCValType.SCV_MAP`. """ return stellar_xdr.SCVal.from_scv_map( stellar_xdr.SCMap(sc_map=[stellar_xdr.SCMapEntry(key=key, val=value) for key, value in value.items()])) -def from_map(sc_val: stellar_xdr.SCVal) -> OrderedDict[stellar_xdr.SCVal, stellar_xdr.SCVal]: - """Creates an OrderedDict value from a :class:`stellar_sdk.xdr.SCVal` XDR object. +def from_map(sc_val: stellar_xdr.SCVal) -> Dict[stellar_xdr.SCVal, stellar_xdr.SCVal]: + """Creates an dict value from a :class:`stellar_sdk.xdr.SCVal` XDR object. :param sc_val: The :class:`stellar_sdk.xdr.SCVal` XDR object. - :return: An OrderedDict value. + :return: The map value. :raises: :exc:`ValueError` if ``sc_val`` is not of type :class:`stellar_sdk.xdr.SCValType.SCV_MAP`. """ if sc_val.type != stellar_xdr.SCValType.SCV_MAP: raise ValueError(f"Invalid sc_val type, must be SCV_MAP, got {sc_val.type}") assert sc_val.map is not None - return OrderedDict([(entry.key, entry.val) for entry in sc_val.map.sc_map]) + return dict([(entry.key, entry.val) for entry in sc_val.map.sc_map]) def to_string(value: Union[str, bytes]) -> stellar_xdr.SCVal: """Creates a new :class:`stellar_sdk.xdr.SCVal` XDR object from a string value. diff --git a/tests/test_scval.py b/tests/test_scval.py new file mode 100644 index 00000000..3ba9f030 --- /dev/null +++ b/tests/test_scval.py @@ -0,0 +1,203 @@ +from collections import OrderedDict + +import pytest + +from stellar_sdk.address import Address +from stellar_sdk.scval import * +from stellar_sdk import xdr + + +def test_address(): + addr = Address("GAHJJJKMOKYE4RVPZEWZTKH5FVI4PA3VL7GK2LFNUBSGBV6OJP7TQSLX") + scval = to_address(addr) + + expected_scval = addr.to_xdr_sc_val() + assert scval == expected_scval + assert from_address(scval) == addr + + +def test_bool(): + scval = to_bool(True) + expected_scval = xdr.SCVal.from_scv_bool(True) + assert scval == expected_scval + assert from_bool(scval) is True + + +def test_bytes(): + v = b"hello" + scval = to_bytes(v) + expected_scval = xdr.SCVal.from_scv_bytes(xdr.SCBytes(v)) + assert scval == expected_scval + assert from_bytes(scval) == v + + +@pytest.mark.parametrize( + "v", [ + (2 ** 64) - 1, + 0 + ] +) +def test_duration(v): + scval = to_duration(v) + expected_scval = xdr.SCVal.from_scv_duration(xdr.Duration(xdr.Uint64(v))) + + assert scval == expected_scval + assert from_duration(scval) == v + + +@pytest.mark.parametrize( + "v", [ + 2 ** 64, + -1 + ] +) +def test_duration_out_of_range_raise(v): + v = (2 ** 64) + with pytest.raises(ValueError, match="Invalid value"): + to_duration(v) + + +@pytest.mark.parametrize( + "v", [ + 2 ** 31 - 1, + -(2 ** 31) + ] +) +def test_int32(v): + scval = to_int32(v) + expected_scval = xdr.SCVal.from_scv_i32(xdr.Int32(v)) + assert scval == expected_scval + assert from_int32(scval) == v + + +@pytest.mark.parametrize( + "v", [ + 2 ** 31, + -(2 ** 31) - 1] +) +def test_int32_out_of_range_raise(v): + with pytest.raises(ValueError, match="Invalid value"): + to_int32(v) + + +@pytest.mark.parametrize( + "v", [ + 2 ** 63 - 1, + -(2 ** 63)] +) +def test_int64(v): + scval = to_int64(v) + expected_scval = xdr.SCVal.from_scv_i64(xdr.Int64(v)) + assert scval == expected_scval + assert from_int64(scval) == v + + +@pytest.mark.parametrize( + "v", [ + 2 ** 63, + -(2 ** 63) - 1] +) +def test_int64_out_of_range_raise(v): + with pytest.raises(ValueError, match="Invalid value"): + to_int64(v) + + +@pytest.mark.parametrize( + "v, expected_xdr", [ + (0, 'AAAACgAAAAAAAAAAAAAAAAAAAAA='), + (1, 'AAAACgAAAAAAAAAAAAAAAAAAAAE='), + (-1, 'AAAACv////////////////////8='), + (2 ** 64, 'AAAACgAAAAAAAAABAAAAAAAAAAA='), + (-(2 ** 64), 'AAAACv//////////AAAAAAAAAAA='), + (2 ** 127 - 1, 'AAAACn////////////////////8='), # TODO: recheck + (-(2 ** 127), 'AAAACoAAAAAAAAAAAAAAAAAAAAA='), + ] +) +def test_int128(v, expected_xdr): + scval = to_int128(v) + assert scval.to_xdr() == expected_xdr + assert from_int128(scval) == v + + +@pytest.mark.parametrize( + "v", [ + 2 ** 127, + -(2 ** 127) - 1] + +) +def test_int128_out_of_range_raise(v): + with pytest.raises(ValueError, match="Invalid value"): + to_int128(v) + + +@pytest.mark.parametrize( + "v, expected_xdr", [ + (0, 'AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'), + (1, 'AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB'), + (-1, 'AAAADP//////////////////////////////////////////'), + (2 ** 64, 'AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAA'), + (-(2 ** 64), 'AAAADP///////////////////////////////wAAAAAAAAAA'), + (2 ** 128, 'AAAADAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAA'), + (-(2 ** 128), 'AAAADP////////////////////8AAAAAAAAAAAAAAAAAAAAA'), + (2 ** 192, 'AAAADAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'), + (-(2 ** 192), 'AAAADP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'), + (2 ** 255 - 1, 'AAAADH//////////////////////////////////////////'), # TODO: recheck + (-(2 ** 255), 'AAAADIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'), + ] +) +def test_int256(v, expected_xdr): + scval = to_int256(v) + assert scval.to_xdr() == expected_xdr + assert from_int256(scval) == v + + +@pytest.mark.parametrize( + "v", [ + 2 ** 255, + -(2 ** 255) - 1] +) +def test_int256_out_of_range_raise(v): + with pytest.raises(ValueError, match="Invalid value"): + to_int256(v) + + +def test_map(): + v = { + to_symbol("hello3"): to_int32(1), + to_symbol("hello1"): to_int256(23423432), + to_string("hello2"): to_string('world'), + } + scval = to_map(v) + expected_scval = xdr.SCVal.from_scv_map( + xdr.SCMap([ + xdr.SCMapEntry(to_symbol("hello3"), to_int32(1)), + xdr.SCMapEntry(to_symbol("hello1"), to_int256(23423432)), + xdr.SCMapEntry(to_string("hello2"), to_string('world')) + ]) + ) + assert scval == expected_scval + assert from_map(scval) == v + + +@pytest.mark.parametrize( + "v, expected_xdr", [ + (0, 'AAAACQAAAAAAAAAAAAAAAAAAAAA='), + (1, 'AAAACQAAAAAAAAAAAAAAAAAAAAE='), + (2 ** 64, 'AAAACQAAAAAAAAABAAAAAAAAAAA='), + (2 ** 128 - 1, 'AAAACf////////////////////8='), # TODO: recheck + ] +) +def test_uint128(v, expected_xdr): + scval = to_uint128(v) + assert scval.to_xdr() == expected_xdr + assert from_uint128(scval) == v + + +@pytest.mark.parametrize( + "v", [ + -1, + 2 ** 128] +) +def test_uint128_out_of_range_raise(v): + with pytest.raises(ValueError, match="Invalid value"): + to_uint128(v)