py-geth 4.3.0__py3-none-any.whl → 5.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- geth/__init__.py +12 -2
- geth/accounts.py +87 -36
- geth/chain.py +68 -83
- geth/exceptions.py +86 -13
- geth/genesis.json +25 -8
- geth/install.py +126 -136
- geth/main.py +27 -5
- geth/mixins.py +54 -29
- geth/process.py +148 -124
- geth/py.typed +0 -0
- geth/reset.py +40 -16
- geth/types.py +65 -0
- geth/utils/encoding.py +36 -24
- geth/utils/filesystem.py +8 -15
- geth/utils/networking.py +18 -6
- geth/utils/proc.py +13 -3
- geth/utils/thread.py +5 -1
- geth/utils/timeout.py +39 -13
- geth/utils/validation.py +179 -0
- geth/wrapper.py +120 -169
- {py_geth-4.3.0.dist-info → py_geth-5.2.0.dist-info}/LICENSE +1 -1
- {py_geth-4.3.0.dist-info → py_geth-5.2.0.dist-info}/METADATA +54 -63
- py_geth-5.2.0.dist-info/RECORD +27 -0
- {py_geth-4.3.0.dist-info → py_geth-5.2.0.dist-info}/WHEEL +1 -1
- geth/utils/dag.py +0 -45
- py_geth-4.3.0.dist-info/RECORD +0 -25
- {py_geth-4.3.0.dist-info → py_geth-5.2.0.dist-info}/top_level.txt +0 -0
geth/mixins.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from __future__ import (
|
2
|
-
|
2
|
+
annotations,
|
3
3
|
)
|
4
4
|
|
5
5
|
import datetime
|
@@ -7,7 +7,15 @@ import logging
|
|
7
7
|
import os
|
8
8
|
import queue
|
9
9
|
import time
|
10
|
+
from typing import (
|
11
|
+
TYPE_CHECKING,
|
12
|
+
Any,
|
13
|
+
Callable,
|
14
|
+
)
|
10
15
|
|
16
|
+
from geth.exceptions import (
|
17
|
+
PyGethAttributeError,
|
18
|
+
)
|
11
19
|
from geth.utils.filesystem import (
|
12
20
|
ensure_path_exists,
|
13
21
|
)
|
@@ -19,13 +27,13 @@ from geth.utils.timeout import (
|
|
19
27
|
)
|
20
28
|
|
21
29
|
|
22
|
-
def construct_logger_file_path(prefix, suffix):
|
30
|
+
def construct_logger_file_path(prefix: str, suffix: str) -> str:
|
23
31
|
ensure_path_exists("./logs")
|
24
32
|
timestamp = datetime.datetime.now().strftime(f"{prefix}-%Y%m%d-%H%M%S-{suffix}.log")
|
25
33
|
return os.path.join("logs", timestamp)
|
26
34
|
|
27
35
|
|
28
|
-
def _get_file_logger(name, filename):
|
36
|
+
def _get_file_logger(name: str, filename: str) -> logging.Logger:
|
29
37
|
# create logger with 'spam_application'
|
30
38
|
logger = logging.getLogger(name)
|
31
39
|
logger.setLevel(logging.DEBUG)
|
@@ -46,8 +54,15 @@ def _get_file_logger(name, filename):
|
|
46
54
|
return logger
|
47
55
|
|
48
56
|
|
49
|
-
|
50
|
-
|
57
|
+
# only needed until we drop support for python 3.8
|
58
|
+
if TYPE_CHECKING:
|
59
|
+
BaseQueue = queue.Queue[Any]
|
60
|
+
else:
|
61
|
+
BaseQueue = queue.Queue
|
62
|
+
|
63
|
+
|
64
|
+
class JoinableQueue(BaseQueue):
|
65
|
+
def __iter__(self) -> Any:
|
51
66
|
while True:
|
52
67
|
item = self.get()
|
53
68
|
|
@@ -65,62 +80,70 @@ class JoinableQueue(queue.Queue):
|
|
65
80
|
|
66
81
|
yield item
|
67
82
|
|
68
|
-
def join(self, timeout=None):
|
83
|
+
def join(self, timeout: int | None = None) -> None:
|
69
84
|
with Timeout(timeout) as _timeout:
|
70
85
|
while not self.empty():
|
71
86
|
time.sleep(0)
|
72
87
|
_timeout.check()
|
73
88
|
|
74
89
|
|
75
|
-
class InterceptedStreamsMixin
|
90
|
+
class InterceptedStreamsMixin:
|
76
91
|
"""
|
77
92
|
Mixin class for GethProcess instances that feeds all of the stdout and
|
78
93
|
stderr lines into some set of provided callback functions.
|
79
94
|
"""
|
80
95
|
|
81
|
-
stdout_callbacks
|
82
|
-
stderr_callbacks
|
96
|
+
stdout_callbacks: list[Callable[[str], None]]
|
97
|
+
stderr_callbacks: list[Callable[[str], None]]
|
83
98
|
|
84
|
-
def __init__(self, *args, **kwargs):
|
85
|
-
super(
|
99
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
100
|
+
super().__init__(*args, **kwargs)
|
86
101
|
self.stdout_callbacks = []
|
87
102
|
self.stdout_queue = JoinableQueue()
|
88
103
|
|
89
104
|
self.stderr_callbacks = []
|
90
105
|
self.stderr_queue = JoinableQueue()
|
91
106
|
|
92
|
-
def register_stdout_callback(self, callback_fn):
|
107
|
+
def register_stdout_callback(self, callback_fn: Callable[[str], None]) -> None:
|
93
108
|
self.stdout_callbacks.append(callback_fn)
|
94
109
|
|
95
|
-
def register_stderr_callback(self, callback_fn):
|
110
|
+
def register_stderr_callback(self, callback_fn: Callable[[str], None]) -> None:
|
96
111
|
self.stderr_callbacks.append(callback_fn)
|
97
112
|
|
98
|
-
def produce_stdout_queue(self):
|
99
|
-
|
100
|
-
self.
|
101
|
-
|
113
|
+
def produce_stdout_queue(self) -> None:
|
114
|
+
if hasattr(self, "proc"):
|
115
|
+
for line in iter(self.proc.stdout.readline, b""):
|
116
|
+
self.stdout_queue.put(line)
|
117
|
+
time.sleep(0)
|
118
|
+
else:
|
119
|
+
raise PyGethAttributeError("No `proc` attribute found")
|
102
120
|
|
103
|
-
def produce_stderr_queue(self):
|
104
|
-
|
105
|
-
self.
|
106
|
-
|
121
|
+
def produce_stderr_queue(self) -> None:
|
122
|
+
if hasattr(self, "proc"):
|
123
|
+
for line in iter(self.proc.stderr.readline, b""):
|
124
|
+
self.stderr_queue.put(line)
|
125
|
+
time.sleep(0)
|
126
|
+
else:
|
127
|
+
raise PyGethAttributeError("No `proc` attribute found")
|
107
128
|
|
108
|
-
def consume_stdout_queue(self):
|
129
|
+
def consume_stdout_queue(self) -> None:
|
109
130
|
for line in self.stdout_queue:
|
110
131
|
for fn in self.stdout_callbacks:
|
111
132
|
fn(line.strip())
|
112
133
|
self.stdout_queue.task_done()
|
113
134
|
time.sleep(0)
|
114
135
|
|
115
|
-
def consume_stderr_queue(self):
|
136
|
+
def consume_stderr_queue(self) -> None:
|
116
137
|
for line in self.stderr_queue:
|
117
138
|
for fn in self.stderr_callbacks:
|
118
139
|
fn(line.strip())
|
119
140
|
self.stderr_queue.task_done()
|
120
141
|
time.sleep(0)
|
121
142
|
|
122
|
-
def start(self):
|
123
|
-
|
143
|
+
def start(self) -> None:
|
144
|
+
# type ignored because this is a mixin but will always have a start method
|
145
|
+
# because it will be mixed with BaseGethProcess
|
146
|
+
super().start() # type: ignore[misc]
|
124
147
|
|
125
148
|
spawn(self.produce_stdout_queue)
|
126
149
|
spawn(self.produce_stderr_queue)
|
@@ -128,8 +151,10 @@ class InterceptedStreamsMixin(object):
|
|
128
151
|
spawn(self.consume_stdout_queue)
|
129
152
|
spawn(self.consume_stderr_queue)
|
130
153
|
|
131
|
-
def stop(self):
|
132
|
-
|
154
|
+
def stop(self) -> None:
|
155
|
+
# type ignored because this is a mixin but will always have a stop method
|
156
|
+
# because it will be mixed with BaseGethProcess
|
157
|
+
super().stop() # type: ignore[misc]
|
133
158
|
|
134
159
|
try:
|
135
160
|
self.stdout_queue.put(StopIteration)
|
@@ -145,7 +170,7 @@ class InterceptedStreamsMixin(object):
|
|
145
170
|
|
146
171
|
|
147
172
|
class LoggingMixin(InterceptedStreamsMixin):
|
148
|
-
def __init__(self, *args, **kwargs):
|
173
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
149
174
|
stdout_logfile_path = kwargs.pop(
|
150
175
|
"stdout_logfile_path",
|
151
176
|
construct_logger_file_path("geth", "stdout"),
|
@@ -155,7 +180,7 @@ class LoggingMixin(InterceptedStreamsMixin):
|
|
155
180
|
construct_logger_file_path("geth", "stderr"),
|
156
181
|
)
|
157
182
|
|
158
|
-
super(
|
183
|
+
super().__init__(*args, **kwargs)
|
159
184
|
|
160
185
|
stdout_logger = _get_file_logger("geth-stdout", stdout_logfile_path)
|
161
186
|
stderr_logger = _get_file_logger("geth-stderr", stderr_logfile_path)
|
geth/process.py
CHANGED
@@ -1,21 +1,34 @@
|
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
4
|
+
|
5
|
+
from abc import (
|
6
|
+
ABC,
|
7
|
+
abstractmethod,
|
8
|
+
)
|
9
|
+
import json
|
1
10
|
import logging
|
2
11
|
import os
|
3
|
-
import socket
|
4
12
|
import subprocess
|
5
13
|
import time
|
6
|
-
import
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
from types import (
|
15
|
+
TracebackType,
|
16
|
+
)
|
17
|
+
from typing import (
|
18
|
+
cast,
|
19
|
+
)
|
20
|
+
from urllib.error import (
|
21
|
+
URLError,
|
22
|
+
)
|
23
|
+
from urllib.request import (
|
24
|
+
urlopen,
|
25
|
+
)
|
26
|
+
|
27
|
+
import semantic_version
|
18
28
|
|
29
|
+
from geth import (
|
30
|
+
get_geth_version,
|
31
|
+
)
|
19
32
|
from geth.accounts import (
|
20
33
|
ensure_account_exists,
|
21
34
|
get_accounts,
|
@@ -25,13 +38,18 @@ from geth.chain import (
|
|
25
38
|
get_default_base_dir,
|
26
39
|
get_genesis_file_path,
|
27
40
|
get_live_data_dir,
|
28
|
-
|
41
|
+
get_sepolia_data_dir,
|
29
42
|
initialize_chain,
|
30
43
|
is_live_chain,
|
31
|
-
|
44
|
+
is_sepolia_chain,
|
45
|
+
)
|
46
|
+
from geth.exceptions import (
|
47
|
+
PyGethNotImplementedError,
|
48
|
+
PyGethValueError,
|
32
49
|
)
|
33
|
-
from geth.
|
34
|
-
|
50
|
+
from geth.types import (
|
51
|
+
GethKwargsTypedDict,
|
52
|
+
IO_Any,
|
35
53
|
)
|
36
54
|
from geth.utils.networking import (
|
37
55
|
get_ipc_socket,
|
@@ -42,24 +60,32 @@ from geth.utils.proc import (
|
|
42
60
|
from geth.utils.timeout import (
|
43
61
|
Timeout,
|
44
62
|
)
|
63
|
+
from geth.utils.validation import (
|
64
|
+
GenesisDataTypedDict,
|
65
|
+
validate_genesis_data,
|
66
|
+
validate_geth_kwargs,
|
67
|
+
)
|
45
68
|
from geth.wrapper import (
|
46
69
|
construct_popen_command,
|
47
70
|
construct_test_chain_kwargs,
|
48
71
|
)
|
49
72
|
|
50
73
|
logger = logging.getLogger(__name__)
|
74
|
+
with open(os.path.join(os.path.dirname(__file__), "genesis.json")) as genesis_file:
|
75
|
+
GENESIS_JSON = json.load(genesis_file)
|
51
76
|
|
52
77
|
|
53
|
-
class BaseGethProcess(
|
78
|
+
class BaseGethProcess(ABC):
|
54
79
|
_proc = None
|
55
80
|
|
56
81
|
def __init__(
|
57
82
|
self,
|
58
|
-
geth_kwargs,
|
59
|
-
stdin=subprocess.PIPE,
|
60
|
-
stdout=subprocess.PIPE,
|
61
|
-
stderr=subprocess.PIPE,
|
83
|
+
geth_kwargs: GethKwargsTypedDict,
|
84
|
+
stdin: IO_Any = subprocess.PIPE,
|
85
|
+
stdout: IO_Any = subprocess.PIPE,
|
86
|
+
stderr: IO_Any = subprocess.PIPE,
|
62
87
|
):
|
88
|
+
validate_geth_kwargs(geth_kwargs)
|
63
89
|
self.geth_kwargs = geth_kwargs
|
64
90
|
self.command = construct_popen_command(**geth_kwargs)
|
65
91
|
self.stdin = stdin
|
@@ -68,12 +94,12 @@ class BaseGethProcess(object):
|
|
68
94
|
|
69
95
|
is_running = False
|
70
96
|
|
71
|
-
def start(self):
|
97
|
+
def start(self) -> None:
|
72
98
|
if self.is_running:
|
73
|
-
raise
|
99
|
+
raise PyGethValueError("Already running")
|
74
100
|
self.is_running = True
|
75
101
|
|
76
|
-
logger.info("Launching geth:
|
102
|
+
logger.info(f"Launching geth: {' '.join(self.command)}")
|
77
103
|
self.proc = subprocess.Popen(
|
78
104
|
self.command,
|
79
105
|
stdin=self.stdin,
|
@@ -81,48 +107,61 @@ class BaseGethProcess(object):
|
|
81
107
|
stderr=self.stderr,
|
82
108
|
)
|
83
109
|
|
84
|
-
def __enter__(self):
|
110
|
+
def __enter__(self) -> BaseGethProcess:
|
85
111
|
self.start()
|
86
112
|
return self
|
87
113
|
|
88
|
-
def stop(self):
|
114
|
+
def stop(self) -> None:
|
89
115
|
if not self.is_running:
|
90
|
-
raise
|
116
|
+
raise PyGethValueError("Not running")
|
91
117
|
|
92
118
|
if self.proc.poll() is None:
|
93
119
|
kill_proc(self.proc)
|
94
120
|
|
95
121
|
self.is_running = False
|
96
122
|
|
97
|
-
def __exit__(
|
123
|
+
def __exit__(
|
124
|
+
self,
|
125
|
+
exc_type: type[BaseException] | None,
|
126
|
+
exc_value: BaseException | None,
|
127
|
+
tb: TracebackType | None,
|
128
|
+
) -> None:
|
98
129
|
self.stop()
|
99
130
|
|
100
131
|
@property
|
101
|
-
|
132
|
+
@abstractmethod
|
133
|
+
def data_dir(self) -> str:
|
134
|
+
raise PyGethNotImplementedError("Must be implemented by subclasses.")
|
135
|
+
|
136
|
+
@property
|
137
|
+
def is_alive(self) -> bool:
|
102
138
|
return self.is_running and self.proc.poll() is None
|
103
139
|
|
104
140
|
@property
|
105
|
-
def is_stopped(self):
|
141
|
+
def is_stopped(self) -> bool:
|
106
142
|
return self.proc is not None and self.proc.poll() is not None
|
107
143
|
|
108
144
|
@property
|
109
|
-
def accounts(self):
|
145
|
+
def accounts(self) -> tuple[str, ...]:
|
110
146
|
return get_accounts(**self.geth_kwargs)
|
111
147
|
|
112
148
|
@property
|
113
|
-
def rpc_enabled(self):
|
114
|
-
|
149
|
+
def rpc_enabled(self) -> bool:
|
150
|
+
_rpc_enabled = self.geth_kwargs.get("rpc_enabled", False)
|
151
|
+
return cast(bool, _rpc_enabled)
|
115
152
|
|
116
153
|
@property
|
117
|
-
def rpc_host(self):
|
118
|
-
|
154
|
+
def rpc_host(self) -> str:
|
155
|
+
_rpc_host = self.geth_kwargs.get("rpc_host", "127.0.0.1")
|
156
|
+
return cast(str, _rpc_host)
|
119
157
|
|
120
158
|
@property
|
121
|
-
def rpc_port(self):
|
122
|
-
|
159
|
+
def rpc_port(self) -> str:
|
160
|
+
_rpc_port = self.geth_kwargs.get("rpc_port", "8545")
|
161
|
+
return cast(str, _rpc_port)
|
123
162
|
|
124
163
|
@property
|
125
|
-
def is_rpc_ready(self):
|
164
|
+
def is_rpc_ready(self) -> bool:
|
126
165
|
try:
|
127
166
|
urlopen(f"http://{self.rpc_host}:{self.rpc_port}")
|
128
167
|
except URLError:
|
@@ -130,9 +169,9 @@ class BaseGethProcess(object):
|
|
130
169
|
else:
|
131
170
|
return True
|
132
171
|
|
133
|
-
def wait_for_rpc(self, timeout=0):
|
172
|
+
def wait_for_rpc(self, timeout: int = 0) -> None:
|
134
173
|
if not self.rpc_enabled:
|
135
|
-
raise
|
174
|
+
raise PyGethValueError("RPC interface is not enabled")
|
136
175
|
|
137
176
|
with Timeout(timeout) as _timeout:
|
138
177
|
while True:
|
@@ -142,36 +181,33 @@ class BaseGethProcess(object):
|
|
142
181
|
_timeout.check()
|
143
182
|
|
144
183
|
@property
|
145
|
-
def ipc_enabled(self):
|
184
|
+
def ipc_enabled(self) -> bool:
|
146
185
|
return not self.geth_kwargs.get("ipc_disable", None)
|
147
186
|
|
148
187
|
@property
|
149
|
-
def ipc_path(self):
|
150
|
-
return self.geth_kwargs.get(
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
self.data_dir,
|
156
|
-
"geth.ipc",
|
157
|
-
)
|
188
|
+
def ipc_path(self) -> str:
|
189
|
+
return self.geth_kwargs.get("ipc_path") or os.path.abspath(
|
190
|
+
os.path.expanduser(
|
191
|
+
os.path.join(
|
192
|
+
self.data_dir,
|
193
|
+
"geth.ipc",
|
158
194
|
)
|
159
|
-
)
|
195
|
+
)
|
160
196
|
)
|
161
197
|
|
162
198
|
@property
|
163
|
-
def is_ipc_ready(self):
|
199
|
+
def is_ipc_ready(self) -> bool:
|
164
200
|
try:
|
165
201
|
with get_ipc_socket(self.ipc_path):
|
166
202
|
pass
|
167
|
-
except
|
203
|
+
except OSError:
|
168
204
|
return False
|
169
205
|
else:
|
170
206
|
return True
|
171
207
|
|
172
|
-
def wait_for_ipc(self, timeout=0):
|
208
|
+
def wait_for_ipc(self, timeout: int = 0) -> None:
|
173
209
|
if not self.ipc_enabled:
|
174
|
-
raise
|
210
|
+
raise PyGethValueError("IPC interface is not enabled")
|
175
211
|
|
176
212
|
with Timeout(timeout) as _timeout:
|
177
213
|
while True:
|
@@ -180,78 +216,49 @@ class BaseGethProcess(object):
|
|
180
216
|
time.sleep(0.1)
|
181
217
|
_timeout.check()
|
182
218
|
|
183
|
-
@property
|
184
|
-
def is_dag_generated(self):
|
185
|
-
return is_dag_generated()
|
186
|
-
|
187
|
-
@property
|
188
|
-
def is_mining(self):
|
189
|
-
return self.geth_kwargs.get("mine", False)
|
190
|
-
|
191
|
-
def wait_for_dag(self, timeout=0):
|
192
|
-
if not self.is_mining and not self.geth_kwargs.get("autodag", False):
|
193
|
-
raise ValueError("Geth not configured to generate DAG")
|
194
|
-
|
195
|
-
with Timeout(timeout) as _timeout:
|
196
|
-
while True:
|
197
|
-
if self.is_dag_generated:
|
198
|
-
break
|
199
|
-
time.sleep(0.1)
|
200
|
-
_timeout.check()
|
201
|
-
|
202
219
|
|
203
220
|
class MainnetGethProcess(BaseGethProcess):
|
204
|
-
def __init__(self, geth_kwargs=None):
|
221
|
+
def __init__(self, geth_kwargs: GethKwargsTypedDict | None = None):
|
205
222
|
if geth_kwargs is None:
|
206
223
|
geth_kwargs = {}
|
207
224
|
|
208
225
|
if "data_dir" in geth_kwargs:
|
209
|
-
raise
|
226
|
+
raise PyGethValueError(
|
227
|
+
"You cannot specify `data_dir` for a MainnetGethProcess"
|
228
|
+
)
|
210
229
|
|
211
|
-
super(
|
230
|
+
super().__init__(geth_kwargs)
|
212
231
|
|
213
232
|
@property
|
214
|
-
def data_dir(self):
|
233
|
+
def data_dir(self) -> str:
|
215
234
|
return get_live_data_dir()
|
216
235
|
|
217
236
|
|
218
|
-
class
|
219
|
-
def __init__(self,
|
220
|
-
warnings.warn(
|
221
|
-
DeprecationWarning(
|
222
|
-
"The `LiveGethProcess` has been renamed to `MainnetGethProcess`. "
|
223
|
-
"The `LiveGethProcess` alias will be removed in subsequent releases"
|
224
|
-
),
|
225
|
-
stacklevel=2,
|
226
|
-
)
|
227
|
-
super(LiveGethProcess, self).__init__(*args, **kwargs)
|
228
|
-
|
229
|
-
|
230
|
-
class RopstenGethProcess(BaseGethProcess):
|
231
|
-
def __init__(self, geth_kwargs=None):
|
237
|
+
class SepoliaGethProcess(BaseGethProcess):
|
238
|
+
def __init__(self, geth_kwargs: GethKwargsTypedDict | None = None):
|
232
239
|
if geth_kwargs is None:
|
233
240
|
geth_kwargs = {}
|
234
241
|
|
235
242
|
if "data_dir" in geth_kwargs:
|
236
|
-
raise
|
243
|
+
raise PyGethValueError(
|
237
244
|
f"You cannot specify `data_dir` for a {type(self).__name__}"
|
238
245
|
)
|
239
246
|
if "network_id" in geth_kwargs:
|
240
|
-
raise
|
247
|
+
raise PyGethValueError(
|
241
248
|
f"You cannot specify `network_id` for a {type(self).__name__}"
|
242
249
|
)
|
243
250
|
|
244
|
-
geth_kwargs["network_id"] = "
|
245
|
-
geth_kwargs["data_dir"] =
|
251
|
+
geth_kwargs["network_id"] = "11155111"
|
252
|
+
geth_kwargs["data_dir"] = get_sepolia_data_dir()
|
246
253
|
|
247
|
-
super(
|
254
|
+
super().__init__(geth_kwargs)
|
248
255
|
|
249
256
|
@property
|
250
|
-
def data_dir(self):
|
251
|
-
return
|
257
|
+
def data_dir(self) -> str:
|
258
|
+
return get_sepolia_data_dir()
|
252
259
|
|
253
260
|
|
254
|
-
class TestnetGethProcess(
|
261
|
+
class TestnetGethProcess(SepoliaGethProcess):
|
255
262
|
"""
|
256
263
|
Alias for whatever the current primary testnet chain is.
|
257
264
|
"""
|
@@ -259,53 +266,70 @@ class TestnetGethProcess(RopstenGethProcess):
|
|
259
266
|
|
260
267
|
class DevGethProcess(BaseGethProcess):
|
261
268
|
"""
|
262
|
-
|
269
|
+
Geth developer mode process for testing purposes.
|
263
270
|
"""
|
264
271
|
|
265
|
-
|
272
|
+
_data_dir: str
|
273
|
+
|
274
|
+
def __init__(
|
275
|
+
self,
|
276
|
+
chain_name: str,
|
277
|
+
base_dir: str | None = None,
|
278
|
+
overrides: GethKwargsTypedDict | None = None,
|
279
|
+
genesis_data: GenesisDataTypedDict | None = None,
|
280
|
+
):
|
266
281
|
if overrides is None:
|
267
282
|
overrides = {}
|
268
283
|
|
269
284
|
if genesis_data is None:
|
270
|
-
genesis_data =
|
285
|
+
genesis_data = GenesisDataTypedDict(**GENESIS_JSON)
|
286
|
+
|
287
|
+
validate_genesis_data(genesis_data)
|
271
288
|
|
272
289
|
if "data_dir" in overrides:
|
273
|
-
raise
|
290
|
+
raise PyGethValueError("You cannot specify `data_dir` for a DevGethProcess")
|
274
291
|
|
275
292
|
if base_dir is None:
|
276
293
|
base_dir = get_default_base_dir()
|
277
294
|
|
278
|
-
self.
|
279
|
-
|
295
|
+
self._data_dir = get_chain_data_dir(base_dir, chain_name)
|
296
|
+
overrides["data_dir"] = self.data_dir
|
297
|
+
geth_kwargs = construct_test_chain_kwargs(**overrides)
|
298
|
+
validate_geth_kwargs(geth_kwargs)
|
280
299
|
|
281
300
|
# ensure that an account is present
|
282
301
|
coinbase = ensure_account_exists(**geth_kwargs)
|
283
302
|
|
284
303
|
# ensure that the chain is initialized
|
285
304
|
genesis_file_path = get_genesis_file_path(self.data_dir)
|
286
|
-
|
287
305
|
needs_init = all(
|
288
306
|
(
|
289
307
|
not os.path.exists(genesis_file_path),
|
290
308
|
not is_live_chain(self.data_dir),
|
291
|
-
not
|
309
|
+
not is_sepolia_chain(self.data_dir),
|
292
310
|
)
|
293
311
|
)
|
294
|
-
|
295
312
|
if needs_init:
|
296
|
-
genesis_data
|
297
|
-
|
298
|
-
|
299
|
-
[
|
300
|
-
(
|
301
|
-
coinbase,
|
302
|
-
{
|
303
|
-
"balance": "1000000000000000000000000000000" # 1 billion ether # noqa: E501
|
304
|
-
},
|
305
|
-
),
|
306
|
-
]
|
307
|
-
),
|
313
|
+
genesis_data["coinbase"] = coinbase
|
314
|
+
genesis_data.setdefault("alloc", {}).setdefault(
|
315
|
+
coinbase, {"balance": "1000000000000000000000000000000"}
|
308
316
|
)
|
309
|
-
initialize_chain(genesis_data, **geth_kwargs)
|
310
317
|
|
311
|
-
|
318
|
+
modify_genesis_based_on_geth_version(genesis_data)
|
319
|
+
initialize_chain(genesis_data, self.data_dir)
|
320
|
+
|
321
|
+
super().__init__(geth_kwargs)
|
322
|
+
|
323
|
+
@property
|
324
|
+
def data_dir(self) -> str:
|
325
|
+
return self._data_dir
|
326
|
+
|
327
|
+
|
328
|
+
def modify_genesis_based_on_geth_version(genesis_data: GenesisDataTypedDict) -> None:
|
329
|
+
geth_version = get_geth_version()
|
330
|
+
if geth_version <= semantic_version.Version("1.14.0"):
|
331
|
+
# geth <= v1.14.0 needs negative `terminalTotalDifficulty` to load EVM
|
332
|
+
# instructions correctly: https://github.com/ethereum/go-ethereum/pull/29579
|
333
|
+
if "config" not in genesis_data:
|
334
|
+
genesis_data["config"] = {}
|
335
|
+
genesis_data["config"]["terminalTotalDifficulty"] = -1
|
geth/py.typed
ADDED
File without changes
|