Browse Source

new MoneroWalletDaemon, MoneroWalletRPCConnection classes

The MMGen Project 5 years ago
parent
commit
779f522d16
5 changed files with 106 additions and 0 deletions
  1. 9 0
      data_files/mmgen.cfg
  2. 55 0
      mmgen/daemon.py
  3. 5 0
      mmgen/globalvars.py
  4. 3 0
      mmgen/opts.py
  5. 34 0
      mmgen/rpc.py

+ 9 - 0
data_files/mmgen.cfg

@@ -84,6 +84,15 @@
 # Set the Ethereum testnet name
 # eth_testnet_chain_name kovan
 
+# Set the Monero wallet RPC host:
+# monero_wallet_rpc_host localhost
+
+# Set the Monero wallet RPC username:
+# monero_wallet_rpc_user monero
+
+# Set the Monero wallet RPC password to something secure:
+# monero_wallet_rpc_password passw0rd
+
 #####################################################################
 # The following options are probably of interest only to developers #
 #####################################################################

+ 55 - 0
mmgen/daemon.py

@@ -137,6 +137,61 @@ class Daemon(MMGenObject):
 			for k in cls.subclasses_must_implement:
 				assert k in subcls.__dict__, m.format(k,subcls.__name__)
 
+class MoneroWalletDaemon(Daemon):
+
+	desc = 'RPC daemon'
+	net_desc = 'Monero wallet'
+	daemon_id = 'xmr'
+	network = 'wallet RPC'
+
+	def __init__(self,wallet_dir,test_suite=False):
+		self.platform = g.platform
+		self.wallet_dir = wallet_dir
+		if test_suite:
+			self.datadir = os.path.join('test','monero-wallet-rpc')
+			self.rpc_port = 13142
+		else:
+			self.datadir = 'monero-wallet-rpc'
+			self.rpc_port = 13131
+		self.daemon_port = CoinDaemon('xmr',test_suite=test_suite).rpc_port
+		self.pidfile = os.path.join(self.datadir,'monero-wallet-rpc.pid')
+		self.logfile = os.path.join(self.datadir,'monero-wallet-rpc.log')
+
+		if not g.monero_wallet_rpc_password:
+			die(1,
+				'You must set your Monero wallet RPC password.\n' +
+				'This can be done on the command line, with the --monero-wallet-rpc-password\n' +
+				"option (insecure, not recommended), or by setting 'monero_wallet_rpc_password'\n" +
+				"in the MMGen config file." )
+
+	@property
+	def start_cmd(self):
+		return ['monero-wallet-rpc',
+				'--daemon-port={}'.format(self.daemon_port),
+				'--rpc-bind-port={}'.format(self.rpc_port),
+				'--wallet-dir='+self.wallet_dir,
+				'--detach',
+				'--log-file='+self.logfile,
+				'--rpc-login={}:{}'.format(g.monero_wallet_rpc_user,g.monero_wallet_rpc_password),
+				'--pidfile='+self.pidfile]
+
+	@property
+	def state(self):
+		from mmgen.rpc import MoneroWalletRPCConnection
+		try:
+			MoneroWalletRPCConnection(
+				g.monero_wallet_rpc_host,
+				self.rpc_port,
+				g.monero_wallet_rpc_user,
+				g.monero_wallet_rpc_password).get_version()
+			return 'ready'
+		except:
+			return 'stopped'
+
+	@property
+	def stop_cmd(self):
+		return ['kill','-Wf',self.pid] if self.platform == 'win' else ['kill',self.pid]
+
 class CoinDaemon(Daemon):
 	cfg_file_hdr = ''
 	subclasses_must_implement = ('state','stop_cmd')

+ 5 - 0
mmgen/globalvars.py

@@ -92,6 +92,9 @@ class g(object):
 	rpc_port             = 0
 	rpc_user             = ''
 	rpc_password         = ''
