Skip to content

Commit

Permalink
ETH: support Geth
Browse files Browse the repository at this point in the history
- Select with --daemon-id=geth
- Geth is not tested on mainnet yet

Testing:

    $ test/test.py -e --coin=eth --daemon-id=geth ethdev
  • Loading branch information
mmgen committed Aug 5, 2021
1 parent 8965c30 commit ac7bced
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 11 deletions.
2 changes: 1 addition & 1 deletion mmgen/altcoins/eth/contract.py
Expand Up @@ -52,7 +52,7 @@ async def do_call(self,method_sig,method_args='',toUnit=False):
data = create_method_id(method_sig) + method_args
if g.debug:
msg('ETH_CALL {}: {}'.format(method_sig,'\n '.join(parse_abi(data))))
ret = await self.rpc.call('eth_call',{ 'to': '0x'+self.addr, 'data': '0x'+data })
ret = await self.rpc.call('eth_call',{ 'to': '0x'+self.addr, 'data': '0x'+data },'pending')
if toUnit:
return int(ret,16) * self.base_unit
else:
Expand Down
2 changes: 1 addition & 1 deletion mmgen/altcoins/eth/tx.py
Expand Up @@ -396,7 +396,7 @@ async def is_in_mempool():
return False
if self.rpc.daemon.id in ('parity','openethereum'):
pool = [x['hash'] for x in await self.rpc.call('parity_pendingTransactions')]
elif self.rpc.daemon.id == 'erigon':
elif self.rpc.daemon.id in ('geth','erigon'):
res = await self.rpc.call('txpool_content')
pmsg('txpool_content:',res) # DEBUG
pool = list(res['pending']) + list(res['queued'])
Expand Down
32 changes: 28 additions & 4 deletions mmgen/daemon.py
Expand Up @@ -344,7 +344,7 @@ class CoinDaemon(Daemon):
'BCH': _cd('Bitcoin Cash Node', ['bitcoin_cash_node']),
'LTC': _cd('Litecoin', ['litecoin_core']),
'XMR': _cd('Monero', ['monero']),
'ETH': _cd('Ethereum', ['openethereum'] + (['erigon'] if g.enable_erigon else []) ),
'ETH': _cd('Ethereum', ['openethereum','geth'] + (['erigon'] if g.enable_erigon else []) ),
'ETC': _cd('Ethereum Classic', ['parity']),
}

Expand Down Expand Up @@ -658,17 +658,41 @@ class parity_daemon(openethereum_daemon):
ports_shift = _nw(100,110,120)
rpc_ports = _nw(*[8545 + n for n in ports_shift]) # non-standard

class geth_daemon(CoinDaemon):
daemon_data = _dd('Geth', 1010007, '1.10.7')
version_pat = r'Geth/v(\d+)\.(\d+)\.(\d+)'
exec_fn = 'geth'
ports_shift = _nw(300,310,320)
rpc_ports = _nw(*[8545 + n for n in ports_shift]) # non-standard
use_pidfile = False
use_threads = True
datadirs = {
'linux': [g.home_dir,'.ethereum','geth'],
'win': [os.getenv('LOCALAPPDATA'),'Geth'] # FIXME
}

def subclass_init(self):
self.datadir = os.path.join(self.datadir,self.id,getattr(self.proto.network_names,self.network))
self.coind_args = list_gen(
['--verbosity=0'],
['--http'],
['--http.api=eth,web3,txpool'], # ,clique,personal,net'],
[f'--http.port={self.rpc_port}'],
['--maxpeers=0', not 'online' in self.opts],
[f'--datadir={self.datadir}'],
['--chain=goerli', self.network=='testnet'],
['--dev', self.network=='regtest'],
)

# https://github.com/ledgerwatch/erigon
class erigon_daemon(CoinDaemon):
class erigon_daemon(geth_daemon):
avail_opts = ('online',)
daemon_data = _dd('Erigon', 2021007005, '2021.07.5')
version_pat = r'erigon/(\d+)\.(\d+)\.(\d+)'
exec_fn = 'erigon'
private_ports = _nw(9090,9091,9092) # testnet and regtest are non-standard
ports_shift = _nw(200,210,220)
rpc_ports = _nw(*[8545 + n for n in ports_shift]) # non-standard
use_pidfile = False
use_threads = True
datadirs = {
'linux': [g.home_dir,'.local','share','erigon'],
'win': [os.getenv('LOCALAPPDATA'),'Erigon'] # FIXME
Expand Down
2 changes: 1 addition & 1 deletion mmgen/protocol.py
Expand Up @@ -407,7 +407,7 @@ class Ethereum(DummyWIF,Secp256k1):
61: 'classic', # ethereum classic mainnet
62: 'morden', # ethereum classic testnet
17: 'developmentchain', # parity dev chain
1337: 'developmentchain', # erigon dev chain
1337: 'developmentchain', # geth dev chain
}

