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/__init__.py
CHANGED
@@ -14,10 +14,20 @@ from .mixins import (
|
|
14
14
|
)
|
15
15
|
from .process import (
|
16
16
|
DevGethProcess,
|
17
|
-
LiveGethProcess,
|
18
17
|
MainnetGethProcess,
|
19
|
-
|
18
|
+
SepoliaGethProcess,
|
20
19
|
TestnetGethProcess,
|
21
20
|
)
|
22
21
|
|
23
22
|
__version__ = __version("py-geth")
|
23
|
+
|
24
|
+
__all__ = (
|
25
|
+
"install_geth",
|
26
|
+
"get_geth_version",
|
27
|
+
"InterceptedStreamsMixin",
|
28
|
+
"LoggingMixin",
|
29
|
+
"MainnetGethProcess",
|
30
|
+
"SepoliaGethProcess",
|
31
|
+
"TestnetGethProcess",
|
32
|
+
"DevGethProcess",
|
33
|
+
)
|
geth/accounts.py
CHANGED
@@ -1,6 +1,24 @@
|
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
4
|
+
|
1
5
|
import os
|
2
6
|
import re
|
3
7
|
|
8
|
+
from typing_extensions import (
|
9
|
+
Unpack,
|
10
|
+
)
|
11
|
+
|
12
|
+
from geth.exceptions import (
|
13
|
+
PyGethValueError,
|
14
|
+
)
|
15
|
+
from geth.types import (
|
16
|
+
GethKwargsTypedDict,
|
17
|
+
)
|
18
|
+
from geth.utils.validation import (
|
19
|
+
validate_geth_kwargs,
|
20
|
+
)
|
21
|
+
|
4
22
|
from .utils.proc import (
|
5
23
|
format_error_message,
|
6
24
|
)
|
@@ -9,29 +27,36 @@ from .wrapper import (
|
|
9
27
|
)
|
10
28
|
|
11
29
|
|
12
|
-
def get_accounts(
|
30
|
+
def get_accounts(
|
31
|
+
**geth_kwargs: Unpack[GethKwargsTypedDict],
|
32
|
+
) -> tuple[str, ...] | tuple[()]:
|
13
33
|
"""
|
14
34
|
Returns all geth accounts as tuple of hex encoded strings
|
15
35
|
|
16
|
-
>>>
|
36
|
+
>>> get_accounts(data_dir='some/data/dir')
|
17
37
|
... ('0x...', '0x...')
|
18
38
|
"""
|
19
|
-
|
20
|
-
|
21
|
-
)
|
39
|
+
validate_geth_kwargs(geth_kwargs)
|
40
|
+
|
41
|
+
if not geth_kwargs.get("data_dir"):
|
42
|
+
raise PyGethValueError("data_dir is required to get accounts")
|
43
|
+
|
44
|
+
geth_kwargs["suffix_args"] = ["account", "list"]
|
45
|
+
|
46
|
+
command, proc = spawn_geth(geth_kwargs)
|
22
47
|
stdoutdata, stderrdata = proc.communicate()
|
23
48
|
|
24
49
|
if proc.returncode:
|
25
|
-
if "no keys in store" in stderrdata.decode(
|
50
|
+
if "no keys in store" in stderrdata.decode():
|
26
51
|
return tuple()
|
27
52
|
else:
|
28
|
-
raise
|
53
|
+
raise PyGethValueError(
|
29
54
|
format_error_message(
|
30
55
|
"Error trying to list accounts",
|
31
56
|
command,
|
32
57
|
proc.returncode,
|
33
|
-
stdoutdata,
|
34
|
-
stderrdata,
|
58
|
+
stdoutdata.decode(),
|
59
|
+
stderrdata.decode(),
|
35
60
|
)
|
36
61
|
)
|
37
62
|
accounts = parse_geth_accounts(stdoutdata)
|
@@ -41,8 +66,8 @@ def get_accounts(data_dir, **geth_kwargs):
|
|
41
66
|
account_regex = re.compile(b"([a-f0-9]{40})")
|
42
67
|
|
43
68
|
|
44
|
-
def create_new_account(
|
45
|
-
"""
|
69
|
+
def create_new_account(**geth_kwargs: Unpack[GethKwargsTypedDict]) -> str:
|
70
|
+
r"""
|
46
71
|
Creates a new Ethereum account on geth.
|
47
72
|
|
48
73
|
This is useful for testing when you want to stress
|
@@ -57,11 +82,11 @@ def create_new_account(data_dir, password, **geth_kwargs):
|
|
57
82
|
If geth process is already running you can create new
|
58
83
|
accounts using
|
59
84
|
`web3.personal.newAccount()
|
60
|
-
<https://
|
61
|
-
RPC API.
|
85
|
+
<https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal>_`
|
62
86
|
|
87
|
+
RPC API.
|
63
88
|
|
64
|
-
Example
|
89
|
+
Example pytest fixture for tests:
|
65
90
|
|
66
91
|
.. code-block:: python
|
67
92
|
|
@@ -92,59 +117,85 @@ def create_new_account(data_dir, password, **geth_kwargs):
|
|
92
117
|
account = create_new_account(data_dir, DEFAULT_PASSWORD_PATH)
|
93
118
|
return account
|
94
119
|
|
95
|
-
:param
|
96
|
-
|
97
|
-
|
98
|
-
:
|
120
|
+
:param \**geth_kwargs:
|
121
|
+
Command line arguments to pass to geth. See below:
|
122
|
+
|
123
|
+
:Required Keyword Arguments:
|
124
|
+
* *data_dir* (``str``) --
|
125
|
+
Geth datadir path - where to keep "keystore" folder
|
126
|
+
* *password* (``str`` or ``bytes``) --
|
127
|
+
Password to use for the new account, either the password as bytes or a str
|
128
|
+
path to a file containing the password.
|
129
|
+
|
99
130
|
:return: Account as 0x prefixed hex string
|
131
|
+
:rtype: str
|
100
132
|
"""
|
101
|
-
if
|
102
|
-
|
133
|
+
if not geth_kwargs.get("data_dir"):
|
134
|
+
raise PyGethValueError("data_dir is required to create a new account")
|
135
|
+
|
136
|
+
if not geth_kwargs.get("password"):
|
137
|
+
raise PyGethValueError("password is required to create a new account")
|
103
138
|
|
104
|
-
|
105
|
-
dict(data_dir=data_dir, suffix_args=["account", "new"], **geth_kwargs)
|
106
|
-
)
|
139
|
+
password = geth_kwargs.get("password")
|
107
140
|
|
108
|
-
|
141
|
+
geth_kwargs.update({"suffix_args": ["account", "new"]})
|
142
|
+
validate_geth_kwargs(geth_kwargs)
|
143
|
+
|
144
|
+
if isinstance(password, str):
|
145
|
+
if not os.path.exists(password):
|
146
|
+
raise PyGethValueError(f"Password file not found at path: {password}")
|
147
|
+
elif not isinstance(password, bytes):
|
148
|
+
raise PyGethValueError(
|
149
|
+
"Password must be either a str (path to a file) or bytes"
|
150
|
+
)
|
151
|
+
|
152
|
+
command, proc = spawn_geth(geth_kwargs)
|
153
|
+
|
154
|
+
if isinstance(password, str):
|
109
155
|
stdoutdata, stderrdata = proc.communicate()
|
110
156
|
else:
|
111
157
|
stdoutdata, stderrdata = proc.communicate(b"\n".join((password, password)))
|
112
158
|
|
113
159
|
if proc.returncode:
|
114
|
-
raise
|
160
|
+
raise PyGethValueError(
|
115
161
|
format_error_message(
|
116
162
|
"Error trying to create a new account",
|
117
163
|
command,
|
118
164
|
proc.returncode,
|
119
|
-
stdoutdata,
|
120
|
-
stderrdata,
|
165
|
+
stdoutdata.decode(),
|
166
|
+
stderrdata.decode(),
|
121
167
|
)
|
122
168
|
)
|
123
169
|
|
124
170
|
match = account_regex.search(stdoutdata)
|
125
171
|
if not match:
|
126
|
-
raise
|
172
|
+
raise PyGethValueError(
|
127
173
|
format_error_message(
|
128
174
|
"Did not find an address in process output",
|
129
175
|
command,
|
130
176
|
proc.returncode,
|
131
|
-
stdoutdata,
|
132
|
-
stderrdata,
|
177
|
+
stdoutdata.decode(),
|
178
|
+
stderrdata.decode(),
|
133
179
|
)
|
134
180
|
)
|
135
181
|
|
136
|
-
return
|
182
|
+
return "0x" + match.groups()[0].decode()
|
183
|
+
|
137
184
|
|
185
|
+
def ensure_account_exists(**geth_kwargs: Unpack[GethKwargsTypedDict]) -> str:
|
186
|
+
if not geth_kwargs.get("data_dir"):
|
187
|
+
raise PyGethValueError("data_dir is required to get accounts")
|
138
188
|
|
139
|
-
|
140
|
-
accounts = get_accounts(
|
189
|
+
validate_geth_kwargs(geth_kwargs)
|
190
|
+
accounts = get_accounts(**geth_kwargs)
|
141
191
|
if not accounts:
|
142
|
-
account = create_new_account(
|
192
|
+
account = create_new_account(**geth_kwargs)
|
143
193
|
else:
|
144
194
|
account = accounts[0]
|
145
195
|
return account
|
146
196
|
|
147
197
|
|
148
|
-
def parse_geth_accounts(raw_accounts_output):
|
198
|
+
def parse_geth_accounts(raw_accounts_output: bytes) -> tuple[str, ...]:
|
149
199
|
accounts = account_regex.findall(raw_accounts_output)
|
150
|
-
|
200
|
+
accounts_set = set(accounts) # remove duplicates
|
201
|
+
return tuple("0x" + account.decode() for account in accounts_set)
|
geth/chain.py
CHANGED
@@ -1,7 +1,23 @@
|
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
4
|
+
|
1
5
|
import json
|
2
6
|
import os
|
7
|
+
import subprocess
|
3
8
|
import sys
|
4
9
|
|
10
|
+
from typing_extensions import (
|
11
|
+
Unpack,
|
12
|
+
)
|
13
|
+
|
14
|
+
from geth.exceptions import (
|
15
|
+
PyGethValueError,
|
16
|
+
)
|
17
|
+
from geth.types import (
|
18
|
+
GenesisDataTypedDict,
|
19
|
+
)
|
20
|
+
|
5
21
|
from .utils.encoding import (
|
6
22
|
force_obj_to_text,
|
7
23
|
)
|
@@ -9,12 +25,16 @@ from .utils.filesystem import (
|
|
9
25
|
ensure_path_exists,
|
10
26
|
is_same_path,
|
11
27
|
)
|
28
|
+
from .utils.validation import (
|
29
|
+
fill_default_genesis_data,
|
30
|
+
validate_genesis_data,
|
31
|
+
)
|
12
32
|
from .wrapper import (
|
13
|
-
|
33
|
+
get_geth_binary_path,
|
14
34
|
)
|
15
35
|
|
16
36
|
|
17
|
-
def get_live_data_dir():
|
37
|
+
def get_live_data_dir() -> str:
|
18
38
|
"""
|
19
39
|
`py-geth` needs a base directory to store it's chain data. By default this is
|
20
40
|
the directory that `geth` uses as it's `datadir`.
|
@@ -46,124 +66,89 @@ def get_live_data_dir():
|
|
46
66
|
)
|
47
67
|
|
48
68
|
else:
|
49
|
-
raise
|
50
|
-
|
51
|
-
|
52
|
-
" supported. You must specify the geth datadir manually"
|
53
|
-
)
|
69
|
+
raise PyGethValueError(
|
70
|
+
f"Unsupported platform: '{sys.platform}'. Only darwin/linux2/win32 are"
|
71
|
+
" supported. You must specify the geth datadir manually"
|
54
72
|
)
|
55
73
|
return data_dir
|
56
74
|
|
57
75
|
|
58
|
-
def
|
76
|
+
def get_sepolia_data_dir() -> str:
|
59
77
|
return os.path.abspath(
|
60
78
|
os.path.expanduser(
|
61
79
|
os.path.join(
|
62
80
|
get_live_data_dir(),
|
63
|
-
"
|
81
|
+
"sepolia",
|
64
82
|
)
|
65
83
|
)
|
66
84
|
)
|
67
85
|
|
68
86
|
|
69
|
-
def get_default_base_dir():
|
87
|
+
def get_default_base_dir() -> str:
|
70
88
|
return get_live_data_dir()
|
71
89
|
|
72
90
|
|
73
|
-
def get_chain_data_dir(base_dir, name):
|
91
|
+
def get_chain_data_dir(base_dir: str, name: str) -> str:
|
74
92
|
data_dir = os.path.abspath(os.path.join(base_dir, name))
|
75
93
|
ensure_path_exists(data_dir)
|
76
94
|
return data_dir
|
77
95
|
|
78
96
|
|
79
|
-
def get_genesis_file_path(data_dir):
|
97
|
+
def get_genesis_file_path(data_dir: str) -> str:
|
80
98
|
return os.path.join(data_dir, "genesis.json")
|
81
99
|
|
82
100
|
|
83
|
-
def is_live_chain(data_dir):
|
101
|
+
def is_live_chain(data_dir: str) -> bool:
|
84
102
|
return is_same_path(data_dir, get_live_data_dir())
|
85
103
|
|
86
104
|
|
87
|
-
def
|
88
|
-
return is_same_path(data_dir,
|
105
|
+
def is_sepolia_chain(data_dir: str) -> bool:
|
106
|
+
return is_same_path(data_dir, get_sepolia_data_dir())
|
89
107
|
|
90
108
|
|
91
109
|
def write_genesis_file(
|
92
|
-
genesis_file_path,
|
93
|
-
overwrite=False,
|
94
|
-
|
95
|
-
|
96
|
-
parentHash="0x0000000000000000000000000000000000000000000000000000000000000000",
|
97
|
-
extraData=None,
|
98
|
-
gasLimit="0x47d5cc",
|
99
|
-
difficulty="0x01",
|
100
|
-
mixhash="0x0000000000000000000000000000000000000000000000000000000000000000",
|
101
|
-
coinbase="0x3333333333333333333333333333333333333333",
|
102
|
-
alloc=None,
|
103
|
-
config=None,
|
104
|
-
clique_period: int = 5,
|
105
|
-
clique_epoch: int = 30000,
|
106
|
-
):
|
110
|
+
genesis_file_path: str,
|
111
|
+
overwrite: bool = False,
|
112
|
+
**genesis_data: Unpack[GenesisDataTypedDict],
|
113
|
+
) -> None:
|
107
114
|
if os.path.exists(genesis_file_path) and not overwrite:
|
108
|
-
raise
|
109
|
-
"Genesis file already present.
|
115
|
+
raise PyGethValueError(
|
116
|
+
"Genesis file already present. Call with "
|
117
|
+
"`overwrite=True` to overwrite this file"
|
110
118
|
)
|
111
119
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
if config is None:
|
116
|
-
config = {
|
117
|
-
"homesteadBlock": 0,
|
118
|
-
"eip150Block": 0,
|
119
|
-
"eip155Block": 0,
|
120
|
-
"eip158Block": 0,
|
121
|
-
"byzantiumBlock": 0,
|
122
|
-
"constantinopleBlock": 0,
|
123
|
-
"petersburgBlock": 0,
|
124
|
-
"istanbulBlock": 0,
|
125
|
-
"berlinBlock": 0,
|
126
|
-
"londonBlock": 0,
|
127
|
-
"shanghaiBlock": 0,
|
128
|
-
"daoForkBlock": 0,
|
129
|
-
"daoForSupport": True,
|
130
|
-
# Using the Ethash consensus algorithm is deprecated
|
131
|
-
# Instead, use the Clique consensus algorithm
|
132
|
-
# https://geth.ethereum.org/docs/interface/private-network
|
133
|
-
"clique": {"period": clique_period, "epoch": clique_epoch},
|
134
|
-
}
|
135
|
-
|
136
|
-
# Assign a signer (coinbase) to the genesis block for Clique
|
137
|
-
extraData = (
|
138
|
-
bytes("0x" + "0" * 64 + coinbase[2:] + "0" * 130, "ascii")
|
139
|
-
if extraData is None
|
140
|
-
else extraData
|
141
|
-
)
|
142
|
-
|
143
|
-
genesis_data = {
|
144
|
-
"nonce": nonce,
|
145
|
-
"timestamp": timestamp,
|
146
|
-
"parentHash": parentHash,
|
147
|
-
"extraData": extraData,
|
148
|
-
"gasLimit": gasLimit,
|
149
|
-
"difficulty": difficulty,
|
150
|
-
"mixhash": mixhash,
|
151
|
-
"coinbase": coinbase,
|
152
|
-
"alloc": alloc,
|
153
|
-
"config": config,
|
154
|
-
}
|
120
|
+
validate_genesis_data(genesis_data)
|
121
|
+
# use GenesisData model to fill defaults
|
122
|
+
filled_genesis_data_model = fill_default_genesis_data(genesis_data)
|
155
123
|
|
156
124
|
with open(genesis_file_path, "w") as genesis_file:
|
157
|
-
genesis_file.write(
|
125
|
+
genesis_file.write(
|
126
|
+
json.dumps(force_obj_to_text(filled_genesis_data_model.model_dump()))
|
127
|
+
)
|
158
128
|
|
159
129
|
|
160
|
-
def initialize_chain(genesis_data, data_dir
|
130
|
+
def initialize_chain(genesis_data: GenesisDataTypedDict, data_dir: str) -> None:
|
131
|
+
validate_genesis_data(genesis_data)
|
132
|
+
# init with genesis.json
|
161
133
|
genesis_file_path = get_genesis_file_path(data_dir)
|
162
134
|
write_genesis_file(genesis_file_path, **genesis_data)
|
163
|
-
|
164
|
-
|
135
|
+
init_proc = subprocess.Popen(
|
136
|
+
(
|
137
|
+
get_geth_binary_path(),
|
138
|
+
"--datadir",
|
139
|
+
data_dir,
|
140
|
+
"init",
|
141
|
+
genesis_file_path,
|
142
|
+
),
|
143
|
+
stdin=subprocess.PIPE,
|
144
|
+
stdout=subprocess.PIPE,
|
145
|
+
stderr=subprocess.PIPE,
|
165
146
|
)
|
166
|
-
stdoutdata, stderrdata =
|
167
|
-
|
168
|
-
if
|
169
|
-
raise
|
147
|
+
stdoutdata, stderrdata = init_proc.communicate()
|
148
|
+
init_proc.wait()
|
149
|
+
if init_proc.returncode:
|
150
|
+
raise PyGethValueError(
|
151
|
+
"Error initializing genesis.json: \n"
|
152
|
+
f" stdout={stdoutdata.decode()}\n"
|
153
|
+
f" stderr={stderrdata.decode()}"
|
154
|
+
)
|
geth/exceptions.py
CHANGED
@@ -1,23 +1,61 @@
|
|
1
|
-
import
|
1
|
+
from __future__ import (
|
2
|
+
annotations,
|
3
|
+
)
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
+
import codecs
|
6
|
+
import textwrap
|
7
|
+
from typing import (
|
8
|
+
Any,
|
5
9
|
)
|
6
10
|
|
7
11
|
|
8
|
-
def force_text_maybe(value):
|
9
|
-
if value
|
10
|
-
return
|
12
|
+
def force_text_maybe(value: bytes | bytearray | str | None) -> str | None:
|
13
|
+
if isinstance(value, (bytes, bytearray)):
|
14
|
+
return codecs.decode(value, "utf8")
|
15
|
+
elif isinstance(value, str) or value is None:
|
16
|
+
return value
|
17
|
+
else:
|
18
|
+
raise PyGethTypeError(f"Unsupported type: {type(value)}")
|
19
|
+
|
20
|
+
|
21
|
+
class PyGethException(Exception):
|
22
|
+
"""
|
23
|
+
Exception mixin inherited by all exceptions of py-geth
|
24
|
+
|
25
|
+
This allows::
|
26
|
+
|
27
|
+
try:
|
28
|
+
some_call()
|
29
|
+
except PyGethException:
|
30
|
+
# deal with py-geth exception
|
31
|
+
except:
|
32
|
+
# deal with other exceptions
|
33
|
+
"""
|
11
34
|
|
35
|
+
user_message: str | None = None
|
12
36
|
|
13
|
-
|
37
|
+
def __init__(
|
38
|
+
self,
|
39
|
+
*args: Any,
|
40
|
+
user_message: str | None = None,
|
41
|
+
):
|
42
|
+
super().__init__(*args)
|
43
|
+
|
44
|
+
# Assign properties of PyGethException
|
45
|
+
self.user_message = user_message
|
14
46
|
|
15
47
|
|
16
48
|
class GethError(Exception):
|
17
|
-
message =
|
49
|
+
message = "An error occurred during execution"
|
18
50
|
|
19
51
|
def __init__(
|
20
|
-
self,
|
52
|
+
self,
|
53
|
+
command: list[str],
|
54
|
+
return_code: int,
|
55
|
+
stdin_data: str | bytes | bytearray | None = None,
|
56
|
+
stdout_data: str | bytes | bytearray | None = None,
|
57
|
+
stderr_data: str | bytes | bytearray | None = None,
|
58
|
+
message: str | None = None,
|
21
59
|
):
|
22
60
|
if message is not None:
|
23
61
|
self.message = message
|
@@ -27,10 +65,9 @@ class GethError(Exception):
|
|
27
65
|
self.stderr_data = force_text_maybe(stderr_data)
|
28
66
|
self.stdout_data = force_text_maybe(stdout_data)
|
29
67
|
|
30
|
-
def __str__(self):
|
68
|
+
def __str__(self) -> str:
|
31
69
|
return textwrap.dedent(
|
32
|
-
|
33
|
-
f"""
|
70
|
+
f"""
|
34
71
|
{self.message}
|
35
72
|
> command: `{" ".join(self.command)}`
|
36
73
|
> return code: `{self.return_code}`
|
@@ -39,5 +76,41 @@ class GethError(Exception):
|
|
39
76
|
> stdout:
|
40
77
|
{self.stderr_data}
|
41
78
|
"""
|
42
|
-
)
|
43
79
|
).strip()
|
80
|
+
|
81
|
+
|
82
|
+
class PyGethGethError(PyGethException, GethError):
|
83
|
+
def __init__(
|
84
|
+
self,
|
85
|
+
*args: Any,
|
86
|
+
**kwargs: Any,
|
87
|
+
):
|
88
|
+
GethError.__init__(*args, **kwargs)
|
89
|
+
|
90
|
+
|
91
|
+
class PyGethAttributeError(PyGethException, AttributeError):
|
92
|
+
pass
|
93
|
+
|
94
|
+
|
95
|
+
class PyGethKeyError(PyGethException, KeyError):
|
96
|
+
pass
|
97
|
+
|
98
|
+
|
99
|
+
class PyGethTypeError(PyGethException, TypeError):
|
100
|
+
pass
|
101
|
+
|
102
|
+
|
103
|
+
class PyGethValueError(PyGethException, ValueError):
|
104
|
+
pass
|
105
|
+
|
106
|
+
|
107
|
+
class PyGethOSError(PyGethException, OSError):
|
108
|
+
pass
|
109
|
+
|
110
|
+
|
111
|
+
class PyGethNotImplementedError(PyGethException, NotImplementedError):
|
112
|
+
pass
|
113
|
+
|
114
|
+
|
115
|
+
class PyGethFileNotFoundError(PyGethException, FileNotFoundError):
|
116
|
+
pass
|
geth/genesis.json
CHANGED
@@ -1,12 +1,29 @@
|
|
1
1
|
{
|
2
|
-
"
|
2
|
+
"config": {
|
3
|
+
"ethash": {},
|
4
|
+
"homesteadBlock": 0,
|
5
|
+
"eip150Block": 0,
|
6
|
+
"eip155Block": 0,
|
7
|
+
"eip158Block": 0,
|
8
|
+
"byzantiumBlock": 0,
|
9
|
+
"constantinopleBlock": 0,
|
10
|
+
"petersburgBlock": 0,
|
11
|
+
"istanbulBlock": 0,
|
12
|
+
"berlinBlock": 0,
|
13
|
+
"londonBlock": 0,
|
14
|
+
"arrowGlacierBlock": 0,
|
15
|
+
"grayGlacierBlock": 0,
|
16
|
+
"terminalTotalDifficulty": 0,
|
17
|
+
"terminalTotalDifficultyPassed": true,
|
18
|
+
"shanghaiTime": 0,
|
19
|
+
"cancunTime": 0
|
20
|
+
},
|
21
|
+
"nonce": "0x0",
|
3
22
|
"timestamp": "0x0",
|
4
23
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
5
|
-
"extraData": "
|
6
|
-
"gasLimit": "
|
7
|
-
"difficulty": "
|
8
|
-
"
|
9
|
-
"
|
10
|
-
"alloc": {
|
11
|
-
}
|
24
|
+
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
25
|
+
"gasLimit": "0x47e7c4",
|
26
|
+
"difficulty": "0x0",
|
27
|
+
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
28
|
+
"alloc": {}
|
12
29
|
}
|