ot_btc_mainnet.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
  5. """
  6. test.objtest_d.ot_btc_mainnet: BTC mainnet test vectors for MMGen data objects
  7. """
  8. from decimal import Decimal
  9. from mmgen.obj import (
  10. Int,
  11. MMGenTxID,
  12. CoinTxID,
  13. MMGenWalletLabel,
  14. MMGenTxComment,
  15. MMGenPWIDString,
  16. TrackingWalletName)
  17. from mmgen.addrlist import AddrIdx, AddrIdxList, AddrListID
  18. from mmgen.seed import Seed, SeedID
  19. from mmgen.subseed import SubSeedList, SubSeedIdx, SubSeedIdxRange
  20. from mmgen.seedsplit import SeedShareIdx, SeedShareCount, MasterShareIdx, SeedSplitSpecifier
  21. from mmgen.key import PrivKey, WifKey, PubKey
  22. from mmgen.amt import BTCAmt
  23. from mmgen.addr import CoinAddr, MMGenID, MMGenAddrType, MMGenPasswordType
  24. from mmgen.tw.shared import TwMMGenID, TwLabel, TwComment
  25. from mmgen.rpc import IPPort
  26. from mmgen.protocol import init_proto
  27. from .ot_common import r16, r32
  28. from ..include.common import (
  29. cfg,
  30. utf8_ctrl,
  31. utf8_text,
  32. utf8_combining,
  33. gr_uc_w_ctrl,
  34. text_jp,
  35. text_zh,
  36. ru_uc,
  37. gr_uc)
  38. proto = init_proto(cfg, 'btc', need_amt=True)
  39. tw_pfx = proto.base_coin.lower() + ':'
  40. zero_addr = '1111111111111111111114oLvT2'
  41. ssm = str(SeedShareCount.max_val)
  42. privkey = PrivKey(proto=proto, s=bytes.fromhex('deadbeef'*8), compressed=True, pubkey_type='std')
  43. tests = {
  44. 'Int': {
  45. 'arg1': 'n',
  46. 'bad': ('1L', 0.0, '0.0', '1.0', 1.0, 's', 1.1, '1.1'),
  47. 'good': (
  48. ('0', 0), ('-1', -1), ('7', 7), -1, 0, 1, 9999999,
  49. {'n': '0x0', 'base': 16, 'ret': 0},
  50. {'n': '0x1', 'base': 16, 'ret': 1},
  51. {'n': '0xf', 'base': 16, 'ret': 15},
  52. {'n': '0xff', 'base': 16, 'ret': 255},
  53. )
  54. },
  55. 'AddrIdx': {
  56. 'arg1': 'n',
  57. 'bad': ('s', 1.1, 10000000, -1, 0),
  58. 'good': (('7', 7), (1, 1), (9999999, 9999999))
  59. },
  60. 'SeedShareIdx': {
  61. 'arg1': 'n',
  62. 'bad': ('s', 1.1, 1025, -1, 0),
  63. 'good': (('7', 7), (1, 1), (1024, 1024))
  64. },
  65. 'SeedShareCount': {
  66. 'arg1': 'n',
  67. 'bad': ('s', 2.1, 1025, -1, 0, 1),
  68. 'good': (('7', 7), (2, 2), (1024, 1024))
  69. },
  70. 'MasterShareIdx': {
  71. 'arg1': 'n',
  72. 'bad': ('s', 1.1, 1025, -1, 0),
  73. 'good': (('7', 7), (1, 1), (1024, 1024))
  74. },
  75. 'AddrIdxList': {
  76. 'arg1': 'fmt_str',
  77. 'bad': ('x', '5,9,1-2-3', '8,-11', '66,3-2', '0-3'),
  78. 'good': (
  79. ('3,2,2', (2, 3)),
  80. ('101,1,3,5,2-7,99', (1, 2, 3, 4, 5, 6, 7, 99, 101)),
  81. ({'idx_list': AddrIdxList('1-5')}, (1, 2, 3, 4, 5))
  82. )
  83. },
  84. 'SubSeedIdxRange': {
  85. 'bad': (33, 'x', '-11', '66,3','0', '3-2', '8000000', '100000000', (1, 2, 3)),
  86. 'good': (
  87. ('3', (3, 3)),
  88. ((3, 5), (3, 5)),
  89. ('1-2', (1, 2)),
  90. (str(SubSeedList.dfl_len), (SubSeedList.dfl_len, SubSeedList.dfl_len)),
  91. (str(SubSeedIdxRange.max_idx), (SubSeedIdxRange.max_idx, SubSeedIdxRange.max_idx)),
  92. )
  93. },
  94. 'BTCAmt': {
  95. 'arg1': 'num',
  96. 'bad': ('-3.2', '0.123456789', 123, '123L', '22000000', 20999999.12345678,
  97. {'num': '1', 'from_decimal': True},
  98. {'num': 1, 'from_decimal': True},
  99. ),
  100. 'good': (('20999999.12345678', Decimal('20999999.12345678')),
  101. # rounding
  102. {'num': Decimal('1.23456789623456789'), 'from_decimal': True, 'ret': Decimal('1.23456790')},
  103. {'num': Decimal('1.234'), 'from_decimal': True, 'ret': Decimal('1.234')},
  104. {'num': Decimal('0.0'), 'from_decimal': True, 'ret': Decimal('0')},
  105. # emulate network fee estimation:
  106. # BTC/kB fee_adjust tx size
  107. { 'num': Decimal('0.00053249') * Decimal('0.9') * 109 / 1024 , # ≈53 sat/byte
  108. 'from_decimal': True,
  109. 'ret': Decimal('0.00005101')},
  110. { 'num': Decimal('0.00003249') * Decimal('1.1') * 109 / 1024 , # ≈3 sat/byte
  111. 'from_decimal': True,
  112. 'ret': Decimal('0.00000380')},
  113. { 'num': Decimal('0.00011249') * Decimal('1.0') * 221 / 1024 , # ≈11 sat/byte
  114. 'from_decimal': True,
  115. 'ret': Decimal('0.00002428')},
  116. {'num': 1234, 'from_unit': 'satoshi', 'ret': Decimal('0.00001234')},
  117. )
  118. },
  119. 'CoinAddr': {
  120. 'arg1': 'addr',
  121. 'good': (
  122. {'addr': '1MjjELEy6EJwk8fSNfpS8b5teFRo4X5fZr', 'proto': proto},
  123. {'addr': '32GiSWo9zJQgkCmjAaLRrbPwXhKry2jHhj', 'proto': proto},
  124. ),
  125. 'bad': (
  126. {'addr': 1, 'proto': proto},
  127. {'addr': 'x', 'proto': proto},
  128. {'addr': 'я', 'proto': proto},
  129. ),
  130. },
  131. 'SeedID': {
  132. 'arg1': 'sid',
  133. 'bad': (
  134. {'sid': 'я'},
  135. {'sid': 'F00F00'},
  136. {'sid': 'xF00F00x'},
  137. {'sid': 1},
  138. {'sid': 'F00BAA123'},
  139. {'sid': 'f00baa12'},
  140. 'я', r32, 'abc'
  141. ),
  142. 'good': (
  143. {'sid': 'F00BAA12'},
  144. {'seed': Seed(cfg, r16), 'ret': SeedID(seed=Seed(cfg, r16))},
  145. {'sid': Seed(cfg, r16).sid, 'ret': SeedID(seed=Seed(cfg, r16))}
  146. )
  147. },
  148. 'SubSeedIdx': {
  149. 'arg1': 's',
  150. 'bad': (33, 'x', 'я', '1x', 200, '1ss', 'L', 's', '200LS', '30ll', 's100',
  151. str(SubSeedIdxRange.max_idx+1), '0'),
  152. 'good': (
  153. ('1', '1L'), ('1s', '1S'), '20S', '30L', ('300l', '300L'), ('200', '200L'),
  154. str(SubSeedIdxRange.max_idx)+'S')
  155. },
  156. 'MMGenID': {
  157. 'arg1': 'id_str',
  158. 'bad': (
  159. {'id_str': 'x', 'proto': proto},
  160. {'id_str': 1, 'proto': proto},
  161. {'id_str': 'f00f00f', 'proto': proto},
  162. {'id_str': 'a:b', 'proto': proto},
  163. {'id_str': 'x:L:3', 'proto': proto},
  164. {'id_str': 'F00BAA12', 'proto': proto},
  165. {'id_str': 'F00BAA12:Z:99', 'proto': proto},
  166. ),
  167. 'good': (
  168. {'id_str': 'F00BAA12:99', 'proto': proto, 'ret': 'F00BAA12:L:99'},
  169. {'id_str': 'F00BAA12:L:99', 'proto': proto},
  170. {'id_str': 'F00BAA12:S:99', 'proto': proto},
  171. ),
  172. },
  173. 'TwMMGenID': {
  174. 'arg1': 'id_str',
  175. 'bad': (
  176. {'id_str': 'x', 'proto': proto},
  177. {'id_str': 'я', 'proto': proto},
  178. {'id_str': 'я:я', 'proto': proto},
  179. {'id_str': 1, 'proto': proto},
  180. {'id_str': 'f00f00f', 'proto': proto},
  181. {'id_str': 'a:b', 'proto': proto},
  182. {'id_str': 'x:L:3', 'proto': proto},
  183. {'id_str': 'F00BAA12:0', 'proto': proto},
  184. {'id_str': 'F00BAA12:Z:99', 'proto': proto},
  185. {'id_str': tw_pfx, 'proto': proto},
  186. {'id_str': tw_pfx+'я', 'proto': proto},
  187. {'id_str': tw_pfx+'x', 'proto': proto},
  188. ),
  189. 'good': (
  190. {'id_str': tw_pfx+zero_addr, 'proto': proto},
  191. {'id_str': 'F00BAA12:99', 'proto': proto, 'ret': 'F00BAA12:L:99'},
  192. {'id_str': 'F00BAA12:L:99', 'proto': proto},
  193. {'id_str': 'F00BAA12:S:9999999', 'proto': proto},
  194. ),
  195. },
  196. 'TrackingWalletName': {
  197. 'arg1': 's',
  198. 'bad': ('-abcdefg', 'a' * 50, 'abc-α-ω'),
  199. 'good': ('ab-cd-def_ghi-', '321-abcd')
  200. },
  201. 'TwLabel': {
  202. 'arg1': 'proto',
  203. 'exc_name': 'BadTwLabel',
  204. 'bad': (
  205. {'text': '', 'proto': proto},
  206. {'text': 'x x', 'proto': proto},
  207. {'text': 'x я', 'proto': proto},
  208. {'text': 'я:я', 'proto': proto},
  209. {'text': 1, 'proto': proto},
  210. {'text': 'f00f00f', 'proto': proto},
  211. {'text': 'a:b', 'proto': proto},
  212. {'text': 'x:L:3', 'proto': proto},
  213. {'text': 'F00BAA12:0 x', 'proto': proto},
  214. {'text': 'F00BAA12:Z:99', 'proto': proto},
  215. {'text': tw_pfx+' x', 'proto': proto},
  216. {'text': tw_pfx+'я x', 'proto': proto},
  217. {'text': utf8_ctrl[:40], 'proto': proto},
  218. {'text': 'F00BAA12:S:1 ' + utf8_ctrl[:40], 'proto': proto, 'exc_name': 'BadTwComment'},
  219. {'text': tw_pfx+'x comment', 'proto': proto},
  220. ),
  221. 'good': (
  222. {'text': 'F00BAA12:99 a comment', 'proto': proto, 'ret': 'F00BAA12:L:99 a comment'},
  223. {'text': 'F00BAA12:L:99 a comment', 'proto': proto},
  224. {'text': 'F00BAA12:L:99 comment (UTF-8) α', 'proto': proto},
  225. {'text': 'F00BAA12:S:9999999 comment', 'proto': proto},
  226. {'text': tw_pfx+zero_addr+' comment', 'proto': proto},
  227. ),
  228. },
  229. 'MMGenTxID': {
  230. 'arg1': 's',
  231. 'bad': (1, [], '\0', '\1', 'я', 'g', 'gg', 'FF', 'f00', 'F00F0012'),
  232. 'good': ('DEADBE', 'F00BAA')
  233. },
  234. 'CoinTxID': {
  235. 'arg1': 's',
  236. 'bad': (1, [], '\0', '\1', 'я', 'g', 'gg', 'FF', 'f00', 'F00F0012', r16.hex(), r32.hex()+'ee'),
  237. 'good': (r32.hex(),)
  238. },
  239. 'WifKey': {
  240. 'arg1': 'proto',
  241. 'bad': (
  242. {'proto': proto, 'wif': 1},
  243. {'proto': proto, 'wif': []},
  244. {'proto': proto, 'wif': '\0'},
  245. {'proto': proto, 'wif': '\1'},
  246. {'proto': proto, 'wif': 'я'},
  247. {'proto': proto, 'wif': 'g'},
  248. {'proto': proto, 'wif': 'gg'},
  249. {'proto': proto, 'wif': 'FF'},
  250. {'proto': proto, 'wif': 'f00'},
  251. {'proto': proto, 'wif': r16.hex()},
  252. {'proto': proto, 'wif': '2MspvWFjBbkv2wzQGqhxJUYPCk3Y2jMaxLN'},
  253. ),
  254. 'good': (
  255. {'proto': proto, 'wif': '5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb', 'ret_idx': 1},
  256. {'proto': proto, 'wif': 'KwWr9rDh8KK5TtDa3HLChEvQXNYcUXpwhRFUPc5uSNnMtqNKLFhk', 'ret_idx': 1},
  257. )
  258. },
  259. 'PubKey': {
  260. 'arg1': 's',
  261. 'bad': (
  262. {'s': 1, 'compressed': True},
  263. {'s': 'F00BAA12', 'compressed': False},
  264. ),
  265. 'good': ( # TODO: add real pubkeys
  266. {'s': bytes.fromhex('deadbeef'), 'compressed': True},
  267. )
  268. },
  269. 'PrivKey': {
  270. 'arg1': 'proto',
  271. 'bad': (
  272. {'proto': proto, 'wif': 1},
  273. {'proto': proto, 'wif': '1'},
  274. {'proto': proto, 'wif': 'cMsqcmDYZP1LdKgqRh9L4ZRU9br28yvdmTPwW2YQwVSN9aQiMAoR'},
  275. {'proto': proto, 's': r32, 'wif': '5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb'},
  276. {'proto': proto, 'pubkey_type': 'std'},
  277. {'proto': proto, 's': r32},
  278. {'proto': proto, 's': r32, 'compressed': 'yes'},
  279. {'proto': proto, 's': r32, 'compressed': 'yes', 'pubkey_type': 'std'},
  280. {'proto': proto, 's': r32, 'compressed': True, 'pubkey_type': 'nonstd'},
  281. {'proto': proto, 's': r32+b'x', 'compressed': True, 'pubkey_type': 'std'}
  282. ),
  283. 'good': (
  284. {'proto': proto, 'wif': '5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb',
  285. 'ret': bytes.fromhex('e0aef965b905a2fedf907151df8e0a6bac832aa697801c51f58bd2ecb4fd381c')},
  286. {'proto': proto, 'wif': 'KwWr9rDh8KK5TtDa3HLChEvQXNYcUXpwhRFUPc5uSNnMtqNKLFhk',
  287. 'ret': bytes.fromhex('08d0ed83b64b68d56fa064be48e2385060ed205be2b1e63cd56d218038c3a05f')},
  288. {'proto': proto, 's': r32, 'compressed': False, 'pubkey_type': 'std', 'ret': r32},
  289. {'proto': proto, 's': r32, 'compressed': True, 'pubkey_type': 'std', 'ret': r32}
  290. )
  291. },
  292. 'AddrListID': {
  293. 'arg1': 'sid',
  294. 'bad': (
  295. {'sid': SeedID(sid='F00BAA12'), 'mmtype': 'Z', 'ret': 'F00BAA12:Z'},
  296. {'id_str': 'G00BAA12:B', 'proto': proto},
  297. ),
  298. 'good': (
  299. {'id_str': 'F00BAA12:B', 'proto': proto, 'ret': 'F00BAA12:B'},
  300. {'sid': SeedID(sid='F00BAA12'), 'mmtype': proto.addr_type(id_str='S'), 'ret': 'F00BAA12:S'},
  301. {'sid': SeedID(sid='F00BAA12'), 'mmtype': proto.addr_type(id_str='L'), 'ret': 'F00BAA12:L'},
  302. )
  303. },
  304. 'MMGenWalletLabel': {
  305. 'arg1': 's',
  306. 'bad': (utf8_text[:49], utf8_combining[:48], utf8_ctrl[:48], gr_uc_w_ctrl),
  307. 'good': (utf8_text[:48],)
  308. },
  309. 'TwComment': {
  310. 'exc_name': 'BadTwComment',
  311. 'arg1': 's',
  312. 'bad': ( utf8_combining[:40],
  313. utf8_ctrl[:40],
  314. text_jp[:41],
  315. text_zh[:41],
  316. gr_uc_w_ctrl,
  317. utf8_text[:81]),
  318. 'good': ( utf8_text[:80],
  319. (ru_uc + gr_uc + utf8_text)[:80],
  320. text_jp[:40],
  321. text_zh[:40])
  322. },
  323. 'MMGenTxComment': {
  324. 'arg1': 's',
  325. 'bad': (utf8_text[:73], utf8_combining[:72], utf8_ctrl[:72], gr_uc_w_ctrl),
  326. 'good': (utf8_text[:72],)
  327. },
  328. 'MMGenPWIDString': { # forbidden = list(u' :/\\')
  329. 'arg1': 's',
  330. 'bad': ('foo/', 'foo:', 'foo:\\'),
  331. 'good': ('qwerty@яяя',)
  332. },
  333. 'MMGenAddrType': {
  334. 'arg1': 'proto',
  335. 'bad': (
  336. {'proto': proto, 'id_str': 'U', 'ret': 'L'},
  337. {'proto': proto, 'id_str': 'z', 'ret': 'L'},
  338. {'proto': proto, 'id_str': 'xx', 'ret': 'C'},
  339. {'proto': proto, 'id_str': 'dogecoin', 'ret': 'C'},
  340. ),
  341. 'good': (
  342. {'proto': proto, 'id_str': 'legacy', 'ret': 'L'},
  343. {'proto': proto, 'id_str': 'L', 'ret': 'L'},
  344. {'proto': proto, 'id_str': 'compressed', 'ret': 'C'},
  345. {'proto': proto, 'id_str': 'C', 'ret': 'C'},
  346. {'proto': proto, 'id_str': 'segwit', 'ret': 'S'},
  347. {'proto': proto, 'id_str': 'S', 'ret': 'S'},
  348. {'proto': proto, 'id_str': 'bech32', 'ret': 'B'},
  349. {'proto': proto, 'id_str': 'B', 'ret': 'B'}
  350. )
  351. },
  352. 'MMGenPasswordType': {
  353. 'arg1': 'proto',
  354. 'bad': (
  355. {'proto': proto, 'id_str': 'U', 'ret': 'L'},
  356. {'proto': proto, 'id_str': 'z', 'ret': 'L'},
  357. {'proto': proto, 'id_str': 'я', 'ret': 'C'},
  358. {'proto': proto, 'id_str': 1, 'ret': 'C'},
  359. {'proto': proto, 'id_str': 'passw0rd', 'ret': 'C'},
  360. ),
  361. 'good': (
  362. {'proto': proto, 'id_str': 'password', 'ret': 'P'},
  363. {'proto': proto, 'id_str': 'P', 'ret': 'P'},
  364. )
  365. },
  366. 'SeedSplitSpecifier': {
  367. 'arg1': 's',
  368. 'bad': ('M', 'αβ:2', 1, '0:1', '1:1', '2:1', '3:2', '1:2000', 'abc:0:2'),
  369. 'good': (
  370. ('1:2', '2:2', 'alice:2:2', 'αβ:2:2', '1:'+ssm, ssm+':'+ssm)
  371. )
  372. },
  373. 'IPPort': {
  374. 'arg1': 's',
  375. 'bad': (
  376. '.0.0.0:0',
  377. '127.0.0.1',
  378. '127.0.0.1:',
  379. '20',
  380. ':20',
  381. '127.0.0.256:0',
  382. '127.0.0.1:65536',
  383. '127.0.0.01:60000',
  384. '0.0.0.0:00',
  385. ),
  386. 'good': (
  387. (
  388. '0.0.0.0:0',
  389. '0.0.0.1:0',
  390. '0.0.1.0:0',
  391. '0.0.1.1:0',
  392. '0.1.0.0:0',
  393. '1.0.0.0:0',
  394. '10.0.0.1:0',
  395. '127.0.0.1:65535',
  396. '255.0.0.0:400',
  397. '255.255.255.255:65535',
  398. )
  399. )
  400. },
  401. }