+	monero_wallet_rpc_host = 'localhost'
+	monero_wallet_rpc_user = 'monero'
+	monero_wallet_rpc_password = ''
 	rpc_fail_on_command  = ''
 	rpch                 = None # global RPC handle
 	use_cached_balances  = False
@@ -141,6 +144,7 @@ class g(object):
 	# 'long' opts - opt sets global var
 	common_opts = (
 		'color','no_license','rpc_host','rpc_port','testnet','rpc_user','rpc_password',
+		'monero_wallet_rpc_host','monero_wallet_rpc_user','monero_wallet_rpc_password',
 		'daemon_data_dir','force_256_color','regtest','coin','bob','alice',
 		'accept_defaults','token'
 	)
@@ -162,6 +166,7 @@ class g(object):
 	cfg_file_opts = (
 		'color','debug','hash_preset','http_timeout','no_license','rpc_host','rpc_port',
 		'quiet','tx_fee_adj','usr_randchars','testnet','rpc_user','rpc_password',
+		'monero_wallet_rpc_host','monero_wallet_rpc_user','monero_wallet_rpc_password',
 		'daemon_data_dir','force_256_color','regtest','subseeds',
 		'btc_max_tx_fee','ltc_max_tx_fee','bch_max_tx_fee','eth_max_tx_fee',
 		'eth_mainnet_chain_name','eth_testnet_chain_name',

+ 3 - 0
mmgen/opts.py

@@ -205,6 +205,9 @@ common_opts_data = {
 --, --rpc-port=p          Communicate with {dn} listening on port 'p'
 --, --rpc-user=user       Override 'rpcuser' in {pn}.conf
 --, --rpc-password=pass   Override 'rpcpassword' in {pn}.conf
+--, --monero-wallet-rpc-host=host Override 'monero_wallet_rpc_host' in mmgen.cfg
+--, --monero-wallet-rpc-user=user Override 'monero_wallet_rpc_user' in mmgen.cfg
+--, --monero-wallet-rpc-password=pass Override 'monero_wallet_rpc_password' in mmgen.cfg
 --, --regtest=0|1         Disable or enable regtest mode
 --, --testnet=0|1         Disable or enable testnet
 --, --skip-cfg-file       Skip reading the configuration file

+ 34 - 0
mmgen/rpc.py

@@ -261,6 +261,40 @@ class EthereumRPCConnection(RPCConnection):
 		'parity_versionInfo',
 	)
 
+class MoneroWalletRPCConnection(RPCConnection):
+	rpcmethods = (
+		'get_version',
+		'get_height',    # sync height of the open wallet
+		'get_balance',   # { "account_index":0,"address_indices":[0,1] }
+		'create_wallet', # { "filename":"name","password":"passw0rd","language":"English" }
+		'open_wallet',   # { "filename":"name","password":"passw0rd" }
+		'close_wallet',
+		'restore_deterministic_wallet', # name,password,seed (restore_height,language,seed_offset,autosave_current)
+		'refresh',       # {"start_height":100000}
+	)
+
+	def request(self,cmd,*args,**kwargs):
+		from subprocess import run,PIPE
+		data = {
+			'jsonrpc': '2.0',
+			'id': '0',
+			'method': cmd,
+			'params': kwargs,
+		}
+		exec_cmd = [
+			'curl', '--silent','--insecure', '--request', 'POST',
+			'--digest', '--user', '{}:{}'.format(g.monero_wallet_rpc_user,g.monero_wallet_rpc_password),
+			'--header', 'Content-Type: application/json',
+			'--data', json.dumps(data),
+			'https://{}:{}/json_rpc'.format(self.host,self.port) ]
+
+		cp = run(exec_cmd,stdout=PIPE,check=True)
+
+		res = json.loads(cp.stdout)
+		if 'error' in res:
+			raise RPCFailure(repr(res['error']))
+		return(res['result'])
+
 def rpc_error(ret):
 	return type(ret) is tuple and ret and ret[0] == 'rpcfail'