from chia_rs import Coin from hashlib import sha256 import copy import pytest from chia_rs.sized_ints import uint64 parent_coin = b"---foo--- " puzzle_hash = b"---bar--- " puzzle_hash2 = b"---bar--- 2 " def test_coin_name() -> None: c = Coin(parent_coin, puzzle_hash, uint64(0)) assert c.name() == sha256(parent_coin + puzzle_hash).digest() c = Coin(parent_coin, puzzle_hash, uint64(1)) assert c.name() == sha256(parent_coin + puzzle_hash + bytes([1])).digest() # 0xFF prefix c = Coin(parent_coin, puzzle_hash, uint64(0xFF)) assert c.name() == sha256(parent_coin + puzzle_hash + bytes([0, 0xFF])).digest() c = Coin(parent_coin, puzzle_hash, uint64(0xFFFF)) assert ( c.name() == sha256(parent_coin + puzzle_hash + bytes([0, 0xFF, 0xFF])).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0xFFFFFF)) assert ( c.name() == sha256(parent_coin + puzzle_hash + bytes([0, 0xFF, 0xFF, 0xFF])).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0xFFFFFFFF)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0, 0xFF, 0xFF, 0xFF, 0xFF]) ).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0xFFFFFFFFFF)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) ).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0xFFFFFFFFFFFF)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) ).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0xFFFFFFFFFFFFFF)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) ).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0xFFFFFFFFFFFFFFFF)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) ).digest() ) # 0x7F prefix c = Coin(parent_coin, puzzle_hash, uint64(0x7F)) assert c.name() == sha256(parent_coin + puzzle_hash + bytes([0x7F])).digest() c = Coin(parent_coin, puzzle_hash, uint64(0x7FFF)) assert c.name() == sha256(parent_coin + puzzle_hash + bytes([0x7F, 0xFF])).digest() c = Coin(parent_coin, puzzle_hash, uint64(0x7FFFFF)) assert ( c.name() == sha256(parent_coin + puzzle_hash + bytes([0x7F, 0xFF, 0xFF])).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x7FFFFFFF)) assert ( c.name() == sha256(parent_coin + puzzle_hash + bytes([0x7F, 0xFF, 0xFF, 0xFF])).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x7FFFFFFFFF)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0x7F, 0xFF, 0xFF, 0xFF, 0xFF]) ).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x7FFFFFFFFFFF)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) ).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x7FFFFFFFFFFFFF)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) ).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x7FFFFFFFFFFFFFFF)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) ).digest() ) # 0x80 prefix c = Coin(parent_coin, puzzle_hash, uint64(0x80)) assert c.name() == sha256(parent_coin + puzzle_hash + bytes([0, 0x80])).digest() c = Coin(parent_coin, puzzle_hash, uint64(0x8000)) assert c.name() == sha256(parent_coin + puzzle_hash + bytes([0, 0x80, 0])).digest() c = Coin(parent_coin, puzzle_hash, uint64(0x800000)) assert ( c.name() == sha256(parent_coin + puzzle_hash + bytes([0, 0x80, 0, 0])).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x80000000)) assert ( c.name() == sha256(parent_coin + puzzle_hash + bytes([0, 0x80, 0, 0, 0])).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x8000000000)) assert ( c.name() == sha256(parent_coin + puzzle_hash + bytes([0, 0x80, 0, 0, 0, 0])).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x800000000000)) assert ( c.name() == sha256(parent_coin + puzzle_hash + bytes([0, 0x80, 0, 0, 0, 0, 0])).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x80000000000000)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0, 0x80, 0, 0, 0, 0, 0, 0]) ).digest() ) c = Coin(parent_coin, puzzle_hash, uint64(0x8000000000000000)) assert ( c.name() == sha256( parent_coin + puzzle_hash + bytes([0, 0x80, 0, 0, 0, 0, 0, 0, 0]) ).digest() ) def test_coin_copy() -> None: c1 = Coin(parent_coin, puzzle_hash, uint64(1000000)) c2 = copy.copy(c1) assert c1 == c2 assert c1 is not c2 def test_coin_deepcopy() -> None: c1 = Coin(parent_coin, puzzle_hash, uint64(1000000)) c2 = copy.deepcopy(c1) assert c1 == c2 assert c1 is not c2 def coin_json_roundtrip(c: Coin) -> bool: d = c.to_json_dict() c2 = Coin.from_json_dict(d) return c == c2 and c.name() == c2.name() def test_coin_to_json() -> None: c1 = Coin(parent_coin, puzzle_hash, uint64(1000000)) assert c1.to_json_dict() == { "parent_coin_info": "0x" + parent_coin.hex(), "puzzle_hash": "0x" + puzzle_hash.hex(), "amount": 1000000, } assert coin_json_roundtrip(c1) c2 = Coin(parent_coin, puzzle_hash2, uint64(0)) assert c2.to_json_dict() == { "parent_coin_info": "0x" + parent_coin.hex(), "puzzle_hash": "0x" + puzzle_hash2.hex(), "amount": 0, } assert coin_json_roundtrip(c2) c3 = Coin(parent_coin, puzzle_hash2, uint64(0xFFFFFFFFFFFFFFFF)) assert c3.to_json_dict() == { "parent_coin_info": "0x" + parent_coin.hex(), "puzzle_hash": "0x" + puzzle_hash2.hex(), "amount": 0xFFFFFFFFFFFFFFFF, } assert coin_json_roundtrip(c3) def test_coin_from_json() -> None: c = { "parent_coin_info": "0x" + parent_coin.hex(), "puzzle_hash": "0x" + puzzle_hash2.hex(), "amount": 12345678, } assert Coin.from_json_dict(c) == Coin(parent_coin, puzzle_hash2, uint64(12345678)) def test_coin_from_json_upper_hex() -> None: c = { "parent_coin_info": "0x" + parent_coin.hex().upper(), "puzzle_hash": "0x" + puzzle_hash2.hex().upper(), "amount": 12345678, } assert Coin.from_json_dict(c) == Coin(parent_coin, puzzle_hash2, uint64(12345678)) def test_coin_from_json_lower_hex() -> None: c = { "parent_coin_info": "0x" + parent_coin.hex().lower(), "puzzle_hash": "0x" + puzzle_hash2.hex().lower(), "amount": 12345678, } assert Coin.from_json_dict(c) == Coin(parent_coin, puzzle_hash2, uint64(12345678)) def test_coin_from_json_invalid_hex_prefix() -> None: c = { # this field is missing "0x"-prefix "parent_coin_info": parent_coin.hex(), "puzzle_hash": "0x" + puzzle_hash2.hex(), "amount": 12345678, } with pytest.raises(ValueError, match="bytes object is expected to start with 0x"): Coin.from_json_dict(c) def test_coin_from_json_invalid_hex_prefix2() -> None: c = { "parent_coin_info": "0x" + parent_coin.hex(), # this field is missing "0x"-prefix "puzzle_hash": puzzle_hash2.hex(), "amount": 12345678, } with pytest.raises(ValueError, match="bytes object is expected to start with 0x"): Coin.from_json_dict(c) def test_coin_from_json_hex_digit() -> None: c = { "parent_coin_info": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "puzzle_hash": "0x" + puzzle_hash2.hex(), "amount": 12345678, } assert Coin.from_json_dict(c) == Coin( bytes.fromhex( "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" ), puzzle_hash2, uint64(12345678), ) def test_coin_from_json_invalid_hex_digit() -> None: c = { # this field has an invalid hex digit "parent_coin_info": "0x0123456789abcdef0123456789abcdef0123456789abcdefg123456789abcdef", "puzzle_hash": "0x" + puzzle_hash2.hex(), "amount": 12345678, } with pytest.raises(ValueError, match="invalid hex"): Coin.from_json_dict(c) def test_coin_from_json_invalid_hex_len() -> None: c = { # this field has an invalid length (missing one character) "parent_coin_info": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde", "puzzle_hash": "0x" + puzzle_hash2.hex(), "amount": 12345678, } with pytest.raises(ValueError, match="invalid hex"): Coin.from_json_dict(c) def test_coin_from_json_invalid_hex_len2() -> None: c = { # this field has an invalid length (missing two character) "parent_coin_info": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd", "puzzle_hash": "0x" + puzzle_hash2.hex(), "amount": 12345678, } with pytest.raises(ValueError, match="invalid length 31 expected 32"): Coin.from_json_dict(c) def test_coin_from_json_missing_field1() -> None: c = { "puzzle_hash": "0x" + puzzle_hash2.hex(), "amount": 12345678, } with pytest.raises(KeyError, match="parent_coin_info"): Coin.from_json_dict(c) def test_coin_from_json_missing_field2() -> None: c = { "parent_coin_info": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "amount": 12345678, } with pytest.raises(KeyError, match="puzzle_hash"): Coin.from_json_dict(c) def test_coin_from_json_missing_field3() -> None: c = { "parent_coin_info": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "puzzle_hash": "0x" + puzzle_hash2.hex(), } with pytest.raises(KeyError, match="amount"): Coin.from_json_dict(c) def test_coin_hash() -> None: c1 = Coin(parent_coin, puzzle_hash, uint64(1000000)) c2 = Coin(parent_coin, puzzle_hash2, uint64(1000000)) c3 = Coin(parent_coin, puzzle_hash, uint64(2000000)) c4 = Coin(parent_coin, puzzle_hash, uint64(1000000)) assert hash(c1) != hash(c2) assert hash(c1) != hash(c3) assert hash(c2) != hash(c3) assert hash(c1) == hash(c4) assert type(hash(c1)) is int def test_coin_fields() -> None: c1 = Coin(parent_coin, puzzle_hash, uint64(1000000)) assert c1.parent_coin_info == parent_coin assert c1.puzzle_hash == puzzle_hash assert c1.amount == 1000000