#!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution # Copyright (C)2013-2024 The MMGen Project """ test.objtest_py_d.ot_btc_mainnet: BTC mainnet test vectors for MMGen data objects """ from decimal import Decimal from mmgen.obj import Int,MMGenTxID,CoinTxID,MMGenWalletLabel,MMGenTxComment,MMGenPWIDString,TrackingWalletName from mmgen.addrlist import AddrIdx,AddrIdxList,AddrListID from mmgen.seed import Seed,SeedID from mmgen.subseed import SubSeedList,SubSeedIdx,SubSeedIdxRange from mmgen.seedsplit import SeedShareIdx,SeedShareCount,MasterShareIdx,SeedSplitSpecifier from mmgen.key import PrivKey,WifKey,PubKey from mmgen.amt import BTCAmt from mmgen.addr import CoinAddr,MMGenID,MMGenAddrType,MMGenPasswordType from mmgen.tw.shared import TwMMGenID,TwLabel,TwComment from mmgen.rpc import IPPort from mmgen.protocol import init_proto from .ot_common import r16,r32 from ..include.common import cfg,utf8_ctrl,utf8_text,utf8_combining,gr_uc_w_ctrl,text_jp,text_zh,ru_uc,gr_uc proto = init_proto( cfg, 'btc', need_amt=True ) tw_pfx = proto.base_coin.lower() + ':' zero_addr = '1111111111111111111114oLvT2' ssm = str(SeedShareCount.max_val) privkey = PrivKey(proto=proto,s=bytes.fromhex('deadbeef'*8),compressed=True,pubkey_type='std') tests = { 'Int': { 'arg1': 'n', 'bad': ('1L',0.0,'0.0','1.0',1.0,'s',1.1,'1.1'), 'good': ( ('0',0),('-1',-1),('7',7),-1,0,1,9999999, {'n':'0x0','base':16,'ret':0}, {'n':'0x1','base':16,'ret':1}, {'n':'0xf','base':16,'ret':15}, {'n':'0xff','base':16,'ret':255}, ) }, 'AddrIdx': { 'arg1': 'n', 'bad': ('s',1.1,10000000,-1,0), 'good': (('7',7),(1,1),(9999999,9999999)) }, 'SeedShareIdx': { 'arg1': 'n', 'bad': ('s',1.1,1025,-1,0), 'good': (('7',7),(1,1),(1024,1024)) }, 'SeedShareCount': { 'arg1': 'n', 'bad': ('s',2.1,1025,-1,0,1), 'good': (('7',7),(2,2),(1024,1024)) }, 'MasterShareIdx': { 'arg1': 'n', 'bad': ('s',1.1,1025,-1,0), 'good': (('7',7),(1,1),(1024,1024)) }, 'AddrIdxList': { 'arg1': 'fmt_str', 'bad': ('x','5,9,1-2-3','8,-11','66,3-2','0-3'), 'good': ( ('3,2,2',(2,3)), ('101,1,3,5,2-7,99',(1,2,3,4,5,6,7,99,101)), ({'idx_list':AddrIdxList('1-5')},(1,2,3,4,5)) ) }, 'SubSeedIdxRange': { 'bad': (33,'x','-11','66,3','0','3-2','8000000','100000000',(1,2,3)), 'good': ( ('3',(3,3)), ((3,5),(3,5)), ('1-2',(1,2)), (str(SubSeedList.dfl_len),(SubSeedList.dfl_len,SubSeedList.dfl_len)), (str(SubSeedIdxRange.max_idx),(SubSeedIdxRange.max_idx,SubSeedIdxRange.max_idx)), ) }, 'BTCAmt': { 'arg1': 'num', 'bad': ('-3.2','0.123456789',123,'123L','22000000',20999999.12345678, {'num':'1','from_decimal':True}, {'num':1,'from_decimal':True}, ), 'good': (('20999999.12345678',Decimal('20999999.12345678')), {'num':Decimal('1.23456789623456789'),'from_decimal':True,'ret':Decimal('1.23456790')}, # rounding {'num':Decimal('1.234'),'from_decimal':True,'ret':Decimal('1.234')}, {'num':Decimal('0.0'),'from_decimal':True,'ret':Decimal('0')}, # emulate network fee estimation: # BTC/kB fee_adjust tx size { 'num':Decimal('0.00053249') * Decimal('0.9') * 109 / 1024 , # ≈53 sat/byte 'from_decimal':True, 'ret':Decimal('0.00005101') }, { 'num':Decimal('0.00003249') * Decimal('1.1') * 109 / 1024 , # ≈3 sat/byte 'from_decimal':True, 'ret':Decimal('0.00000380') }, { 'num':Decimal('0.00011249') * Decimal('1.0') * 221 / 1024 , # ≈11 sat/byte 'from_decimal':True, 'ret':Decimal('0.00002428') }, {'num':1234,'from_unit':'satoshi','ret':Decimal('0.00001234')}, ) }, 'CoinAddr': { 'arg1': 'addr', 'good': ( {'addr':'1MjjELEy6EJwk8fSNfpS8b5teFRo4X5fZr', 'proto':proto}, {'addr':'32GiSWo9zJQgkCmjAaLRrbPwXhKry2jHhj', 'proto':proto}, ), 'bad': ( {'addr':1, 'proto':proto}, {'addr':'x', 'proto':proto}, {'addr':'я', 'proto':proto}, ), }, 'SeedID': { 'arg1': 'sid', 'bad': ( {'sid':'я'}, {'sid':'F00F00'}, {'sid':'xF00F00x'}, {'sid':1}, {'sid':'F00BAA123'}, {'sid':'f00baa12'}, 'я',r32,'abc' ), 'good': ( {'sid':'F00BAA12'}, {'seed': Seed(cfg,r16), 'ret': SeedID(seed=Seed(cfg,r16))}, {'sid': Seed(cfg,r16).sid, 'ret': SeedID(seed=Seed(cfg,r16))} ) }, 'SubSeedIdx': { 'arg1': 's', 'bad': (33,'x','я','1x',200,'1ss','L','s','200LS','30ll','s100',str(SubSeedIdxRange.max_idx+1),'0'), 'good': (('1','1L'),('1s','1S'),'20S','30L',('300l','300L'),('200','200L'),str(SubSeedIdxRange.max_idx)+'S') }, 'MMGenID': { 'arg1': 'id_str', 'bad': ( {'id_str':'x', 'proto':proto}, {'id_str':1, 'proto':proto}, {'id_str':'f00f00f', 'proto':proto}, {'id_str':'a:b', 'proto':proto}, {'id_str':'x:L:3', 'proto':proto}, {'id_str':'F00BAA12', 'proto':proto}, {'id_str':'F00BAA12:Z:99', 'proto':proto}, ), 'good': ( {'id_str':'F00BAA12:99', 'proto':proto, 'ret':'F00BAA12:L:99'}, {'id_str':'F00BAA12:L:99', 'proto':proto}, {'id_str':'F00BAA12:S:99', 'proto':proto}, ), }, 'TwMMGenID': { 'arg1': 'id_str', 'bad': ( {'id_str':'x', 'proto':proto}, {'id_str':'я', 'proto':proto}, {'id_str':'я:я', 'proto':proto}, {'id_str':1, 'proto':proto}, {'id_str':'f00f00f', 'proto':proto}, {'id_str':'a:b', 'proto':proto}, {'id_str':'x:L:3', 'proto':proto}, {'id_str':'F00BAA12:0', 'proto':proto}, {'id_str':'F00BAA12:Z:99', 'proto':proto}, {'id_str':tw_pfx, 'proto':proto}, {'id_str':tw_pfx+'я', 'proto':proto}, {'id_str':tw_pfx+'x', 'proto':proto}, ), 'good': ( {'id_str':tw_pfx+zero_addr, 'proto':proto}, {'id_str':'F00BAA12:99', 'proto':proto, 'ret':'F00BAA12:L:99'}, {'id_str':'F00BAA12:L:99', 'proto':proto}, {'id_str':'F00BAA12:S:9999999', 'proto':proto}, ), }, 'TrackingWalletName': { 'arg1': 's', 'bad': ('-abcdefg', 'a' * 50, 'abc-α-ω'), 'good': ('ab-cd-def_ghi-', '321-abcd') }, 'TwLabel': { 'arg1': 'proto', 'exc_name': 'BadTwLabel', 'bad': ( {'text':'', 'proto':proto}, {'text':'x x', 'proto':proto}, {'text':'x я', 'proto':proto}, {'text':'я:я', 'proto':proto}, {'text':1, 'proto':proto}, {'text':'f00f00f', 'proto':proto}, {'text':'a:b', 'proto':proto}, {'text':'x:L:3', 'proto':proto}, {'text':'F00BAA12:0 x', 'proto':proto}, {'text':'F00BAA12:Z:99', 'proto':proto}, {'text':tw_pfx+' x', 'proto':proto}, {'text':tw_pfx+'я x', 'proto':proto}, {'text':utf8_ctrl[:40], 'proto':proto}, {'text':'F00BAA12:S:1 ' + utf8_ctrl[:40], 'proto':proto, 'exc_name': 'BadTwComment'}, {'text':tw_pfx+'x comment','proto':proto}, ), 'good': ( {'text':'F00BAA12:99 a comment', 'proto':proto, 'ret':'F00BAA12:L:99 a comment'}, {'text':'F00BAA12:L:99 a comment', 'proto':proto}, {'text': 'F00BAA12:L:99 comment (UTF-8) α', 'proto':proto}, {'text':'F00BAA12:S:9999999 comment', 'proto':proto}, {'text':tw_pfx+zero_addr+' comment', 'proto':proto}, ), }, 'MMGenTxID': { 'arg1': 's', 'bad': (1,[],'\0','\1','я','g','gg','FF','f00','F00F0012'), 'good': ('DEADBE','F00BAA') }, 'CoinTxID':{ 'arg1': 's', 'bad': (1,[],'\0','\1','я','g','gg','FF','f00','F00F0012',r16.hex(),r32.hex()+'ee'), 'good': (r32.hex(),) }, 'WifKey': { 'arg1': 'proto', 'bad': ( {'proto':proto, 'wif':1}, {'proto':proto, 'wif':[]}, {'proto':proto, 'wif':'\0'}, {'proto':proto, 'wif':'\1'}, {'proto':proto, 'wif':'я'}, {'proto':proto, 'wif':'g'}, {'proto':proto, 'wif':'gg'}, {'proto':proto, 'wif':'FF'}, {'proto':proto, 'wif':'f00'}, {'proto':proto, 'wif':r16.hex()}, {'proto':proto, 'wif':'2MspvWFjBbkv2wzQGqhxJUYPCk3Y2jMaxLN'}, ), 'good': ( {'proto':proto, 'wif':'5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb', 'ret_idx':1}, {'proto':proto, 'wif':'KwWr9rDh8KK5TtDa3HLChEvQXNYcUXpwhRFUPc5uSNnMtqNKLFhk', 'ret_idx':1}, ) }, 'PubKey': { 'arg1': 's', 'bad': ( {'s':1, 'compressed':True }, {'s':'F00BAA12','compressed':False}, ), 'good': ( # TODO: add real pubkeys {'s':bytes.fromhex('deadbeef'),'compressed':True}, ) }, 'PrivKey': { 'arg1': 'proto', 'bad': ( {'proto':proto, 'wif':1}, {'proto':proto, 'wif':'1'}, {'proto':proto, 'wif':'cMsqcmDYZP1LdKgqRh9L4ZRU9br28yvdmTPwW2YQwVSN9aQiMAoR'}, {'proto':proto, 's':r32,'wif':'5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb'}, {'proto':proto, 'pubkey_type':'std'}, {'proto':proto, 's':r32}, {'proto':proto, 's':r32,'compressed':'yes'}, {'proto':proto, 's':r32,'compressed':'yes','pubkey_type':'std'}, {'proto':proto, 's':r32,'compressed':True,'pubkey_type':'nonstd'}, {'proto':proto, 's':r32+b'x','compressed':True,'pubkey_type':'std'} ), 'good': ( {'proto':proto, 'wif':'5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb', 'ret':bytes.fromhex('e0aef965b905a2fedf907151df8e0a6bac832aa697801c51f58bd2ecb4fd381c')}, {'proto':proto, 'wif':'KwWr9rDh8KK5TtDa3HLChEvQXNYcUXpwhRFUPc5uSNnMtqNKLFhk', 'ret':bytes.fromhex('08d0ed83b64b68d56fa064be48e2385060ed205be2b1e63cd56d218038c3a05f')}, {'proto':proto, 's':r32,'compressed':False,'pubkey_type':'std','ret':r32}, {'proto':proto, 's':r32,'compressed':True,'pubkey_type':'std','ret':r32} ) }, 'AddrListID': { 'arg1': 'sid', 'bad': ( {'sid':SeedID(sid='F00BAA12'),'mmtype':'Z','ret':'F00BAA12:Z'}, {'id_str':'G00BAA12:B','proto':proto}, ), 'good': ( {'id_str':'F00BAA12:B','proto':proto,'ret':'F00BAA12:B'}, {'sid':SeedID(sid='F00BAA12'),'mmtype':proto.addr_type(id_str='S'),'ret':'F00BAA12:S'}, {'sid':SeedID(sid='F00BAA12'),'mmtype':proto.addr_type(id_str='L'),'ret':'F00BAA12:L'}, ) }, 'MMGenWalletLabel': { 'arg1': 's', 'bad': (utf8_text[:49],utf8_combining[:48],utf8_ctrl[:48],gr_uc_w_ctrl), 'good': (utf8_text[:48],) }, 'TwComment': { 'exc_name': 'BadTwComment', 'arg1': 's', 'bad': ( utf8_combining[:40], utf8_ctrl[:40], text_jp[:41], text_zh[:41], gr_uc_w_ctrl, utf8_text[:81] ), 'good': ( utf8_text[:80], (ru_uc + gr_uc + utf8_text)[:80], text_jp[:40], text_zh[:40] ) }, 'MMGenTxComment':{ 'arg1': 's', 'bad': (utf8_text[:73],utf8_combining[:72],utf8_ctrl[:72],gr_uc_w_ctrl), 'good': (utf8_text[:72],) }, 'MMGenPWIDString': { # forbidden = list(u' :/\\') 'arg1': 's', 'bad': ('foo/','foo:','foo:\\'), 'good': ('qwerty@яяя',) }, 'MMGenAddrType': { 'arg1': 'proto', 'bad': ( {'proto':proto, 'id_str':'U', 'ret':'L'}, {'proto':proto, 'id_str':'z', 'ret':'L'}, {'proto':proto, 'id_str':'xx', 'ret':'C'}, {'proto':proto, 'id_str':'dogecoin', 'ret':'C'}, ), 'good': ( {'proto':proto, 'id_str':'legacy', 'ret':'L'}, {'proto':proto, 'id_str':'L', 'ret':'L'}, {'proto':proto, 'id_str':'compressed','ret':'C'}, {'proto':proto, 'id_str':'C', 'ret':'C'}, {'proto':proto, 'id_str':'segwit', 'ret':'S'}, {'proto':proto, 'id_str':'S', 'ret':'S'}, {'proto':proto, 'id_str':'bech32', 'ret':'B'}, {'proto':proto, 'id_str':'B', 'ret':'B'} ) }, 'MMGenPasswordType': { 'arg1': 'proto', 'bad': ( {'proto':proto, 'id_str':'U', 'ret':'L'}, {'proto':proto, 'id_str':'z', 'ret':'L'}, {'proto':proto, 'id_str':'я', 'ret':'C'}, {'proto':proto, 'id_str':1, 'ret':'C'}, {'proto':proto, 'id_str':'passw0rd', 'ret':'C'}, ), 'good': ( {'proto':proto, 'id_str':'password', 'ret':'P'}, {'proto':proto, 'id_str':'P', 'ret':'P'}, ) }, 'SeedSplitSpecifier': { 'arg1': 's', 'bad': ('M','αβ:2',1,'0:1','1:1','2:1','3:2','1:2000','abc:0:2'), 'good': ( ('1:2','2:2','alice:2:2','αβ:2:2','1:'+ssm,ssm+':'+ssm) ) }, 'IPPort': { 'arg1': 's', 'bad': ( '.0.0.0:0', '127.0.0.1', '127.0.0.1:', '20', ':20', '127.0.0.256:0', '127.0.0.1:65536', '127.0.0.01:60000', '0.0.0.0:00', ), 'good': ( ( '0.0.0.0:0', '0.0.0.1:0', '0.0.1.0:0', '0.0.1.1:0', '0.1.0.0:0', '1.0.0.0:0', '10.0.0.1:0', '127.0.0.1:65535', '255.0.0.0:400', '255.255.255.255:65535', ) ) }, }