Skip to content

Commit e5f54c8

Browse files
Merge pull request #68 from HathorNetwork/feat/token_version_support
feat(dynamic-transaction-model): token version
2 parents 2f09816 + e2672b7 commit e5f54c8

File tree

3 files changed

+75
-16
lines changed

3 files changed

+75
-16
lines changed

hathorlib/token_creation_tx.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
1515
"""
16-
16+
from enum import IntEnum
1717
from struct import error as StructError, pack
1818
from typing import Tuple
1919

@@ -32,9 +32,13 @@
3232
# Signal bist (B), version (B), inputs len (B), outputs len (B)
3333
_SIGHASH_ALL_FORMAT_STRING = '!BBBB'
3434

35+
3536
# used when (de)serializing token information
36-
# version 1 expects only token name and symbol
37-
TOKEN_INFO_VERSION = 1
37+
# version 1 is the default behavior
38+
class TokenVersion(IntEnum):
39+
NATIVE = 0
40+
DEPOSIT = 1
41+
FEE = 2
3842

3943

4044
class TokenCreationTransaction(Transaction):
@@ -43,11 +47,19 @@ def __init__(self) -> None:
4347
# for this special tx, its own hash is used as the created token uid. We're artificially
4448
# creating the tokens list here
4549
self.tokens = []
50+
self.token_version: TokenVersion = TokenVersion.DEPOSIT
4651

4752
def __str__(self) -> str:
48-
return ('TokenCreationTransaction(nonce=%d, timestamp=%s, version=%s, weight=%f, hash=%s, '
49-
'token_name=%s, token_symbol=%s)' % (self.nonce, self.timestamp, int(self.version),
50-
self.weight, self.hash_hex, self.token_name, self.token_symbol))
53+
return (
54+
f'TokenCreationTransaction(nonce={self.nonce}, '
55+
f'timestamp={self.timestamp}, '
56+
f'version={int(self.version)}, '
57+
f'weight={self.weight:.6f}, '
58+
f'hash={self.hash_hex}, '
59+
f'token_name={self.token_name}, '
60+
f'token_symbol={self.token_symbol}, '
61+
f'token_version={self.token_version})'
62+
)
5163

5264
def update_hash(self) -> None:
5365
""" When we update the hash, we also have to update the tokens uid list
@@ -78,7 +90,12 @@ def get_funds_fields_from_struct(self, buf: bytes) -> bytes:
7890
self.outputs.append(txout)
7991

8092
# token name and symbol
81-
self.token_name, self.token_symbol, buf = TokenCreationTransaction.deserialize_token_info(buf)
93+
(
94+
self.token_name,
95+
self.token_symbol,
96+
self.token_version,
97+
buf
98+
) = TokenCreationTransaction.deserialize_token_info(buf)
8299

83100
return buf
84101

@@ -148,31 +165,35 @@ def serialize_token_info(self) -> bytes:
148165
encoded_symbol = self.token_symbol.encode('utf-8')
149166

150167
ret = b''
151-
ret += int_to_bytes(TOKEN_INFO_VERSION, 1)
168+
ret += int_to_bytes(self.token_version, 1)
152169
ret += int_to_bytes(len(encoded_name), 1)
153170
ret += encoded_name
154171
ret += int_to_bytes(len(encoded_symbol), 1)
155172
ret += encoded_symbol
173+
156174
return ret
157175

158176
@classmethod
159-
def deserialize_token_info(cls, buf: bytes) -> Tuple[str, str, bytes]:
160-
""" Gets the token name and symbol from serialized format
177+
def deserialize_token_info(cls, buf: bytes) -> Tuple[str, str, TokenVersion, bytes]:
178+
""" Gets the token name, symbol and version from serialized format
161179
"""
162-
(token_info_version,), buf = unpack('!B', buf)
163-
if token_info_version != TOKEN_INFO_VERSION:
164-
raise ValueError('unknown token info version: {}'.format(token_info_version))
180+
(raw_token_version,), buf = unpack('!B', buf)
181+
try:
182+
token_version = TokenVersion(raw_token_version)
183+
except ValueError:
184+
raise ValueError('unknown token version: {}'.format(raw_token_version))
165185

166186
(name_len,), buf = unpack('!B', buf)
167187
name, buf = unpack_len(name_len, buf)
188+
168189
(symbol_len,), buf = unpack('!B', buf)
169190
symbol, buf = unpack_len(symbol_len, buf)
170191

171192
# Token name and symbol can be only utf-8 valid strings for now
172193
decoded_name = decode_string_utf8(name, 'Token name')
173194
decoded_symbol = decode_string_utf8(symbol, 'Token symbol')
174195

175-
return decoded_name, decoded_symbol, buf
196+
return decoded_name, decoded_symbol, token_version, buf
176197

177198
def verify_token_info(self) -> None:
178199
""" Validates token info
@@ -190,6 +211,10 @@ def verify_token_info(self) -> None:
190211
if clean_token_string(self.token_symbol) == clean_token_string(settings.HATHOR_TOKEN_SYMBOL):
191212
raise TransactionDataError('Invalid token symbol ({})'.format(self.token_symbol))
192213

