|
| 1 | +import contextlib |
| 2 | +import os |
| 3 | +import shutil |
| 4 | +import signal |
| 5 | +import socket |
| 6 | +import subprocess |
| 7 | +import tempfile |
| 8 | +import time |
| 9 | + |
| 10 | +from eth_utils import ( |
| 11 | + is_checksum_address, |
| 12 | + to_text, |
| 13 | +) |
| 14 | + |
| 15 | +from web3.exceptions import ( |
| 16 | + TransactionNotFound, |
| 17 | +) |
| 18 | + |
| 19 | +COINBASE = '0xdc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' |
| 20 | +COINBASE_PK = '0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d' |
| 21 | + |
| 22 | +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 |
| 23 | + |
| 24 | +KEYFILE_PW = 'web3py-test' |
| 25 | +KEYFILE_FILENAME = 'UTC--2017-08-24T19-42-47.517572178Z--dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' # noqa: E501 |
| 26 | + |
| 27 | +RAW_TXN_ACCOUNT = '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6' |
| 28 | + |
| 29 | +UNLOCKABLE_PRIVATE_KEY = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01' |
| 30 | +UNLOCKABLE_ACCOUNT = '0x12efdc31b1a8fa1a1e756dfd8a1601055c971e13' |
| 31 | +UNLOCKABLE_ACCOUNT_PW = KEYFILE_PW |
| 32 | + |
| 33 | +GENESIS_DATA = { |
| 34 | + "config": { |
| 35 | + "chainId": 131277322940537, # the string 'web3py' as an integer |
| 36 | + "homesteadBlock": 0, |
| 37 | + "byzantiumBlock": 0, |
| 38 | + "constantinopleBlock": 0, |
| 39 | + "eip150Block": 0, |
| 40 | + "eip155Block": 0, |
| 41 | + "eip158Block": 0, |
| 42 | + "istanbulBlock": 0, |
| 43 | + "petersburgBlock": 0, |
| 44 | + "berlinBlock": 0, |
| 45 | + "londonBlock": 0, |
| 46 | + }, |
| 47 | + "nonce": "0x0000000000000042", |
| 48 | + "alloc": { |
| 49 | + COINBASE: {"balance": "1000000000000000000000000000"}, |
| 50 | + UNLOCKABLE_ACCOUNT: {"balance": "1000000000000000000000000000"}, |
| 51 | + RAW_TXN_ACCOUNT: {"balance": "1000000000000000000000000000"}, |
| 52 | + "0000000000000000000000000000000000000001": {"balance": "1"}, |
| 53 | + "0000000000000000000000000000000000000002": {"balance": "1"}, |
| 54 | + "0000000000000000000000000000000000000003": {"balance": "1"}, |
| 55 | + "0000000000000000000000000000000000000004": {"balance": "1"}, |
| 56 | + "0000000000000000000000000000000000000005": {"balance": "1"}, |
| 57 | + "0000000000000000000000000000000000000006": {"balance": "1"}, |
| 58 | + }, |
| 59 | + "timestamp": "0x00", |
| 60 | + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", |
| 61 | + "extraData": "0x3535353535353535353535353535353535353535353535353535353535353535", |
| 62 | + "gasLimit": "0x3b9aca00", # 1,000,000,000 |
| 63 | + "difficulty": "0x10000", |
| 64 | + "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", |
| 65 | + "coinbase": COINBASE |
| 66 | +} |
| 67 | + |
| 68 | + |
| 69 | +def ensure_path_exists(dir_path): |
| 70 | + """ |
| 71 | + Make sure that a path exists |
| 72 | + """ |
| 73 | + if not os.path.exists(dir_path): |
| 74 | + os.makedirs(dir_path) |
| 75 | + return True |
| 76 | + return False |
| 77 | + |
| 78 | + |
| 79 | +@contextlib.contextmanager |
| 80 | +def tempdir(): |
| 81 | + dir_path = tempfile.mkdtemp() |
| 82 | + try: |
| 83 | + yield dir_path |
| 84 | + finally: |
| 85 | + shutil.rmtree(dir_path) |
| 86 | + |
| 87 | + |
| 88 | +def get_geth_binary(): |
| 89 | + from geth.install import ( |
| 90 | + get_executable_path, |
| 91 | + install_geth, |
| 92 | + ) |
| 93 | + |
| 94 | + if 'GETH_BINARY' in os.environ: |
| 95 | + return os.environ['GETH_BINARY'] |
| 96 | + elif 'GETH_VERSION' in os.environ: |
| 97 | + geth_version = os.environ['GETH_VERSION'] |
| 98 | + _geth_binary = get_executable_path(geth_version) |
| 99 | + if not os.path.exists(_geth_binary): |
| 100 | + install_geth(geth_version) |
| 101 | + assert os.path.exists(_geth_binary) |
| 102 | + return _geth_binary |
| 103 | + else: |
| 104 | + return 'geth' |
| 105 | + |
| 106 | + |
| 107 | +def wait_for_popen(proc, timeout): |
| 108 | + start = time.time() |
| 109 | + while time.time() < start + timeout: |
| 110 | + if proc.poll() is None: |
| 111 | + time.sleep(0.01) |
| 112 | + else: |
| 113 | + break |
| 114 | + |
| 115 | + |
| 116 | +def kill_proc_gracefully(proc): |
| 117 | + if proc.poll() is None: |
| 118 | + proc.send_signal(signal.SIGINT) |
| 119 | + wait_for_popen(proc, 13) |
| 120 | + |
| 121 | + if proc.poll() is None: |
| 122 | + proc.terminate() |
| 123 | + wait_for_popen(proc, 5) |
| 124 | + |
| 125 | + if proc.poll() is None: |
| 126 | + proc.kill() |
| 127 | + wait_for_popen(proc, 2) |
| 128 | + |
| 129 | + |
| 130 | +def wait_for_socket(ipc_path, timeout=30): |
| 131 | + start = time.time() |
| 132 | + while time.time() < start + timeout: |
| 133 | + try: |
| 134 | + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
| 135 | + sock.connect(ipc_path) |
| 136 | + sock.settimeout(timeout) |
| 137 | + except (FileNotFoundError, socket.error): |
| 138 | + time.sleep(0.01) |
| 139 | + else: |
| 140 | + break |
| 141 | + |
| 142 | + |
| 143 | +@contextlib.contextmanager |
| 144 | +def get_geth_process(geth_binary, |
| 145 | + datadir, |
| 146 | + genesis_file_path, |
| 147 | + ipc_path, |
| 148 | + port, |
| 149 | + networkid, |
| 150 | + skip_init=False): |
| 151 | + if not skip_init: |
| 152 | + init_datadir_command = ( |
| 153 | + geth_binary, |
| 154 | + '--datadir', datadir, |
| 155 | + 'init', |
| 156 | + genesis_file_path, |
| 157 | + ) |
| 158 | + print(' '.join(init_datadir_command)) |
| 159 | + subprocess.check_output( |
| 160 | + init_datadir_command, |
| 161 | + stdin=subprocess.PIPE, |
| 162 | + stderr=subprocess.PIPE, |
| 163 | + ) |
| 164 | + |
| 165 | + run_geth_command = ( |
| 166 | + geth_binary, |
| 167 | + '--datadir', datadir, |
| 168 | + '--ipcpath', ipc_path, |
| 169 | + '--nodiscover', |
| 170 | + '--port', port, |
| 171 | + '--networkid', networkid, |
| 172 | + '--etherbase', COINBASE[2:], |
| 173 | + ) |
| 174 | + print(' '.join(run_geth_command)) |
| 175 | + try: |
| 176 | + proc = get_process(run_geth_command) |
| 177 | + yield proc |
| 178 | + finally: |
| 179 | + kill_proc_gracefully(proc) |
| 180 | + output, errors = proc.communicate() |
| 181 | + print( |
| 182 | + "Geth Process Exited:\n" |
| 183 | + "stdout:{0}\n\n" |
| 184 | + "stderr:{1}\n\n".format( |
| 185 | + to_text(output), |
| 186 | + to_text(errors), |
| 187 | + ) |
| 188 | + ) |
| 189 | + |
| 190 | + |
| 191 | +def get_process(run_command): |
| 192 | + proc = subprocess.Popen( |
| 193 | + run_command, |
| 194 | + stdin=subprocess.PIPE, |
| 195 | + stdout=subprocess.PIPE, |
| 196 | + stderr=subprocess.PIPE, |
| 197 | + ) |
| 198 | + return proc |
| 199 | + |
| 200 | + |
| 201 | +def mine_block(web3): |
| 202 | + origin_block_number = web3.eth.block_number |
| 203 | + |
| 204 | + start_time = time.time() |
| 205 | + web3.geth.miner.start(1) |
| 206 | + while time.time() < start_time + 120: |
| 207 | + block_number = web3.eth.block_number |
| 208 | + if block_number > origin_block_number: |
| 209 | + web3.geth.miner.stop() |
| 210 | + return block_number |
| 211 | + else: |
| 212 | + time.sleep(0.1) |
| 213 | + else: |
| 214 | + raise ValueError("No block mined during wait period") |
| 215 | + |
| 216 | + |
| 217 | +def mine_transaction_hash(web3, txn_hash): |
| 218 | + start_time = time.time() |
| 219 | + web3.geth.miner.start(1) |
| 220 | + while time.time() < start_time + 120: |
| 221 | + try: |
| 222 | + receipt = web3.eth.get_transaction_receipt(txn_hash) |
| 223 | + except TransactionNotFound: |
| 224 | + continue |
| 225 | + if receipt is not None: |
| 226 | + web3.geth.miner.stop() |
| 227 | + return receipt |
| 228 | + else: |
| 229 | + time.sleep(0.1) |
| 230 | + else: |
| 231 | + raise ValueError("Math contract deploy transaction not mined during wait period") |
| 232 | + |
| 233 | + |
| 234 | +def deploy_contract(web3, name, factory): |
| 235 | + web3.geth.personal.unlock_account(web3.eth.coinbase, KEYFILE_PW) |
| 236 | + deploy_txn_hash = factory.constructor().transact( |
| 237 | + { |
| 238 | + "from": web3.eth.coinbase, |
| 239 | + "maxFeePerGas": hex(100000000), |
| 240 | + "maxPriorityFeePerGas": hex(1), |
| 241 | + "gas": 2000000, |
| 242 | + } |
| 243 | + ) |
| 244 | + print('{0}_CONTRACT_DEPLOY_HASH: '.format(name.upper()), deploy_txn_hash) |
| 245 | + deploy_receipt = mine_transaction_hash(web3, deploy_txn_hash) |
| 246 | + print('{0}_CONTRACT_DEPLOY_TRANSACTION_MINED'.format(name.upper())) |
| 247 | + contract_address = deploy_receipt['contractAddress'] |
| 248 | + assert is_checksum_address(contract_address) |
| 249 | + print('{0}_CONTRACT_ADDRESS:'.format(name.upper()), contract_address) |
| 250 | + return deploy_receipt |
0 commit comments