Basic Ethereum RPC support (Parity)

This commit is contained in:
The MMGen Project 2018-05-24 09:08:42 +00:00
commit 7ad2fdf66c
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
3 changed files with 77 additions and 34 deletions

View file

@ -294,6 +294,10 @@ class EthereumProtocol(DummyWIF,BitcoinProtocolAddrgen):
base_coin = 'ETH'
pubkey_type = 'std' # required by DummyWIF
daemon_name = 'parity'
rpc_port = 8545
mmcaps = ('key','addr','rpc')
@classmethod
def verify_addr(cls,addr,hex_width,return_dict=False):
from mmgen.util import is_hex_str_lc
@ -310,7 +314,8 @@ class EthereumProtocol(DummyWIF,BitcoinProtocolAddrgen):
class EthereumTestnetProtocol(EthereumProtocol): pass
class EthereumClassicProtocol(EthereumProtocol):
name = 'ethereum_classic'
name = 'ethereum_classic'
mmcaps = ('key','addr')
class EthereumClassicTestnetProtocol(EthereumClassicProtocol): pass
class ZcashProtocol(BitcoinProtocolAddrgen):
@ -400,7 +405,7 @@ class CoinProtocol(MMGenObject):
'btc': (BitcoinProtocol,BitcoinTestnetProtocol,None),
'bch': (BitcoinCashProtocol,BitcoinCashTestnetProtocol,None),
'ltc': (LitecoinProtocol,LitecoinTestnetProtocol,None),
'eth': (EthereumProtocol,EthereumTestnetProtocol,2),
'eth': (EthereumProtocol,EthereumTestnetProtocol,None),
'etc': (EthereumClassicProtocol,EthereumClassicTestnetProtocol,2),
'zec': (ZcashProtocol,ZcashTestnetProtocol,2),
'xmr': (MoneroProtocol,MoneroTestnetProtocol,None)

View file

@ -32,9 +32,9 @@ class RPCFailure(Exception): pass
class CoinDaemonRPCConnection(object):
def __init__(self,host=None,port=None,user=None,passwd=None,auth_cookie=None):
def __init__(self,host=None,port=None,user=None,passwd=None,auth_cookie=None,auth=True):
dmsg_rpc('=== CoinDaemonRPCConnection.__init__() debug ===')
dmsg_rpc('=== {}.__init__() debug ==='.format(type(self).__name__))
dmsg_rpc(' host [{}] port [{}] user [{}] passwd [{}] auth_cookie [{}]\n'.format(
host,port,user,passwd,auth_cookie))
@ -44,7 +44,9 @@ class CoinDaemonRPCConnection(object):
except:
die(1,'Unable to connect to {}:{}'.format(host,port))
if user and passwd:
if not auth:
self.auth_str = ''
elif user and passwd:
self.auth_str = '{}:{}'.format(user,passwd)
elif auth_cookie:
self.auth_str = auth_cookie
@ -85,9 +87,9 @@ class CoinDaemonRPCConnection(object):
hc = httplib.HTTPConnection(self.host, self.port, False, cf['timeout'])
if cf['batch']:
p = [{'method':cmd,'params':r,'id':n} for n,r in enumerate(args[0],1)]
p = [{'method':cmd,'params':r,'id':n,'jsonrpc':'2.0'} for n,r in enumerate(args[0],1)]
else:
p = {'method':cmd,'params':args,'id':1}
p = {'method':cmd,'params':args,'id':1,'jsonrpc':'2.0'}
def do_fail(*args):
if cf['on_fail'] in ('return','silent'):
@ -110,14 +112,16 @@ class CoinDaemonRPCConnection(object):
return g.proto.get_rpc_coin_amt_type()(obj)
return json.JSONEncoder.default(self, obj)
dmsg_rpc(' RPC AUTHORIZATION data ==> raw: [{}]\n{}enc: [Basic {}]\n'.format(
self.auth_str,' '*31,base64.b64encode(self.auth_str)))
http_hdr = { 'Content-Type': 'application/json' }
if self.auth_str:
dmsg_rpc(' RPC AUTHORIZATION data ==> raw: [{}]\n{}enc: [Basic {}]\n'.format(
self.auth_str,' '*31,base64.b64encode(self.auth_str)))
http_hdr.update({
'Host': self.host,
'Authorization': 'Basic {}'.format(base64.b64encode(self.auth_str)) })
try:
hc.request('POST', '/', json.dumps(p,cls=MyJSONEncoder), {
'Host': self.host,
'Authorization': 'Basic {}'.format(base64.b64encode(self.auth_str))
})
hc.request('POST','/',json.dumps(p,cls=MyJSONEncoder),http_hdr)
except Exception as e:
m = '{}\nUnable to connect to {} at {}:{}'
return do_fail(None,2,m.format(e,g.proto.daemon_name,self.host,self.port))
@ -197,6 +201,33 @@ class CoinDaemonRPCConnection(object):
for name in rpcmethods:
exec "def {n}(self,*a,**k):return self.request('{n}',*a,**k)\n".format(n=name)
class EthereumRPCConnection(CoinDaemonRPCConnection):
rpcmethods = (
'eth_accounts',
'eth_blockNumber',
'eth_gasPrice',
'eth_getBalance',
'eth_getBlockByHash',
'eth_getBlockByNumber',
'eth_getTransactionByHash',
'eth_protocolVersion',
'eth_sendRawTransaction',
'eth_signTransaction',
'eth_syncing',
'parity_accountsInfo',
'parity_chainStatus',
'parity_gasCeilTarget',
'parity_gasFloorTarget',
'parity_minGasPrice',
'parity_netPeers',
'parity_versionInfo',
)
for name in rpcmethods:
exec "def {n}(self,*a,**k):return self.request('{n}',*a,**k)\n".format(n=name)
def rpc_error(ret):
return type(ret) is tuple and ret and ret[0] == 'rpcfail'