@property
Expand Down
9 changes: 7 additions & 2 deletions mmgen/rpc.py
Expand Up @@ -624,8 +624,9 @@ async def __ainit__(self,proto,daemon,backend):
self.caps += ('full_node',)
self.chainID = None if ci == None else Int(ci,16) # parity/oe return chainID only for dev chain
self.chain = (await self.call('parity_chain')).replace(' ','_').replace('_testnet','')
elif self.daemon.id == 'erigon':
daemon_warning(self.daemon.id)
elif self.daemon.id in ('geth','erigon'):
if self.daemon.network == 'mainnet':
daemon_warning(self.daemon.id)
self.caps += ('full_node',)
self.chainID = Int(ci,16)
self.chain = self.proto.chain_ids[self.chainID]
Expand Down Expand Up @@ -718,6 +719,10 @@ class MoneroWalletRPCClient(MoneroRPCClient):

class daemon_warning(oneshot_warning):

class geth:
color = 'yellow'
message = 'Geth has not been tested on mainnet. You may experience problems.'

class erigon:
color = 'red'
message = 'Erigon support is EXPERIMENTAL. Use at your own risk!!!'
Expand Down
78 changes: 76 additions & 2 deletions test/test_py_d/ts_ethdev.py
Expand Up @@ -152,7 +152,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
('wallet_upgrade2', 'upgrading the tracking wallet (v2 -> v3)'),
('addrgen', 'generating addresses'),
('addrimport', 'importing addresses'),
('addrimport_dev_addr', "importing OpenEthereum dev address 'Ox00a329c..'"),
('addrimport_dev_addr', "importing dev faucet address 'Ox00a329c..'"),

('txcreate1', 'creating a transaction (spend from dev address to address :1)'),
('txview1_raw', 'viewing the raw transaction'),
Expand Down Expand Up @@ -333,13 +333,87 @@ async def setup(self):
for d in ('mm1','mm2'):
copytree(os.path.join(srcdir,d),os.path.join(self.tmpdir,d))
if not opt.no_daemon_autostart:
if not start_test_daemons(self.proto.coin+'_rt',remove_datadir=True):
if g.daemon_id == 'geth':
self.geth_setup()
if not start_test_daemons(
self.proto.coin+'_rt',
remove_datadir = not g.daemon_id=='geth' ):
return False
from mmgen.rpc import rpc_init
rpc = await rpc_init(self.proto)
imsg('Daemon: {} v{}'.format(rpc.daemon.coind_name,rpc.daemon_version_str))
return 'ok'

def geth_setup(self):

def make_key(keystore):
pwfile = joinpath(self.tmpdir,'account_passwd')
write_to_file(pwfile,'')
run(['rm','-rf',keystore])
cmd = f'geth account new --password={pwfile} --lightkdf --keystore {keystore}'
cp = run(cmd.split(),stdout=PIPE,stderr=PIPE)
if cp.returncode:
die(1,cp.stderr.decode())
keyfile = os.path.join(keystore,os.listdir(keystore)[0])
return json.loads(open(keyfile).read())['address']

def make_genesis(signer_addr,prealloc_addr,prealloc_amt):
return {
'config': {
'chainId': 1337, # TODO: replace constant with var
'homesteadBlock': 0,
'eip150Block': 0,
'eip155Block': 0,
'eip158Block': 0,
'byzantiumBlock': 0,
'constantinopleBlock': 0,
'petersburgBlock': 0,
'clique': {
'period': 0,
'epoch': 30000
}
},
'difficulty': '1',
'gasLimit': '8000000',
'extradata': '0x' + 64*'0' + signer_addr + 130*'0',
'alloc': {
prealloc_addr: { 'balance': str(prealloc_amt.toWei()) }
}
}

def init_genesis(fn):
cmd = f'geth init --datadir {d.datadir} {fn}'
cp = run(cmd.split(),stdout=PIPE,stderr=PIPE)
if cp.returncode:
die(1,cp.stderr.decode())

from mmgen.daemon import CoinDaemon
import json

d = CoinDaemon(proto=self.proto,test_suite=True)
d.stop(quiet=True)
d.remove_datadir()

imsg(cyan('Initializing Geth:'))

keystore = os.path.relpath(os.path.join(d.datadir,'keystore'))
imsg(f' Keystore: {keystore}')

signer_addr = make_key(keystore)
imsg(f' Signer address: {signer_addr}')

prealloc_amt = ETHAmt('1_000_000_000')
imsg(f' Faucet: {dfl_addr} ({prealloc_amt} ETH)')

genesis_data = make_genesis(signer_addr,dfl_addr,prealloc_amt)

genesis_fn = joinpath(self.tmpdir,'genesis.json')
imsg(f' Genesis block data: {genesis_fn}')

write_to_file( genesis_fn, json.dumps(genesis_data,indent=' ')+'\n' )

init_genesis(genesis_fn)

def wallet_upgrade(self,src_file):
if self.proto.coin == 'ETC':
msg('skipping test {!r} for ETC'.format(self.test_name))
Expand Down

0 comments on commit ac7bced

Please sign in to comment.