214+
# Can't create the token with NATIVE version
215+
if self.token_version == TokenVersion.NATIVE:
216+
raise TransactionDataError('Invalid token version ({})'.format(self.token_version))
217+
193218
def is_nft_creation_standard(self) -> bool:
194219
"""Returns True if it's a standard NFT creation transaction"""
195220
# We will check the outputs to validate that we have an NFT standard creation

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
[tool.poetry]
1616
name = "hathorlib"
17-
version = "0.11.0"
17+
version = "0.12.0"
1818
description = "Hathor Network base objects library"
1919
authors = ["Hathor Team <contact@hathor.network>"]
2020
license = "Apache-2.0"

tests/test_basic.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def test_token_creation_basics(self):
9090
str(tx),
9191
'TokenCreationTransaction(nonce=33518441, timestamp=1578090723, version=2, weight=20.645186, '
9292
'hash=00000828d80dd4cd809c959139f7b4261df41152f4cce65a8777eb1c3a1f9702, '
93-
'token_name=ToTheMoon, token_symbol=🚀)'
93+
'token_name=ToTheMoon, token_symbol=🚀, token_version=1)'
9494
)
9595
self.assertEqual(
9696
repr(tx),
@@ -107,6 +107,40 @@ def test_token_creation_basics(self):
107107
tx.update_hash()
108108
self.assertFalse(tx.verify_pow())
109109

110+
def test_token_creation_with_fee_version(self):
111+
"""Test TokenCreationTransaction with token_version=2 (FEE)"""
112+
from hathorlib.token_creation_tx import TokenVersion
113+
114+
# Using the same structure as test_token_creation_basics but with token_version=2
115+
data = bytes.fromhex('00020104000005551d7740fd7d3c0acc50b5677fdd844f1225985aa431e1712af2a2fd'
116+
'8900006a473045022100a445edb5cd6c79a0a7b5ed837582fd65b8d511ee60b64fd076'
117+
'e07bd8f63f75a202202dca24320bffc4c3ca2a07cdfff38f7c839bde70ed49ef634ac6'
118+
'588972836cab2103bfa995d676e3c0ed7b863c74cfef9683fab3163b42b6f21442326a'
119+
'023fc57fba0000264800001976a9146876f9578221fdb678d4e8376503098a9228b132'
120+
'88ac00004e2001001976a914031761ef85a24603203c97e75af355b83209f08f88ac00'
121+
'00000181001976a9149f091256cb98649c7c35df0aad44d7805710691e88ac00000002'
122+
'81001976a914b1d7a5ee505ad4d3b93ea1a5162ba83d5049ec4e88ac0209546f546865'
123+
'4d6f6f6e04f09f9a804034a52aec6cece75e0fc0e30200001a72272f48339fcc5d5ec5'
124+
'deaf197855964b0eb912e8c6eefe00928b6cf600001055641c20b71871ed2c5c7d4096'
125+
'a34f40888d79c25bce74421646e732dc01ff730d')
126+
tx = TokenCreationTransaction.create_from_struct(data)
127+
128+
# Verify the token version is FEE (2)
129+
self.assertEqual(tx.token_version, TokenVersion.FEE)
130+
131+
# Verify the transaction can be serialized and deserialized correctly
132+
self.assertEqual(data, bytes(tx))
133+
134+
# Verify basic transaction properties
135+
self.assertTrue(tx.is_transaction)
136+
self.assertFalse(tx.is_block)
137+
138+
# Verify the string representation includes token_version=2
139+
str_repr = str(tx)
140+
self.assertIn('token_version=2', str_repr)
141+
self.assertIn('token_name=ToTheMoon', str_repr)
142+
self.assertIn('token_symbol=🚀', str_repr)
143+
110144
def test_script_basics(self):
111145
create_output_script(decode_address('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ'))
112146

0 commit comments

Comments
 (0)