xrpld-lab 3.0.0__tar.gz
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.
- xrpld_lab-3.0.0/PKG-INFO +16 -0
- xrpld_lab-3.0.0/pyproject.toml +25 -0
- xrpld_lab-3.0.0/xrpld_lab/__init__.py +1 -0
- xrpld_lab-3.0.0/xrpld_lab/amendments.py +147 -0
- xrpld_lab-3.0.0/xrpld_lab/ansible_builder.py +1333 -0
- xrpld_lab-3.0.0/xrpld_lab/cli.py +534 -0
- xrpld_lab-3.0.0/xrpld_lab/compose_builder.py +213 -0
- xrpld_lab-3.0.0/xrpld_lab/config.py +170 -0
- xrpld_lab-3.0.0/xrpld_lab/config_builder.py +438 -0
- xrpld_lab-3.0.0/xrpld_lab/genesis.xahau.json +185 -0
- xrpld_lab-3.0.0/xrpld_lab/genesis.xrpl.json +52 -0
- xrpld_lab-3.0.0/xrpld_lab/models.py +390 -0
- xrpld_lab-3.0.0/xrpld_lab/node_factory.py +258 -0
- xrpld_lab-3.0.0/xrpld_lab/operations.py +320 -0
- xrpld_lab-3.0.0/xrpld_lab/protocol.py +106 -0
- xrpld_lab-3.0.0/xrpld_lab/script_builder.py +454 -0
- xrpld_lab-3.0.0/xrpld_lab/source_resolver.py +176 -0
- xrpld_lab-3.0.0/xrpld_lab/utils.py +92 -0
- xrpld_lab-3.0.0/xrpld_lab/workflows.py +634 -0
- xrpld_lab-3.0.0/xrpld_lab/workspace.py +47 -0
xrpld_lab-3.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xrpld-lab
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Build xrpld networks and standalone ledgers
|
|
5
|
+
Author: Denis Angell
|
|
6
|
+
Author-email: dangell@transia.co
|
|
7
|
+
Requires-Python: >=3.9.6,<4.0.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
15
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
16
|
+
Requires-Dist: xrpld-publisher (>=2.0.0,<3.0.0)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "xrpld-lab"
|
|
3
|
+
version = "3.0.0"
|
|
4
|
+
description = "Build xrpld networks and standalone ledgers"
|
|
5
|
+
authors = ["Denis Angell <dangell@transia.co>"]
|
|
6
|
+
packages = [
|
|
7
|
+
{ include = "xrpld_lab" },
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
[tool.poetry.dependencies]
|
|
11
|
+
python = "^3.9.6"
|
|
12
|
+
pyyaml = "^6.0.1"
|
|
13
|
+
requests = "^2.31.0"
|
|
14
|
+
xrpld-publisher = "^2.0.0"
|
|
15
|
+
|
|
16
|
+
[tool.poetry.dev-dependencies]
|
|
17
|
+
pytest = "^7.2.1"
|
|
18
|
+
flake8 = "^3.8.4"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["poetry-core>=1.0.0"]
|
|
22
|
+
build-backend = "poetry.core.masonry.api"
|
|
23
|
+
|
|
24
|
+
[tool.poetry.scripts]
|
|
25
|
+
xrpld-lab = "xrpld_lab.cli:main"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.0.0"
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""Amendment parsing and genesis-file helpers.
|
|
2
|
+
|
|
3
|
+
Parses C++ feature macro lines (XRPL_FEATURE, XRPL_FIX, REGISTER_FEATURE,
|
|
4
|
+
REGISTER_FIX) to extract amendment names and their SHA-512-half hashes,
|
|
5
|
+
then optionally writes them into a genesis JSON ledger.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import hashlib
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
from typing import Dict, List
|
|
12
|
+
|
|
13
|
+
from xrpld_lab.utils import read_json
|
|
14
|
+
|
|
15
|
+
_PACKAGE_DIR = os.path.abspath(os.path.dirname(__file__))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Helpers
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _amendment_name_hash(name: str) -> str:
|
|
24
|
+
"""SHA-512 half of a UTF-8 amendment name (first 64 hex chars, uppercase)."""
|
|
25
|
+
return hashlib.sha512(name.encode("utf-8")).digest().hex().upper()[:64]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# Public API
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def parse_supported(value: str) -> bool:
|
|
34
|
+
"""Parse C++ Supported/DefaultVote value. ``'no'`` -> False, else True."""
|
|
35
|
+
return value != "no"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_feature_lines_from_path(path: str) -> list[str]:
|
|
39
|
+
"""Read a C++ feature file and return its lines."""
|
|
40
|
+
with open(path, "r") as f:
|
|
41
|
+
return f.readlines()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_feature_lines_from_content(content: bytes) -> list[str]:
|
|
45
|
+
"""Decode *bytes* content and split into lines."""
|
|
46
|
+
return content.decode("utf-8").splitlines()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def parse_amendments(lines: list) -> Dict[str, str]:
|
|
50
|
+
"""Parse C++ macro lines into ``{amendment_name: sha512_half_hash}``.
|
|
51
|
+
|
|
52
|
+
Handles four macro styles:
|
|
53
|
+
|
|
54
|
+
* ``XRPL_FEATURE(Name, ...)``
|
|
55
|
+
* ``XRPL_FIX(Name, ...)`` -- prepends ``"fix"`` to the name
|
|
56
|
+
* ``REGISTER_FEATURE(Name, ...)``
|
|
57
|
+
* ``REGISTER_FIX(Name, ...)``
|
|
58
|
+
|
|
59
|
+
Only amendments marked ``Supported::yes`` (or any value other than ``no``)
|
|
60
|
+
are included. The hash is the first 64 hex characters of the SHA-512
|
|
61
|
+
digest of the UTF-8 encoded amendment name.
|
|
62
|
+
"""
|
|
63
|
+
amendments: Dict[str, dict] = {}
|
|
64
|
+
|
|
65
|
+
for line in lines:
|
|
66
|
+
amendment_name: str = ""
|
|
67
|
+
|
|
68
|
+
if re.match(r"XRPL_FIX", line):
|
|
69
|
+
m = re.search(r"XRPL_FIX\)?.*?\((.*?),", line)
|
|
70
|
+
if not m:
|
|
71
|
+
continue
|
|
72
|
+
amendment_name = f"fix{m.group(1)}"
|
|
73
|
+
elif re.match(r"XRPL_FEATURE", line):
|
|
74
|
+
m = re.search(r"XRPL_FEATURE\((.*?),", line)
|
|
75
|
+
if not m:
|
|
76
|
+
continue
|
|
77
|
+
amendment_name = m.group(1)
|
|
78
|
+
elif re.match(r"REGISTER_FIX", line):
|
|
79
|
+
m = re.search(r"REGISTER_FIX\)?.*?\((.*?),", line)
|
|
80
|
+
if not m:
|
|
81
|
+
continue
|
|
82
|
+
amendment_name = m.group(1)
|
|
83
|
+
elif re.match(r"REGISTER_FEATURE", line):
|
|
84
|
+
m = re.search(r"REGISTER_FEATURE\((.*?),", line)
|
|
85
|
+
if not m:
|
|
86
|
+
continue
|
|
87
|
+
amendment_name = m.group(1)
|
|
88
|
+
else:
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
supported_match = re.findall(r"Supported::(yes|no)", line)
|
|
92
|
+
default_vote_match = re.findall(r"DefaultVote::(yes|no)", line)
|
|
93
|
+
|
|
94
|
+
amendments[amendment_name] = {
|
|
95
|
+
"supported": parse_supported(
|
|
96
|
+
supported_match[0] if supported_match else "no"
|
|
97
|
+
),
|
|
98
|
+
"default_vote": parse_supported(
|
|
99
|
+
default_vote_match[0] if default_vote_match else "no"
|
|
100
|
+
),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
k: _amendment_name_hash(k)
|
|
105
|
+
for k, v in amendments.items()
|
|
106
|
+
if v["supported"] is True
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def convert_to_list_of_hashes(features: Dict[str, str]) -> List[str]:
|
|
111
|
+
"""Return a flat list of hash strings from *features*."""
|
|
112
|
+
return list(features.values())
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def update_genesis(
|
|
116
|
+
features: Dict[str, str],
|
|
117
|
+
protocol_name: str,
|
|
118
|
+
genesis_path: str = None,
|
|
119
|
+
) -> dict:
|
|
120
|
+
"""Update a genesis JSON file with amendment hashes.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
features:
|
|
125
|
+
``{name: hash}`` mapping produced by :func:`parse_amendments`.
|
|
126
|
+
protocol_name:
|
|
127
|
+
``"xrpl"`` or ``"xahau"``.
|
|
128
|
+
genesis_path:
|
|
129
|
+
Explicit path to a genesis JSON file. When *None* the default
|
|
130
|
+
package location ``xrpld_lab/genesis.<protocol>.json`` is used.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
dict
|
|
135
|
+
The full genesis dict with the ``Amendments`` list replaced.
|
|
136
|
+
"""
|
|
137
|
+
if genesis_path is None:
|
|
138
|
+
genesis_path = os.path.join(_PACKAGE_DIR, f"genesis.{protocol_name}.json")
|
|
139
|
+
|
|
140
|
+
json_dict = read_json(genesis_path)
|
|
141
|
+
new_amendments: List[str] = convert_to_list_of_hashes(features)
|
|
142
|
+
|
|
143
|
+
for entry in json_dict["ledger"]["accountState"]:
|
|
144
|
+
if "Amendments" in entry:
|
|
145
|
+
entry["Amendments"] = new_amendments
|
|
146
|
+
|
|
147
|
+
return json_dict
|