Skip to content

Commit 4cdaeff

Browse files
Merge pull request #560 from multiversx/localnet-supernova
Localnet: add support for Supernova
2 parents 3ab008f + 54511a7 commit 4cdaeff

File tree

8 files changed

+113
-52
lines changed

8 files changed

+113
-52
lines changed

multiversx_sdk_cli/localnet/config_default.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
genesis_delay_seconds=10,
1616
rounds_per_epoch=100,
1717
round_duration_milliseconds=6000,
18+
# For the purpose of the localnet, we'll have 3x for Supernova (by default).
19+
rounds_per_epoch_in_supernova=300,
20+
round_duration_milliseconds_in_supernova=2000,
1821
)
1922

2023
software = Software(

multiversx_sdk_cli/localnet/config_general.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@ def __init__(
1010
genesis_delay_seconds: int,
1111
rounds_per_epoch: int,
1212
round_duration_milliseconds: int,
13+
rounds_per_epoch_in_supernova: int,
14+
round_duration_milliseconds_in_supernova: int
1315
):
14-
self.log_level: str = log_level
15-
self.genesis_delay_seconds: int = genesis_delay_seconds
16-
self.rounds_per_epoch: int = rounds_per_epoch
17-
self.round_duration_milliseconds: int = round_duration_milliseconds
16+
self.log_level = log_level
17+
self.genesis_delay_seconds = genesis_delay_seconds
18+
self.rounds_per_epoch = rounds_per_epoch
19+
self.round_duration_milliseconds = round_duration_milliseconds
20+
self.rounds_per_epoch_in_supernova = rounds_per_epoch_in_supernova
21+
self.round_duration_milliseconds_in_supernova = round_duration_milliseconds_in_supernova
1822

1923
def get_name(self) -> str:
2024
return "general"
@@ -24,3 +28,5 @@ def _do_override(self, other: Dict[str, Any]):
2428
self.genesis_delay_seconds = other.get("genesis_delay_seconds", self.genesis_delay_seconds)
2529
self.rounds_per_epoch = other.get("rounds_per_epoch", self.rounds_per_epoch)
2630
self.round_duration_milliseconds = other.get("round_duration_milliseconds", self.round_duration_milliseconds)
31+
self.rounds_per_epoch_in_supernova = other.get("rounds_per_epoch_in_supernova", self.rounds_per_epoch_in_supernova)
32+
self.round_duration_milliseconds_in_supernova = other.get("round_duration_milliseconds_in_supernova", self.round_duration_milliseconds_in_supernova)

multiversx_sdk_cli/localnet/constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@
66
FILE_MODE_EXECUTABLE = (
77
stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH
88
)
9+
10+
# See https://github.com/multiversx/mx-chain-go/blob/master/cmd/node/config/config.toml.
11+
ROUNDS_PER_EPOCH_TO_MIN_ROUNDS_BETWEEN_EPOCHS_RATIO = 4
12+
# See https://github.com/multiversx/mx-chain-go/blob/master/cmd/node/config/enableRounds.toml.
13+
NUM_ROUNDS_BETWEEN_SUPERNOVA_ACTIVATION_EPOCH_AND_ACTIVATION_ROUND = 20
Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
from typing import Any, Dict
22

33
from multiversx_sdk_cli.localnet.config_root import ConfigRoot
4+
from multiversx_sdk_cli.localnet.constants import (
5+
NUM_ROUNDS_BETWEEN_SUPERNOVA_ACTIVATION_EPOCH_AND_ACTIVATION_ROUND,
6+
ROUNDS_PER_EPOCH_TO_MIN_ROUNDS_BETWEEN_EPOCHS_RATIO,
7+
)
48
from multiversx_sdk_cli.localnet.nodes_setup_json import CHAIN_ID
59

610
ConfigDict = Dict[str, Any]
711

812

9-
def patch_config(data: ConfigDict, config: ConfigRoot):
13+
def patch_config(data: ConfigDict, config: ConfigRoot, enable_epochs_config: ConfigDict):
14+
supernova_activation_epoch = enable_epochs_config["EnableEpochs"].get("SupernovaEnableEpoch", None)
15+
1016
data["GeneralSettings"]["ChainID"] = CHAIN_ID
1117

1218
# "--operation-mode=historical-balances" is not available for nodes,
@@ -18,33 +24,58 @@ def patch_config(data: ConfigDict, config: ConfigRoot):
1824
data["StoragePruning"]["ObserverCleanOldEpochsData"] = False
1925
data["StoragePruning"]["AccountsTrieCleanOldEpochsData"] = False
2026

21-
# Make epochs shorter
22-
epoch_start_config: ConfigDict = dict()
23-
epoch_start_config["RoundsPerEpoch"] = config.general.rounds_per_epoch
24-
epoch_start_config["MinRoundsBetweenEpochs"] = int(config.general.rounds_per_epoch / 4)
27+
# Some time after the release of Supernova, we should drop this custom (and somewhat cumbersome) logic.
28+
if supernova_activation_epoch is None:
29+
# Before Supernova (as software version, not as "era after activation"),
30+
# we alter epoch duration by adjusting "RoundsPerEpoch" and "MinRoundsBetweenEpochs" in section "EpochStartConfig".
31+
# In a Supernova-aware node configuration, these entries do not exist anymore (see "ChainParametersByEpoch").
32+
epoch_start_config: ConfigDict = dict()
33+
epoch_start_config["RoundsPerEpoch"] = config.general.rounds_per_epoch
34+
epoch_start_config["MinRoundsBetweenEpochs"] = int(
35+
config.general.rounds_per_epoch / ROUNDS_PER_EPOCH_TO_MIN_ROUNDS_BETWEEN_EPOCHS_RATIO
36+
)
37+
38+
data["EpochStartConfig"].update(epoch_start_config)
2539

26-
data["EpochStartConfig"].update(epoch_start_config)
2740
data["WebServerAntiflood"]["VmQueryDelayAfterStartInSec"] = 30
2841

2942
# Always use the latest VM
3043
data["VirtualMachine"]["Execution"]["WasmVMVersions"] = [{"StartEpoch": 0, "Version": "*"}]
3144
data["VirtualMachine"]["Querying"]["WasmVMVersions"] = [{"StartEpoch": 0, "Version": "*"}]
3245

33-
# Adjust "ChainParametersByEpoch" (for Andromeda)
46+
# Adjust "ChainParametersByEpoch"
3447
chain_parameters_by_epoch = data["GeneralSettings"].get("ChainParametersByEpoch", [])
3548

36-
if chain_parameters_by_epoch:
37-
# For convenience, keep a single entry ...
38-
chain_parameters_by_epoch.clear()
39-
chain_parameters_by_epoch.append({})
49+
for item in chain_parameters_by_epoch:
50+
enable_epoch = item["EnableEpoch"]
51+
52+
is_supernova_enabled = supernova_activation_epoch is not None and enable_epoch >= supernova_activation_epoch
53+
if is_supernova_enabled:
54+
item["RoundDuration"] = config.general.round_duration_milliseconds_in_supernova
55+
item["RoundsPerEpoch"] = config.general.rounds_per_epoch_in_supernova
56+
item["MinRoundsBetweenEpochs"] = int(
57+
config.general.rounds_per_epoch_in_supernova / ROUNDS_PER_EPOCH_TO_MIN_ROUNDS_BETWEEN_EPOCHS_RATIO
58+
)
59+
else:
60+
item["RoundDuration"] = config.general.round_duration_milliseconds
61+
item["RoundsPerEpoch"] = config.general.rounds_per_epoch
62+
item["MinRoundsBetweenEpochs"] = int(
63+
config.general.rounds_per_epoch / ROUNDS_PER_EPOCH_TO_MIN_ROUNDS_BETWEEN_EPOCHS_RATIO
64+
)
65+
66+
item["ShardConsensusGroupSize"] = config.shards.consensus_size
67+
item["ShardMinNumNodes"] = config.shards.num_validators_per_shard
68+
item["MetachainConsensusGroupSize"] = config.metashard.consensus_size
69+
item["MetachainMinNumNodes"] = config.metashard.num_validators
4070

41-
# ... and set the activation epoch to 0
42-
chain_parameters_by_epoch[0]["EnableEpoch"] = 0
43-
chain_parameters_by_epoch[0]["RoundDuration"] = config.general.round_duration_milliseconds
44-
chain_parameters_by_epoch[0]["ShardConsensusGroupSize"] = config.shards.consensus_size
45-
chain_parameters_by_epoch[0]["ShardMinNumNodes"] = config.shards.num_validators_per_shard
46-
chain_parameters_by_epoch[0]["MetachainConsensusGroupSize"] = config.metashard.consensus_size
47-
chain_parameters_by_epoch[0]["MetachainMinNumNodes"] = config.metashard.num_validators
71+
# Adjust "Versions" (of blocks)
72+
versions_by_epoch = data["Versions"].get("VersionsByEpochs", [])
73+
74+
for item in versions_by_epoch:
75+
enable_epoch = item["StartEpoch"]
76+
77+
if enable_epoch == supernova_activation_epoch:
78+
item["StartRound"] = _compute_supernova_activation_round(config, supernova_activation_epoch)
4879

4980

5081
def patch_api(data: ConfigDict, config: ConfigRoot):
@@ -55,10 +86,6 @@ def patch_api(data: ConfigDict, config: ConfigRoot):
5586

5687
def patch_enable_epochs(data: ConfigDict, config: ConfigRoot):
5788
enable_epochs = data["EnableEpochs"]
58-
enable_epochs["SCDeployEnableEpoch"] = 0
59-
enable_epochs["BuiltInFunctionsEnableEpoch"] = 0
60-
enable_epochs["RelayedTransactionsEnableEpoch"] = 0
61-
enable_epochs["PenalizedTooMuchGasEnableEpoch"] = 0
6289
enable_epochs["AheadOfTimeGasUsageEnableEpoch"] = 0
6390
enable_epochs["GasPriceModifierEnableEpoch"] = 0
6491
enable_epochs["RepairCallbackEnableEpoch"] = 0
@@ -69,15 +96,34 @@ def patch_enable_epochs(data: ConfigDict, config: ConfigRoot):
6996
enable_epochs["ESDTMultiTransferEnableEpoch"] = 0
7097
enable_epochs["GlobalMintBurnDisableEpoch"] = 0
7198
enable_epochs["ESDTTransferRoleEnableEpoch"] = 0
72-
enable_epochs["BuiltInFunctionOnMetaEnableEpoch"] = 0
7399
enable_epochs["MultiESDTTransferFixOnCallBackOnEnableEpoch"] = 0
74100
enable_epochs["ESDTNFTCreateOnMultiShard"] = 0
75101
enable_epochs["MetaESDTSetEnableEpoch"] = 0
76102
enable_epochs["DelegationManagerEnableEpoch"] = 0
77103

104+
# Adjust "MaxNumNodes":
78105
max_nodes_change_enable_epoch = enable_epochs["MaxNodesChangeEnableEpoch"]
79106
last_entry = max_nodes_change_enable_epoch[-1]
80107
penultimate_entry = max_nodes_change_enable_epoch[-2]
81108
last_entry["MaxNumNodes"] = (
82109
penultimate_entry["MaxNumNodes"] - (config.shards.num_shards + 1) * penultimate_entry["NodesToShufflePerShard"]
83110
)
111+
112+
113+
def patch_enable_rounds(data: ConfigDict, config: ConfigRoot, enable_epochs_config: ConfigDict):
114+
supernova_activation_epoch = enable_epochs_config["EnableEpochs"].get("SupernovaEnableEpoch", None)
115+
116+
activations = data["RoundActivations"]
117+
supernova_entry = activations.get("SupernovaEnableRound")
118+
119+
if supernova_entry and supernova_activation_epoch is not None:
120+
supernova_computed_activation_round = _compute_supernova_activation_round(config, supernova_activation_epoch)
121+
supernova_entry["Round"] = str(supernova_computed_activation_round)
122+
123+
124+
def _compute_supernova_activation_round(config: ConfigRoot, supernova_activation_epoch: int) -> int:
125+
# Epochs are zero-indexed.
126+
return (
127+
config.general.rounds_per_epoch * supernova_activation_epoch
128+
+ NUM_ROUNDS_BETWEEN_SUPERNOVA_ACTIVATION_EPOCH_AND_ACTIVATION_ROUND
129+
)

multiversx_sdk_cli/localnet/nodes_setup_json.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,5 @@ def build(config: ConfigRoot) -> Any:
2626

2727
return {
2828
"startTime": config.genesis_time(),
29-
"roundDuration": config.general.round_duration_milliseconds,
30-
"consensusGroupSize": config.shards.consensus_size,
31-
"minNodesPerShard": config.shards.consensus_size,
32-
"metaChainConsensusGroupSize": config.metashard.consensus_size,
33-
"metaChainMinNodes": config.metashard.num_validators,
34-
"hysteresis": 0,
35-
"adaptivity": False,
36-
"chainID": CHAIN_ID,
37-
"minTransactionVersion": 1,
3829
"initialNodes": initial_nodes,
3930
}

multiversx_sdk_cli/localnet/step_config.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,28 @@ def copy_validator_keys(config: ConfigRoot):
101101
def patch_node_config(config: ConfigRoot):
102102
for node_config in config.all_nodes_config_folders():
103103
node_config_file = node_config / "config.toml"
104-
data = utils.read_toml_file(node_config_file)
105-
node_config_toml.patch_config(data, config)
106-
utils.write_toml_file(node_config_file, data)
107-
108104
api_config_file = node_config / "api.toml"
109-
data = utils.read_toml_file(api_config_file)
110-
node_config_toml.patch_api(data, config)
111-
utils.write_toml_file(api_config_file, data)
112-
113105
enable_epochs_config_file = node_config / "enableEpochs.toml"
114-
data = utils.read_toml_file(enable_epochs_config_file)
115-
node_config_toml.patch_enable_epochs(data, config)
116-
utils.write_toml_file(enable_epochs_config_file, data)
117-
106+
enable_rounds_config_file = node_config / "enableRounds.toml"
118107
genesis_smart_contracts_file = node_config / "genesisSmartContracts.json"
119-
data = utils.read_json_file(genesis_smart_contracts_file)
120-
genesis_smart_contracts_json.patch(data, config)
121-
utils.write_json_file(genesis_smart_contracts_file, data)
108+
109+
node_config_data = utils.read_toml_file(node_config_file)
110+
api_config_data = utils.read_toml_file(api_config_file)
111+
enable_epochs_config_data = utils.read_toml_file(enable_epochs_config_file)
112+
enable_rounds_config_data = utils.read_toml_file(enable_rounds_config_file)
113+
genesis_smart_contracts_data = utils.read_json_file(genesis_smart_contracts_file)
114+
115+
node_config_toml.patch_config(node_config_data, config, enable_epochs_config_data)
116+
node_config_toml.patch_api(api_config_data, config)
117+
node_config_toml.patch_enable_epochs(enable_epochs_config_data, config)
118+
node_config_toml.patch_enable_rounds(enable_rounds_config_data, config, enable_epochs_config_data)
119+
genesis_smart_contracts_json.patch(genesis_smart_contracts_data, config)
120+
121+
utils.write_toml_file(node_config_file, node_config_data)
122+
utils.write_toml_file(api_config_file, api_config_data)
123+
utils.write_toml_file(enable_epochs_config_file, enable_epochs_config_data)
124+
utils.write_toml_file(enable_rounds_config_file, enable_rounds_config_data)
125+
utils.write_json_file(genesis_smart_contracts_file, genesis_smart_contracts_data)
122126

123127

124128
def copy_config_to_seednode(config: ConfigRoot):

multiversx_sdk_cli/tests/test_testnet.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ def test_override_config() -> None:
1414
# Check a few default values
1515
assert config.general.rounds_per_epoch == 100
1616
assert config.general.round_duration_milliseconds == 6000
17+
assert config.general.rounds_per_epoch_in_supernova == 300
18+
assert config.general.round_duration_milliseconds_in_supernova == 2000
1719
assert config.metashard.consensus_size == 1
1820
assert config.networking.port_proxy == 7950
1921
assert config.software.mx_chain_go.resolution == SoftwareResolution.Remote
@@ -27,6 +29,8 @@ def test_override_config() -> None:
2729
config_patch["general"] = {
2830
"rounds_per_epoch": 200,
2931
"round_duration_milliseconds": 4000,
32+
"rounds_per_epoch_in_supernova": 400,
33+
"round_duration_milliseconds_in_supernova": 1000,
3034
}
3135
config_patch["metashard"] = {
3236
"consensus_size": 2,
@@ -43,6 +47,8 @@ def test_override_config() -> None:
4347
# Check the overridden values
4448
assert config.general.rounds_per_epoch == 200
4549
assert config.general.round_duration_milliseconds == 4000
50+
assert config.general.rounds_per_epoch_in_supernova == 400
51+
assert config.general.round_duration_milliseconds_in_supernova == 1000
4652
assert config.metashard.consensus_size == 2
4753
assert config.networking.port_proxy == 7951
4854
assert config.software.mx_chain_go.resolution == SoftwareResolution.Remote

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "multiversx-sdk-cli"
7-
version = "11.2.3"
7+
version = "11.3.0"
88
authors = [
99
{ name="MultiversX" },
1010
]

0 commit comments

Comments
 (0)