2222
2323from keepkeylib import messages_pb2 as messages
2424from keepkeylib import messages_ton_pb2 as ton_messages
25- from keepkeylib import types_pb2 as types
2625from keepkeylib .client import CallException
2726from keepkeylib .tools import parse_path
2827
28+ TON_PATH = "m/44'/607'/0'/0'/0'/0'"
2929
30- def make_ton_address (workchain = 0 , hash_bytes = None , bounceable = True , testnet = False ):
31- """Construct a TON user-friendly address (48-char base64)."""
32- if hash_bytes is None :
33- hash_bytes = b'\xBB ' * 32
34-
35- if bounceable :
36- flags = 0x11
37- else :
38- flags = 0x51
39-
40- if testnet :
41- flags |= 0x80
42-
43- raw = bytes ([flags , workchain & 0xFF ]) + hash_bytes
44-
45- # CRC16-XMODEM
46- crc = 0
47- for byte in raw :
48- crc ^= byte << 8
49- for _ in range (8 ):
50- if crc & 0x8000 :
51- crc = (crc << 1 ) ^ 0x1021
52- else :
53- crc <<= 1
54- crc &= 0xFFFF
5530
31+ def make_ton_address (workchain = 0 , hash_bytes = None , bounceable = True ):
32+ """Build a base64url TON address string."""
33+ if hash_bytes is None :
34+ hash_bytes = b'\xAA ' * 32
35+ tag = 0x11 if bounceable else 0x51
36+ raw = bytes ([tag , workchain & 0xFF ]) + hash_bytes
37+ crc = binascii .crc_hqx (raw , 0 )
5638 raw += struct .pack ('>H' , crc )
5739 return base64 .b64encode (raw ).decode ('ascii' )
5840
@@ -70,123 +52,106 @@ def test_ton_get_address(self):
7052 self .setup_mnemonic_allallall ()
7153
7254 msg = ton_messages .TonGetAddress (
73- address_n = parse_path ("m/44'/607'/0'/0/0" ),
55+ address_n = parse_path (TON_PATH ),
7456 show_display = False ,
7557 )
7658 resp = self .client .call (msg )
7759
78- # Should return a raw address
7960 self .assertTrue (resp .raw_address is not None or resp .address is not None )
8061
8162 def test_ton_sign_structured (self ):
82- """Test TON transfer using structured fields (reconstruct-then-sign) ."""
63+ """Test TON transfer using structured fields."""
8364 self .requires_fullFeature ()
8465 self .setup_mnemonic_allallall ()
8566
86- dest_addr = make_ton_address (
87- workchain = 0 ,
88- hash_bytes = b'\xCC ' * 32 ,
89- bounceable = True
90- )
67+ dest_addr = make_ton_address (workchain = 0 , hash_bytes = b'\xCC ' * 32 , bounceable = True )
9168
9269 msg = ton_messages .TonSignTx (
93- address_n = parse_path ("m/44'/607'/0'/0/0" ),
94- destination = dest_addr ,
95- ton_amount = 1000000000 , # 1 TON
70+ address_n = parse_path (TON_PATH ),
71+ to_address = dest_addr ,
72+ amount = 1000000000 , # 1 TON in nanotons
9673 seqno = 1 ,
9774 expire_at = 1700000000 ,
9875 bounce = True ,
99- mode = 3 ,
10076 )
10177 resp = self .client .call (msg )
10278
103- # Should have a 64-byte Ed25519 signature
10479 self .assertEqual (len (resp .signature ), 64 )
105-
106- # Should return the cell hash
107- self .assertEqual (len (resp .cell_hash ), 32 )
108-
109- # Verify signature is not all zeros
11080 self .assertFalse (all (b == 0 for b in resp .signature ))
11181
112- def test_ton_sign_with_comment (self ):
113- """Test TON transfer with a text comment ."""
82+ def test_ton_sign_with_memo (self ):
83+ """Test TON transfer with a text memo ."""
11484 self .requires_fullFeature ()
11585 self .setup_mnemonic_allallall ()
11686
11787 dest_addr = make_ton_address ()
11888
11989 msg = ton_messages .TonSignTx (
120- address_n = parse_path ("m/44'/607'/0'/0/0" ),
121- destination = dest_addr ,
122- ton_amount = 500000000 , # 0.5 TON
90+ address_n = parse_path (TON_PATH ),
91+ to_address = dest_addr ,
92+ amount = 500000000 , # 0.5 TON
12393 seqno = 2 ,
12494 expire_at = 1700000000 ,
125- comment = "Hello TON!" ,
95+ memo = "Hello TON!" ,
12696 )
12797 resp = self .client .call (msg )
12898
12999 self .assertEqual (len (resp .signature ), 64 )
130- self .assertEqual (len (resp .cell_hash ), 32 )
131100
132101 def test_ton_sign_legacy_raw_tx (self ):
133102 """Test legacy blind-sign with raw_tx field."""
134103 self .requires_fullFeature ()
135104 self .setup_mnemonic_allallall ()
136105
137- # Provide arbitrary raw_tx bytes (simulating a pre-built signing message)
138- raw_tx = b'\x00 ' * 64 # dummy signing message
106+ raw_tx = b'\x00 ' * 64
139107
140108 msg = ton_messages .TonSignTx (
141- address_n = parse_path ("m/44'/607'/0'/0/0" ),
109+ address_n = parse_path (TON_PATH ),
142110 raw_tx = raw_tx ,
143111 )
144112 resp = self .client .call (msg )
145113
146- # Should have a 64-byte Ed25519 signature
147114 self .assertEqual (len (resp .signature ), 64 )
148115
149116 def test_ton_sign_missing_fields_rejected (self ):
150117 """Test that incomplete structured fields are rejected."""
151118 self .requires_fullFeature ()
152119 self .setup_mnemonic_allallall ()
153120
154- # Has destination but no amount or seqno
155121 msg = ton_messages .TonSignTx (
156- address_n = parse_path ("m/44'/607'/0'/0/0" ),
157- destination = make_ton_address (),
122+ address_n = parse_path (TON_PATH ),
123+ to_address = make_ton_address (),
158124 )
159125
160126 with pytest .raises (CallException ):
161127 self .client .call (msg )
162128
163129 def test_ton_sign_deterministic (self ):
164- """Test that signing the same message produces same cell hash ."""
130+ """Test that signing the same message produces same signature ."""
165131 self .requires_fullFeature ()
166132 self .setup_mnemonic_allallall ()
167133
168134 dest_addr = make_ton_address ()
169135
170136 msg1 = ton_messages .TonSignTx (
171- address_n = parse_path ("m/44'/607'/0'/0/0" ),
172- destination = dest_addr ,
173- ton_amount = 1000000000 ,
137+ address_n = parse_path (TON_PATH ),
138+ to_address = dest_addr ,
139+ amount = 1000000000 ,
174140 seqno = 1 ,
175141 expire_at = 1700000000 ,
176142 )
177143 resp1 = self .client .call (msg1 )
178144
179145 msg2 = ton_messages .TonSignTx (
180- address_n = parse_path ("m/44'/607'/0'/0/0" ),
181- destination = dest_addr ,
182- ton_amount = 1000000000 ,
146+ address_n = parse_path (TON_PATH ),
147+ to_address = dest_addr ,
148+ amount = 1000000000 ,
183149 seqno = 1 ,
184150 expire_at = 1700000000 ,
185151 )
186152 resp2 = self .client .call (msg2 )
187153
188- # Cell hash should be identical for same inputs
189- self .assertEqual (resp1 .cell_hash , resp2 .cell_hash )
154+ self .assertEqual (resp1 .signature , resp2 .signature )
190155
191156
192157if __name__ == '__main__' :
0 commit comments