ts_ethdev.py 42 KB

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