From c78e86f0251eab6d5956aca6f3e7683df456fe47 Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 00:40:40 +0100 Subject: [PATCH 01/12] wip --- cli/package-lock.json | 8 +-- containers/testnet/Dockerfile | 4 +- containers/testnet/economics.toml | 14 +++-- containers/testnet/localnet.toml | 13 +++++ containers/testnet/replace_localnet_toml.py | 32 +++++++++++ containers/testnet/replace_testnet_toml.py | 35 ------------ containers/testnet/run.sh | 2 +- .../testnet/systemSmartContractsConfig.toml | 55 +++++++++---------- containers/testnet/testnet.toml | 13 ----- 9 files changed, 85 insertions(+), 91 deletions(-) create mode 100644 containers/testnet/localnet.toml create mode 100644 containers/testnet/replace_localnet_toml.py delete mode 100644 containers/testnet/replace_testnet_toml.py delete mode 100644 containers/testnet/testnet.toml diff --git a/cli/package-lock.json b/cli/package-lock.json index 5a9e934..53c46e7 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { - "name": "xnetwork", - "version": "0.0.2", + "name": "@gfusee/xnetwork", + "version": "0.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "xnetwork", - "version": "0.0.2", + "name": "@gfusee/xnetwork", + "version": "0.1.1", "license": "GNU GENERAL PUBLIC LICENSE", "dependencies": { "@multiversx/sdk-core": "^11.4.1", diff --git a/containers/testnet/Dockerfile b/containers/testnet/Dockerfile index 5c5be32..183d4c4 100644 --- a/containers/testnet/Dockerfile +++ b/containers/testnet/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=amd64 ghcr.io/gfusee/mx-testnet-docker:main@sha256:710e39d017fe6be27b3c9a1b61a06e9b168e157a263f19ea89e02b8c0924a2d3 +FROM ghcr.io/gfusee/mx-testnet-docker:sha256-df38bc2a5782350ea3fb854dcc483f905a30edcb5b72654a11d01c395d233b57.sig STOPSIGNAL SIGTERM @@ -11,7 +11,7 @@ RUN sudo chmod +x wait-for-it.sh COPY run.sh . COPY change_prefs.py change_prefs.py -COPY replace_testnet_toml.py replace_testnet_toml.py +COPY replace_localnet_toml.py replace_localnet_toml.py COPY genesis.json genesis.json COPY create_wallet.py create_wallet.py COPY change_genesis.py change_genesis.py diff --git a/containers/testnet/economics.toml b/containers/testnet/economics.toml index a635995..2485fbc 100644 --- a/containers/testnet/economics.toml +++ b/containers/testnet/economics.toml @@ -1,6 +1,6 @@ #Economics config of the node [GlobalSettings] -GenesisTotalSupply = "${MX_RESULT_TOTAL_SUPPLY}" +GenesisTotalSupply = "${MX_RESULT_TOTAL_SUPPLY}" #20MIL eGLD MinimumInflation = 0.0 YearSettings = [ {Year = 1, MaximumInflation = 0.10845130}, @@ -24,25 +24,27 @@ EpochEnable = 0 LeaderPercentage = 0.1 #fraction of value 0.1 - 10% DeveloperPercentage = 0.3 #fraction of value 0.3 - 30% ProtocolSustainabilityPercentage = 0.1 #fraction of value 0.1 - 10% -ProtocolSustainabilityAddress = "erd1j25xk97yf820rgdp3mj5scavhjkn6tjyn0t63pmv5qyjj7wxlcfqqe2rw5" +ProtocolSustainabilityAddress = "erd135enn78u7fy352ny9mcl26a08wvu02mjdumfkt7cn587cwtnerksagqsfm" TopUpGradientPoint = "3000000000000000000000000" # 3MIL eGLD (eligible topUp) TopUpFactor = 0.25 # fraction of value 0.25 - 25% [[RewardsSettings.RewardsConfigByEpoch]] -EpochEnable = 1 +EpochEnable = 326 LeaderPercentage = 0.1 #fraction of value 0.1 - 10% DeveloperPercentage = 0.3 #fraction of value 0.3 - 30% ProtocolSustainabilityPercentage = 0.1 #fraction of value 0.1 - 10% -ProtocolSustainabilityAddress = "erd1j25xk97yf820rgdp3mj5scavhjkn6tjyn0t63pmv5qyjj7wxlcfqqe2rw5" +ProtocolSustainabilityAddress = "erd135enn78u7fy352ny9mcl26a08wvu02mjdumfkt7cn587cwtnerksagqsfm" TopUpGradientPoint = "2000000000000000000000000" # 2MIL eGLD (eligible topUp) TopUpFactor = 0.5 # fraction of value 0.5 - 50% [FeeSettings] GasLimitSettings = [ - {EnableEpoch = 0, MaxGasLimitPerBlock = "1500000000", MaxGasLimitPerMiniBlock = "1500000000", MaxGasLimitPerMetaBlock = "15000000000", MaxGasLimitPerMetaMiniBlock = "15000000000", MaxGasLimitPerTx = "1500000000", MinGasLimit = "50000"}, - {EnableEpoch = 1, MaxGasLimitPerBlock = "1500000000", MaxGasLimitPerMiniBlock = "250000000", MaxGasLimitPerMetaBlock = "15000000000", MaxGasLimitPerMetaMiniBlock = "250000000", MaxGasLimitPerTx = "600000000", MinGasLimit = "50000"}, + {EnableEpoch = 0, MaxGasLimitPerBlock = "1500000000", MaxGasLimitPerMiniBlock = "1500000000", MaxGasLimitPerMetaBlock = "15000000000", MaxGasLimitPerMetaMiniBlock = "15000000000", MaxGasLimitPerTx = "1500000000", MinGasLimit = "50000", ExtraGasLimitGuardedTx = "50000"}, + {EnableEpoch = 460, MaxGasLimitPerBlock = "1500000000", MaxGasLimitPerMiniBlock = "600000000", MaxGasLimitPerMetaBlock = "15000000000", MaxGasLimitPerMetaMiniBlock = "600000000", MaxGasLimitPerTx = "600000000", MinGasLimit = "50000", ExtraGasLimitGuardedTx = "50000"}, + {EnableEpoch = 613, MaxGasLimitPerBlock = "1500000000", MaxGasLimitPerMiniBlock = "250000000", MaxGasLimitPerMetaBlock = "15000000000", MaxGasLimitPerMetaMiniBlock = "250000000", MaxGasLimitPerTx = "600000000", MinGasLimit = "50000", ExtraGasLimitGuardedTx = "50000"}, ] MinGasPrice = "1000000000" #will yield min tx fee of 0.00005 eGLD GasPriceModifier = 0.01 GasPerDataByte = "1500" DataLimitForBaseCalc = "10000" +MaxGasPriceSetGuardian = "2000000000" diff --git a/containers/testnet/localnet.toml b/containers/testnet/localnet.toml new file mode 100644 index 0000000..029b0ac --- /dev/null +++ b/containers/testnet/localnet.toml @@ -0,0 +1,13 @@ +[networking] +port_proxy = 7950 + +[shards] +num_shards = 3 + +[software.mx_chain_go] +resolution = "local" +local_path = "/home/ubuntu/mx-chain-go" + +[software.mx_chain_proxy_go] +resolution = "local" +local_path = "/home/ubuntu/mx-chain-proxy-go" diff --git a/containers/testnet/replace_localnet_toml.py b/containers/testnet/replace_localnet_toml.py new file mode 100644 index 0000000..8aca0b5 --- /dev/null +++ b/containers/testnet/replace_localnet_toml.py @@ -0,0 +1,32 @@ +import os +import sys + +num_shards = sys.argv[1] + +toml_file_content = f""" +[networking] +port_proxy = 7950 + +[shards] +num_shards = 3 + +[software.mx_chain_go] +resolution = "local" +local_path = "/home/ubuntu/mx-chain-go" + +[software.mx_chain_proxy_go] +resolution = "local" +local_path = "/home/ubuntu/mx-chain-proxy-go" +""" + + +def replace_toml_file(): + # Delete testnet.toml if it exists + testnet_toml_path = os.path.join(os.getcwd(), 'localnet.toml') + + # Create testnet.toml + with open(testnet_toml_path, 'w') as file: + file.write(toml_file_content) + + +replace_toml_file() diff --git a/containers/testnet/replace_testnet_toml.py b/containers/testnet/replace_testnet_toml.py deleted file mode 100644 index 71f84e6..0000000 --- a/containers/testnet/replace_testnet_toml.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -import sys - -num_shards = sys.argv[1] - -toml_file_content = f""" -[networking] -port_proxy = 7950 - -[shards] -num_shards = {num_shards} - -[software] -resolution = "local_prebuilt_cmd_folders" - -[software.local_prebuilt_cmd_folders] -# Has to contain "node" binary, "config" folder, wasmer libraries -mx_chain_go_node = "/home/ubuntu/mx-chain-go/cmd/node" -# Has to contain "seednode" binary, "config" folder, wasmer libraries -mx_chain_go_seednode = "/home/ubuntu/mx-chain-go/cmd/seednode" -# Has to contain "proxy" binary, "config" folder, wasmer libraries -mx_chain_proxy_go = "/home/ubuntu/mx-chain-proxy-go/cmd/proxy" -""" - - -def replace_toml_file(): - # Delete testnet.toml if it exists - testnet_toml_path = os.path.join(os.getcwd(), 'testnet.toml') - - # Create testnet.toml - with open(testnet_toml_path, 'w') as file: - file.write(toml_file_content) - - -replace_toml_file() diff --git a/containers/testnet/run.sh b/containers/testnet/run.sh index 5901423..057e2e0 100644 --- a/containers/testnet/run.sh +++ b/containers/testnet/run.sh @@ -11,7 +11,7 @@ setup() { rm results.json echo "Replacing testnet.toml..." - sudo python3 replace_testnet_toml.py "$MX_LT_NUM_SHARDS" + sudo python3 replace_localnet_toml.py "$MX_LT_NUM_SHARDS" echo "Compiling nodes..." rm -rf testnet diff --git a/containers/testnet/systemSmartContractsConfig.toml b/containers/testnet/systemSmartContractsConfig.toml index 0373e65..674fa50 100644 --- a/containers/testnet/systemSmartContractsConfig.toml +++ b/containers/testnet/systemSmartContractsConfig.toml @@ -1,46 +1,41 @@ [StakingSystemSCConfig] -GenesisNodePrice = "2500000000000000000000" -MinStakeValue = "100000000000000000000" -MinUnstakeTokensValue = "10000000000000000000" -UnBondPeriod = 250 -UnBondPeriodInEpochs = 1 +GenesisNodePrice = "2500000000000000000000" #2.5K eGLD +MinStakeValue = "100000000000000000000" #100 eGLD +MinUnstakeTokensValue = "10000000000000000000" #10eGLD +UnBondPeriod = 144000 +UnBondPeriodInEpochs = 10 MinStepValue = "100000000000000000000" NumRoundsWithoutBleed = 100 MaximumPercentageToBleed = 0.5 -BleedPercentagePerRound = 1e-5 -MaxNumberOfNodesForStake = 36 -UnJailValue = "2500000000000000000" +BleedPercentagePerRound = 0.00001 +MaxNumberOfNodesForStake = 2169 +UnJailValue = "2500000000000000000" #0.1% of genesis node price ActivateBLSPubKeyMessageVerification = true [ESDTSystemSCConfig] BaseIssuingCost = "50000000000000000" #0.05 eGLD -OwnerAddress = "erd1fpkcgel4gcmh8zqqdt043yfcn5tyx8373kg6q2qmkxzu4dqamc0swts65c" -EnabledEpoch = 0 +OwnerAddress = "erd1ze6pgefwc7mdx0rhje3h50kkgt26ut3gx8ytezndkz8fjcf96gsqsd7ugw" [GovernanceSystemSCConfig] -FirstWhitelistedAddress = "erd1vxy22x0fj4zv6hktmydg8vpfh6euv02cz4yg0aaws6rrad5a5awqgqky80" #should use a multisign contract instead of a wallet address -EnabledEpoch = 0 - +OwnerAddress = "erd1qqqqqqqqqqqqqpgq0gq09608js3gje72v0yzc56k6glc852gl94sgtahk2" [GovernanceSystemSCConfig.V1] -NumNodes = 500 -ProposalCost = "5000000000000000000" -MinQuorum = 400 -MinPassThreshold = 300 -MinVetoThreshold = 50 - +NumNodes = 500 +ProposalCost = "5000000000000000000" #5 eGLD +MinQuorum = 400 +MinPassThreshold = 300 +MinVetoThreshold = 50 [GovernanceSystemSCConfig.Active] -ProposalCost = "5000000000000000000" -MinQuorum = "500000000000" -MinPassThreshold = "251000000000" -MinVetoThreshold = "249000000000" +ProposalCost = "1000000000000000000000" #1000 eGLD +LostProposalFee = "10000000000000000000" #10 eGLD +MinQuorum = 0.2 #fraction of value 0.2 - 20% +MinPassThreshold = 0.5 #fraction of value 0.5 - 50% +MinVetoThreshold = 0.33 #fraction of value 0.33 - 33% [DelegationManagerSystemSCConfig] -MinCreationDeposit = "1250000000000000000000" -MinStakeAmount = "10000000000000000000" -ConfigChangeAddress = "erd1vxy22x0fj4zv6hktmydg8vpfh6euv02cz4yg0aaws6rrad5a5awqgqky80" -EnabledEpoch = 1 +MinCreationDeposit = "1250000000000000000000" #1.25K eGLD +MinStakeAmount = "1000000000000000000" #1 eGLD +ConfigChangeAddress = "erd1qqqqqqqqqqqqqpgq0gq09608js3gje72v0yzc56k6glc852gl94sgtahk2" [DelegationSystemSCConfig] -MinServiceFee = 0 -MaxServiceFee = 10000 -EnabledEpoch = 1 +MinServiceFee = 0 +MaxServiceFee = 10000 diff --git a/containers/testnet/testnet.toml b/containers/testnet/testnet.toml deleted file mode 100644 index c303f3a..0000000 --- a/containers/testnet/testnet.toml +++ /dev/null @@ -1,13 +0,0 @@ -[networking] -port_proxy = 7950 - -[software] -resolution = "local_prebuilt_cmd_folders" - -[software.local_prebuilt_cmd_folders] -# Has to contain "node" binary, "config" folder, wasmer libraries -mx_chain_go_node = "/home/ubuntu/mx-chain-go/cmd/node" -# Has to contain "seednode" binary, "config" folder, wasmer libraries -mx_chain_go_seednode = "/home/ubuntu/mx-chain-go/cmd/seednode" -# Has to contain "proxy" binary, "config" folder, wasmer libraries -mx_chain_proxy_go = "/home/ubuntu/mx-chain-proxy-go/cmd/proxy" From 28b265c8d9cb839e166c3e3a3c84211df56cf327 Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 00:44:49 +0100 Subject: [PATCH 02/12] wip --- containers/testnet/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/containers/testnet/Dockerfile b/containers/testnet/Dockerfile index 183d4c4..c11f40c 100644 --- a/containers/testnet/Dockerfile +++ b/containers/testnet/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/gfusee/mx-testnet-docker:sha256-df38bc2a5782350ea3fb854dcc483f905a30edcb5b72654a11d01c395d233b57.sig +FROM ghcr.io/gfusee/mx-testnet-docker@sha256:df38bc2a5782350ea3fb854dcc483f905a30edcb5b72654a11d01c395d233b57 STOPSIGNAL SIGTERM From fa8d38879fa2575e301400b67e96574aff6f9b20 Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 00:44:49 +0100 Subject: [PATCH 03/12] wip --- containers/testnet/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/containers/testnet/Dockerfile b/containers/testnet/Dockerfile index 183d4c4..2e44f1a 100644 --- a/containers/testnet/Dockerfile +++ b/containers/testnet/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/gfusee/mx-testnet-docker:sha256-df38bc2a5782350ea3fb854dcc483f905a30edcb5b72654a11d01c395d233b57.sig +FROM ghcr.io/gfusee/mx-testnet-docker:0.0.2 STOPSIGNAL SIGTERM From 0a7a032b03de22fe95607de6ee21d7897ad1c26a Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 02:12:23 +0100 Subject: [PATCH 04/12] wip --- README.md | 6 +- cli/src/config/constants.ts | 4 +- ...on.ts => freshLocalnetFeaturesQuestion.ts} | 6 +- .../questions/fresh/numberShardsQuestion.ts | 4 +- cli/src/questions/startQuestion.ts | 10 +-- cli/src/result/resultLogger.ts | 16 ++--- cli/src/utils/docker/createNetwork.ts | 6 +- cli/src/utils/docker/getNetworkState.ts | 24 +++---- cli/src/utils/docker/pauseExistingNetwork.ts | 6 +- cli/src/utils/docker/resumeExistingNetwork.ts | 2 +- containers/api/api-config.devnet.yaml | 2 +- containers/api/run.sh | 4 +- containers/{testnet => localnet}/Dockerfile | 2 +- .../{testnet => localnet}/add_result.py | 0 .../{testnet => localnet}/change_genesis.py | 0 .../{testnet => localnet}/change_prefs.py | 0 .../{testnet => localnet}/create_wallet.py | 0 .../{testnet => localnet}/economics.toml | 0 containers/{testnet => localnet}/genesis.json | 0 .../{testnet => localnet}/handle_signal.sh | 2 +- .../{testnet => localnet}/localnet.toml | 0 .../mxops-init/genesis_account.yaml | 2 +- containers/{testnet => localnet}/pause.sh | 0 .../{testnet => localnet}/read_result.py | 0 .../replace_economics.py | 0 .../replace_localnet_toml.py | 8 +-- .../replace_system_contracts_config.py | 0 containers/localnet/run.sh | 63 +++++++++++++++++++ containers/{testnet => localnet}/run_mxops.py | 0 .../systemSmartContractsConfig.toml | 0 .../{testnet => localnet}/wait-for-it.sh | 0 containers/testnet/run.sh | 63 ------------------- docker-compose.yaml | 5 +- 33 files changed, 117 insertions(+), 118 deletions(-) rename cli/src/questions/features/{freshTestnetFeaturesQuestion.ts => freshLocalnetFeaturesQuestion.ts} (73%) rename containers/{testnet => localnet}/Dockerfile (96%) rename containers/{testnet => localnet}/add_result.py (100%) rename containers/{testnet => localnet}/change_genesis.py (100%) rename containers/{testnet => localnet}/change_prefs.py (100%) rename containers/{testnet => localnet}/create_wallet.py (100%) rename containers/{testnet => localnet}/economics.toml (100%) rename containers/{testnet => localnet}/genesis.json (100%) rename containers/{testnet => localnet}/handle_signal.sh (72%) rename containers/{testnet => localnet}/localnet.toml (100%) rename containers/{testnet => localnet}/mxops-init/genesis_account.yaml (61%) rename containers/{testnet => localnet}/pause.sh (100%) rename containers/{testnet => localnet}/read_result.py (100%) rename containers/{testnet => localnet}/replace_economics.py (100%) rename containers/{testnet => localnet}/replace_localnet_toml.py (68%) rename containers/{testnet => localnet}/replace_system_contracts_config.py (100%) create mode 100644 containers/localnet/run.sh rename containers/{testnet => localnet}/run_mxops.py (100%) rename containers/{testnet => localnet}/systemSmartContractsConfig.toml (100%) rename containers/{testnet => localnet}/wait-for-it.sh (100%) delete mode 100644 containers/testnet/run.sh diff --git a/README.md b/README.md index 4f0bb53..00740fd 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # xNetwork -This project offers an effortless way to run a local testnet of MultiversX and its corresponding API without any configuration needed. -While you can run a local testnet of MultiversX using the [MultiversX CLI](https://multiversx.com/), this project provides a more comprehensive environment, including a proxy and a complete API. +This project offers an effortless way to run a localnet of MultiversX and its corresponding API without any configuration needed. +While you can run a localnet of MultiversX using the [MultiversX CLI](https://multiversx.com/), this project provides a more comprehensive environment, including a proxy and a complete API. ## Features -- Run your own local testnet of MultiversX +- Run your own localnet of MultiversX - Choose the number of shards you prefer - Create an initial address with 1,000,000 $EGLD - Run the corresponding API, identical to https://api.multiversx.com diff --git a/cli/src/config/constants.ts b/cli/src/config/constants.ts index f8307a1..ab550c5 100644 --- a/cli/src/config/constants.ts +++ b/cli/src/config/constants.ts @@ -41,9 +41,9 @@ export class Constants { return path.join(Constants.CLI_USER_STORAGE_PATH, 'latest_config.json') } - static get TESTNET_CONTAINER(): ContainerInfos { + static get LOCALNET_CONTAINER(): ContainerInfos { return { - name: "testnet", + name: "localnet", pauseBehavior: PauseBehavior.PAUSE } } diff --git a/cli/src/questions/features/freshTestnetFeaturesQuestion.ts b/cli/src/questions/features/freshLocalnetFeaturesQuestion.ts similarity index 73% rename from cli/src/questions/features/freshTestnetFeaturesQuestion.ts rename to cli/src/questions/features/freshLocalnetFeaturesQuestion.ts index 449c32a..a1bb803 100644 --- a/cli/src/questions/features/freshTestnetFeaturesQuestion.ts +++ b/cli/src/questions/features/freshLocalnetFeaturesQuestion.ts @@ -4,15 +4,15 @@ import {Answers} from "inquirer"; import {CLIConfig} from "../../config/config.js"; import {CustomAddressToGiveEGLDQuestion} from "./customAddressToGiveEGLDQuestion.js"; -export class FreshTestnetFeaturesQuestion extends FeaturesQuestion { +export class FreshLocalnetFeaturesQuestion extends FeaturesQuestion { static readonly giveToCustomAddressChoice = 'Give 1,000,000 EGLD to a custom address (otherwise a new one will be generated for you)' - cliChoices = [FreshTestnetFeaturesQuestion.giveToCustomAddressChoice] + cliChoices = [FreshLocalnetFeaturesQuestion.giveToCustomAddressChoice] override async handleAnswer(answers: Answers, config: CLIConfig): Promise { const questions = await super.handleAnswer(answers, config) - if (answers.choice.includes(FreshTestnetFeaturesQuestion.giveToCustomAddressChoice)) { + if (answers.choice.includes(FreshLocalnetFeaturesQuestion.giveToCustomAddressChoice)) { questions.push(new CustomAddressToGiveEGLDQuestion()) } diff --git a/cli/src/questions/fresh/numberShardsQuestion.ts b/cli/src/questions/fresh/numberShardsQuestion.ts index 1beb4a0..2c97f57 100644 --- a/cli/src/questions/fresh/numberShardsQuestion.ts +++ b/cli/src/questions/fresh/numberShardsQuestion.ts @@ -1,5 +1,5 @@ import {CLIQuestion} from "../question.js"; -import {FreshTestnetFeaturesQuestion} from "../features/freshTestnetFeaturesQuestion.js"; +import {FreshLocalnetFeaturesQuestion} from "../features/freshLocalnetFeaturesQuestion.js"; import {Answers, Question} from "inquirer"; import {CLIConfig} from "../../config/config.js"; @@ -31,6 +31,6 @@ export class NumberShardsQuestion extends CLIQuestion { config.numberOfShards = answers.numberShards - return [new FreshTestnetFeaturesQuestion()] + return [new FreshLocalnetFeaturesQuestion()] } } diff --git a/cli/src/questions/startQuestion.ts b/cli/src/questions/startQuestion.ts index db711a5..c3652e6 100644 --- a/cli/src/questions/startQuestion.ts +++ b/cli/src/questions/startQuestion.ts @@ -22,11 +22,11 @@ export class StartQuestion extends CLIQuestion { const state = await getNetworkState() - if (state.testnetContainerState !== ContainerState.NonExistent) { + if (state.localnetContainerState !== ContainerState.NonExistent) { - if (state.testnetContainerState === ContainerState.Running) { + if (state.localnetContainerState === ContainerState.Running) { cliChoices.push(StartQuestion.pauseNetworkChoice) - } else if (state.testnetContainerState === ContainerState.Stopped) { + } else if (state.localnetContainerState === ContainerState.Stopped) { cliChoices.push(StartQuestion.resumeNetworkChoice) } @@ -34,7 +34,7 @@ export class StartQuestion extends CLIQuestion { cliChoices.push(new inquirer.Separator()) - const stateChalk = state.testnetContainerState === ContainerState.Running ? chalk.bold.green('running') : chalk.bold.yellow('paused') + const stateChalk = state.localnetContainerState === ContainerState.Running ? chalk.bold.green('running') : chalk.bold.yellow('paused') cliChoiceMessage = `A network is ${stateChalk}, what do you want to do ?` } @@ -69,7 +69,7 @@ export class StartQuestion extends CLIQuestion { } override async process(config: CLIConfig) { - if ((await getNetworkState()).testnetContainerState !== ContainerState.NonExistent) { + if ((await getNetworkState()).localnetContainerState !== ContainerState.NonExistent) { await super.process(config) } else { await (new NumberShardsQuestion()).process(config) diff --git a/cli/src/result/resultLogger.ts b/cli/src/result/resultLogger.ts index 41cf1cb..581f966 100644 --- a/cli/src/result/resultLogger.ts +++ b/cli/src/result/resultLogger.ts @@ -8,19 +8,19 @@ export class ResultLogger { async printResults(config: CLIConfig) { const state = await getNetworkState() - const containerResults = state.testnetResult + const containerResults = state.localnetResult if (!containerResults) { - console.log(dontIndent(chalk.bold.red("Something went wrong while checking the status of the local testnet."))) + console.log(dontIndent(chalk.bold.red("Something went wrong while checking the status of the localnet."))) return } - let firstPartResultString = `${chalk.bold.green("Local testnet successfully started !")}` + let firstPartResultString = `${chalk.bold.green("Localnet successfully started !")}` firstPartResultString += ` Proxy/Gateway URL: ${chalk.blue("http://localhost:7950")} - ChainID: ${chalk.blue("local-testnet")} + ChainID: ${chalk.blue("localnet")} ` if (config.shouldHaveElasticSearch) { @@ -36,15 +36,15 @@ export class ResultLogger { } if (containerResults.genesisEgldPemPath) { - const addressPrivateKey = (await execCustomInRepo(`docker-compose exec testnet cat ${containerResults.genesisEgldPemPath}`)).stdout.toString() + const addressPrivateKey = (await execCustomInRepo(`docker-compose exec localnet cat ${containerResults.genesisEgldPemPath}`)).stdout.toString() firstPartResultString += ` An address with 1,000,000 EGLD was generated for you. Here are the details: - ${chalk.bold.red("Here is the private key. Keep it safe and don't use it in another place than the local testnet!")} + ${chalk.bold.red("Here is the private key. Keep it safe and don't use it in another place than the localnet!")} ${addressPrivateKey} - ${chalk.bold.red("End of the private key. You can copy/paste the content into a .pem file and use it to do transactions on the local testnet.")} + ${chalk.bold.red("End of the private key. You can copy/paste the content into a .pem file and use it to do transactions on the localnet.")} ` } else { firstPartResultString += ` @@ -55,7 +55,7 @@ export class ResultLogger { let mxOpsDisplayString = '' if (config.mxOpsScenesPath) { - const mxopsXNetworkValuesRaw = (await execCustomInRepo(`docker-compose exec testnet python3 -m mxops data get -n LOCAL -s xnetwork`)).stdout.toString() + const mxopsXNetworkValuesRaw = (await execCustomInRepo(`docker-compose exec localnet python3 -m mxops data get -n LOCAL -s xnetwork`)).stdout.toString() const searchString = 'ABSOLUTELY NO WARRANTY\n' const mxopsXNetworkValues = mxopsXNetworkValuesRaw.substring(mxopsXNetworkValuesRaw.lastIndexOf(searchString) + searchString.length).trim() const mxopsXNetworkValuesObject = JSON.parse(mxopsXNetworkValues) diff --git a/cli/src/utils/docker/createNetwork.ts b/cli/src/utils/docker/createNetwork.ts index 5180fec..001265b 100644 --- a/cli/src/utils/docker/createNetwork.ts +++ b/cli/src/utils/docker/createNetwork.ts @@ -38,7 +38,7 @@ export async function createNetwork(config: CLIConfig) { } const startingNetworkSpinner = ora('Starting network...').start() - await upContainer(Constants.TESTNET_CONTAINER.name, { + await upContainer(Constants.LOCALNET_CONTAINER.name, { ...process.env, "MX_LT_NUM_SHARDS": config.numberOfShards.toString(), "MX_LT_ELASTIC_ENABLED": config.shouldHaveElasticSearch.toString(), @@ -62,11 +62,11 @@ export async function createNetwork(config: CLIConfig) { if (config.mxOpsScenesPath) { const copyingScenesSpinner = ora('Copying mxops scenes...').start() - await execCustomInRepo(`docker-compose cp ${config.mxOpsScenesPath} testnet:/home/ubuntu/mxops`) + await execCustomInRepo(`docker-compose cp ${config.mxOpsScenesPath} localnet:/home/ubuntu/mxops`) copyingScenesSpinner.succeed('Copied mxops scenes') const runningScenesSpinner = ora('Running mxops scenes...').start() - await execCustomInRepo(`docker-compose exec testnet python3 run_mxops.py`) + await execCustomInRepo(`docker-compose exec localnet python3 run_mxops.py`) runningScenesSpinner.succeed('Ran mxops scenes') } diff --git a/cli/src/utils/docker/getNetworkState.ts b/cli/src/utils/docker/getNetworkState.ts index d132977..5f155f9 100644 --- a/cli/src/utils/docker/getNetworkState.ts +++ b/cli/src/utils/docker/getNetworkState.ts @@ -1,13 +1,13 @@ import {execCustomInRepo} from "../exec.js"; -type TestnetResult = { +type LocalnetResult = { genesisEgldAddress: string, genesisEgldPemPath?: string, } type NetworkState = { - testnetContainerState: ContainerState, - testnetResult?: TestnetResult + localnetContainerState: ContainerState, + localnetResult?: LocalnetResult } export enum ContainerState { @@ -15,27 +15,27 @@ export enum ContainerState { } export async function getNetworkState(): Promise { - const containerState = await getTestnetContainerState() + const containerState = await getLocalnetContainerState() try { - const containerResultsRaw = (await execCustomInRepo("docker-compose exec testnet cat /home/ubuntu/results.json")).stdout.toString() - const containerResults: TestnetResult = JSON.parse(containerResultsRaw) + const containerResultsRaw = (await execCustomInRepo("docker-compose exec localnet cat /home/ubuntu/results.json")).stdout.toString() + const containerResults: LocalnetResult = JSON.parse(containerResultsRaw) return { - testnetContainerState: containerState, - testnetResult: containerResults, + localnetContainerState: containerState, + localnetResult: containerResults, } } catch (e) { return { - testnetContainerState: containerState, - testnetResult: undefined, + localnetContainerState: containerState, + localnetResult: undefined, } } } -async function getTestnetContainerState(): Promise { +async function getLocalnetContainerState(): Promise { try { - const stdout = (await execCustomInRepo('docker-compose ps -a -q testnet')).stdout.toString() + const stdout = (await execCustomInRepo('docker-compose ps -a -q localnet')).stdout.toString() const containerId = stdout.trim() diff --git a/cli/src/utils/docker/pauseExistingNetwork.ts b/cli/src/utils/docker/pauseExistingNetwork.ts index 70e3e27..c23e158 100644 --- a/cli/src/utils/docker/pauseExistingNetwork.ts +++ b/cli/src/utils/docker/pauseExistingNetwork.ts @@ -26,9 +26,9 @@ export async function pauseExistingNetwork() { await pauseContainer(Constants.RABBITMQ_CONTAINER) } - const pausingTestnetSpinner = ora(`Pausing testnet...`).start() - await pauseContainer(Constants.TESTNET_CONTAINER) - pausingTestnetSpinner.succeed(`Paused testnet successfully`) + const pausingLocalnetSpinner = ora(`Pausing localnet...`).start() + await pauseContainer(Constants.LOCALNET_CONTAINER) + pausingLocalnetSpinner.succeed(`Paused localnet successfully`) } async function pauseContainer(containerInfos: ContainerInfos) { diff --git a/cli/src/utils/docker/resumeExistingNetwork.ts b/cli/src/utils/docker/resumeExistingNetwork.ts index 3879303..d8cae1a 100644 --- a/cli/src/utils/docker/resumeExistingNetwork.ts +++ b/cli/src/utils/docker/resumeExistingNetwork.ts @@ -24,7 +24,7 @@ export async function resumeExistingNetwork() { await resumeContainer(Constants.RABBITMQ_CONTAINER) } - await resumeContainer(Constants.TESTNET_CONTAINER) + await resumeContainer(Constants.LOCALNET_CONTAINER) await waitForVMQueryToBeReady() diff --git a/containers/api/api-config.devnet.yaml b/containers/api/api-config.devnet.yaml index 1927ace..505d99c 100644 --- a/containers/api/api-config.devnet.yaml +++ b/containers/api/api-config.devnet.yaml @@ -45,7 +45,7 @@ urls: elastic: - 'http://elastic:9200' gateway: - - 'http://testnet:7950' + - 'http://localnet:7950' redis: 'redis' rabbitmq: 'amqp://rabbitmq:5672' providers: 'https://devnet-delegation-api.multiversx.com/providers' diff --git a/containers/api/run.sh b/containers/api/run.sh index cadf551..bb4b1b3 100644 --- a/containers/api/run.sh +++ b/containers/api/run.sh @@ -7,8 +7,8 @@ sudo ./wait-for-it.sh rabbitmq:5672 --timeout=0 echo "Waiting for Elastic Search..." sudo ./wait-for-it.sh elastic:9200 --timeout=0 -echo "Waiting for testnet..." -sudo ./wait-for-it.sh testnet:7950 --timeout=0 +echo "Waiting for localnet..." +sudo ./wait-for-it.sh localnet:7950 --timeout=0 echo "Running api service..." cd mx-api-service diff --git a/containers/testnet/Dockerfile b/containers/localnet/Dockerfile similarity index 96% rename from containers/testnet/Dockerfile rename to containers/localnet/Dockerfile index 2e44f1a..d2b2dc1 100644 --- a/containers/testnet/Dockerfile +++ b/containers/localnet/Dockerfile @@ -2,7 +2,7 @@ FROM ghcr.io/gfusee/mx-testnet-docker:0.0.2 STOPSIGNAL SIGTERM -RUN pip3 install mxops==1.0.0 +RUN pip3 install mxops==2.1.0 RUN sudo apt install lsof -y diff --git a/containers/testnet/add_result.py b/containers/localnet/add_result.py similarity index 100% rename from containers/testnet/add_result.py rename to containers/localnet/add_result.py diff --git a/containers/testnet/change_genesis.py b/containers/localnet/change_genesis.py similarity index 100% rename from containers/testnet/change_genesis.py rename to containers/localnet/change_genesis.py diff --git a/containers/testnet/change_prefs.py b/containers/localnet/change_prefs.py similarity index 100% rename from containers/testnet/change_prefs.py rename to containers/localnet/change_prefs.py diff --git a/containers/testnet/create_wallet.py b/containers/localnet/create_wallet.py similarity index 100% rename from containers/testnet/create_wallet.py rename to containers/localnet/create_wallet.py diff --git a/containers/testnet/economics.toml b/containers/localnet/economics.toml similarity index 100% rename from containers/testnet/economics.toml rename to containers/localnet/economics.toml diff --git a/containers/testnet/genesis.json b/containers/localnet/genesis.json similarity index 100% rename from containers/testnet/genesis.json rename to containers/localnet/genesis.json diff --git a/containers/testnet/handle_signal.sh b/containers/localnet/handle_signal.sh similarity index 72% rename from containers/testnet/handle_signal.sh rename to containers/localnet/handle_signal.sh index 25618ee..08fe21b 100644 --- a/containers/testnet/handle_signal.sh +++ b/containers/localnet/handle_signal.sh @@ -1,5 +1,5 @@ function handle_sigterm { - echo "Testnet is being stopped..." + echo "Localnet is being stopped..." ./pause.sh } diff --git a/containers/testnet/localnet.toml b/containers/localnet/localnet.toml similarity index 100% rename from containers/testnet/localnet.toml rename to containers/localnet/localnet.toml diff --git a/containers/testnet/mxops-init/genesis_account.yaml b/containers/localnet/mxops-init/genesis_account.yaml similarity index 61% rename from containers/testnet/mxops-init/genesis_account.yaml rename to containers/localnet/mxops-init/genesis_account.yaml index 6116e24..9330821 100644 --- a/containers/testnet/mxops-init/genesis_account.yaml +++ b/containers/localnet/mxops-init/genesis_account.yaml @@ -6,4 +6,4 @@ allowed_scenario: accounts: - account_name: xnetwork_genesis - pem_path: /home/ubuntu/testnet/genesis-egld-wallet/wallet.pem + pem_path: /home/ubuntu/localnet/genesis-egld-wallet/wallet.pem diff --git a/containers/testnet/pause.sh b/containers/localnet/pause.sh similarity index 100% rename from containers/testnet/pause.sh rename to containers/localnet/pause.sh diff --git a/containers/testnet/read_result.py b/containers/localnet/read_result.py similarity index 100% rename from containers/testnet/read_result.py rename to containers/localnet/read_result.py diff --git a/containers/testnet/replace_economics.py b/containers/localnet/replace_economics.py similarity index 100% rename from containers/testnet/replace_economics.py rename to containers/localnet/replace_economics.py diff --git a/containers/testnet/replace_localnet_toml.py b/containers/localnet/replace_localnet_toml.py similarity index 68% rename from containers/testnet/replace_localnet_toml.py rename to containers/localnet/replace_localnet_toml.py index 8aca0b5..f88b9ee 100644 --- a/containers/testnet/replace_localnet_toml.py +++ b/containers/localnet/replace_localnet_toml.py @@ -21,11 +21,11 @@ def replace_toml_file(): - # Delete testnet.toml if it exists - testnet_toml_path = os.path.join(os.getcwd(), 'localnet.toml') + # Delete localnet.toml if it exists + localnet_toml_path = os.path.join(os.getcwd(), 'localnet.toml') - # Create testnet.toml - with open(testnet_toml_path, 'w') as file: + # Create localnet.toml + with open(localnet_toml_path, 'w') as file: file.write(toml_file_content) diff --git a/containers/testnet/replace_system_contracts_config.py b/containers/localnet/replace_system_contracts_config.py similarity index 100% rename from containers/testnet/replace_system_contracts_config.py rename to containers/localnet/replace_system_contracts_config.py diff --git a/containers/localnet/run.sh b/containers/localnet/run.sh new file mode 100644 index 0000000..a968c15 --- /dev/null +++ b/containers/localnet/run.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +function handle_sigterm { + echo "Container is being stopped..." + ./pause.sh +} + +trap handle_sigterm SIGTERM + +setup() { + rm results.json + + echo "Replacing localnet.toml..." + sudo python3 replace_localnet_toml.py "$MX_LT_NUM_SHARDS" + + echo "Compiling nodes..." + rm -rf localnet + mxpy localnet config + + echo "Copying files..." + cp read_result.py localnet/read_result.py + cp change_prefs.py localnet/change_prefs.py + cp change_genesis.py localnet/change_genesis.py + cp create_wallet.py localnet/create_wallet.py + cp economics.toml localnet/economics.toml + cp genesis.json localnet/genesis.json + cp replace_economics.py localnet/replace_economics.py + cp systemSmartContractsConfig.toml localnet/systemSmartContractsConfig.toml + cp replace_system_contracts_config.py localnet/replace_system_contracts_config.py + + echo "Changing nodes system contracts config..." + cd localnet && sudo python3 replace_system_contracts_config.py && cd .. + + echo "Changing nodes preferences..." + cd localnet && sudo python3 change_prefs.py "$MX_LT_ELASTIC_ENABLED" && cd .. + + echo "Changing nodes genesis..." + cd localnet && sudo python3 change_genesis.py "$MX_LT_CUSTOM_EGLD_ADDRESS" "$MX_LT_NUM_SHARDS" && cd .. + + echo "Changing nodes economics..." + cd localnet && sudo python3 replace_economics.py "$MX_RESULT_TOTAL_SUPPLY" && cd .. +} + +if [ "$(python3 read_result.py "state")" != "paused" ]; then + echo "Localnet is not paused, starting setup..." + + setup +fi + +if [ "$MX_LT_ELASTIC_ENABLED" = "true" ]; then + echo "Waiting for Elastic Search..." + sudo bash ./wait-for-it.sh elastic:9200 --timeout=0 +fi + +sudo python3 add_result.py "state" "running" + +echo "Running localnet..." +mxpy localnet start & +localnet_pid=$! + +while kill -0 $localnet_pid > /dev/null 2>&1; do + sleep 1 +done diff --git a/containers/testnet/run_mxops.py b/containers/localnet/run_mxops.py similarity index 100% rename from containers/testnet/run_mxops.py rename to containers/localnet/run_mxops.py diff --git a/containers/testnet/systemSmartContractsConfig.toml b/containers/localnet/systemSmartContractsConfig.toml similarity index 100% rename from containers/testnet/systemSmartContractsConfig.toml rename to containers/localnet/systemSmartContractsConfig.toml diff --git a/containers/testnet/wait-for-it.sh b/containers/localnet/wait-for-it.sh similarity index 100% rename from containers/testnet/wait-for-it.sh rename to containers/localnet/wait-for-it.sh diff --git a/containers/testnet/run.sh b/containers/testnet/run.sh deleted file mode 100644 index 057e2e0..0000000 --- a/containers/testnet/run.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash - -function handle_sigterm { - echo "Container is being stopped..." - ./pause.sh -} - -trap handle_sigterm SIGTERM - -setup() { - rm results.json - - echo "Replacing testnet.toml..." - sudo python3 replace_localnet_toml.py "$MX_LT_NUM_SHARDS" - - echo "Compiling nodes..." - rm -rf testnet - mxpy testnet config - - echo "Copying files..." - cp read_result.py testnet/read_result.py - cp change_prefs.py testnet/change_prefs.py - cp change_genesis.py testnet/change_genesis.py - cp create_wallet.py testnet/create_wallet.py - cp economics.toml testnet/economics.toml - cp genesis.json testnet/genesis.json - cp replace_economics.py testnet/replace_economics.py - cp systemSmartContractsConfig.toml testnet/systemSmartContractsConfig.toml - cp replace_system_contracts_config.py testnet/replace_system_contracts_config.py - - echo "Changing nodes system contracts config..." - cd testnet && sudo python3 replace_system_contracts_config.py && cd .. - - echo "Changing nodes preferences..." - cd testnet && sudo python3 change_prefs.py "$MX_LT_ELASTIC_ENABLED" && cd .. - - echo "Changing nodes genesis..." - cd testnet && sudo python3 change_genesis.py "$MX_LT_CUSTOM_EGLD_ADDRESS" "$MX_LT_NUM_SHARDS" && cd .. - - echo "Changing nodes economics..." - cd testnet && sudo python3 replace_economics.py "$MX_RESULT_TOTAL_SUPPLY" && cd .. -} - -if [ "$(python3 read_result.py "state")" != "paused" ]; then - echo "Testnet is not paused, starting setup..." - - setup -fi - -if [ "$MX_LT_ELASTIC_ENABLED" = "true" ]; then - echo "Waiting for Elastic Search..." - sudo bash ./wait-for-it.sh elastic:9200 --timeout=0 -fi - -sudo python3 add_result.py "state" "running" - -echo "Running testnet..." -mxpy testnet start & -testnet_pid=$! - -while kill -0 $testnet_pid > /dev/null 2>&1; do - sleep 1 -done diff --git a/docker-compose.yaml b/docker-compose.yaml index 297d631..d834a27 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,9 +1,8 @@ version: "2.4" services: - testnet: - build: containers/testnet - platform: linux/x86_64 + localnet: + build: containers/localnet ports: - "7950:7950" environment: From ac68ee67463021d09c0ea4999efcd857ef743d9f Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 02:20:56 +0100 Subject: [PATCH 05/12] wip --- containers/localnet/change_genesis.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/containers/localnet/change_genesis.py b/containers/localnet/change_genesis.py index 1876f1a..9eea239 100644 --- a/containers/localnet/change_genesis.py +++ b/containers/localnet/change_genesis.py @@ -4,8 +4,6 @@ import sys import json -mxpy_path = "/home/ubuntu/multiversx-sdk/mxpy" - genesis_egld_wallet = 'genesis-egld-wallet' validator01_object = { @@ -154,7 +152,7 @@ def replace_in_files(): print("No genesis address provided, generating a new one...") genesis_pem_path = os.path.join(cwd, genesis_egld_wallet, 'wallet.pem') subprocess.run(f'python3 create_wallet.py "{genesis_egld_wallet}"', shell=True) - genesis_address = subprocess.check_output(f"{mxpy_path} wallet pem-address {genesis_pem_path}", shell=True).decode('utf-8').strip() + genesis_address = subprocess.check_output(f"wallet pem-address {genesis_pem_path}", shell=True).decode('utf-8').strip() print("Generated genesis address: " + genesis_address) subprocess.run(f'{add_result_command} "genesisEgldPemPath" "{genesis_pem_path}"', shell=True) From 2e1a52a431c8d65db4a6555e420c319564fbec70 Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 02:23:25 +0100 Subject: [PATCH 06/12] wip --- containers/localnet/change_genesis.py | 2 +- containers/localnet/create_wallet.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/containers/localnet/change_genesis.py b/containers/localnet/change_genesis.py index 9eea239..706fff2 100644 --- a/containers/localnet/change_genesis.py +++ b/containers/localnet/change_genesis.py @@ -152,7 +152,7 @@ def replace_in_files(): print("No genesis address provided, generating a new one...") genesis_pem_path = os.path.join(cwd, genesis_egld_wallet, 'wallet.pem') subprocess.run(f'python3 create_wallet.py "{genesis_egld_wallet}"', shell=True) - genesis_address = subprocess.check_output(f"wallet pem-address {genesis_pem_path}", shell=True).decode('utf-8').strip() + genesis_address = subprocess.check_output(f"mxpy wallet pem-address {genesis_pem_path}", shell=True).decode('utf-8').strip() print("Generated genesis address: " + genesis_address) subprocess.run(f'{add_result_command} "genesisEgldPemPath" "{genesis_pem_path}"', shell=True) diff --git a/containers/localnet/create_wallet.py b/containers/localnet/create_wallet.py index 16ae1ae..39c20ee 100644 --- a/containers/localnet/create_wallet.py +++ b/containers/localnet/create_wallet.py @@ -1,8 +1,6 @@ import subprocess import sys -mxpy_path = "/home/ubuntu/multiversx-sdk/mxpy" - wallet_name = sys.argv[1] wallet_output = f"{wallet_name}/wallet.pem" @@ -13,7 +11,7 @@ def create_wallet(): # Remove "Mnemonic: " from the beginning of the string mnemonic = raw_mnemonic[9:].decode('utf-8').strip() subprocess.run(f"mkdir -p {wallet_name}", shell=True) - subprocess.run(f"{mxpy_path} wallet derive {wallet_output} --mnemonic", shell=True, input=mnemonic, text=True) + subprocess.run(f"mxpy wallet derive {wallet_output} --mnemonic", shell=True, input=mnemonic, text=True) create_wallet() From 45d87887efa9bc8efae5ff2813a1b40c34fed975 Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 02:26:03 +0100 Subject: [PATCH 07/12] wip --- containers/localnet/create_wallet.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/containers/localnet/create_wallet.py b/containers/localnet/create_wallet.py index 39c20ee..57b99ad 100644 --- a/containers/localnet/create_wallet.py +++ b/containers/localnet/create_wallet.py @@ -5,9 +5,8 @@ wallet_output = f"{wallet_name}/wallet.pem" - def create_wallet(): - raw_mnemonic = subprocess.check_output(f'{mxpy_path} wallet new', shell=True) + raw_mnemonic = subprocess.check_output(f'mxpy wallet new', shell=True) # Remove "Mnemonic: " from the beginning of the string mnemonic = raw_mnemonic[9:].decode('utf-8').strip() subprocess.run(f"mkdir -p {wallet_name}", shell=True) From eaaaccce75096283756d2945ae588d6f6e455edb Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 02:31:40 +0100 Subject: [PATCH 08/12] wip --- containers/localnet/create_wallet.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/containers/localnet/create_wallet.py b/containers/localnet/create_wallet.py index 57b99ad..19400c4 100644 --- a/containers/localnet/create_wallet.py +++ b/containers/localnet/create_wallet.py @@ -6,11 +6,7 @@ wallet_output = f"{wallet_name}/wallet.pem" def create_wallet(): - raw_mnemonic = subprocess.check_output(f'mxpy wallet new', shell=True) - # Remove "Mnemonic: " from the beginning of the string - mnemonic = raw_mnemonic[9:].decode('utf-8').strip() subprocess.run(f"mkdir -p {wallet_name}", shell=True) - subprocess.run(f"mxpy wallet derive {wallet_output} --mnemonic", shell=True, input=mnemonic, text=True) - + raw_mnemonic = subprocess.check_output(f'mxpy wallet new --format pem --outfile {wallet_output}', shell=True) create_wallet() From 44dcbd82f3d1910e662476fc712ed5d7ff5bd96c Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 02:40:42 +0100 Subject: [PATCH 09/12] wip --- containers/localnet/change_genesis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/containers/localnet/change_genesis.py b/containers/localnet/change_genesis.py index 706fff2..92928c2 100644 --- a/containers/localnet/change_genesis.py +++ b/containers/localnet/change_genesis.py @@ -152,7 +152,7 @@ def replace_in_files(): print("No genesis address provided, generating a new one...") genesis_pem_path = os.path.join(cwd, genesis_egld_wallet, 'wallet.pem') subprocess.run(f'python3 create_wallet.py "{genesis_egld_wallet}"', shell=True) - genesis_address = subprocess.check_output(f"mxpy wallet pem-address {genesis_pem_path}", shell=True).decode('utf-8').strip() + genesis_address = subprocess.check_output(f"mxpy wallet convert --infile {genesis_pem_path} --in-format pem --out-format address-bech32", shell=True).decode('utf-8').replace("Output :\n", "").strip() print("Generated genesis address: " + genesis_address) subprocess.run(f'{add_result_command} "genesisEgldPemPath" "{genesis_pem_path}"', shell=True) From 683894f2949e69eb7fd4ac73ad18f7db8b76fae9 Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 02:58:32 +0100 Subject: [PATCH 10/12] wip --- containers/localnet/Dockerfile | 1 + containers/localnet/change_genesis.py | 2 +- containers/localnet/read_result.py | 2 +- containers/localnet/run.sh | 11 ++++++++- .../localnet/systemSmartContractsConfig.toml | 9 ++++++++ containers/localnet/temp_copy_libs.py | 23 +++++++++++++++++++ 6 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 containers/localnet/temp_copy_libs.py diff --git a/containers/localnet/Dockerfile b/containers/localnet/Dockerfile index d2b2dc1..8d9f072 100644 --- a/containers/localnet/Dockerfile +++ b/containers/localnet/Dockerfile @@ -15,6 +15,7 @@ COPY replace_localnet_toml.py replace_localnet_toml.py COPY genesis.json genesis.json COPY create_wallet.py create_wallet.py COPY change_genesis.py change_genesis.py +COPY temp_copy_libs.py temp_copy_libs.py COPY economics.toml economics.toml COPY replace_economics.py replace_economics.py COPY add_result.py add_result.py diff --git a/containers/localnet/change_genesis.py b/containers/localnet/change_genesis.py index 92928c2..8115c02 100644 --- a/containers/localnet/change_genesis.py +++ b/containers/localnet/change_genesis.py @@ -152,7 +152,7 @@ def replace_in_files(): print("No genesis address provided, generating a new one...") genesis_pem_path = os.path.join(cwd, genesis_egld_wallet, 'wallet.pem') subprocess.run(f'python3 create_wallet.py "{genesis_egld_wallet}"', shell=True) - genesis_address = subprocess.check_output(f"mxpy wallet convert --infile {genesis_pem_path} --in-format pem --out-format address-bech32", shell=True).decode('utf-8').replace("Output :\n", "").strip() + genesis_address = subprocess.check_output(f"mxpy wallet convert --infile {genesis_pem_path} --in-format pem --out-format address-bech32", shell=True).decode('utf-8').replace("Output:\n", "").strip() print("Generated genesis address: " + genesis_address) subprocess.run(f'{add_result_command} "genesisEgldPemPath" "{genesis_pem_path}"', shell=True) diff --git a/containers/localnet/read_result.py b/containers/localnet/read_result.py index 2d17dbf..851b16b 100644 --- a/containers/localnet/read_result.py +++ b/containers/localnet/read_result.py @@ -12,7 +12,7 @@ def get_result(key): with open(file_path, 'r') as f: data = json.load(f) - return data[key] + return data.get(key, "") if __name__ == '__main__': diff --git a/containers/localnet/run.sh b/containers/localnet/run.sh index a968c15..be2320f 100644 --- a/containers/localnet/run.sh +++ b/containers/localnet/run.sh @@ -6,9 +6,12 @@ function handle_sigterm { } trap handle_sigterm SIGTERM +set -e setup() { - rm results.json + set -e + + rm -f results.json echo "Replacing localnet.toml..." sudo python3 replace_localnet_toml.py "$MX_LT_NUM_SHARDS" @@ -18,6 +21,12 @@ setup() { mxpy localnet config echo "Copying files..." + + # Workarounds because mxpy doesn't copy arm libs yet + sudo cp /home/ubuntu/mx-chain-vm-go/wasmer2/libvmexeccapi.so /usr/lib/libvmexeccapi.so + sudo cp /home/ubuntu/mx-chain-vm-go/wasmer2/libvmexeccapi_arm.so /usr/lib/libvmexeccapi_arm.so + sudo cp /home/ubuntu/mx-chain-vm-go/wasmer/libwasmer_linux_arm64_shim.so /usr/lib/libwasmer_linux_arm64_shim.so + cp read_result.py localnet/read_result.py cp change_prefs.py localnet/change_prefs.py cp change_genesis.py localnet/change_genesis.py diff --git a/containers/localnet/systemSmartContractsConfig.toml b/containers/localnet/systemSmartContractsConfig.toml index 674fa50..781abbd 100644 --- a/containers/localnet/systemSmartContractsConfig.toml +++ b/containers/localnet/systemSmartContractsConfig.toml @@ -11,6 +11,8 @@ BleedPercentagePerRound = 0.00001 MaxNumberOfNodesForStake = 2169 UnJailValue = "2500000000000000000" #0.1% of genesis node price ActivateBLSPubKeyMessageVerification = true +StakeLimitPercentage = 1.0 #fraction of value 1 - 100%, for the time being no stake limit +NodeLimitPercentage = 0.1 #fraction of value 0.1 - 10% [ESDTSystemSCConfig] BaseIssuingCost = "50000000000000000" #0.05 eGLD @@ -39,3 +41,10 @@ ConfigChangeAddress = "erd1qqqqqqqqqqqqqpgq0gq09608js3gje72v0yzc56k6glc852gl94sg [DelegationSystemSCConfig] MinServiceFee = 0 MaxServiceFee = 10000 + +# Changing this config is not backwards compatible +[SoftAuctionConfig] +TopUpStep = "10000000000000000000" # 10 EGLD +MinTopUp = "1000000000000000000" # 1 EGLD should be minimum +MaxTopUp = "32000000000000000000000000" # 32 mil EGLD +MaxNumberOfIterations = 100000 # 100k max number of iterations for soft auction config diff --git a/containers/localnet/temp_copy_libs.py b/containers/localnet/temp_copy_libs.py new file mode 100644 index 0000000..d93888a --- /dev/null +++ b/containers/localnet/temp_copy_libs.py @@ -0,0 +1,23 @@ +import os +import re +import subprocess + +def temp_copy_libs(): + cwd = os.getcwd() + + wasmer2_lib_files = [ + "libvmexeccapi.so", + "libvmexeccapi_arm.so", + "libvmexeccapi_arm.dylib", + ] + + validator_dirs = [dir_name for dir_name in os.listdir(cwd) if re.match(r'validator\d\d', dir_name)] + for validator_dir in validator_dirs: + for wasmer2_lib_file in wasmer2_lib_files: + source_path = os.path.join("/home/ubuntu/mx-chain-vm-go/wasmer2", wasmer2_lib_file) + dest_path = os.path.join(cwd, validator_dir, wasmer2_lib_file) + subprocess.run(f"cp {source_path} {dest_path}", shell=True) + + + +temp_copy_libs() From a0d06dc2e88999d4030d3bf64d8b51d7806b2272 Mon Sep 17 00:00:00 2001 From: Fusee Date: Sat, 9 Mar 2024 18:39:43 +0100 Subject: [PATCH 11/12] wip --- containers/localnet/change_genesis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/containers/localnet/change_genesis.py b/containers/localnet/change_genesis.py index 8115c02..fc4b544 100644 --- a/containers/localnet/change_genesis.py +++ b/containers/localnet/change_genesis.py @@ -172,6 +172,7 @@ def replace_in_files(): with open(external_json_path, 'r') as f: existing_data = json.load(f) + print(f"validators_objects: {validators_objects}, shards_count: {shards_count}") existing_data = validators_objects[0:shards_count] + existing_data existing_data.append(genesis_object) From 4b37089386ea5cf584547bbc3ccc8a2f6394c330 Mon Sep 17 00:00:00 2001 From: Fusee Date: Sun, 10 Mar 2024 18:25:08 +0100 Subject: [PATCH 12/12] made xnetwork to work with v1.7.0 --- containers/api/Dockerfile | 4 +- containers/api/api-config.devnet.yaml | 50 ++- containers/localnet/Dockerfile | 4 +- containers/localnet/change_prefs.py | 3 +- containers/localnet/replace_localnet_toml.py | 2 +- containers/localnet/run.sh | 10 + .../localnet/systemSmartContractsConfig.toml | 18 +- .../tempGenesisContracts/delegation.wasm | Bin 0 -> 57293 bytes .../localnet/tempGenesisContracts/dns.wasm | Bin 0 -> 9740 bytes containers/localnet/temp_copy_libs.py | 23 -- .../localnet/temp_replace_enable_epochs.py | 343 ++++++++++++++++++ .../temp_replace_genesis_smart_contracts.py | 45 +++ 12 files changed, 452 insertions(+), 50 deletions(-) create mode 100644 containers/localnet/tempGenesisContracts/delegation.wasm create mode 100644 containers/localnet/tempGenesisContracts/dns.wasm delete mode 100644 containers/localnet/temp_copy_libs.py create mode 100644 containers/localnet/temp_replace_enable_epochs.py create mode 100644 containers/localnet/temp_replace_genesis_smart_contracts.py diff --git a/containers/api/Dockerfile b/containers/api/Dockerfile index 5a4c0cb..925a704 100644 --- a/containers/api/Dockerfile +++ b/containers/api/Dockerfile @@ -19,8 +19,8 @@ RUN curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - && sudo ap RUN sudo apt install git -y RUN sudo apt install ffmpeg -y RUN sudo npm install -g @nestjs/cli -RUN git clone https://github.com/multiversx/mx-api-service.git -b development -RUN cd mx-api-service && git checkout 8cc3910 +RUN git clone https://github.com/multiversx/mx-api-service.git -b main +RUN cd mx-api-service && git checkout 93a9abd0033050e46242836c6cf12e7b396e294c RUN cd mx-api-service && npm install RUN rm -rf mx-api-service/config/config.devnet.json COPY api-config.devnet.yaml mx-api-service/config/config.devnet.yaml diff --git a/containers/api/api-config.devnet.yaml b/containers/api/api-config.devnet.yaml index 505d99c..d41444e 100644 --- a/containers/api/api-config.devnet.yaml +++ b/containers/api/api-config.devnet.yaml @@ -3,7 +3,6 @@ metaChainShardId: 4294967295 api: public: true private: false - auth: false cron: transactionProcessor: true transactionProcessorMaxLookBehind: 100 @@ -15,21 +14,52 @@ cron: fastWarm: true queueWorker: true elasticUpdater: false + statusChecker: false flags: useRequestCaching: true useKeepAliveAgent: true - useTracing: true - useRequestLogging: true - useVmQueryTracing: true + useTracing: false + useRequestLogging: false + useVmQueryTracing: false processNfts: false indexer-v3: true + collectionPropertiesFromGateway: false features: eventsNotifier: enabled: false port: 5674 - url: 'amqp://guest:guest@127.0.0.1:5672' + url: 'amqp://guest:guest@127.0.0.1:5673' exchange: 'all_events' queue: 'api-process-logs-and-events' + guestCaching: + enabled: false + hitsThreshold: 100 + ttl: 12 + transactionPool: + enabled: true + transactionPoolWarmer: + enabled: true + cronExpression: '*/5 * * * * *' + ttlInSeconds: 60 + updateCollectionExtraDetails: + enabled: false + marketplace: + enabled: false + serviceUrl: 'https://devnet-nfts-graph.multiversx.com/graphql' + exchange: + enabled: false + serviceUrl: 'https://devnet-graph.xexchange.com/graphql' + dataApi: + enabled: false + serviceUrl: 'https://devnet-data-api.multiversx.com' + auth: + enabled: false + maxExpirySeconds: 86400 + acceptedOrigins: + - '' + admins: + - '' + jwtSecret: '' image: width: 600 height: 600 @@ -40,12 +70,12 @@ aws: s3Bucket: 'devnet-media.elrond.com' s3Region: '' urls: - api: - - 'http://localhost:3001' + self: 'http://localhost:3001' elastic: - 'http://elastic:9200' gateway: - 'http://localnet:7950' + verifier: 'https://play-api.multiversx.com' redis: 'redis' rabbitmq: 'amqp://rabbitmq:5672' providers: 'https://devnet-delegation-api.multiversx.com/providers' @@ -90,12 +120,6 @@ inflation: - 1130177 - 924690 - 719203 -security: - admins: - jwtSecret: nftProcess: parallelism: 1 maxRetries: 3 -transaction-action: - mex: - microServiceUrl: '' diff --git a/containers/localnet/Dockerfile b/containers/localnet/Dockerfile index 8d9f072..5b7ea9e 100644 --- a/containers/localnet/Dockerfile +++ b/containers/localnet/Dockerfile @@ -15,7 +15,6 @@ COPY replace_localnet_toml.py replace_localnet_toml.py COPY genesis.json genesis.json COPY create_wallet.py create_wallet.py COPY change_genesis.py change_genesis.py -COPY temp_copy_libs.py temp_copy_libs.py COPY economics.toml economics.toml COPY replace_economics.py replace_economics.py COPY add_result.py add_result.py @@ -26,6 +25,9 @@ COPY pause.sh pause.sh COPY handle_signal.sh handle_signal.sh COPY mxops-init mxops-init COPY run_mxops.py run_mxops.py +COPY temp_replace_enable_epochs.py temp_replace_enable_epochs.py +COPY temp_replace_genesis_smart_contracts.py temp_replace_genesis_smart_contracts.py +COPY tempGenesisContracts genesisContracts RUN sudo chmod +x pause.sh RUN sudo chmod +x handle_signal.sh diff --git a/containers/localnet/change_prefs.py b/containers/localnet/change_prefs.py index 8d52eab..0c8e91a 100644 --- a/containers/localnet/change_prefs.py +++ b/containers/localnet/change_prefs.py @@ -5,6 +5,7 @@ should_use_elastic = sys.argv[1] == 'true' prefs_to_add = f""" +[Preferences] OverridableConfigTomlValues = [ {{ File = "external.toml", Path = "ElasticSearchConnector.Enabled", Value = "{should_use_elastic}" }}, {{ File = "external.toml", Path = "ElasticSearchConnector.URL", Value = "http://elastic:9200" }}, @@ -21,7 +22,7 @@ def replace_in_files(): if os.path.exists(external_toml_path): with open(external_toml_path, 'r') as file: contents = file.read() - contents = contents + prefs_to_add + contents = prefs_to_add + contents.replace("[Preferences]", "") with open(external_toml_path, 'w') as file: file.write(contents) diff --git a/containers/localnet/replace_localnet_toml.py b/containers/localnet/replace_localnet_toml.py index f88b9ee..59aa839 100644 --- a/containers/localnet/replace_localnet_toml.py +++ b/containers/localnet/replace_localnet_toml.py @@ -8,7 +8,7 @@ port_proxy = 7950 [shards] -num_shards = 3 +num_shards = {num_shards} [software.mx_chain_go] resolution = "local" diff --git a/containers/localnet/run.sh b/containers/localnet/run.sh index be2320f..018a130 100644 --- a/containers/localnet/run.sh +++ b/containers/localnet/run.sh @@ -37,6 +37,10 @@ setup() { cp systemSmartContractsConfig.toml localnet/systemSmartContractsConfig.toml cp replace_system_contracts_config.py localnet/replace_system_contracts_config.py + cp temp_replace_enable_epochs.py localnet/temp_replace_enable_epochs.py # Workaround while mxpy doesn't support v1.7.0 localnet + cp temp_replace_genesis_smart_contracts.py localnet/temp_replace_genesis_smart_contracts.py # Workaround while mxpy doesn't support v1.7.0 localnet + cp -R genesisContracts localnet/genesisContracts # Workaround while mxpy doesn't support v1.7.0 localnet + echo "Changing nodes system contracts config..." cd localnet && sudo python3 replace_system_contracts_config.py && cd .. @@ -48,6 +52,12 @@ setup() { echo "Changing nodes economics..." cd localnet && sudo python3 replace_economics.py "$MX_RESULT_TOTAL_SUPPLY" && cd .. + + echo "Applying workaround for enableEpochs..." + cd localnet && sudo python3 temp_replace_enable_epochs.py && cd .. + + echo "Applying workaround for genesisContracts..." + cd localnet && sudo python3 temp_replace_genesis_smart_contracts.py && cd .. } if [ "$(python3 read_result.py "state")" != "paused" ]; then diff --git a/containers/localnet/systemSmartContractsConfig.toml b/containers/localnet/systemSmartContractsConfig.toml index 781abbd..72503e1 100644 --- a/containers/localnet/systemSmartContractsConfig.toml +++ b/containers/localnet/systemSmartContractsConfig.toml @@ -2,24 +2,24 @@ GenesisNodePrice = "2500000000000000000000" #2.5K eGLD MinStakeValue = "100000000000000000000" #100 eGLD MinUnstakeTokensValue = "10000000000000000000" #10eGLD -UnBondPeriod = 144000 -UnBondPeriodInEpochs = 10 +UnBondPeriod = 250 +UnBondPeriodInEpochs = 1 MinStepValue = "100000000000000000000" NumRoundsWithoutBleed = 100 MaximumPercentageToBleed = 0.5 BleedPercentagePerRound = 0.00001 -MaxNumberOfNodesForStake = 2169 +MaxNumberOfNodesForStake = 64 UnJailValue = "2500000000000000000" #0.1% of genesis node price -ActivateBLSPubKeyMessageVerification = true +ActivateBLSPubKeyMessageVerification = false StakeLimitPercentage = 1.0 #fraction of value 1 - 100%, for the time being no stake limit NodeLimitPercentage = 0.1 #fraction of value 0.1 - 10% [ESDTSystemSCConfig] BaseIssuingCost = "50000000000000000" #0.05 eGLD -OwnerAddress = "erd1ze6pgefwc7mdx0rhje3h50kkgt26ut3gx8ytezndkz8fjcf96gsqsd7ugw" +OwnerAddress = "erd1fpkcgel4gcmh8zqqdt043yfcn5tyx8373kg6q2qmkxzu4dqamc0swts65c" [GovernanceSystemSCConfig] -OwnerAddress = "erd1qqqqqqqqqqqqqpgq0gq09608js3gje72v0yzc56k6glc852gl94sgtahk2" +OwnerAddress = "erd1vxy22x0fj4zv6hktmydg8vpfh6euv02cz4yg0aaws6rrad5a5awqgqky80" #should use a multisign contract instead of a wallet address [GovernanceSystemSCConfig.V1] NumNodes = 500 ProposalCost = "5000000000000000000" #5 eGLD @@ -29,14 +29,14 @@ MinVetoThreshold = 50 [GovernanceSystemSCConfig.Active] ProposalCost = "1000000000000000000000" #1000 eGLD LostProposalFee = "10000000000000000000" #10 eGLD -MinQuorum = 0.2 #fraction of value 0.2 - 20% +MinQuorum = 0.2 #fraction of value 0.2 - 20% MinPassThreshold = 0.5 #fraction of value 0.5 - 50% MinVetoThreshold = 0.33 #fraction of value 0.33 - 33% [DelegationManagerSystemSCConfig] MinCreationDeposit = "1250000000000000000000" #1.25K eGLD -MinStakeAmount = "1000000000000000000" #1 eGLD -ConfigChangeAddress = "erd1qqqqqqqqqqqqqpgq0gq09608js3gje72v0yzc56k6glc852gl94sgtahk2" +MinStakeAmount = "10000000000000000000" #10 eGLD +ConfigChangeAddress = "erd1vxy22x0fj4zv6hktmydg8vpfh6euv02cz4yg0aaws6rrad5a5awqgqky80" #should use a multisign contract instead of a wallet address [DelegationSystemSCConfig] MinServiceFee = 0 diff --git a/containers/localnet/tempGenesisContracts/delegation.wasm b/containers/localnet/tempGenesisContracts/delegation.wasm new file mode 100644 index 0000000000000000000000000000000000000000..d00f3f7d065089bb08570e007ad77955e2455555 GIT binary patch literal 57293 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 yt$ij~I(PDq1+^1$>O&A4$~cVUD0t&Wud literal 0 HcmV?d00001 diff --git a/containers/localnet/tempGenesisContracts/dns.wasm b/containers/localnet/tempGenesisContracts/dns.wasm new file mode 100644 index 0000000000000000000000000000000000000000..ce692a1260bf44b86b79f8ad1095ff015c8e9ae8 GIT binary patch literal 9740 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 a`rV3r$uv86>KUcfJbe*O!zwmz%sBE