CoinDaemon: add subclasses, support multiple chains for ETH
This commit is contained in:
parent
0ff49400b6
commit
8bb6c40ec6
5 changed files with 126 additions and 149 deletions
257
mmgen/daemon.py
257
mmgen/daemon.py
|
|
@ -26,6 +26,9 @@ from collections import namedtuple
|
|||
from .exception import *
|
||||
from .common import *
|
||||
|
||||
_dd = namedtuple('daemon_data',['coind_name','coind_version','coind_version_str']) # latest tested version
|
||||
_pd = namedtuple('rpc_ports_data',['mainnet','testnet','regtest'])
|
||||
|
||||
class Daemon(MMGenObject):
|
||||
|
||||
desc = 'daemon'
|
||||
|
|
@ -181,7 +184,7 @@ class Daemon(MMGenObject):
|
|||
time.sleep(0.2)
|
||||
else:
|
||||
m = 'Wait for state {!r} timeout exceeded for daemon {} {} (port {})'
|
||||
die(2,m.format(req_state,self.daemon_id.upper(),self.network,self.rpc_port))
|
||||
die(2,m.format(req_state,self.coin,self.network,self.rpc_port))
|
||||
|
||||
@classmethod
|
||||
def check_implement(cls):
|
||||
|
|
@ -223,7 +226,7 @@ class MoneroWalletDaemon(Daemon):
|
|||
|
||||
desc = 'RPC daemon'
|
||||
net_desc = 'Monero wallet'
|
||||
daemon_id = 'xmr'
|
||||
coin = 'XMR'
|
||||
network = 'wallet RPC'
|
||||
new_console_mswin = True
|
||||
ps_pid_mswin = True
|
||||
|
|
@ -314,6 +317,7 @@ class MoneroWalletDaemon(Daemon):
|
|||
return ['kill','-Wf',self.pid] if self.platform == 'win' else ['kill',self.pid]
|
||||
|
||||
class CoinDaemon(Daemon):
|
||||
networks = ('mainnet','testnet','regtest')
|
||||
cfg_file_hdr = ''
|
||||
subclasses_must_implement = ('state','stop_cmd')
|
||||
avail_flags = ('keep_cfg_file',)
|
||||
|
|
@ -321,109 +325,24 @@ class CoinDaemon(Daemon):
|
|||
datadir_is_subdir = False
|
||||
data_subdir = ''
|
||||
|
||||
network_ids = (
|
||||
'btc','btc_tn','btc_rt',
|
||||
'bch','bch_tn','bch_rt',
|
||||
'ltc','ltc_tn','ltc_rt',
|
||||
'xmr','xmr_tn',
|
||||
'eth','etc'
|
||||
)
|
||||
|
||||
cd = namedtuple('daemon_data', [
|
||||
'id',
|
||||
'coin',
|
||||
'cls_pfx',
|
||||
'coind_name',
|
||||
'coind_version', 'coind_version_str', # latest tested version
|
||||
'exec_fn',
|
||||
'cli_fn',
|
||||
'cfg_file',
|
||||
'testnet_dir',
|
||||
'dfl_rpc',
|
||||
'dfl_rpc_tn',
|
||||
'dfl_rpc_rt' ])
|
||||
|
||||
daemon_ids = {
|
||||
'btc': cd(
|
||||
'bitcoin_core',
|
||||
'Bitcoin',
|
||||
'Bitcoin',
|
||||
'Bitcoin Core', 210100, '0.21.1',
|
||||
'bitcoind',
|
||||
'bitcoin-cli',
|
||||
'bitcoin.conf',
|
||||
'testnet3',
|
||||
8332, 18332, 18444),
|
||||
'bch': cd(
|
||||
'bitcoin_cash_node',
|
||||
'BitcoinCashNode',
|
||||
'Bitcoin',
|
||||
'Bitcoin Cash Node', 23000000, '23.0.0',
|
||||
'bitcoind-bchn',
|
||||
'bitcoin-cli-bchn',
|
||||
'bitcoin.conf',
|
||||
'testnet3',
|
||||
8442, 18442, 18553), # for BCH we use non-standard RPC ports
|
||||
'ltc': cd(
|
||||
'litecoin_core',
|
||||
'Litecoin',
|
||||
'Bitcoin',
|
||||
'Litecoin Core', 180100, '0.18.1',
|
||||
'litecoind',
|
||||
'litecoin-cli',
|
||||
'litecoin.conf',
|
||||
'testnet4',
|
||||
9332, 19332, 19444),
|
||||
'xmr': cd(
|
||||
'monerod',
|
||||
'Monero',
|
||||
'Monero',
|
||||
'Monero', 'N/A', 'N/A',
|
||||
'monerod',
|
||||
'monerod',
|
||||
'bitmonero.conf',
|
||||
'stagenet',
|
||||
18081, 38081, None),
|
||||
'eth': cd(
|
||||
'openethereum',
|
||||
'Ethereum',
|
||||
'Ethereum',
|
||||
'OpenEthereum', 3003000, '3.3.0',
|
||||
'openethereum',
|
||||
'openethereum',
|
||||
'parity.conf',
|
||||
None,
|
||||
8545, 8545, 8545),
|
||||
'etc': cd(
|
||||
'openethereum',
|
||||
'Ethereum Classic',
|
||||
'Ethereum',
|
||||
'OpenEthereum', 3003000, '3.3.0',
|
||||
'openethereum',
|
||||
'openethereum',
|
||||
'parity.conf',
|
||||
None,
|
||||
8545, 8545, 8545)
|
||||
_cd = namedtuple('coins_data',['coin_name','daemon_ids'])
|
||||
coins = {
|
||||
'BTC': _cd('Bitcoin', ['bitcoin_core']),
|
||||
'BCH': _cd('Bitcoin Cash Node', ['bitcoin_cash_node']),
|
||||
'LTC': _cd('Litecoin', ['litecoin_core']),
|
||||
'XMR': _cd('Monero', ['monero']),
|
||||
'ETH': _cd('Ethereum', ['openethereum']),
|
||||
'ETC': _cd('Ethereum Classic', ['openethereum_etc']),
|
||||
}
|
||||
|
||||
dfl_datadirs = {
|
||||
'linux': {
|
||||
'btc': [g.home_dir,'.bitcoin'],
|
||||
'bch': [g.home_dir,'.bitcoin-bchn'],
|
||||
'ltc': [g.home_dir,'.litecoin'],
|
||||
'xmr': [g.home_dir,'.bitmonero'],
|
||||
'eth': [g.home_dir,'.local','share','io.parity.ethereum'],
|
||||
'etc': [g.home_dir,'.local','share','io.parity.ethereum'],
|
||||
},
|
||||
'win': {
|
||||
'btc': [os.getenv('APPDATA'),'Bitcoin'],
|
||||
'bch': [os.getenv('APPDATA'),'Bitcoin_ABC'],
|
||||
'ltc': [os.getenv('APPDATA'),'Litecoin'],
|
||||
'xmr': ['/','c','ProgramData','bitmonero'],
|
||||
'eth': [g.home_dir,'.local','share','io.parity.ethereum'],
|
||||
'etc': [g.home_dir,'.local','share','io.parity.ethereum'],
|
||||
},
|
||||
}
|
||||
@classmethod
|
||||
def get_network_ids(cls): # FIXME: gets IDs for _default_ daemon only
|
||||
from .protocol import CoinProtocol
|
||||
def gen():
|
||||
for coin,data in cls.coins.items():
|
||||
for network in globals()[data.daemon_ids[0]+'_daemon'].networks:
|
||||
yield CoinProtocol.Base.create_network_id(coin,network)
|
||||
return list(gen())
|
||||
|
||||
def __new__(cls,
|
||||
network_id = None,
|
||||
|
|
@ -440,17 +359,23 @@ class CoinDaemon(Daemon):
|
|||
if proto:
|
||||
network_id = proto.network_id
|
||||
network = proto.network
|
||||
daemon_id = proto.coin.lower()
|
||||
coin = proto.coin
|
||||
else:
|
||||
network_id = network_id.lower()
|
||||
assert network_id in cls.network_ids, f'{network_id!r}: invalid network ID'
|
||||
from .protocol import CoinProtocol
|
||||
daemon_id,network = CoinProtocol.Base.parse_network_id(network_id)
|
||||
from .protocol import CoinProtocol,init_proto
|
||||
proto = init_proto(network_id=network_id)
|
||||
coin,network = CoinProtocol.Base.parse_network_id(network_id)
|
||||
coin = coin.upper()
|
||||
|
||||
me = Daemon.__new__(globals()[cls.daemon_ids[daemon_id].cls_pfx+'Daemon'])
|
||||
me.network_id = network_id
|
||||
daemon_id = cls.coins[coin].daemon_ids[0]
|
||||
me = Daemon.__new__(globals()[daemon_id + '_daemon'])
|
||||
assert network in me.networks, f'{network!r}: unsupported network for daemon {daemon_id}'
|
||||
me.network = network
|
||||
me.daemon_id = daemon_id
|
||||
me.network_id = network_id
|
||||
me.coin = coin
|
||||
me.coin_name = cls.coins[coin].coin_name
|
||||
me.id = daemon_id
|
||||
me.proto = proto
|
||||
|
||||
return me
|
||||
|
||||
|
|
@ -485,25 +410,23 @@ class CoinDaemon(Daemon):
|
|||
for flag in flags:
|
||||
self.add_flag(flag)
|
||||
|
||||
for k in self.daemon_ids[self.daemon_id]._fields:
|
||||
setattr(self,k,getattr(self.daemon_ids[self.daemon_id],k))
|
||||
for k,v in self.daemon_data._asdict().items():
|
||||
setattr(self,k,v)
|
||||
|
||||
if self.network == 'regtest':
|
||||
self.desc = 'regtest daemon'
|
||||
if self.network == 'regtest' and isinstance(self,bitcoin_core_daemon):
|
||||
if test_suite:
|
||||
rel_datadir = os.path.join(
|
||||
'test',
|
||||
'data_dir{}'.format('-α' if g.debug_utf8 else ''),
|
||||
'regtest',
|
||||
self.daemon_id )
|
||||
self.coin.lower() )
|
||||
else:
|
||||
dfl_datadir = os.path.join(g.data_dir_root,'regtest',self.daemon_id)
|
||||
dfl_datadir = os.path.join(g.data_dir_root,'regtest',self.coin.lower())
|
||||
elif test_suite:
|
||||
self.desc = 'test suite daemon'
|
||||
rel_datadir = os.path.join('test','daemons',self.daemon_id)
|
||||
rel_datadir = os.path.join('test','daemons',self.coin.lower())
|
||||
else:
|
||||
dfl_datadir = os.path.join( *self.dfl_datadirs[g.platform][self.daemon_id] )
|
||||
|
||||
dfl_datadir = os.path.join(*self.datadirs[g.platform])
|
||||
|
||||
if test_suite:
|
||||
dfl_datadir = os.path.join(os.getcwd(),rel_datadir)
|
||||
|
|
@ -519,17 +442,13 @@ class CoinDaemon(Daemon):
|
|||
self.datadir = os.path.join(self.datadir,self.testnet_dir)
|
||||
|
||||
self.port_shift = (1237 if test_suite else 0) + (port_shift or 0)
|
||||
self.rpc_port = {
|
||||
'mainnet': self.dfl_rpc,
|
||||
'testnet': self.dfl_rpc_tn,
|
||||
'regtest': self.dfl_rpc_rt,
|
||||
}[self.network] + self.port_shift
|
||||
self.rpc_port = getattr(self.rpc_ports,self.network) + self.port_shift
|
||||
|
||||
if g.rpc_port: # user-set global overrides everything else
|
||||
self.rpc_port = g.rpc_port
|
||||
|
||||
self.pidfile = '{}/{}-daemon-{}.pid'.format(self.datadir,self.network,self.rpc_port)
|
||||
self.net_desc = '{} {}'.format(self.coin,self.network)
|
||||
self.net_desc = '{} {}'.format(self.coin_name,self.network)
|
||||
self.subclass_init()
|
||||
|
||||
@property
|
||||
|
|
@ -544,13 +463,23 @@ class CoinDaemon(Daemon):
|
|||
+ self.shared_args
|
||||
+ list(cmds) )
|
||||
|
||||
class BitcoinDaemon(CoinDaemon):
|
||||
cfg_file_hdr = '# BitcoinDaemon config file\n'
|
||||
class bitcoin_core_daemon(CoinDaemon):
|
||||
daemon_data = _dd('Bitcoin Core', 210100, '0.21.1')
|
||||
exec_fn = 'bitcoind'
|
||||
cli_fn = 'bitcoin-cli'
|
||||
testnet_dir = 'testnet3'
|
||||
cfg_file_hdr = '# BitcoinCoreDaemon config file\n'
|
||||
tracking_wallet_name = 'mmgen-tracking-wallet'
|
||||
rpc_ports = _pd(8332, 18332, 18444)
|
||||
cfg_file = 'bitcoin.conf'
|
||||
datadirs = {
|
||||
'linux': [g.home_dir,'.bitcoin'],
|
||||
'win': [os.getenv('APPDATA'),'Bitcoin']
|
||||
}
|
||||
|
||||
def subclass_init(self):
|
||||
|
||||
if self.platform == 'win' and self.daemon_id == 'bch':
|
||||
if self.platform == 'win' and self.coin == 'BCH':
|
||||
self.use_pidfile = False
|
||||
|
||||
from .regtest import MMGenRegtest
|
||||
|
|
@ -570,10 +499,10 @@ class BitcoinDaemon(CoinDaemon):
|
|||
[f'--rpcbind=127.0.0.1:{self.rpc_port}'],
|
||||
['--pid='+self.pidfile, self.use_pidfile],
|
||||
['--daemon', self.platform == 'linux' and not 'no_daemonize' in self.opts],
|
||||
['--fallbackfee=0.0002', self.daemon_id == 'btc' and self.network == 'regtest'],
|
||||
['--usecashaddr=0', self.daemon_id == 'bch'],
|
||||
['--mempoolreplacement=1', self.daemon_id == 'ltc'],
|
||||
['--txindex=1', self.daemon_id == 'ltc'],
|
||||
['--fallbackfee=0.0002', self.coin == 'BTC' and self.network == 'regtest'],
|
||||
['--usecashaddr=0', self.coin == 'BCH'],
|
||||
['--mempoolreplacement=1', self.coin == 'LTC'],
|
||||
['--txindex=1', self.coin == 'LTC'],
|
||||
)
|
||||
|
||||
if self.network == 'testnet':
|
||||
|
|
@ -602,16 +531,47 @@ class BitcoinDaemon(CoinDaemon):
|
|||
def stop_cmd(self):
|
||||
return self.cli_cmd('stop')
|
||||
|
||||
class MoneroDaemon(CoinDaemon):
|
||||
class bitcoin_cash_node_daemon(bitcoin_core_daemon):
|
||||
daemon_data = _dd('Bitcoin Cash Node', 23000000, '23.0.0')
|
||||
exec_fn = 'bitcoind-bchn'
|
||||
cli_fn = 'bitcoin-cli-bchn'
|
||||
rpc_ports = _pd(8442, 18442, 18553) # use non-standard ports
|
||||
datadirs = {
|
||||
'linux': [g.home_dir,'.bitcoin-bchn'],
|
||||
'win': [os.getenv('APPDATA'),'Bitcoin_ABC']
|
||||
}
|
||||
|
||||
class litecoin_core_daemon(bitcoin_core_daemon):
|
||||
daemon_data = _dd('Litecoin Core', 180100, '0.18.1')
|
||||
exec_fn = 'litecoind'
|
||||
cli_fn = 'litecoin-cli'
|
||||
testnet_dir = 'testnet4'
|
||||
rpc_ports = _pd(9332, 19332, 19444)
|
||||
cfg_file = 'litecoin.conf'
|
||||
datadirs = {
|
||||
'linux': [g.home_dir,'.litecoin'],
|
||||
'win': [os.getenv('APPDATA'),'Litecoin']
|
||||
}
|
||||
|
||||
class monero_daemon(CoinDaemon):
|
||||
daemon_data = _dd('Monero', 'N/A', 'N/A')
|
||||
networks = ('mainnet','testnet')
|
||||
exec_fn = 'monerod'
|
||||
testnet_dir = 'stagenet'
|
||||
ps_pid_mswin = True
|
||||
new_console_mswin = True
|
||||
host = 'localhost' # FIXME
|
||||
rpc_ports = _pd(18081, 38081, None)
|
||||
cfg_file = 'bitmonero.conf'
|
||||
datadir_is_subdir = True
|
||||
datadirs = {
|
||||
'linux': [g.home_dir,'.bitmonero'],
|
||||
'win': ['/','c','ProgramData','bitmonero']
|
||||
}
|
||||
|
||||
def subclass_init(self):
|
||||
if self.network == 'testnet':
|
||||
self.net_desc = f'{self.coin} stagenet'
|
||||
self.net_desc = f'{self.coin_name} stagenet'
|
||||
|
||||
self.p2p_port = self.rpc_port - 1
|
||||
self.zmq_port = self.rpc_port + 1
|
||||
|
|
@ -656,28 +616,40 @@ class MoneroDaemon(CoinDaemon):
|
|||
else:
|
||||
return [self.exec_fn] + self.shared_args + ['exit']
|
||||
|
||||
class EthereumDaemon(CoinDaemon):
|
||||
|
||||
class openethereum_daemon(CoinDaemon):
|
||||
daemon_data = _dd('OpenEthereum', 3003000, '3.3.0')
|
||||
exec_fn = 'openethereum'
|
||||
ps_pid_mswin = True
|
||||
ports_shift = { 'mainnet': 0, 'testnet': 20, 'regtest': 40 }
|
||||
rpc_ports = _pd(*[8545 + n for n in ports_shift.values()]) # testnet and regtest are non-standard
|
||||
cfg_file = 'parity.conf'
|
||||
datadirs = {
|
||||
'linux': [g.home_dir,'.local','share','io.parity.ethereum'],
|
||||
'win': [g.home_dir,'.local','share','io.parity.ethereum'] # FIXME
|
||||
}
|
||||
testnet_dir = 'testnet' # FIXME
|
||||
|
||||
def subclass_init(self):
|
||||
# defaults:
|
||||
# linux: $HOME/.local/share/io.parity.ethereum/chains/DevelopmentChain
|
||||
# win: $LOCALAPPDATA/Parity/Ethereum/chains/DevelopmentChain
|
||||
|
||||
base_path = os.path.join(self.datadir,'devchain')
|
||||
base_path = os.path.join(self.datadir,self.proto.chain_name)
|
||||
shutil.rmtree(base_path,ignore_errors=True)
|
||||
|
||||
ps = self.port_shift + self.ports_shift[self.network]
|
||||
ld = self.platform == 'linux' and not 'no_daemonize' in self.opts
|
||||
|
||||
self.coind_args = list_gen(
|
||||
['--no-ws'],
|
||||
['--no-ipc'],
|
||||
['--no-secretstore'],
|
||||
[f'--ports-shift={self.port_shift}'],
|
||||
[f'--ports-shift={ps}'],
|
||||
[f'--base-path={base_path}'],
|
||||
['--config=dev'],
|
||||
['--mode=offline',self.test_suite],
|
||||
['--log-file='+os.path.join(self.datadir,'openethereum.log')],
|
||||
[f'--chain={self.proto.chain_name}', self.network!='regtest'],
|
||||
[f'--config=dev', self.network=='regtest'], # no presets for mainnet or testnet
|
||||
['--mode=offline', self.test_suite or self.network=='regtest'],
|
||||
['--log-file='+os.path.join(self.datadir, f'openethereum-{self.network}.log')],
|
||||
['daemon', ld],
|
||||
[self.pidfile, ld],
|
||||
)
|
||||
|
|
@ -700,4 +672,7 @@ class EthereumDaemon(CoinDaemon):
|
|||
def stop_cmd(self):
|
||||
return ['kill','-Wf',self.pid] if self.platform == 'win' else ['kill',self.pid]
|
||||
|
||||
class openethereum_etc_daemon(openethereum_daemon):
|
||||
rpc_ports = _pd(*[8645 + n for n in openethereum_daemon.ports_shift.values()])
|
||||
|
||||
CoinDaemon.check_implement()
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ class MMGenRegtest(MMGenObject):
|
|||
msg(fs.format('Total balance:',sum(v for k,v in bal.items())))
|
||||
|
||||
async def send(self,addr,amt):
|
||||
gmsg('Sending {} miner {} to address {}'.format(amt,self.d.daemon_id.upper(),addr))
|
||||
gmsg('Sending {} miner {} to address {}'.format(amt,self.d.coin,addr))
|
||||
cp = await self.rpc_call('sendtoaddress',addr,str(amt),wallet='miner')
|
||||
await self.generate(1)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from include.tests_header import repo_root
|
|||
from mmgen.common import *
|
||||
from mmgen.daemon import CoinDaemon
|
||||
|
||||
network_ids = CoinDaemon.network_ids
|
||||
network_ids = CoinDaemon.get_network_ids()
|
||||
|
||||
action = g.prog_name.split('-')[0]
|
||||
|
||||
|
|
|
|||
|
|
@ -332,7 +332,8 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
|
|||
for d in ('mm1','mm2'):
|
||||
copytree(os.path.join(srcdir,d),os.path.join(self.tmpdir,d))
|
||||
if not opt.no_daemon_autostart:
|
||||
start_test_daemons(self.proto.coin,remove_datadir=True)
|
||||
if not start_test_daemons(self.proto.coin+'_rt',remove_datadir=True):
|
||||
return False
|
||||
return 'ok'
|
||||
|
||||
def wallet_upgrade(self,src_file):
|
||||
|
|
@ -995,5 +996,6 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
|
|||
def stop(self):
|
||||
self.spawn('',msg_only=True)
|
||||
if not opt.no_daemon_stop:
|
||||
stop_test_daemons(self.proto.coin)
|
||||
if not stop_test_daemons(self.proto.coin+'_rt'):
|
||||
return False
|
||||
return 'ok'
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from mmgen.exception import *
|
|||
|
||||
from mmgen.protocol import init_proto
|
||||
from mmgen.rpc import rpc_init,MoneroWalletRPCClient
|
||||
from mmgen.daemon import CoinDaemon,MoneroWalletDaemon
|
||||
from mmgen.daemon import CoinDaemon,MoneroWalletDaemon,bitcoin_core_daemon
|
||||
|
||||
def auth_test(proto,d):
|
||||
if g.platform != 'win':
|
||||
|
|
@ -62,9 +62,9 @@ class init_test:
|
|||
|
||||
def run_test(coin,auth):
|
||||
|
||||
proto = init_proto(coin,network=('mainnet','regtest')[coin=='eth']) # FIXME CoinDaemon's network handling broken
|
||||
proto = init_proto(coin)
|
||||
|
||||
d = CoinDaemon(network_id=coin,test_suite=True)
|
||||
d = CoinDaemon(proto=proto,test_suite=True)
|
||||
|
||||
if not opt.no_daemon_stop:
|
||||
d.stop()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue