Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify geth flags (fixes #88, #90) #91

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 29 additions & 19 deletions etheno/__main__.py
Expand Up @@ -35,11 +35,11 @@ def main(argv=None):
help='Allow the web server to accept external connections')
parser.add_argument('-p', '--port', type=int, default=GETH_DEFAULT_RPC_PORT,
help='Port on which to run the JSON RPC webserver (default=%d)' % GETH_DEFAULT_RPC_PORT)
parser.add_argument('-a', '--accounts', type=int, default=None,
parser.add_argument('-a', '--accounts', type=int, default=10,
help='Number of accounts to create in the client (default=10)')
parser.add_argument('-b', '--balance', type=float, default=100.0,
help='Default balance (in Ether) to seed to each account (default=100.0)')
parser.add_argument('-c', '--gas-price', type=int, default=None,
parser.add_argument('-c', '--gas-price', type=int, default=200000000000,
help='Default gas price (default=20000000000)')
parser.add_argument('-i', '--network-id', type=int, default=None,
help='Specify a network ID (default is the network ID of the master client)')
Expand Down Expand Up @@ -74,6 +74,9 @@ def main(argv=None):
parser.add_argument('--geth-port', type=int, default=None,
help='Port on which to run Geth (defaults to the closest available port to the port specified '
'with --port plus one)')
parser.add_argument('-u', '--geth-unlock-accounts', action='store_true', default=False, help='Unlock accounts in geth')
parser.add_argument('--geth-ipcpath', type=str, default=None,
help='Custom path for geth ipc file')
parser.add_argument('-pa', '--parity', action='store_true', default=False, help='Run Parity as a JSON RPC client')
parser.add_argument('--parity-port', type=int, default=None,
help='Port on which to run Parity (defaults to the closest available port to the port '
Expand Down Expand Up @@ -115,7 +118,7 @@ def main(argv=None):

if argv is None:
argv = sys.argv

args = parser.parse_args(argv[1:])

if args.version:
Expand Down Expand Up @@ -149,7 +152,7 @@ def main(argv=None):
"your own deleting this directory!" % abspath)
sys.exit(1)
clear_directory(args.log_dir)

ETHENO.logger.save_to_directory(args.log_dir)
if not args.log_file:
# Also create a unified log in the log dir:
Expand All @@ -174,34 +177,40 @@ def main(argv=None):
ETHENO.logger.error('Etheno failed to install Echidna. Please install it manually '
'https://github.com/trailofbits/echidna')
sys.exit(1)

if args.genesis is None:
# Set defaults since no genesis was supplied
if args.accounts is None:
args.accounts = 10
if args.gas_price is None:
args.gas_price = 20000000000

accounts = []

if args.genesis:
with open(args.genesis, 'rb') as f:
genesis = json.load(f)
if 'config' not in genesis:
ETHENO.logger.warn("No `config` entry in genesis json")
genesis['config'] = {}
if 'alloc' not in genesis:
ETHENO.logger.warn("No `alloc` entry in genesis json")
genesis['alloc'] = {}
if args.network_id is None:
args.network_id = genesis['config'].get('chainId', None)
args.network_id = genesis['config'].get('chainId')
if args.constantinople_block is None:
args.constantinople_block = genesis['config'].get('constantinopleBlock', None)
args.constantinople_block = genesis['config'].get('constantinopleBlock')
args.constantinople = args.constantinople_block is not None
for addr, bal in genesis['alloc'].items():
pkey = None
if 'privateKey' in bal:
pkey = bal['privateKey']
accounts.append(Account(address=int(addr, 16), balance=decode_value(bal['balance']),
private_key=decode_value(pkey)))
pkey = bal.get('privateKey')
if pkey:
pkey = decode_value(pkey)
ETHENO.logger.info("Account %s w/ balance %s found", addr, bal['balance'])
accounts.append(Account(address=int(addr, 16),
balance=decode_value(bal['balance']),
private_key=pkey))
else:
# We will generate it further below once we've resolved all of the parameters
genesis = None
Expand All @@ -226,7 +235,7 @@ def main(argv=None):
if args.ganache and args.master:
parser.print_help()
sys.stderr.write('\nError: You cannot specify both --ganache and --master at the same time!\n')
sys.exit(1)
sys.exit(1)
elif args.ganache:
if args.ganache_port is None:
args.ganache_port = find_open_port(args.port + 1)
Expand Down Expand Up @@ -254,7 +263,7 @@ def main(argv=None):
elif args.raw and not args.geth and not args.parity:
ETHENO.master_client = RawTransactionClient(RpcProxyClient(args.raw[0]), accounts)
args.raw = args.raw[1:]

if args.network_id is None:
if ETHENO.master_client:
args.network_id = int(ETHENO.master_client.post({
Expand Down Expand Up @@ -283,11 +292,12 @@ def main(argv=None):

geth_instance = geth.GethClient(genesis=genesis, port=args.geth_port)
geth_instance.etheno = ETHENO
for account in accounts:
# TODO: Make some sort of progress bar here
geth_instance.logger.info("Unlocking Geth account %s" % format_hex_address(account.address, True))
geth_instance.import_account(account.private_key)
geth_instance.start(unlock_accounts=True)
if args.geth_unlock_accounts:
for account in accounts:
# TODO: Make some sort of progress bar here
geth_instance.logger.info("Unlocking Geth account %s" % format_hex_address(account.address, True))
geth_instance.import_account(account.private_key)
geth_instance.start(unlock_accounts=args.geth_unlock_accounts)
if ETHENO.master_client is None:
ETHENO.master_client = geth_instance
else:
Expand All @@ -309,7 +319,7 @@ def main(argv=None):
if ETHENO.master_client is None:
ETHENO.master_client = parity_instance
else:
ETHENO.add_client(AddressSynchronizingClient(parity_instance))
ETHENO.add_client(AddressSynchronizingClient(parity_instance))

for client in args.client:
ETHENO.add_client(AddressSynchronizingClient(RpcProxyClient(client)))
Expand Down
4 changes: 3 additions & 1 deletion etheno/genesis.py
Expand Up @@ -119,8 +119,10 @@ def geth_to_parity(genesis):

def make_accounts(num_accounts, default_balance = None):
ret = []
if not num_accounts:
raise ValueError("No accounts passed in genesis.json")
if num_accounts > len(DEFAULT_PRIVATE_KEYS):
raise Exception('TODO: Too many accounts')
raise ValueError('TODO: Too many accounts')
for i in range(num_accounts):
acct = w3.eth.account.from_key(DEFAULT_PRIVATE_KEYS[i])
ret.append(Account(address=int(acct.address, 16), private_key=int(acct.privateKey.hex(), 16), balance=default_balance))
Expand Down
11 changes: 10 additions & 1 deletion etheno/geth.py
Expand Up @@ -55,16 +55,24 @@ def etheno_set(self):
raise e

def import_account(self, private_key):
if not private_key:
self.logger.warn("Skipping import of account without private key")
return

content = format_hex_address(private_key).encode('utf-8') + bytes([ord('\n')])
import_dir = os.path.join(self.log_directory, 'private_keys')
keyfile = self.logger.make_constant_logged_file(content, prefix='private', suffix='.key', dir=import_dir)
while True:
args = ['/usr/bin/env', 'geth', 'account', 'import', '--datadir', self.logger.to_log_path(self.datadir), '--password', self.logger.to_log_path(self.passwords), self.logger.to_log_path(keyfile)]

self.add_to_run_script(args)

geth = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.log_directory)
geth.communicate()
if geth.wait() == 0:
return
# This sometimes happens with geth, I have no idea why, so just try again
self.logger.warn("Could not import account using command `%s` ..retrying", " ".join(args))

def post(self, data):
# geth takes a while to unlock all of the accounts, so check to see if that caused an error and just wait a bit
Expand All @@ -89,8 +97,9 @@ def get_start_command(self, unlock_accounts=True):
verbosity = 3
else:
verbosity = 4
base_args = ['/usr/bin/env', 'geth', '--nodiscover', '--rpc', '--rpcport', "%d" % self.port, '--networkid', "%d" % self.genesis['config']['chainId'], '--datadir', self.logger.to_log_path(self.datadir), '--mine', '--etherbase', format_hex_address(self.miner_account.address), f"--verbosity={verbosity}", '--minerthreads=1']
base_args = ['/usr/bin/env', 'geth', '--nodiscover', '--http', '--http.rpcprefix', '/', '--rpc.allow-unprotected-txs', '--http.port', "%d" % self.port, '--networkid', "%d" % self.genesis['config']['chainId'], '--datadir', self.logger.to_log_path(self.datadir), '--mine', '--miner.etherbase', format_hex_address(self.miner_account.address), f"--verbosity={verbosity}", '--miner.threads=1']
if unlock_accounts:
# FIXME Account unlock with HTTP access is now forbidden, we should catch that direcly during arg parsing
addresses = filter(lambda a : a != format_hex_address(self.miner_account.address), map(format_hex_address, self.genesis['alloc']))
unlock_args = ['--unlock', ','.join(addresses), '--password', self.passwords]
else:
Expand Down
4 changes: 3 additions & 1 deletion etheno/utils.py
Expand Up @@ -41,6 +41,8 @@ def decode_hex(data: Optional[str]) -> Optional[bytes]:


def decode_value(v: Union[str, int]) -> int:
if v is None:
return 0
if isinstance(v, int):
return v
elif v.startswith('0x') or (frozenset(['a', 'b', 'c', 'd', 'e', 'f']) & frozenset(v)):
Expand Down Expand Up @@ -92,7 +94,7 @@ def find_open_port(starting_port: int = 1025) -> int:

def clear_directory(path: str):
"""
Deletes the contents of a directory, but not the directory itself.
Deletes the contents of a directory, but not the directory itself.
This is safe to use on symlinked directories.
Symlinks will be deleted, but the files and directories they point to will not be deleted.
If `path` itself is a symlink, the symlink will be deleted.
Expand Down