ts_ethdev.py 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. ts_ethdev.py: Ethdev tests for the test.py test suite
  20. """
  21. import sys,os,re,shutil
  22. from decimal import Decimal
  23. from collections import namedtuple
  24. from subprocess import run,PIPE,DEVNULL
  25. from mmgen.globalvars import g
  26. from mmgen.opts import opt
  27. from mmgen.util import die
  28. from mmgen.amt import ETHAmt
  29. from mmgen.protocol import CoinProtocol
  30. from ..include.common import *
  31. from .common import *
  32. del_addrs = ('4','1')
  33. dfl_sid = '98831F3A'
  34. # The OpenEthereum dev address with lots of coins. Create with "ethkey -b info ''":
  35. dfl_devaddr = '00a329c0648769a73afac7f9381e08fb43dbea72'
  36. dfl_devkey = '4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7'
  37. prealloc_amt = ETHAmt('1_000_000_000')
  38. burn_addr = 'deadbeef'*5
  39. burn_addr2 = 'beadcafe'*5
  40. amt1 = '999999.12345689012345678'
  41. amt2 = '888.111122223333444455'
  42. parity_devkey_fn = 'parity.devkey'
  43. erigon_devkey_fn = 'erigon.devkey'
  44. vbal1 = '1.2288409'
  45. vbal9 = '1.22626295'
  46. vbal2 = '99.997088755'
  47. vbal3 = '1.23142525'
  48. vbal4 = '127.0287909'
  49. vbal5 = '1000126.14828654512345678'
  50. bals = {
  51. '1': [ ('98831F3A:E:1','123.456')],
  52. '2': [ ('98831F3A:E:1','123.456'),('98831F3A:E:11','1.234')],
  53. '3': [ ('98831F3A:E:1','123.456'),('98831F3A:E:11','1.234'),('98831F3A:E:21','2.345')],
  54. '4': [ ('98831F3A:E:1','100'),
  55. ('98831F3A:E:2','23.45495'),
  56. ('98831F3A:E:11','1.234'),
  57. ('98831F3A:E:21','2.345')],
  58. '5': [ ('98831F3A:E:1','100'),
  59. ('98831F3A:E:2','23.45495'),
  60. ('98831F3A:E:11','1.234'),
  61. ('98831F3A:E:21','2.345'),
  62. (burn_addr + r'\s+Non-MMGen',amt1)],
  63. '8': [ ('98831F3A:E:1','0'),
  64. ('98831F3A:E:2','23.45495'),
  65. ('98831F3A:E:11',vbal1,'a1'),
  66. ('98831F3A:E:12','99.99895'),
  67. ('98831F3A:E:21','2.345'),
  68. (burn_addr + r'\s+Non-MMGen',amt1)],
  69. '9': [ ('98831F3A:E:1','0'),
  70. ('98831F3A:E:2','23.45495'),
  71. ('98831F3A:E:11',vbal1,'a1'),
  72. ('98831F3A:E:12',vbal2),
  73. ('98831F3A:E:21','2.345'),
  74. (burn_addr + r'\s+Non-MMGen',amt1)],
  75. '10': [ ('98831F3A:E:1','0'),
  76. ('98831F3A:E:2','23.0218'),
  77. ('98831F3A:E:3','0.4321'),
  78. ('98831F3A:E:11',vbal1,'a1'),
  79. ('98831F3A:E:12',vbal2),
  80. ('98831F3A:E:21','2.345'),
  81. (burn_addr + r'\s+Non-MMGen',amt1)]
  82. }
  83. token_bals = {
  84. '1': [ ('98831F3A:E:11','1000','1.234')],
  85. '2': [ ('98831F3A:E:11','998.76544',vbal3,'a1'),
  86. ('98831F3A:E:12','1.23456','0')],
  87. '3': [ ('98831F3A:E:11','110.654317776666555545',vbal1,'a1'),
  88. ('98831F3A:E:12','1.23456','0')],
  89. '4': [ ('98831F3A:E:11','110.654317776666555545',vbal1,'a1'),
  90. ('98831F3A:E:12','1.23456','0'),
  91. (burn_addr + r'\s+Non-MMGen',amt2,amt1)],
  92. '5': [ ('98831F3A:E:11','110.654317776666555545',vbal1,'a1'),
  93. ('98831F3A:E:12','1.23456','99.99895'),
  94. (burn_addr + r'\s+Non-MMGen',amt2,amt1)],
  95. '6': [ ('98831F3A:E:11','110.654317776666555545',vbal1,'a1'),
  96. ('98831F3A:E:12','0',vbal2),
  97. ('98831F3A:E:13','1.23456','0'),
  98. (burn_addr + r'\s+Non-MMGen',amt2,amt1)],
  99. '7': [ ('98831F3A:E:11','67.444317776666555545',vbal9,'a2'),
  100. ('98831F3A:E:12','43.21',vbal2),
  101. ('98831F3A:E:13','1.23456','0'),
  102. (burn_addr + r'\s+Non-MMGen',amt2,amt1)]
  103. }
  104. token_bals_getbalance = {
  105. '1': (vbal4,'999999.12345689012345678'),
  106. '2': ('111.888877776666555545','888.111122223333444455')
  107. }
  108. from .ts_base import *
  109. from .ts_shared import *
  110. coin = g.coin
  111. class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
  112. 'Ethereum transacting, token deployment and tracking wallet operations'
  113. networks = ('eth','etc')
  114. passthru_opts = ('coin','daemon_id','http_timeout')
  115. extra_spawn_args = ['--regtest=1']
  116. tmpdir_nums = [22]
  117. color = True
  118. cmd_group = (
  119. ('setup', f'dev mode tests for coin {coin} (start daemon)'),
  120. ('daemon_version', 'mmgen-tool daemon_version'),
  121. ('wallet_upgrade1', 'upgrading the tracking wallet (v1 -> v2)'),
  122. ('wallet_upgrade2', 'upgrading the tracking wallet (v2 -> v3)'),
  123. ('addrgen', 'generating addresses'),
  124. ('addrimport', 'importing addresses'),
  125. ('addrimport_dev_addr', "importing dev faucet address 'Ox00a329c..'"),
  126. ('addrimport_erigon_dev_addr', 'importing Erigon dev faucet address'),
  127. ('fund_dev_address', 'funding the default (Parity dev) address'),
  128. ('msgsign_chk', "signing a message (low-level, check against 'eth_sign' RPC call)"),
  129. ('msgcreate', 'creating a message file'),
  130. ('msgsign', 'signing the message file'),
  131. ('msgverify', 'verifying the message file'),
  132. ('msgexport', 'exporting the message file data to JSON for third-party verifier'),
  133. ('msgverify_export', 'verifying the exported JSON data'),
  134. ('msgcreate_raw', 'creating a message file (--msghash-type=raw)'),
  135. ('msgsign_raw', 'signing the message file (msghash_type=raw)'),
  136. ('msgverify_raw', 'verifying the message file (msghash_type=raw)'),
  137. ('msgexport_raw', 'exporting the message file data to JSON (msghash_type=raw)'),
  138. ('msgverify_export_raw', 'verifying the exported JSON data (msghash_type=raw)'),
  139. ('txcreate1', 'creating a transaction (spend from dev address to address :1)'),
  140. ('txview1_raw', 'viewing the raw transaction'),
  141. ('txsign1', 'signing the transaction'),
  142. ('txview1_sig', 'viewing the signed transaction'),
  143. ('tx_status0_bad', 'getting the transaction status'),
  144. ('txsign1_ni', 'signing the transaction (non-interactive)'),
  145. ('txsend1', 'sending the transaction'),
  146. ('bal1', f'the {coin} balance'),
  147. ('txcreate2', 'creating a transaction (spend from dev address to address :11)'),
  148. ('txsign2', 'signing the transaction'),
  149. ('txsend2', 'sending the transaction'),
  150. ('bal2', f'the {coin} balance'),
  151. ('txcreate3', 'creating a transaction (spend from dev address to address :21)'),
  152. ('txsign3', 'signing the transaction'),
  153. ('txsend3', 'sending the transaction'),
  154. ('bal3', f'the {coin} balance'),
  155. ('tx_status1', 'getting the transaction status'),
  156. ('txcreate4', 'creating a transaction (spend from MMGen address, low TX fee)'),
  157. ('txbump', 'bumping the transaction fee'),
  158. ('txsign4', 'signing the transaction'),
  159. ('txsend4', 'sending the transaction'),
  160. ('tx_status1a', 'getting the transaction status'),
  161. ('bal4', f'the {coin} balance'),
  162. ('txcreate5', 'creating a transaction (fund burn address)'),
  163. ('txsign5', 'signing the transaction'),
  164. ('txsend5', 'sending the transaction'),
  165. ('addrimport_burn_addr', 'importing burn address'),
  166. ('bal5', f'the {coin} balance'),
  167. ('add_label1', 'adding a UTF-8 label (zh)'),
  168. ('chk_label1', 'the label'),
  169. ('add_label2', 'adding a UTF-8 label (lat+cyr+gr)'),
  170. ('chk_label2', 'the label'),
  171. ('remove_label', 'removing the label'),
  172. ('token_compile1', 'compiling ERC20 token #1'),
  173. ('token_deploy1a', 'deploying ERC20 token #1 (SafeMath)'),
  174. ('token_deploy1b', 'deploying ERC20 token #1 (Owned)'),
  175. ('token_deploy1c', 'deploying ERC20 token #1 (Token)'),
  176. ('tx_status2', 'getting the transaction status'),
  177. ('bal6', f'the {coin} balance'),
  178. ('token_compile2', 'compiling ERC20 token #2'),
  179. ('token_deploy2a', 'deploying ERC20 token #2 (SafeMath)'),
  180. ('token_deploy2b', 'deploying ERC20 token #2 (Owned)'),
  181. ('token_deploy2c', 'deploying ERC20 token #2 (Token)'),
  182. ('contract_deploy', 'deploying contract (create,sign,send)'),
  183. ('token_fund_users', 'transferring token funds from dev to user'),
  184. ('token_user_bals', 'show balances after transfer'),
  185. ('token_addrgen', 'generating token addresses'),
  186. ('token_addrimport_badaddr1', 'importing token addresses (no token address)'),
  187. ('token_addrimport_badaddr2', 'importing token addresses (bad token address)'),
  188. ('token_addrimport_addr1', 'importing token addresses using token address (MM1)'),
  189. ('token_addrimport_addr2', 'importing token addresses using token address (MM2)'),
  190. ('token_addrimport_batch', 'importing token addresses (dummy batch mode) (MM1)'),
  191. ('token_addrimport_sym', 'importing token addresses using token symbol (MM2)'),
  192. ('bal7', f'the {coin} balance'),
  193. ('token_bal1', f'the {coin} balance and token balance'),
  194. ('token_txcreate1', 'creating a token transaction'),
  195. ('token_txview1_raw', 'viewing the raw transaction'),
  196. ('token_txsign1', 'signing the transaction'),
  197. ('token_txsend1', 'sending the transaction'),
  198. ('token_txview1_sig', 'viewing the signed transaction'),
  199. ('tx_status3', 'getting the transaction status'),
  200. ('token_bal2', f'the {coin} balance and token balance'),
  201. ('token_txcreate2', 'creating a token transaction (to burn address)'),
  202. ('token_txbump', 'bumping the transaction fee'),
  203. ('token_txsign2', 'signing the transaction'),
  204. ('token_txsend2', 'sending the transaction'),
  205. ('token_bal3', f'the {coin} balance and token balance'),
  206. ('del_dev_addr', 'deleting the dev address'),
  207. ('bal1_getbalance', f'the {coin} balance (getbalance)'),
  208. ('addrimport_token_burn_addr', 'importing the token burn address'),
  209. ('token_bal4', f'the {coin} balance and token balance'),
  210. ('token_bal_getbalance', 'the token balance (getbalance)'),
  211. ('txcreate_noamt', 'creating a transaction (full amount send)'),
  212. ('txsign_noamt', 'signing the transaction'),
  213. ('txsend_noamt', 'sending the transaction'),
  214. ('bal8', f'the {coin} balance'),
  215. ('token_bal5', 'the token balance'),
  216. ('token_txcreate_noamt', 'creating a token transaction (full amount send)'),
  217. ('token_txsign_noamt', 'signing the transaction'),
  218. ('token_txsend_noamt', 'sending the transaction'),
  219. ('bal9', f'the {coin} balance'),
  220. ('token_bal6', 'the token balance'),
  221. ('listaddresses1', 'listaddresses'),
  222. ('listaddresses2', 'listaddresses minconf=999999999 (ignored)'),
  223. ('listaddresses3', 'listaddresses sort=age (ignored)'),
  224. ('listaddresses4', 'listaddresses showempty=1 sort=age (ignored)'),
  225. ('token_listaddresses1', 'listaddresses --token=mm1'),
  226. ('token_listaddresses2', 'listaddresses --token=mm1 showempty=1'),
  227. ('twview_cached_balances', 'twview (cached balances)'),
  228. ('token_twview_cached_balances', 'token twview (cached balances)'),
  229. ('txcreate_cached_balances', 'txcreate (cached balances)'),
  230. ('token_txcreate_cached_balances', 'token txcreate (cached balances)'),
  231. ('txdo_cached_balances', 'txdo (cached balances)'),
  232. ('txcreate_refresh_balances', 'refreshing balances'),
  233. ('bal10', f'the {coin} balance'),
  234. ('token_txdo_cached_balances', 'token txdo (cached balances)'),
  235. ('token_txcreate_refresh_balances', 'refreshing token balances'),
  236. ('token_bal7', 'the token balance'),
  237. ('twview1', 'twview'),
  238. ('twview2', 'twview wide=1'),
  239. ('twview3', 'twview wide=1 sort=age (ignored)'),
  240. ('twview4', 'twview wide=1 minconf=999999999 (ignored)'),
  241. ('twview5', 'twview wide=1 minconf=0 (ignored)'),
  242. ('token_twview1', 'twview --token=mm1'),
  243. ('token_twview2', 'twview --token=mm1 wide=1'),
  244. ('token_twview3', 'twview --token=mm1 wide=1 sort=age (ignored)'),
  245. ('edit_label1', f'adding label to addr #{del_addrs[0]} in {coin} tracking wallet (zh)'),
  246. ('edit_label2', f'adding label to addr #{del_addrs[1]} in {coin} tracking wallet (lat+cyr+gr)'),
  247. ('edit_label3', f'removing label from addr #{del_addrs[0]} in {coin} tracking wallet'),
  248. ('token_edit_label1', f'adding label to addr #{del_addrs[0]} in {coin} token tracking wallet'),
  249. ('remove_addr1', f'removing addr #{del_addrs[0]} from {coin} tracking wallet'),
  250. ('remove_addr2', f'removing addr #{del_addrs[1]} from {coin} tracking wallet'),
  251. ('token_remove_addr1', f'removing addr #{del_addrs[0]} from {coin} token tracking wallet'),
  252. ('token_remove_addr2', f'removing addr #{del_addrs[1]} from {coin} token tracking wallet'),
  253. ('stop', 'stopping daemon'),
  254. )
  255. def __init__(self,trunner,cfgs,spawn):
  256. TestSuiteBase.__init__(self,trunner,cfgs,spawn)
  257. if trunner == None:
  258. return
  259. self.erase_input = Ctrl_U if opt.pexpect_spawn else ''
  260. from mmgen.protocol import init_proto
  261. self.proto = init_proto(g.coin,network='regtest',need_amt=True)
  262. from mmgen.daemon import CoinDaemon
  263. d = CoinDaemon(proto=self.proto,test_suite=True)
  264. self.rpc_port = d.rpc_port
  265. self.daemon_datadir = d.datadir
  266. self.using_solc = check_solc_ver()
  267. if not self.using_solc:
  268. omsg(yellow('Using precompiled contract data'))
  269. write_to_file(
  270. joinpath(self.tmpdir,parity_devkey_fn),
  271. dfl_devkey+'\n' )
  272. if g.daemon_id == 'erigon':
  273. from hashlib import sha256
  274. from mmgen.tool.api import tool_api
  275. devkey = sha256(b'erigon devnet key').hexdigest()
  276. t = tool_api()
  277. t.init_coin(g.coin,'regtest')
  278. self.erigon_devaddr = t.wif2addr(devkey)
  279. write_to_file(
  280. joinpath(self.tmpdir,erigon_devkey_fn),
  281. devkey+'\n' )
  282. os.environ['MMGEN_BOGUS_SEND'] = ''
  283. self.message = 'attack at dawn'
  284. def __del__(self):
  285. os.environ['MMGEN_BOGUS_SEND'] = '1'
  286. @property
  287. def eth_args(self):
  288. return [
  289. f'--outdir={self.tmpdir}',
  290. f'--coin={self.proto.coin}',
  291. f'--rpc-port={self.rpc_port}',
  292. '--quiet'
  293. ]
  294. @property
  295. def eth_args_noquiet(self):
  296. return self.eth_args[:-1]
  297. async def setup(self):
  298. self.spawn('',msg_only=True)
  299. async def geth_devnet_init_bug_workaround(): # devnet_init_bug
  300. from mmgen.daemon import CoinDaemon
  301. d = CoinDaemon(
  302. self.proto.coin+'_rt',
  303. test_suite = True,
  304. daemon_id = g.daemon_id,
  305. opts = ['devnet_init_bug'] )
  306. d.start()
  307. import asyncio
  308. await asyncio.sleep(1)
  309. d.stop()
  310. await asyncio.sleep(1)
  311. if not self.using_solc:
  312. srcdir = os.path.join(self.tr.repo_root,'test','ref','ethereum','bin')
  313. from shutil import copytree
  314. for d in ('mm1','mm2'):
  315. copytree(os.path.join(srcdir,d),os.path.join(self.tmpdir,d))
  316. if g.daemon_id == 'geth' and not (opt.resume or opt.resume_after or opt.skip_deps):
  317. self.geth_setup()
  318. set_vt100()
  319. # await geth_devnet_init_bug_workaround() # uncomment to enable testing with v1.10.17
  320. if not opt.no_daemon_autostart:
  321. if not start_test_daemons(
  322. self.proto.coin+'_rt',
  323. remove_datadir = not g.daemon_id in ('geth','erigon') ):
  324. return False
  325. from mmgen.rpc import rpc_init
  326. rpc = await rpc_init(self.proto)
  327. imsg(f'Daemon: {rpc.daemon.coind_name} v{rpc.daemon_version_str}')
  328. return 'ok'
  329. def geth_setup(self):
  330. def make_key(keystore):
  331. pwfile = joinpath(self.tmpdir,'account_passwd')
  332. write_to_file(pwfile,'')
  333. run(['rm','-rf',keystore])
  334. cmd = f'geth account new --password={pwfile} --lightkdf --keystore {keystore}'
  335. cp = run(cmd.split(),stdout=PIPE,stderr=PIPE)
  336. if cp.returncode:
  337. die(1,cp.stderr.decode())
  338. keyfile = os.path.join(keystore,os.listdir(keystore)[0])
  339. with open(keyfile) as fp:
  340. return json.loads(fp.read())['address']
  341. def make_genesis(signer_addr,prealloc_addr):
  342. return {
  343. 'config': {
  344. 'chainId': 1337, # TODO: replace constant with var
  345. 'homesteadBlock': 0,
  346. 'eip150Block': 0,
  347. 'eip155Block': 0,
  348. 'eip158Block': 0,
  349. 'byzantiumBlock': 0,
  350. 'constantinopleBlock': 0,
  351. 'petersburgBlock': 0,
  352. 'clique': {
  353. 'period': 0,
  354. 'epoch': 30000
  355. }
  356. },
  357. 'difficulty': '1',
  358. 'gasLimit': '8000000',
  359. 'extradata': '0x' + 64*'0' + signer_addr + 130*'0',
  360. 'alloc': {
  361. prealloc_addr: { 'balance': str(prealloc_amt.toWei()) }
  362. }
  363. }
  364. def init_genesis(fn):
  365. cmd = f'geth init --datadir {d.datadir} {fn}'
  366. cp = run( cmd.split(), stdout=PIPE, stderr=PIPE )
  367. if cp.returncode:
  368. die(1,cp.stderr.decode())
  369. from mmgen.daemon import CoinDaemon
  370. import json
  371. d = CoinDaemon(proto=self.proto,test_suite=True)
  372. d.stop(quiet=True)
  373. d.remove_datadir()
  374. imsg(cyan('Initializing Geth:'))
  375. keystore = os.path.relpath(os.path.join(d.datadir,'keystore'))
  376. imsg(f' Keystore: {keystore}')
  377. signer_addr = make_key(keystore)
  378. self.write_to_tmpfile( 'signer_addr', signer_addr + '\n' )
  379. imsg(f' Signer address: {signer_addr}')
  380. imsg(f' Faucet: {dfl_devaddr} ({prealloc_amt} ETH)')
  381. genesis_data = make_genesis(signer_addr,dfl_devaddr)
  382. genesis_fn = joinpath(self.tmpdir,'genesis.json')
  383. imsg(f' Genesis block data: {genesis_fn}')
  384. write_to_file( genesis_fn, json.dumps(genesis_data,indent=' ')+'\n' )
  385. init_genesis(genesis_fn)
  386. def wallet_upgrade(self,src_file):
  387. if self.proto.coin == 'ETC':
  388. msg(f'skipping test {self.test_name!r} for ETC')
  389. return 'skip'
  390. src_dir = joinpath(ref_dir,'ethereum')
  391. dest_dir = joinpath(self.tr.data_dir,'altcoins',self.proto.coin.lower())
  392. w_from = joinpath(src_dir,src_file)
  393. w_to = joinpath(dest_dir,'tracking-wallet.json')
  394. os.makedirs(dest_dir,mode=0o750,exist_ok=True)
  395. dest = shutil.copy2(w_from,w_to)
  396. assert dest == w_to, dest
  397. t = self.spawn('mmgen-tool', self.eth_args + ['twview'])
  398. t.read()
  399. os.unlink(w_to)
  400. return t
  401. def daemon_version(self):
  402. t = self.spawn('mmgen-tool', self.eth_args + ['daemon_version'])
  403. t.expect('version')
  404. return t
  405. def wallet_upgrade1(self): return self.wallet_upgrade('tracking-wallet-v1.json')
  406. def wallet_upgrade2(self): return self.wallet_upgrade('tracking-wallet-v2.json')
  407. def addrgen(self,addrs='1-3,11-13,21-23'):
  408. t = self.spawn('mmgen-addrgen', self.eth_args + [dfl_words_file,addrs])
  409. t.written_to_file('Addresses')
  410. return t
  411. def addrimport(self,ext='21-23]{}.regtest.addrs',expect='9/9',add_args=[],bad_input=False):
  412. ext = ext.format('-α' if g.debug_utf8 else '')
  413. fn = self.get_file_with_ext(ext,no_dot=True,delete=False)
  414. t = self.spawn('mmgen-addrimport', self.eth_args[1:-1] + add_args + [fn])
  415. if bad_input:
  416. return t
  417. t.expect('Importing')
  418. t.expect(expect)
  419. return t
  420. def addrimport_one_addr(self,addr=None,extra_args=[]):
  421. t = self.spawn('mmgen-addrimport', self.eth_args[1:] + extra_args + ['--address='+addr])
  422. t.expect('OK')
  423. return t
  424. def addrimport_dev_addr(self):
  425. return self.addrimport_one_addr(addr=dfl_devaddr)
  426. def addrimport_erigon_dev_addr(self):
  427. if not g.daemon_id == 'erigon':
  428. return 'skip'
  429. return self.addrimport_one_addr(addr=self.erigon_devaddr)
  430. def addrimport_burn_addr(self):
  431. return self.addrimport_one_addr(addr=burn_addr)
  432. def txcreate(self,
  433. args = [],
  434. menu = [],
  435. acct = '1',
  436. caller = 'txcreate',
  437. interactive_fee = '50G',
  438. eth_fee_res = None,
  439. fee_res_data = ('0.00105','50'),
  440. fee_desc = 'gas price',
  441. no_read = False,
  442. tweaks = [] ):
  443. fee_res = r'\D{}\D.*{c} .*\D{}\D.*gas price in Gwei'.format( *fee_res_data, c=self.proto.coin )
  444. t = self.spawn('mmgen-'+caller, self.eth_args + ['-B'] + args)
  445. t.expect(r'add \[l\]abel, .*?:.','p', regex=True)
  446. t.written_to_file('Account balances listing')
  447. t = self.txcreate_ui_common(t,
  448. menu = menu,
  449. caller = caller,
  450. input_sels_prompt = 'to spend from',
  451. inputs = acct,
  452. file_desc = 'transaction',
  453. bad_input_sels = True,
  454. interactive_fee = interactive_fee,
  455. fee_res = fee_res,
  456. fee_desc = fee_desc,
  457. eth_fee_res = eth_fee_res,
  458. add_comment = tx_label_jp,
  459. tweaks = tweaks )
  460. if not no_read:
  461. t.read()
  462. return t
  463. def txsign(self,ni=False,ext='{}.regtest.rawtx',add_args=[]):
  464. ext = ext.format('-α' if g.debug_utf8 else '')
  465. keyfile = joinpath(self.tmpdir,parity_devkey_fn)
  466. txfile = self.get_file_with_ext(ext,no_dot=True)
  467. t = self.spawn( 'mmgen-txsign',
  468. [f'--outdir={self.tmpdir}']
  469. + [f'--coin={self.proto.coin}']
  470. + ['--quiet']
  471. + ['--rpc-host=bad_host'] # ETH signing must work without RPC
  472. + add_args
  473. + ([],['--yes'])[ni]
  474. + ['-k', keyfile, txfile, dfl_words_file] )
  475. return self.txsign_ui_common(t,ni=ni,has_label=True)
  476. def txsend(self,ni=False,ext='{}.regtest.sigtx',add_args=[]):
  477. ext = ext.format('-α' if g.debug_utf8 else '')
  478. txfile = self.get_file_with_ext(ext,no_dot=True)
  479. t = self.spawn('mmgen-txsend', self.eth_args + add_args + [txfile])
  480. txid = self.txsend_ui_common(t,
  481. quiet = not g.debug,
  482. bogus_send = False,
  483. has_label = True )
  484. return t
  485. def txview(self,ext_fs):
  486. ext = ext_fs.format('-α' if g.debug_utf8 else '')
  487. txfile = self.get_file_with_ext(ext,no_dot=True)
  488. return self.spawn( 'mmgen-tool',['--verbose','txview',txfile] )
  489. def fund_dev_address(self):
  490. """
  491. For Erigon, fund the default (Parity) dev address from the Erigon dev address
  492. For the others, send a junk TX to keep block counts equal for all daemons
  493. """
  494. dt = namedtuple('data',['devkey_fn','dest','amt'])
  495. if g.daemon_id == 'erigon':
  496. d = dt( erigon_devkey_fn, dfl_devaddr, prealloc_amt )
  497. else:
  498. d = dt( parity_devkey_fn, burn_addr2, '1' )
  499. t = self.txcreate(
  500. args = [
  501. f'--keys-from-file={joinpath(self.tmpdir,d.devkey_fn)}',
  502. f'{d.dest},{d.amt}',
  503. ],
  504. menu = ['a','r'],
  505. caller = 'txdo',
  506. acct = '1',
  507. no_read = True )
  508. self._do_confirm_send(t,quiet=not g.debug,sure=False)
  509. t.read()
  510. self.get_file_with_ext('sigtx',delete_all=True)
  511. return t
  512. def txcreate1(self):
  513. if g.daemon_id == 'erigon':
  514. # delete Erigon devaddr so that wallet is same as for other daemons
  515. menu = ['a','r','D','1\n','y\n'] # sort by reverse amount
  516. else:
  517. # include one invalid keypress 'X' -- see EthereumTwUnspentOutputs.key_mappings
  518. menu = ['a','d','r','M','X','e','m','m']
  519. args = ['98831F3A:E:1,123.456']
  520. return self.txcreate(args=args,menu=menu,acct='1',tweaks=['confirm_non_mmgen'])
  521. def txview1_raw(self):
  522. return self.txview(ext_fs='{}.regtest.rawtx')
  523. def txsign1(self): return self.txsign(add_args=['--use-internal-keccak-module'])
  524. def tx_status0_bad(self):
  525. return self.tx_status(ext='{}.regtest.sigtx',expect_str='neither in mempool nor blockchain',exit_val=1)
  526. def txsign1_ni(self): return self.txsign(ni=True)
  527. def txsend1(self): return self.txsend()
  528. def txview1_sig(self): # do after send so that TxID is displayed
  529. return self.txview(ext_fs='{}.regtest.sigtx')
  530. def bal1(self): return self.bal(n='1')
  531. def txcreate2(self):
  532. args = ['98831F3A:E:11,1.234']
  533. return self.txcreate(args=args,acct='10',tweaks=['confirm_non_mmgen'])
  534. def txsign2(self): return self.txsign(ni=True,ext='1.234,50000]{}.regtest.rawtx')
  535. def txsend2(self): return self.txsend(ext='1.234,50000]{}.regtest.sigtx')
  536. def bal2(self): return self.bal(n='2')
  537. def txcreate3(self):
  538. args = ['98831F3A:E:21,2.345']
  539. return self.txcreate(args=args,acct='10',tweaks=['confirm_non_mmgen'])
  540. def txsign3(self): return self.txsign(ni=True,ext='2.345,50000]{}.regtest.rawtx')
  541. def txsend3(self): return self.txsend(ext='2.345,50000]{}.regtest.sigtx')
  542. def bal3(self): return self.bal(n='3')
  543. def tx_status(self,ext,expect_str,expect_str2='',add_args=[],exit_val=0):
  544. ext = ext.format('-α' if g.debug_utf8 else '')
  545. txfile = self.get_file_with_ext(ext,no_dot=True)
  546. t = self.spawn('mmgen-txsend', self.eth_args + add_args + ['--status',txfile])
  547. t.expect(expect_str)
  548. if expect_str2:
  549. t.expect(expect_str2)
  550. t.req_exit_val = exit_val
  551. return t
  552. def tx_status1(self):
  553. return self.tx_status(ext='2.345,50000]{}.regtest.sigtx',expect_str='has 1 confirmation')
  554. def tx_status1a(self):
  555. return self.tx_status(ext='2.345,50000]{}.regtest.sigtx',expect_str='has 2 confirmations')
  556. async def msgsign_chk(self): # NB: Geth only!
  557. def create_signature_mmgen():
  558. wallet_dir = os.path.relpath(os.path.join(self.daemon_datadir,'keystore'))
  559. wallet_fn = os.path.join(wallet_dir,os.listdir(wallet_dir)[0])
  560. from mmgen.base_proto.ethereum.misc import extract_key_from_geth_keystore_wallet
  561. key = extract_key_from_geth_keystore_wallet(
  562. wallet_fn = wallet_fn,
  563. passwd = b'' )
  564. imsg(f'Key: {key.hex()}')
  565. from mmgen.base_proto.ethereum.misc import ec_sign_message_with_privkey
  566. return ec_sign_message_with_privkey(self.message,key,'eth_sign')
  567. async def create_signature_rpc():
  568. from mmgen.rpc import rpc_init
  569. rpc = await rpc_init(self.proto)
  570. addr = self.read_from_tmpfile('signer_addr').strip()
  571. imsg(f'Address: {addr}')
  572. return await rpc.call(
  573. 'eth_sign',
  574. '0x' + addr,
  575. '0x' + self.message.encode().hex() )
  576. if not g.daemon_id == 'geth':
  577. return 'skip'
  578. self.spawn('',msg_only=True)
  579. sig = '0x' + create_signature_mmgen()
  580. sig_chk = await create_signature_rpc()
  581. # Compare signatures
  582. imsg(f'Message: {self.message}')
  583. imsg(f'Signature: {sig}')
  584. cmp_or_die(sig,sig_chk,'message signatures')
  585. imsg('Geth and MMGen signatures match')
  586. return 'ok'
  587. def msgcreate(self,add_args=[]):
  588. t = self.spawn('mmgen-msg', self.eth_args_noquiet + add_args + [ 'create', self.message, '98831F3A:E:1' ])
  589. t.written_to_file('Unsigned message data')
  590. return t
  591. def msgsign(self):
  592. fn = get_file_with_ext(self.tmpdir,'rawmsg.json')
  593. t = self.spawn('mmgen-msg', self.eth_args_noquiet + [ 'sign', fn, dfl_words_file ])
  594. t.written_to_file('Signed message data')
  595. return t
  596. def msgverify(self,fn=None,msghash_type='eth_sign'):
  597. fn = fn or get_file_with_ext(self.tmpdir,'sigmsg.json')
  598. t = self.spawn('mmgen-msg', self.eth_args_noquiet + [ 'verify', fn ])
  599. t.expect(msghash_type)
  600. t.expect('1 signature verified')
  601. return t
  602. def msgexport(self):
  603. fn = get_file_with_ext(self.tmpdir,'sigmsg.json')
  604. t = self.spawn('mmgen-msg', self.eth_args_noquiet + [ 'export', fn ])
  605. t.written_to_file('Signature data')
  606. return t
  607. def msgverify_export(self):
  608. return self.msgverify(
  609. fn = os.path.join(self.tmpdir,'signatures.json') )
  610. def msgcreate_raw(self):
  611. get_file_with_ext(self.tmpdir,'rawmsg.json',delete_all=True)
  612. return self.msgcreate(add_args=['--msghash-type=raw'])
  613. def msgsign_raw(self):
  614. get_file_with_ext(self.tmpdir,'sigmsg.json',delete_all=True)
  615. return self.msgsign()
  616. def msgverify_raw(self):
  617. return self.msgverify(msghash_type='raw')
  618. def msgexport_raw(self):
  619. get_file_with_ext(self.tmpdir,'signatures.json',no_dot=True,delete_all=True)
  620. return self.msgexport()
  621. def msgverify_export_raw(self):
  622. return self.msgverify(
  623. fn = os.path.join(self.tmpdir,'signatures.json'),
  624. msghash_type = 'raw' )
  625. def txcreate4(self):
  626. return self.txcreate(
  627. args = ['98831F3A:E:2,23.45495'],
  628. acct = '1',
  629. interactive_fee = '40G',
  630. fee_res_data = ('0.00084','40'),
  631. eth_fee_res = True )
  632. def txbump(self,ext=',40000]{}.regtest.rawtx',fee='50G',add_args=[]):
  633. ext = ext.format('-α' if g.debug_utf8 else '')
  634. txfile = self.get_file_with_ext(ext,no_dot=True)
  635. t = self.spawn('mmgen-txbump', self.eth_args + add_args + ['--yes',txfile])
  636. t.expect('or gas price: ',fee+'\n')
  637. return t
  638. def txsign4(self): return self.txsign(ni=True,ext='.45495,50000]{}.regtest.rawtx')
  639. def txsend4(self): return self.txsend(ext='.45495,50000]{}.regtest.sigtx')
  640. def bal4(self): return self.bal(n='4')
  641. def txcreate5(self):
  642. args = [burn_addr + ','+amt1]
  643. return self.txcreate(args=args,acct='10',tweaks=['confirm_non_mmgen'])
  644. def txsign5(self): return self.txsign(ni=True,ext=amt1+',50000]{}.regtest.rawtx')
  645. def txsend5(self): return self.txsend(ext=amt1+',50000]{}.regtest.sigtx')
  646. def bal5(self): return self.bal(n='5')
  647. #bal_corr = Decimal('0.0000032') # gas use for token sends varies between ETH and ETC!
  648. bal_corr = Decimal('0.0000000') # update: OpenEthereum team seems to have corrected this
  649. def bal(self,n):
  650. t = self.spawn('mmgen-tool', self.eth_args + ['twview','wide=1'])
  651. text = strip_ansi_escapes(t.read())
  652. for b in bals[n]:
  653. addr,amt,adj = b if len(b) == 3 else b + (False,)
  654. if adj and self.proto.coin == 'ETC':
  655. amt = str(Decimal(amt) + Decimal(adj[1]) * self.bal_corr)
  656. pat = r'\D{}\D.*\D{}\D'.format( addr, amt.replace('.',r'\.') )
  657. assert re.search(pat,text), pat
  658. ss = f'Total {self.proto.coin}:'
  659. assert re.search(ss,text),ss
  660. return t
  661. def token_bal(self,n=None):
  662. t = self.spawn('mmgen-tool', self.eth_args + ['--token=mm1','twview','wide=1'])
  663. text = strip_ansi_escapes(t.read())
  664. for b in token_bals[n]:
  665. addr,_amt1,_amt2,adj = b if len(b) == 4 else b + (False,)
  666. if adj and self.proto.coin == 'ETC':
  667. _amt2 = str(Decimal(_amt2) + Decimal(adj[1]) * self.bal_corr)
  668. pat = fr'{addr}\b.*\D{_amt1}\D.*\b{_amt2}\D'
  669. assert re.search(pat,text), pat
  670. ss = 'Total MM1:'
  671. assert re.search(ss,text),ss
  672. return t
  673. def bal_getbalance(self,idx,etc_adj=False,extra_args=[]):
  674. bal1 = token_bals_getbalance[idx][0]
  675. bal2 = token_bals_getbalance[idx][1]
  676. bal1 = Decimal(bal1)
  677. if etc_adj and self.proto.coin == 'ETC':
  678. bal1 += self.bal_corr
  679. t = self.spawn('mmgen-tool', self.eth_args + extra_args + ['getbalance'])
  680. t.expect(r'\n[0-9A-F]{8}: .*\D'+str(bal1),regex=True)
  681. t.expect(r'\nNon-MMGen: .*\D'+bal2,regex=True)
  682. total = strip_ansi_escapes(t.expect_getend(r'\nTOTAL:\s+',regex=True)).split()[0]
  683. assert Decimal(bal1) + Decimal(bal2) == Decimal(total)
  684. return t
  685. def add_label(self,lbl,addr='98831F3A:E:3'):
  686. t = self.spawn('mmgen-tool', self.eth_args + ['add_label',addr,lbl])
  687. t.expect('Added label.*in tracking wallet',regex=True)
  688. return t
  689. def chk_label(self,lbl_pat,addr='98831F3A:E:3'):
  690. t = self.spawn('mmgen-tool', self.eth_args + ['listaddresses','all_labels=1'])
  691. t.expect(fr'{addr}\b.*\S{{30}}\b.*{lbl_pat}\b',regex=True)
  692. return t
  693. def add_label1(self): return self.add_label(lbl=tw_label_zh)
  694. def chk_label1(self): return self.chk_label(lbl_pat=tw_label_zh)
  695. def add_label2(self): return self.add_label(lbl=tw_label_lat_cyr_gr)
  696. def chk_label2(self): return self.chk_label(lbl_pat=tw_label_lat_cyr_gr)
  697. def remove_label(self,addr='98831F3A:E:3'):
  698. t = self.spawn('mmgen-tool', self.eth_args + ['remove_label',addr])
  699. t.expect('Removed label.*in tracking wallet',regex=True)
  700. return t
  701. def token_compile(self,token_data={}):
  702. odir = joinpath(self.tmpdir,token_data['symbol'].lower())
  703. if not self.using_solc:
  704. imsg(f'Using precompiled contract data in {odir}')
  705. return 'skip' if os.path.exists(odir) else False
  706. self.spawn('',msg_only=True)
  707. cmd_args = [f'--{k}={v}' for k,v in list(token_data.items())]
  708. imsg("Compiling solidity token contract '{}' with 'solc'".format( token_data['symbol'] ))
  709. try: os.mkdir(odir)
  710. except: pass
  711. cmd = [
  712. 'python3',
  713. 'scripts/exec_wrapper.py',
  714. 'scripts/create-token.py',
  715. '--coin=' + self.proto.coin,
  716. '--outdir=' + odir
  717. ] + cmd_args + [self.proto.checksummed_addr(dfl_devaddr)]
  718. imsg('Executing: {}'.format( ' '.join(cmd) ))
  719. cp = run(cmd,stdout=DEVNULL,stderr=PIPE)
  720. if cp.returncode != 0:
  721. rmsg('solc failed with the following output:')
  722. die(2,cp.stderr.decode())
  723. imsg('ERC20 token {!r} compiled'.format( token_data['symbol'] ))
  724. return 'ok'
  725. def token_compile1(self):
  726. token_data = { 'name':'MMGen Token 1', 'symbol':'MM1', 'supply':10**26, 'decimals':18 }
  727. return self.token_compile(token_data)
  728. def token_compile2(self):
  729. token_data = { 'name':'MMGen Token 2', 'symbol':'MM2', 'supply':10**18, 'decimals':10 }
  730. return self.token_compile(token_data)
  731. async def get_tx_receipt(self,txid):
  732. from mmgen.tx import NewTX
  733. tx = await NewTX(proto=self.proto)
  734. from mmgen.rpc import rpc_init
  735. tx.rpc = await rpc_init(self.proto)
  736. res = await tx.get_receipt(txid)
  737. imsg(f'Gas sent: {res.gas_sent.hl():<9} {(res.gas_sent*res.gas_price).hl2(encl="()")}')
  738. imsg(f'Gas used: {res.gas_used.hl():<9} {(res.gas_used*res.gas_price).hl2(encl="()")}')
  739. imsg(f'Gas price: {res.gas_price.hl2()}')
  740. if res.gas_used == res.gas_sent:
  741. omsg(yellow(f'Warning: all gas was used!'))
  742. return res
  743. async def token_deploy(self,num,key,gas,mmgen_cmd='txdo',tx_fee='8G'):
  744. keyfile = joinpath(self.tmpdir,parity_devkey_fn)
  745. fn = joinpath(self.tmpdir,'mm'+str(num),key+'.bin')
  746. args = [
  747. '-B',
  748. f'--tx-fee={tx_fee}',
  749. f'--tx-gas={gas}',
  750. f'--contract-data={fn}',
  751. f'--inputs={dfl_devaddr}',
  752. '--yes',
  753. ]
  754. if mmgen_cmd == 'txdo':
  755. args += ['-k',keyfile]
  756. t = self.spawn( 'mmgen-'+mmgen_cmd, self.eth_args + args)
  757. if mmgen_cmd == 'txcreate':
  758. t.written_to_file('transaction')
  759. ext = '[0,8000]{}.regtest.rawtx'.format('-α' if g.debug_utf8 else '')
  760. txfile = self.get_file_with_ext(ext,no_dot=True)
  761. t = self.spawn('mmgen-txsign', self.eth_args + ['--yes','-k',keyfile,txfile],no_msg=True)
  762. self.txsign_ui_common(t,ni=True)
  763. txfile = txfile.replace('.rawtx','.sigtx')
  764. t = self.spawn('mmgen-txsend', self.eth_args + [txfile],no_msg=True)
  765. txid = self.txsend_ui_common(t,
  766. caller = mmgen_cmd,
  767. quiet = mmgen_cmd == 'txdo' or not g.debug,
  768. bogus_send = False )
  769. addr = strip_ansi_escapes(t.expect_getend('Contract address: '))
  770. if (await self.get_tx_receipt(txid)).status == 0:
  771. die(2,f'Contract {num}:{key} failed to execute. Aborting')
  772. if key == 'Token':
  773. self.write_to_tmpfile( f'token_addr{num}', addr+'\n' )
  774. imsg(f'\nToken MM{num} deployed!')
  775. return t
  776. async def token_deploy1a(self): return await self.token_deploy(num=1,key='SafeMath',gas=500_000)
  777. async def token_deploy1b(self): return await self.token_deploy(num=1,key='Owned', gas=1_000_000)
  778. async def token_deploy1c(self): return await self.token_deploy(num=1,key='Token', gas=4_000_000,tx_fee='7G')
  779. def tx_status2(self):
  780. return self.tx_status(ext=self.proto.coin+'[0,7000]{}.regtest.sigtx',expect_str='successfully executed')
  781. def bal6(self): return self.bal5()
  782. async def token_deploy2a(self): return await self.token_deploy(num=2,key='SafeMath',gas=500_000)
  783. async def token_deploy2b(self): return await self.token_deploy(num=2,key='Owned', gas=1_000_000)
  784. async def token_deploy2c(self): return await self.token_deploy(num=2,key='Token', gas=4_000_000)
  785. async def contract_deploy(self): # test create,sign,send
  786. return await self.token_deploy(num=2,key='SafeMath',gas=500_000,mmgen_cmd='txcreate')
  787. async def token_transfer_ops(self,op,amt=1000):
  788. self.spawn('',msg_only=True)
  789. sid = dfl_sid
  790. from mmgen.tool.wallet import tool_cmd
  791. usr_mmaddrs = [f'{sid}:E:{i}' for i in (11,21)]
  792. usr_addrs = [tool_cmd(cmdname='gen_addr',proto=self.proto).gen_addr(addr,dfl_words_file) for addr in usr_mmaddrs]
  793. from mmgen.base_proto.ethereum.contract import TokenResolve
  794. async def do_transfer(rpc):
  795. for i in range(2):
  796. tk = await TokenResolve(
  797. self.proto,
  798. rpc,
  799. self.read_from_tmpfile(f'token_addr{i+1}').strip() )
  800. imsg_r( '\n' + await tk.info() )
  801. imsg('dev token balance (pre-send): {}'.format( await tk.get_balance(dfl_devaddr) ))
  802. imsg(f'Sending {amt} {self.proto.dcoin} to address {usr_addrs[i]} ({usr_mmaddrs[i]})')
  803. txid = await tk.transfer(
  804. dfl_devaddr,
  805. usr_addrs[i],
  806. amt,
  807. dfl_devkey,
  808. start_gas = ETHAmt(60000,'wei'),
  809. gasPrice = ETHAmt(8,'Gwei') )
  810. if (await self.get_tx_receipt(txid)).status == 0:
  811. die(2,'Transfer of token funds failed. Aborting')
  812. async def show_bals(rpc):
  813. for i in range(2):
  814. tk = await TokenResolve(
  815. self.proto,
  816. rpc,
  817. self.read_from_tmpfile(f'token_addr{i+1}').strip() )
  818. imsg('Token: {}'.format( await tk.get_symbol() ))
  819. imsg(f'dev token balance: {await tk.get_balance(dfl_devaddr)}')
  820. imsg('usr token balance: {} ({} {})'.format(
  821. await tk.get_balance(usr_addrs[i]),
  822. usr_mmaddrs[i],
  823. usr_addrs[i] ))
  824. from mmgen.rpc import rpc_init
  825. rpc = await rpc_init(self.proto)
  826. silence()
  827. if op == 'show_bals':
  828. await show_bals(rpc)
  829. elif op == 'do_transfer':
  830. await do_transfer(rpc)
  831. end_silence()
  832. return 'ok'
  833. def token_fund_users(self):
  834. return self.token_transfer_ops(op='do_transfer')
  835. def token_user_bals(self):
  836. return self.token_transfer_ops(op='show_bals')
  837. def token_addrgen(self):
  838. self.addrgen(addrs='11-13')
  839. ok_msg()
  840. return self.addrgen(addrs='21-23')
  841. def token_addrimport_badaddr1(self):
  842. t = self.addrimport(ext='[11-13]{}.regtest.addrs',add_args=['--token=abc'],bad_input=True)
  843. t.expect('could not be resolved')
  844. t.req_exit_val = 2
  845. return t
  846. def token_addrimport_badaddr2(self):
  847. t = self.addrimport(ext='[11-13]{}.regtest.addrs',add_args=['--token='+'00deadbeef'*4],bad_input=True)
  848. t.expect('could not be resolved')
  849. t.req_exit_val = 2
  850. return t
  851. def token_addrimport(self,addr_file,addr_range,expect,extra_args=[]):
  852. token_addr = self.read_from_tmpfile(addr_file).strip()
  853. return self.addrimport(
  854. ext = f'[{addr_range}]{{}}.regtest.addrs',
  855. expect = expect,
  856. add_args = ['--token-addr='+token_addr]+extra_args )
  857. def token_addrimport_addr1(self):
  858. return self.token_addrimport('token_addr1','11-13',expect='3/3')
  859. def token_addrimport_addr2(self):
  860. return self.token_addrimport('token_addr2','21-23',expect='3/3')
  861. def token_addrimport_batch(self):
  862. return self.token_addrimport('token_addr1','11-13',expect='OK: 3',extra_args=['--batch'])
  863. def token_addrimport_sym(self):
  864. return self.addrimport(
  865. ext = '[21-23]{}.regtest.addrs',
  866. expect = '3/3',
  867. add_args = ['--token=MM2'] )
  868. def bal7(self): return self.bal5()
  869. def token_bal1(self): return self.token_bal(n='1')
  870. def token_txcreate(self,args=[],token='',inputs='1',fee='50G'):
  871. return self.txcreate_ui_common(
  872. self.spawn('mmgen-txcreate', self.eth_args + ['--token='+token,'-B','--tx-fee='+fee] + args),
  873. menu = [],
  874. inputs = inputs,
  875. input_sels_prompt = 'to spend from',
  876. add_comment = tx_label_lat_cyr_gr )
  877. def token_txsign(self,ext='',token=''):
  878. return self.txsign(ni=True,ext=ext,add_args=['--token='+token])
  879. def token_txsend(self,ext='',token=''):
  880. return self.txsend(ext=ext,add_args=['--token=mm1'])
  881. def token_txcreate1(self):
  882. return self.token_txcreate(args=['98831F3A:E:12,1.23456'],token='mm1')
  883. def token_txview1_raw(self):
  884. return self.txview(ext_fs='1.23456,50000]{}.regtest.rawtx')
  885. def token_txsign1(self):
  886. return self.token_txsign(ext='1.23456,50000]{}.regtest.rawtx',token='mm1')
  887. def token_txsend1(self):
  888. return self.token_txsend(ext='1.23456,50000]{}.regtest.sigtx',token='mm1')
  889. def token_txview1_sig(self):
  890. return self.txview(ext_fs='1.23456,50000]{}.regtest.sigtx')
  891. def tx_status3(self):
  892. return self.tx_status(
  893. ext='1.23456,50000]{}.regtest.sigtx',
  894. add_args=['--token=mm1'],
  895. expect_str='successfully executed',
  896. expect_str2='has 1 confirmation')
  897. def token_bal2(self):
  898. return self.token_bal(n='2')
  899. def twview(self,args=[],expect_str='',tool_args=[]):
  900. t = self.spawn('mmgen-tool', self.eth_args + args + ['twview'] + tool_args)
  901. if expect_str:
  902. t.expect(expect_str,regex=True)
  903. return t
  904. def token_txcreate2(self):
  905. return self.token_txcreate(args=[burn_addr+','+amt2],token='mm1')
  906. def token_txbump(self):
  907. return self.txbump(ext=amt2+',50000]{}.regtest.rawtx',fee='56G',add_args=['--token=mm1'])
  908. def token_txsign2(self):
  909. return self.token_txsign(ext=amt2+',50000]{}.regtest.rawtx',token='mm1')
  910. def token_txsend2(self):
  911. return self.token_txsend(ext=amt2+',50000]{}.regtest.sigtx',token='mm1')
  912. def token_bal3(self):
  913. return self.token_bal(n='3')
  914. def del_dev_addr(self):
  915. return self.spawn('mmgen-tool', self.eth_args + ['remove_address',dfl_devaddr])
  916. def bal1_getbalance(self):
  917. return self.bal_getbalance('1',etc_adj=True)
  918. def addrimport_token_burn_addr(self):
  919. return self.addrimport_one_addr(addr=burn_addr,extra_args=['--token=mm1'])
  920. def token_bal4(self):
  921. return self.token_bal(n='4')
  922. def token_bal_getbalance(self):
  923. return self.bal_getbalance('2',extra_args=['--token=mm1'])
  924. def txcreate_noamt(self):
  925. return self.txcreate(args=['98831F3A:E:12'],eth_fee_res=True)
  926. def txsign_noamt(self):
  927. return self.txsign(ext='99.99895,50000]{}.regtest.rawtx')
  928. def txsend_noamt(self):
  929. return self.txsend(ext='99.99895,50000]{}.regtest.sigtx')
  930. def bal8(self): return self.bal(n='8')
  931. def token_bal5(self): return self.token_bal(n='5')
  932. def token_txcreate_noamt(self):
  933. return self.token_txcreate(args=['98831F3A:E:13'],token='mm1',inputs='2',fee='51G')
  934. def token_txsign_noamt(self):
  935. return self.token_txsign(ext='1.23456,51000]{}.regtest.rawtx',token='mm1')
  936. def token_txsend_noamt(self):
  937. return self.token_txsend(ext='1.23456,51000]{}.regtest.sigtx',token='mm1')
  938. def bal9(self): return self.bal(n='9')
  939. def token_bal6(self): return self.token_bal(n='6')
  940. def listaddresses(self,args=[],tool_args=['all_labels=1']):
  941. return self.spawn('mmgen-tool', self.eth_args + args + ['listaddresses'] + tool_args)
  942. def listaddresses1(self):
  943. return self.listaddresses()
  944. def listaddresses2(self):
  945. return self.listaddresses(tool_args=['minconf=999999999'])
  946. def listaddresses3(self):
  947. return self.listaddresses(tool_args=['sort=age'])
  948. def listaddresses4(self):
  949. return self.listaddresses(tool_args=['sort=age','showempty=1'])
  950. def token_listaddresses1(self):
  951. return self.listaddresses(args=['--token=mm1'])
  952. def token_listaddresses2(self):
  953. return self.listaddresses(args=['--token=mm1'],tool_args=['showempty=1'])
  954. def twview_cached_balances(self):
  955. return self.twview(args=['--cached-balances'])
  956. def token_twview_cached_balances(self):
  957. return self.twview(args=['--token=mm1','--cached-balances'])
  958. def txcreate_cached_balances(self):
  959. args = ['--tx-fee=20G','--cached-balances','98831F3A:E:3,0.1276']
  960. return self.txcreate(args=args,acct='2')
  961. def token_txcreate_cached_balances(self):
  962. args=['--cached-balances','--tx-fee=12G','98831F3A:E:12,1.2789']
  963. return self.token_txcreate(args=args,token='mm1')
  964. def txdo_cached_balances(self,
  965. acct = '2',
  966. fee_res_data = ('0.00105','50'),
  967. add_args = ['98831F3A:E:3,0.4321']):
  968. args = ['--tx-fee=20G','--cached-balances'] + add_args + [dfl_words_file]
  969. t = self.txcreate(args=args,acct=acct,caller='txdo',fee_res_data=fee_res_data,no_read=True)
  970. self._do_confirm_send(t,quiet=not g.debug,sure=False)
  971. return t
  972. def txcreate_refresh_balances(self,
  973. bals=['2','3'],
  974. args=['-B','--cached-balances','-i'],
  975. total=vbal5,
  976. adj_total=True,
  977. total_coin=None ):
  978. if total_coin is None:
  979. total_coin = self.proto.coin
  980. if self.proto.coin == 'ETC' and adj_total:
  981. total = str(Decimal(total) + self.bal_corr)
  982. t = self.spawn('mmgen-txcreate', self.eth_args + args)
  983. for n in bals:
  984. t.expect('[R]efresh balance:\b','R')
  985. t.expect(' main menu): ',n+'\n')
  986. t.expect('Is this what you want? (y/N): ','y')
  987. t.expect('[R]efresh balance:\b','q')
  988. t.expect(rf'Total unspent:.*\D{total}\D.*{total_coin}',regex=True)
  989. return t
  990. def bal10(self): return self.bal(n='10')
  991. def token_txdo_cached_balances(self):
  992. return self.txdo_cached_balances(
  993. acct='1',
  994. fee_res_data=('0.0026','50'),
  995. add_args=['--token=mm1','98831F3A:E:12,43.21'])
  996. def token_txcreate_refresh_balances(self):
  997. return self.txcreate_refresh_balances(
  998. bals=['1','2'],
  999. args=['--token=mm1','-B','--cached-balances','-i'],
  1000. total='1000',adj_total=False,total_coin='MM1')
  1001. def token_bal7(self): return self.token_bal(n='7')
  1002. def twview1(self):
  1003. return self.twview()
  1004. def twview2(self):
  1005. return self.twview(tool_args=['wide=1'])
  1006. def twview3(self):
  1007. return self.twview(tool_args=['wide=1','sort=age'])
  1008. def twview4(self):
  1009. return self.twview(tool_args=['wide=1','minconf=999999999'])
  1010. def twview5(self):
  1011. return self.twview(tool_args=['wide=1','minconf=0'])
  1012. def token_twview1(self):
  1013. return self.twview(args=['--token=mm1'])
  1014. def token_twview2(self):
  1015. return self.twview(args=['--token=mm1'],tool_args=['wide=1'])
  1016. def token_twview3(self):
  1017. return self.twview(args=['--token=mm1'],tool_args=['wide=1','sort=age'])
  1018. def edit_label(self,out_num,args=[],action='l',label_text=None):
  1019. t = self.spawn('mmgen-txcreate', self.eth_args + args + ['-B','-i'])
  1020. p1,p2 = ('efresh balance:\b','return to main menu): ')
  1021. p3,r3 = (p2,label_text+'\n') if label_text is not None else ('(y/N): ','y')
  1022. p4,r4 = (('(y/N): ',),('y',)) if label_text == self.erase_input else ((),())
  1023. for p,r in zip((p1,p1,p2,p3)+p4,('M',action,out_num+'\n',r3)+r4):
  1024. t.expect(p,r)
  1025. m = ( 'Account #{} removed' if action == 'D' else
  1026. 'Label added to account #{}' if label_text and label_text != self.erase_input else
  1027. 'Label removed from account #{}' )
  1028. t.expect(m.format(out_num))
  1029. for p,r in zip((p1,p1),('M','q')):
  1030. t.expect(p,r)
  1031. t.expect('Total unspent:')
  1032. return t
  1033. def edit_label1(self):
  1034. return self.edit_label(out_num=del_addrs[0],label_text=tw_label_zh)
  1035. def edit_label2(self):
  1036. return self.edit_label(out_num=del_addrs[1],label_text=tw_label_lat_cyr_gr)
  1037. def edit_label3(self):
  1038. return self.edit_label(out_num=del_addrs[0],label_text=self.erase_input)
  1039. def token_edit_label1(self):
  1040. return self.edit_label(out_num='1',label_text='Token label #1',args=['--token=mm1'])
  1041. def remove_addr1(self):
  1042. return self.edit_label(out_num=del_addrs[0],action='D')
  1043. def remove_addr2(self):
  1044. return self.edit_label(out_num=del_addrs[1],action='D')
  1045. def token_remove_addr1(self):
  1046. return self.edit_label(out_num=del_addrs[0],args=['--token=mm1'],action='D')
  1047. def token_remove_addr2(self):
  1048. return self.edit_label(out_num=del_addrs[1],args=['--token=mm1'],action='D')
  1049. def stop(self):
  1050. self.spawn('',msg_only=True)
  1051. if not opt.no_daemon_stop:
  1052. if not stop_test_daemons(self.proto.coin+'_rt'):
  1053. return False
  1054. set_vt100()
  1055. return 'ok'