Skip to content

Instantly share code, notes, and snippets.

@dylanjw
Created January 27, 2018 07:00
Show Gist options
  • Save dylanjw/03d0fc40e025106e2653b593720c3d0d to your computer and use it in GitHub Desktop.
Save dylanjw/03d0fc40e025106e2653b593720c3d0d to your computer and use it in GitHub Desktop.
generate_parity_fixtures.py
import contextlib
import json
import os
import pprint
import shutil
import signal
import socket
import subprocess
import sys
import time
import tempfile
from cytoolz import merge
from eth_utils import (
to_wei,
remove_0x_prefix,
is_dict,
is_checksum_address,
is_same_address,
force_text,
)
from web3 import Web3
from web3.utils.module_testing.math_contract import (
MATH_BYTECODE,
MATH_ABI,
)
from web3.utils.module_testing.emitter_contract import (
EMITTER_BYTECODE,
EMITTER_ABI,
EMITTER_ENUM,
)
COINBASE = '0xdc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd'
COINBASE_PK = '0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d'
KEYFILE_DATA = '{"address":"dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd","crypto":{"cipher":"aes-128-ctr","ciphertext":"52e06bc9397ea9fa2f0dae8de2b3e8116e92a2ecca9ad5ff0061d1c449704e98","cipherparams":{"iv":"aa5d0a5370ef65395c1a6607af857124"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9fdf0764eb3645ffc184e166537f6fe70516bf0e34dc7311dea21f100f0c9263"},"mac":"4e0b51f42b865c15c485f4faefdd1f01a38637e5247f8c75ffe6a8c0eba856f6"},"id":"5a6124e0-10f1-4c1c-ae3e-d903eacb740a","version":3}' # noqa: E501
KEYFILE_PW = 'web3py-test'
KEYFILE_FILENAME = 'UTC--2017-08-24T19-42-47.517572178Z--dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' # noqa: E501
RAW_TXN_ACCOUNT = '0x39eeed73fb1d3855e90cbd42f348b3d7b340aaa6'
UNLOCKABLE_PRIVATE_KEY = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01'
UNLOCKABLE_ACCOUNT = '0x12efdc31b1a8fa1a1e756dfd8a1601055c971e13'
UNLOCKABLE_ACCOUNT_PW = KEYFILE_PW
CHAIN_CONFIG = {
"name": "Private",
"engine": {
"instantSeal": None
},
"params": {
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID": "0xad",
"gasLimitBoundDivisor": "0x400"
},
"genesis": {
"seal": {
"ethereum": {
"nonce": "0xdeadbeefdeadbeef",
"mixHash":
"0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x01",
"author": "0x3333333333333333333333333333333333333333",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x47d5cc"
},
"nodes": [
],
"accounts": {
remove_0x_prefix(COINBASE): {
'balance': str(to_wei(1000000000, 'ether')),
},
remove_0x_prefix(RAW_TXN_ACCOUNT): {
'balance': str(to_wei(10, 'ether')),
},
remove_0x_prefix(UNLOCKABLE_ACCOUNT): {
'balance': str(to_wei(10, 'ether')),
},
}
}
def get_parity_binary():
return 'parity'
def ensure_path_exists(dir_path):
"""
Make sure that a path exists
"""
if not os.path.exists(dir_path):
os.makedirs(dir_path)
return True
return False
@contextlib.contextmanager
def tempdir():
dir_path = tempfile.mkdtemp()
try:
yield dir_path
finally:
shutil.rmtree(dir_path)
def get_open_port():
sock = socket.socket()
sock.bind(('127.0.0.1', 0))
port = sock.getsockname()[1]
sock.close()
return str(port)
@contextlib.contextmanager
def get_parity_process(
parity_binary,
datadir,
ipc_path,
keys_path,
chain_config_file_path,
parity_port):
run_command = (
parity_binary,
'--base-path', datadir,
'--ipc-path', ipc_path,
'--no-ws',
'--no-ui',
'--chain', chain_config_file_path,
'--keys-path', keys_path,
'--rpcapi', 'all',
'--rpcport', parity_port,
'--author', COINBASE[2:],
)
print(' '.join(run_command))
input()
proc = subprocess.Popen(
run_command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=1,
)
try:
yield proc
finally:
kill_proc_gracefully(proc)
output, errors = proc.communicate()
print(
"Parity Process Exited:\n"
"stdout:{0}\n\n"
"stderr:{1}\n\n".format(
force_text(output),
force_text(errors),
)
)
def wait_for_popen(proc, timeout):
start = time.time()
while time.time() < start + timeout:
if proc.poll() is None:
time.sleep(0.01)
else:
break
def kill_proc_gracefully(proc):
if proc.poll() is None:
proc.send_signal(signal.SIGINT)
wait_for_popen(proc, 13)
if proc.poll() is None:
proc.terminate()
wait_for_popen(proc, 5)
if proc.poll() is None:
proc.kill()
wait_for_popen(proc, 2)
def wait_for_socket(ipc_path, timeout=30):
start = time.time()
while time.time() < start + timeout:
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(ipc_path)
sock.settimeout(timeout)
except (FileNotFoundError, socket.error):
time.sleep(0.01)
else:
break
def generate_parity_fixture(destination_dir):
with contextlib.ExitStack() as stack:
datadir = stack.enter_context(tempdir())
keystore_dir = os.path.join(datadir, 'keys')
ensure_path_exists(keystore_dir)
keyfile_path = os.path.join(keystore_dir, KEYFILE_FILENAME)
with open(keyfile_path, 'w') as keyfile:
keyfile.write(KEYFILE_DATA)
print(keyfile_path)
chain_config_file_path = os.path.join(datadir, 'chain_config.json')
with open(chain_config_file_path, 'w') as chain_file:
chain_file.write(json.dumps(CHAIN_CONFIG))
print(chain_config_file_path)
parity_ipc_path_dir = stack.enter_context(tempdir())
parity_ipc_path = os.path.join(parity_ipc_path_dir, 'jsonrpc.ipc')
print(parity_ipc_path)
parity_port = get_open_port()
parity_binary = get_parity_binary()
parity_proc = stack.enter_context(get_parity_process( # noqa: F841
parity_binary=parity_binary,
datadir=datadir,
ipc_path=parity_ipc_path,
keys_path=keystore_dir,
chain_config_file_path=chain_config_file_path,
parity_port=parity_port,
))
wait_for_socket(parity_ipc_path)
web3 = Web3(Web3.IPCProvider(parity_ipc_path))
chain_data = setup_chain_state(web3)
static_data = {
'raw_txn_account': RAW_TXN_ACCOUNT,
'keyfile_pw': KEYFILE_PW,
}
pprint.pprint(merge(chain_data, static_data))
shutil.copytree(datadir, destination_dir)
def mine_transaction_hash(web3, txn_hash):
start_time = time.time()
web3.miner.start(1)
while time.time() < start_time + 60:
receipt = web3.eth.getTransactionReceipt(txn_hash)
if receipt is not None:
web3.miner.stop()
return receipt
else:
time.sleep(0.1)
else:
raise ValueError("Math contract deploy transaction not mined during wait period")
def mine_block(web3):
origin_block_number = web3.eth.blockNumber
start_time = time.time()
web3.miner.start(1)
while time.time() < start_time + 60:
block_number = web3.eth.blockNumber
if block_number > origin_block_number:
web3.miner.stop()
return block_number
else:
time.sleep(0.1)
else:
raise ValueError("No block mined during wait period")
def deploy_contract(web3, name, factory):
import pdb;
pdb.set_trace()
web3.personal.unlockAccount(web3.eth.coinbase, KEYFILE_PW)
deploy_txn_hash = factory.deploy({'from': web3.eth.coinbase})
print('{0}_CONTRACT_DEPLOY_HASH: '.format(name.upper()), deploy_txn_hash)
deploy_receipt = mine_transaction_hash(web3, deploy_txn_hash)
print('{0}_CONTRACT_DEPLOY_TRANSACTION_MINED'.format(name.upper()))
contract_address = deploy_receipt['contractAddress']
assert is_checksum_address(contract_address)
print('{0}_CONTRACT_ADDRESS:'.format(name.upper()), contract_address)
return deploy_receipt
def setup_chain_state(web3):
coinbase = web3.eth.coinbase
assert is_same_address(coinbase, COINBASE)
#
# Math Contract
#
math_contract_factory = web3.eth.contract(
abi=MATH_ABI,
bytecode=MATH_BYTECODE,
)
math_deploy_receipt = deploy_contract(web3, 'math', math_contract_factory)
assert is_dict(math_deploy_receipt)
#
# Emitter Contract
#
emitter_contract_factory = web3.eth.contract(
abi=EMITTER_ABI,
bytecode=EMITTER_BYTECODE,
)
emitter_deploy_receipt = deploy_contract(web3, 'emitter', emitter_contract_factory)
emitter_contract = emitter_contract_factory(emitter_deploy_receipt['contractAddress'])
txn_hash_with_log = emitter_contract.transact({
'from': web3.eth.coinbase,
}).logDouble(which=EMITTER_ENUM['LogDoubleWithIndex'], arg0=12345, arg1=54321)
print('TXN_HASH_WITH_LOG:', txn_hash_with_log)
txn_receipt_with_log = mine_transaction_hash(web3, txn_hash_with_log)
block_with_log = web3.eth.getBlock(txn_receipt_with_log['blockHash'])
print('BLOCK_HASH_WITH_LOG:', block_with_log['hash'])
#
# Empty Block
#
empty_block_number = mine_block(web3)
print('MINED_EMPTY_BLOCK')
empty_block = web3.eth.getBlock(empty_block_number)
assert is_dict(empty_block)
assert not empty_block['transactions']
print('EMPTY_BLOCK_HASH:', empty_block['hash'])
#
# Block with Transaction
#
web3.personal.unlockAccount(coinbase, KEYFILE_PW)
web3.miner.start(1)
mined_txn_hash = web3.eth.sendTransaction({
'from': coinbase,
'to': coinbase,
'value': 1,
'gas': 21000,
'gas_price': web3.eth.gasPrice,
})
mined_txn_receipt = mine_transaction_hash(web3, mined_txn_hash)
print('MINED_TXN_HASH:', mined_txn_hash)
block_with_txn = web3.eth.getBlock(mined_txn_receipt['blockHash'])
print('BLOCK_WITH_TXN_HASH:', block_with_txn['hash'])
parity_fixture = {
'math_deploy_txn_hash': math_deploy_receipt['transactionHash'],
'math_address': math_deploy_receipt['contractAddress'],
'emitter_deploy_txn_hash': emitter_deploy_receipt['transactionHash'],
'emitter_address': emitter_deploy_receipt['contractAddress'],
'txn_hash_with_log': txn_hash_with_log,
'block_hash_with_log': block_with_log['hash'],
'empty_block_hash': empty_block['hash'],
'mined_txn_hash': mined_txn_hash,
'block_with_txn_hash': block_with_txn['hash'],
}
return parity_fixture
if __name__ == '__main__':
fixture_dir = sys.argv[1]
generate_parity_fixture(fixture_dir)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment