py-geth 4.2.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 -132
- 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.2.0.dist-info → py_geth-5.2.0.dist-info}/LICENSE +1 -1
- {py_geth-4.2.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.2.0.dist-info → py_geth-5.2.0.dist-info}/WHEEL +1 -1
- geth/utils/dag.py +0 -45
- py_geth-4.2.0.dist-info/RECORD +0 -25
- {py_geth-4.2.0.dist-info → py_geth-5.2.0.dist-info}/top_level.txt +0 -0
geth/reset.py
CHANGED
@@ -1,5 +1,23 @@
|
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
4
|
+
|
1
5
|
import os
|
2
6
|
|
7
|
+
from typing_extensions import (
|
8
|
+
Unpack,
|
9
|
+
)
|
10
|
+
|
11
|
+
from geth.exceptions import (
|
12
|
+
PyGethValueError,
|
13
|
+
)
|
14
|
+
from geth.types import (
|
15
|
+
GethKwargsTypedDict,
|
16
|
+
)
|
17
|
+
from geth.utils.validation import (
|
18
|
+
validate_geth_kwargs,
|
19
|
+
)
|
20
|
+
|
3
21
|
from .chains import (
|
4
22
|
is_live_chain,
|
5
23
|
is_testnet_chain,
|
@@ -9,47 +27,53 @@ from .utils.filesystem import (
|
|
9
27
|
remove_file_if_exists,
|
10
28
|
)
|
11
29
|
from .wrapper import (
|
12
|
-
|
30
|
+
spawn_geth,
|
13
31
|
)
|
14
32
|
|
15
33
|
|
16
|
-
def soft_reset_chain(
|
34
|
+
def soft_reset_chain(
|
35
|
+
allow_live: bool = False,
|
36
|
+
allow_testnet: bool = False,
|
37
|
+
**geth_kwargs: Unpack[GethKwargsTypedDict],
|
38
|
+
) -> None:
|
39
|
+
validate_geth_kwargs(geth_kwargs)
|
17
40
|
data_dir = geth_kwargs.get("data_dir")
|
18
41
|
|
19
42
|
if data_dir is None or (not allow_live and is_live_chain(data_dir)):
|
20
|
-
raise
|
43
|
+
raise PyGethValueError(
|
21
44
|
"To reset the live chain you must call this function with `allow_live=True`"
|
22
45
|
)
|
23
46
|
|
24
47
|
if not allow_testnet and is_testnet_chain(data_dir):
|
25
|
-
raise
|
48
|
+
raise PyGethValueError(
|
26
49
|
"To reset the testnet chain you must call this function with `allow_testnet=True`" # noqa: E501
|
27
50
|
)
|
28
51
|
|
29
|
-
suffix_args = geth_kwargs.pop("suffix_args"
|
52
|
+
suffix_args = geth_kwargs.pop("suffix_args") or []
|
30
53
|
suffix_args.extend(("removedb",))
|
54
|
+
geth_kwargs.update({"suffix_args": suffix_args})
|
31
55
|
|
32
|
-
|
33
|
-
|
34
|
-
_, proc = spawn_geth_subprocess(**geth_kwargs)
|
56
|
+
_, proc = spawn_geth(geth_kwargs)
|
35
57
|
|
36
|
-
stdoutdata, stderrdata = proc.communicate("y")
|
58
|
+
stdoutdata, stderrdata = proc.communicate(b"y")
|
37
59
|
|
38
|
-
if "Removing chaindata" not in stdoutdata:
|
39
|
-
raise
|
40
|
-
|
41
|
-
f"
|
60
|
+
if "Removing chaindata" not in stdoutdata.decode():
|
61
|
+
raise PyGethValueError(
|
62
|
+
"An error occurred while removing the chain:\n\nError:\n"
|
63
|
+
f"{stderrdata.decode()}\n\nOutput:\n{stdoutdata.decode()}"
|
42
64
|
)
|
43
65
|
|
44
66
|
|
45
|
-
def hard_reset_chain(
|
67
|
+
def hard_reset_chain(
|
68
|
+
data_dir: str, allow_live: bool = False, allow_testnet: bool = False
|
69
|
+
) -> None:
|
46
70
|
if not allow_live and is_live_chain(data_dir):
|
47
|
-
raise
|
71
|
+
raise PyGethValueError(
|
48
72
|
"To reset the live chain you must call this function with `allow_live=True`"
|
49
73
|
)
|
50
74
|
|
51
75
|
if not allow_testnet and is_testnet_chain(data_dir):
|
52
|
-
raise
|
76
|
+
raise PyGethValueError(
|
53
77
|
"To reset the testnet chain you must call this function with `allow_testnet=True`" # noqa: E501
|
54
78
|
)
|
55
79
|
|
geth/types.py
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
4
|
+
|
5
|
+
from typing import (
|
6
|
+
IO,
|
7
|
+
Any,
|
8
|
+
Literal,
|
9
|
+
TypedDict,
|
10
|
+
Union,
|
11
|
+
)
|
12
|
+
|
13
|
+
IO_Any = Union[IO[Any], int, None]
|
14
|
+
|
15
|
+
|
16
|
+
class GethKwargsTypedDict(TypedDict, total=False):
|
17
|
+
cache: str | None
|
18
|
+
data_dir: str | None
|
19
|
+
dev_mode: bool | None
|
20
|
+
gcmode: Literal["full", "archive"] | None
|
21
|
+
geth_executable: str | None
|
22
|
+
ipc_disable: bool | None
|
23
|
+
ipc_path: str | None
|
24
|
+
max_peers: str | None
|
25
|
+
network_id: str | None
|
26
|
+
nice: bool | None
|
27
|
+
no_discover: bool | None
|
28
|
+
password: bytes | str | None
|
29
|
+
port: str | None
|
30
|
+
preload: str | None
|
31
|
+
rpc_addr: str | None
|
32
|
+
rpc_api: str | None
|
33
|
+
rpc_cors_domain: str | None
|
34
|
+
rpc_enabled: bool | None
|
35
|
+
rpc_port: str | None
|
36
|
+
stdin: str | None
|
37
|
+
suffix_args: list[str] | None
|
38
|
+
suffix_kwargs: dict[str, str] | None
|
39
|
+
tx_pool_global_slots: str | None
|
40
|
+
tx_pool_lifetime: str | None
|
41
|
+
tx_pool_price_limit: str | None
|
42
|
+
verbosity: str | None
|
43
|
+
ws_addr: str | None
|
44
|
+
ws_api: str | None
|
45
|
+
ws_enabled: bool | None
|
46
|
+
ws_origins: str | None
|
47
|
+
ws_port: str | None
|
48
|
+
|
49
|
+
|
50
|
+
class GenesisDataTypedDict(TypedDict, total=False):
|
51
|
+
alloc: dict[str, dict[str, Any]]
|
52
|
+
baseFeePerGas: str
|
53
|
+
blobGasUsed: str
|
54
|
+
coinbase: str
|
55
|
+
config: dict[str, Any]
|
56
|
+
difficulty: str
|
57
|
+
excessBlobGas: str
|
58
|
+
extraData: str
|
59
|
+
gasLimit: str
|
60
|
+
gasUsed: str
|
61
|
+
mixHash: str
|
62
|
+
nonce: str
|
63
|
+
number: str
|
64
|
+
parentHash: str
|
65
|
+
timestamp: str
|
geth/utils/encoding.py
CHANGED
@@ -1,41 +1,53 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
|
4
|
-
text_types = (str,)
|
5
|
-
string_types = (bytes, str, bytearray)
|
6
|
-
|
7
|
-
|
8
|
-
def is_binary(value):
|
9
|
-
return isinstance(value, binary_types)
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
10
4
|
|
5
|
+
import codecs
|
6
|
+
from typing import (
|
7
|
+
Any,
|
8
|
+
)
|
11
9
|
|
12
|
-
|
13
|
-
|
10
|
+
from geth.exceptions import (
|
11
|
+
PyGethTypeError,
|
12
|
+
)
|
14
13
|
|
15
14
|
|
16
|
-
def is_string(value):
|
17
|
-
return isinstance(value,
|
15
|
+
def is_string(value: Any) -> bool:
|
16
|
+
return isinstance(value, (bytes, bytearray, str))
|
18
17
|
|
19
18
|
|
20
|
-
def force_bytes(value, encoding="iso-8859-1"):
|
21
|
-
if
|
19
|
+
def force_bytes(value: bytes | bytearray | str, encoding: str = "iso-8859-1") -> bytes:
|
20
|
+
if isinstance(value, bytes):
|
21
|
+
return value
|
22
|
+
elif isinstance(value, bytearray):
|
22
23
|
return bytes(value)
|
23
|
-
elif
|
24
|
-
|
24
|
+
elif isinstance(value, str):
|
25
|
+
encoded = codecs.encode(value, encoding)
|
26
|
+
if isinstance(encoded, (bytes, bytearray)):
|
27
|
+
return encoded
|
28
|
+
else:
|
29
|
+
raise PyGethTypeError(
|
30
|
+
f"Encoding {encoding!r} produced non-binary result: {encoded!r}"
|
31
|
+
)
|
25
32
|
else:
|
26
|
-
raise
|
33
|
+
raise PyGethTypeError(
|
34
|
+
f"Unsupported type: {type(value)}, expected bytes, bytearray or str"
|
35
|
+
)
|
27
36
|
|
28
37
|
|
29
|
-
def force_text(value, encoding="iso-8859-1"):
|
30
|
-
if
|
31
|
-
return value
|
32
|
-
elif is_binary(value):
|
38
|
+
def force_text(value: bytes | bytearray | str, encoding: str = "iso-8859-1") -> str:
|
39
|
+
if isinstance(value, (bytes, bytearray)):
|
33
40
|
return codecs.decode(value, encoding)
|
41
|
+
elif isinstance(value, str):
|
42
|
+
return value
|
34
43
|
else:
|
35
|
-
raise
|
44
|
+
raise PyGethTypeError(
|
45
|
+
f"Unsupported type: {type(value)}, "
|
46
|
+
"expected value to be bytes, bytearray or str"
|
47
|
+
)
|
36
48
|
|
37
49
|
|
38
|
-
def force_obj_to_text(obj):
|
50
|
+
def force_obj_to_text(obj: Any) -> Any:
|
39
51
|
if is_string(obj):
|
40
52
|
return force_text(obj)
|
41
53
|
elif isinstance(obj, dict):
|
geth/utils/filesystem.py
CHANGED
@@ -1,19 +1,12 @@
|
|
1
|
-
import errno
|
2
1
|
import os
|
3
2
|
import shutil
|
4
3
|
|
5
4
|
|
6
|
-
def mkdir(path):
|
7
|
-
|
8
|
-
os.makedirs(path)
|
9
|
-
except OSError as exc: # Python >2.5
|
10
|
-
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
11
|
-
pass
|
12
|
-
else:
|
13
|
-
raise
|
5
|
+
def mkdir(path: str) -> None:
|
6
|
+
os.makedirs(path, exist_ok=True)
|
14
7
|
|
15
8
|
|
16
|
-
def ensure_path_exists(dir_path):
|
9
|
+
def ensure_path_exists(dir_path: str) -> bool:
|
17
10
|
"""
|
18
11
|
Make sure that a path exists
|
19
12
|
"""
|
@@ -23,22 +16,22 @@ def ensure_path_exists(dir_path):
|
|
23
16
|
return False
|
24
17
|
|
25
18
|
|
26
|
-
def remove_file_if_exists(path):
|
19
|
+
def remove_file_if_exists(path: str) -> bool:
|
27
20
|
if os.path.isfile(path):
|
28
21
|
os.remove(path)
|
29
22
|
return True
|
30
23
|
return False
|
31
24
|
|
32
25
|
|
33
|
-
def remove_dir_if_exists(path):
|
26
|
+
def remove_dir_if_exists(path: str) -> bool:
|
34
27
|
if os.path.isdir(path):
|
35
28
|
shutil.rmtree(path)
|
36
29
|
return True
|
37
30
|
return False
|
38
31
|
|
39
32
|
|
40
|
-
def is_executable_available(program):
|
41
|
-
def is_exe(fpath):
|
33
|
+
def is_executable_available(program: str) -> bool:
|
34
|
+
def is_exe(fpath: str) -> bool:
|
42
35
|
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
43
36
|
|
44
37
|
fpath = os.path.dirname(program)
|
@@ -55,7 +48,7 @@ def is_executable_available(program):
|
|
55
48
|
return False
|
56
49
|
|
57
50
|
|
58
|
-
def is_same_path(p1, p2):
|
51
|
+
def is_same_path(p1: str, p2: str) -> bool:
|
59
52
|
n_p1 = os.path.abspath(os.path.expanduser(p1))
|
60
53
|
n_p2 = os.path.abspath(os.path.expanduser(p2))
|
61
54
|
|
geth/utils/networking.py
CHANGED
@@ -1,17 +1,24 @@
|
|
1
1
|
import contextlib
|
2
2
|
import socket
|
3
3
|
import time
|
4
|
+
from typing import (
|
5
|
+
Generator,
|
6
|
+
)
|
7
|
+
|
8
|
+
from geth.exceptions import (
|
9
|
+
PyGethValueError,
|
10
|
+
)
|
4
11
|
|
5
12
|
from .timeout import (
|
6
13
|
Timeout,
|
7
14
|
)
|
8
15
|
|
9
16
|
|
10
|
-
def is_port_open(port):
|
17
|
+
def is_port_open(port: int) -> bool:
|
11
18
|
sock = socket.socket()
|
12
19
|
try:
|
13
20
|
sock.bind(("127.0.0.1", port))
|
14
|
-
except
|
21
|
+
except OSError:
|
15
22
|
return False
|
16
23
|
else:
|
17
24
|
return True
|
@@ -19,7 +26,7 @@ def is_port_open(port):
|
|
19
26
|
sock.close()
|
20
27
|
|
21
28
|
|
22
|
-
def get_open_port():
|
29
|
+
def get_open_port() -> str:
|
23
30
|
sock = socket.socket()
|
24
31
|
sock.bind(("127.0.0.1", 0))
|
25
32
|
port = sock.getsockname()[1]
|
@@ -28,7 +35,9 @@ def get_open_port():
|
|
28
35
|
|
29
36
|
|
30
37
|
@contextlib.contextmanager
|
31
|
-
def get_ipc_socket(
|
38
|
+
def get_ipc_socket(
|
39
|
+
ipc_path: str, timeout: float = 0.1
|
40
|
+
) -> Generator[socket.socket, None, None]:
|
32
41
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
33
42
|
sock.connect(ipc_path)
|
34
43
|
sock.settimeout(timeout)
|
@@ -38,7 +47,7 @@ def get_ipc_socket(ipc_path, timeout=0.1):
|
|
38
47
|
sock.close()
|
39
48
|
|
40
49
|
|
41
|
-
def wait_for_http_connection(port, timeout=5):
|
50
|
+
def wait_for_http_connection(port: int, timeout: int = 5) -> None:
|
42
51
|
with Timeout(timeout) as _timeout:
|
43
52
|
while True:
|
44
53
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
@@ -52,4 +61,7 @@ def wait_for_http_connection(port, timeout=5):
|
|
52
61
|
else:
|
53
62
|
break
|
54
63
|
else:
|
55
|
-
raise
|
64
|
+
raise PyGethValueError(
|
65
|
+
"Unable to establish HTTP connection, "
|
66
|
+
f"timed out after {timeout} seconds"
|
67
|
+
)
|
geth/utils/proc.py
CHANGED
@@ -1,12 +1,20 @@
|
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
4
|
+
|
1
5
|
import signal
|
6
|
+
import subprocess
|
2
7
|
import time
|
8
|
+
from typing import (
|
9
|
+
AnyStr,
|
10
|
+
)
|
3
11
|
|
4
12
|
from .timeout import (
|
5
13
|
Timeout,
|
6
14
|
)
|
7
15
|
|
8
16
|
|
9
|
-
def wait_for_popen(proc, timeout=30):
|
17
|
+
def wait_for_popen(proc: subprocess.Popen[AnyStr], timeout: int = 30) -> None:
|
10
18
|
try:
|
11
19
|
with Timeout(timeout) as _timeout:
|
12
20
|
while proc.poll() is None:
|
@@ -16,7 +24,7 @@ def wait_for_popen(proc, timeout=30):
|
|
16
24
|
pass
|
17
25
|
|
18
26
|
|
19
|
-
def kill_proc(proc):
|
27
|
+
def kill_proc(proc: subprocess.Popen[AnyStr]) -> None:
|
20
28
|
try:
|
21
29
|
if proc.poll() is None:
|
22
30
|
try:
|
@@ -43,7 +51,9 @@ def kill_proc(proc):
|
|
43
51
|
proc.kill()
|
44
52
|
|
45
53
|
|
46
|
-
def format_error_message(
|
54
|
+
def format_error_message(
|
55
|
+
prefix: str, command: list[str], return_code: int, stdoutdata: str, stderrdata: str
|
56
|
+
) -> str:
|
47
57
|
lines = [prefix]
|
48
58
|
|
49
59
|
lines.append(f"Command : {' '.join(command)}")
|
geth/utils/thread.py
CHANGED
geth/utils/timeout.py
CHANGED
@@ -1,4 +1,19 @@
|
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
4
|
+
|
1
5
|
import time
|
6
|
+
from types import (
|
7
|
+
TracebackType,
|
8
|
+
)
|
9
|
+
from typing import (
|
10
|
+
Any,
|
11
|
+
Literal,
|
12
|
+
)
|
13
|
+
|
14
|
+
from geth.exceptions import (
|
15
|
+
PyGethValueError,
|
16
|
+
)
|
2
17
|
|
3
18
|
|
4
19
|
class Timeout(Exception):
|
@@ -11,43 +26,54 @@ class Timeout(Exception):
|
|
11
26
|
begun_at = None
|
12
27
|
is_running = None
|
13
28
|
|
14
|
-
def __init__(
|
29
|
+
def __init__(
|
30
|
+
self,
|
31
|
+
seconds: int | None = None,
|
32
|
+
exception: Any | None = None,
|
33
|
+
*args: Any,
|
34
|
+
**kwargs: Any,
|
35
|
+
):
|
15
36
|
self.seconds = seconds
|
16
37
|
self.exception = exception
|
17
38
|
|
18
|
-
def __enter__(self):
|
39
|
+
def __enter__(self) -> Timeout:
|
19
40
|
self.start()
|
20
41
|
return self
|
21
42
|
|
22
|
-
def __exit__(
|
43
|
+
def __exit__(
|
44
|
+
self,
|
45
|
+
exc_type: type[BaseException] | None,
|
46
|
+
exc_value: BaseException | None,
|
47
|
+
tb: TracebackType | None,
|
48
|
+
) -> Literal[False]:
|
23
49
|
return False
|
24
50
|
|
25
|
-
def __str__(self):
|
51
|
+
def __str__(self) -> str:
|
26
52
|
if self.seconds is None:
|
27
53
|
return ""
|
28
54
|
return f"{self.seconds} seconds"
|
29
55
|
|
30
56
|
@property
|
31
|
-
def expire_at(self):
|
57
|
+
def expire_at(self) -> float:
|
32
58
|
if self.seconds is None:
|
33
|
-
raise
|
59
|
+
raise PyGethValueError(
|
34
60
|
"Timeouts with `seconds == None` do not have an expiration time"
|
35
61
|
)
|
36
62
|
elif self.begun_at is None:
|
37
|
-
raise
|
63
|
+
raise PyGethValueError("Timeout has not been started")
|
38
64
|
return self.begun_at + self.seconds
|
39
65
|
|
40
|
-
def start(self):
|
66
|
+
def start(self) -> None:
|
41
67
|
if self.is_running is not None:
|
42
|
-
raise
|
68
|
+
raise PyGethValueError("Timeout has already been started")
|
43
69
|
self.begun_at = time.time()
|
44
70
|
self.is_running = True
|
45
71
|
|
46
|
-
def check(self):
|
72
|
+
def check(self) -> None:
|
47
73
|
if self.is_running is None:
|
48
|
-
raise
|
74
|
+
raise PyGethValueError("Timeout has not been started")
|
49
75
|
elif self.is_running is False:
|
50
|
-
raise
|
76
|
+
raise PyGethValueError("Timeout has already been cancelled")
|
51
77
|
elif self.seconds is None:
|
52
78
|
return
|
53
79
|
elif time.time() > self.expire_at:
|
@@ -59,5 +85,5 @@ class Timeout(Exception):
|
|
59
85
|
else:
|
60
86
|
raise self
|
61
87
|
|
62
|
-
def cancel(self):
|
88
|
+
def cancel(self) -> None:
|
63
89
|
self.is_running = False
|
geth/utils/validation.py
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
4
|
+
|
5
|
+
from typing import (
|
6
|
+
Any,
|
7
|
+
Literal,
|
8
|
+
)
|
9
|
+
|
10
|
+
from pydantic import (
|
11
|
+
BaseModel,
|
12
|
+
ConfigDict,
|
13
|
+
ValidationError,
|
14
|
+
)
|
15
|
+
|
16
|
+
from geth.exceptions import (
|
17
|
+
PyGethValueError,
|
18
|
+
)
|
19
|
+
from geth.types import (
|
20
|
+
GenesisDataTypedDict,
|
21
|
+
GethKwargsTypedDict,
|
22
|
+
)
|
23
|
+
|
24
|
+
|
25
|
+
class GethKwargs(BaseModel):
|
26
|
+
cache: str | None = None
|
27
|
+
data_dir: str | None = None
|
28
|
+
dev_mode: bool | None = False
|
29
|
+
gcmode: Literal["full", "archive"] | None = None
|
30
|
+
geth_executable: str | None = None
|
31
|
+
ipc_disable: bool | None = None
|
32
|
+
ipc_path: str | None = None
|
33
|
+
max_peers: str | None = None
|
34
|
+
network_id: str | None = None
|
35
|
+
nice: bool | None = True
|
36
|
+
no_discover: bool | None = None
|
37
|
+
password: bytes | str | None = None
|
38
|
+
port: str | None = None
|
39
|
+
preload: str | None = None
|
40
|
+
rpc_addr: str | None = None
|
41
|
+
rpc_api: str | None = None
|
42
|
+
rpc_cors_domain: str | None = None
|
43
|
+
rpc_enabled: bool | None = None
|
44
|
+
rpc_port: str | None = None
|
45
|
+
stdin: str | None = None
|
46
|
+
suffix_args: list[str] | None = None
|
47
|
+
suffix_kwargs: dict[str, str] | None = None
|
48
|
+
tx_pool_global_slots: str | None = None
|
49
|
+
tx_pool_lifetime: str | None = None
|
50
|
+
tx_pool_price_limit: str | None = None
|
51
|
+
verbosity: str | None = None
|
52
|
+
ws_addr: str | None = None
|
53
|
+
ws_api: str | None = None
|
54
|
+
ws_enabled: bool | None = None
|
55
|
+
ws_origins: str | None = None
|
56
|
+
ws_port: str | None = None
|
57
|
+
|
58
|
+
model_config = ConfigDict(extra="forbid")
|
59
|
+
|
60
|
+
|
61
|
+
def validate_geth_kwargs(geth_kwargs: GethKwargsTypedDict) -> None:
|
62
|
+
"""
|
63
|
+
Converts geth_kwargs to GethKwargs and raises a ValueError if the conversion fails.
|
64
|
+
"""
|
65
|
+
try:
|
66
|
+
GethKwargs(**geth_kwargs)
|
67
|
+
except ValidationError as e:
|
68
|
+
raise PyGethValueError(f"geth_kwargs validation failed: {e}")
|
69
|
+
except TypeError as e:
|
70
|
+
raise PyGethValueError(f"error while validating geth_kwargs: {e}")
|
71
|
+
|
72
|
+
|
73
|
+
class GenesisDataConfig(BaseModel):
|
74
|
+
chainId: int = 0
|
75
|
+
ethash: dict[str, Any] = {} # so that geth treats config as PoW -> PoS transition
|
76
|
+
homesteadBlock: int = 0
|
77
|
+
daoForkBlock: int = 0
|
78
|
+
daoForkSupport: bool = True
|
79
|
+
eip150Block: int = 0
|
80
|
+
eip155Block: int = 0
|
81
|
+
eip158Block: int = 0
|
82
|
+
byzantiumBlock: int = 0
|
83
|
+
constantinopleBlock: int = 0
|
84
|
+
petersburgBlock: int = 0
|
85
|
+
istanbulBlock: int = 0
|
86
|
+
berlinBlock: int = 0
|
87
|
+
londonBlock: int = 0
|
88
|
+
arrowGlacierBlock: int = 0
|
89
|
+
grayGlacierBlock: int = 0
|
90
|
+
# merge
|
91
|
+
terminalTotalDifficulty: int = 0
|
92
|
+
terminalTotalDifficultyPassed: bool = True
|
93
|
+
# post-merge, timestamp is used for network transitions
|
94
|
+
shanghaiTime: int = 0
|
95
|
+
cancunTime: int = 0
|
96
|
+
|
97
|
+
model_config = ConfigDict(extra="forbid")
|
98
|
+
|
99
|
+
|
100
|
+
class GenesisData(BaseModel):
|
101
|
+
alloc: dict[str, dict[str, Any]] = {}
|
102
|
+
baseFeePerGas: str = "0x0"
|
103
|
+
blobGasUsed: str = "0x0"
|
104
|
+
coinbase: str = "0x3333333333333333333333333333333333333333"
|
105
|
+
config: dict[str, Any] = GenesisDataConfig().model_dump()
|
106
|
+
difficulty: str = "0x0"
|
107
|
+
excessBlobGas: str = "0x0"
|
108
|
+
extraData: str = (
|
109
|
+
"0x0000000000000000000000000000000000000000000000000000000000000000"
|
110
|
+
)
|
111
|
+
gasLimit: str = "0x47e7c4"
|
112
|
+
gasUsed: str = "0x0"
|
113
|
+
mixHash: str = "0x0000000000000000000000000000000000000000000000000000000000000000"
|
114
|
+
nonce: str = "0x0"
|
115
|
+
number: str = "0x0"
|
116
|
+
parentHash: str = (
|
117
|
+
"0x0000000000000000000000000000000000000000000000000000000000000000"
|
118
|
+
)
|
119
|
+
timestamp: str = "0x0"
|
120
|
+
|
121
|
+
model_config = ConfigDict(extra="forbid")
|
122
|
+
|
123
|
+
|
124
|
+
def validate_genesis_data(genesis_data: GenesisDataTypedDict) -> None:
|
125
|
+
"""
|
126
|
+
Validates the genesis data
|
127
|
+
"""
|
128
|
+
try:
|
129
|
+
GenesisData(**genesis_data)
|
130
|
+
except ValidationError as e:
|
131
|
+
raise PyGethValueError(f"genesis_data validation failed: {e}")
|
132
|
+
except TypeError as e:
|
133
|
+
raise PyGethValueError(f"error while validating genesis_data: {e}")
|
134
|
+
|
135
|
+
"""
|
136
|
+
Validates the genesis data config field
|
137
|
+
"""
|
138
|
+
genesis_data_config = genesis_data.get("config", None)
|
139
|
+
if genesis_data_config:
|
140
|
+
try:
|
141
|
+
GenesisDataConfig(**genesis_data_config)
|
142
|
+
except ValidationError as e:
|
143
|
+
raise PyGethValueError(f"genesis_data config field validation failed: {e}")
|
144
|
+
except TypeError as e:
|
145
|
+
raise PyGethValueError(
|
146
|
+
f"error while validating genesis_data config field: {e}"
|
147
|
+
)
|
148
|
+
|
149
|
+
|
150
|
+
def fill_default_genesis_data(
|
151
|
+
genesis_data: GenesisDataTypedDict,
|
152
|
+
) -> GenesisData:
|
153
|
+
"""
|
154
|
+
Fills in default values for the genesis data
|
155
|
+
"""
|
156
|
+
try:
|
157
|
+
genesis_data_filled = GenesisData(**genesis_data)
|
158
|
+
except ValidationError as e:
|
159
|
+
raise PyGethValueError(
|
160
|
+
f"genesis_data validation failed while filling defaults: {e}"
|
161
|
+
)
|
162
|
+
except TypeError as e:
|
163
|
+
raise PyGethValueError(f"error while filling default genesis_data: {e}")
|
164
|
+
|
165
|
+
if genesis_data.get("config"):
|
166
|
+
try:
|
167
|
+
genesis_data_config_filled = GenesisDataConfig(**genesis_data["config"])
|
168
|
+
except ValidationError as e:
|
169
|
+
raise PyGethValueError(
|
170
|
+
f"genesis_data validation failed while filling config defaults: {e}"
|
171
|
+
)
|
172
|
+
except TypeError as e:
|
173
|
+
raise PyGethValueError(
|
174
|
+
f"error while filling default genesis_data config: {e}"
|
175
|
+
)
|
176
|
+
|
177
|
+
genesis_data_filled.config = genesis_data_config_filled.model_dump()
|
178
|
+
|
179
|
+
return genesis_data_filled
|