View file

@ -825,8 +825,7 @@ def rpc_init(reinit=False):
assert block0 == g.proto.block0,'Incorrect Genesis block for {}'.format(g.proto.__name__)
for fork in g.proto.forks:
if fork[0] == None or latest < fork[0]: break
bhash = conn.getblockhash(fork[0])
assert bhash == fork[1], (
assert conn.getblockhash(fork[0]) == fork[1], (
'Bad block hash at fork block {}. Is this the {} chain?'.format(fork[0],fork[2].upper()))
except Exception as e:
die(2,"{}\n'{c}' requested, but this is not the {c} chain!".format(e,c=g.coin))
@ -839,26 +838,34 @@ def rpc_init(reinit=False):
except Exception as e:
die(1,'{}\nChain is {}!'.format(e,g.chain))
cfg = get_daemon_cfg_options(('rpcuser','rpcpassword'))
import mmgen.rpc
conn = mmgen.rpc.CoinDaemonRPCConnection(
g.rpc_host or 'localhost',
g.rpc_port or g.proto.rpc_port,
g.rpc_user or cfg['rpcuser'], # MMGen's rpcuser,rpcpassword override coin daemon's
g.rpc_password or cfg['rpcpassword'],
auth_cookie=get_coin_daemon_auth_cookie())
if g.coin == 'ETH':
conn = mmgen.rpc.EthereumRPCConnection(
g.rpc_host or 'localhost',
g.rpc_port or g.proto.rpc_port,
auth=False)
if not g.daemon_version: # First call
g.daemon_version = conn.parity_versionInfo()['version'] # fail immediately if daemon is geth
else:
cfg = get_daemon_cfg_options(('rpcuser','rpcpassword'))
conn = mmgen.rpc.CoinDaemonRPCConnection(
g.rpc_host or 'localhost',
g.rpc_port or g.proto.rpc_port,
g.rpc_user or cfg['rpcuser'], # MMGen's rpcuser,rpcpassword override coin daemon's
g.rpc_password or cfg['rpcpassword'],
auth_cookie=get_coin_daemon_auth_cookie())
if not g.daemon_version: # First call
if g.bob or g.alice:
import regtest as rt
rt.user(('alice','bob')[g.bob],quiet=True)
g.daemon_version = int(conn.getnetworkinfo()['version'])
g.chain = conn.getblockchaininfo()['chain']
if g.chain != 'regtest': g.chain += 'net'
assert g.chain in g.chains
check_chaintype_mismatch()
if g.chain == 'mainnet': # skip this for testnet, as Genesis block may change
check_chainfork_mismatch(conn)
if not g.daemon_version: # First call
if g.bob or g.alice:
import regtest as rt
rt.user(('alice','bob')[g.bob],quiet=True)
g.daemon_version = int(conn.getnetworkinfo()['version'])
g.chain = conn.getblockchaininfo()['chain']
if g.chain != 'regtest': g.chain += 'net'
assert g.chain in g.chains
check_chaintype_mismatch()
if g.chain == 'mainnet': # skip this for testnet, as Genesis block may change
check_chainfork_mismatch(conn)
g.rpch = conn
return conn