Skip to content

Commit

Permalink
Merge pull request #23 from multiversx/testing-suite-for-staking-v4
Browse files Browse the repository at this point in the history
Testing suite for staking v4
  • Loading branch information
gabi-vuls authored Apr 16, 2024
2 parents d6e955e + 954eebd commit 92e97bb
Show file tree
Hide file tree
Showing 81 changed files with 1,576 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
*.dll
*.so
*.dylib
*.DS_Store

# python stuff
__pycache__/
venv/
.idea/
.pytest_cache/

# Test binary, built with `go test -c`
*.test
Expand Down
26 changes: 26 additions & 0 deletions testing-suite/staking-v4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Testing suite using mvx python sdk and chain-simulator for staking-v4 feature

## Overview:
- All tests are written based on scenarios from the internal testing plan.

## How to run:

### 1) Create a virtual env
`python3 -m venv ./venv`
`source ./venv/bin/activate`

### 2) Install all dependencies
`pip3 install -r ./req.txt`

### 3) Export PythonPath
`export PYTHONPATH=.`

### 4) Make sure [chain-simulator](https://github.com/multiversx/mx-chain-simulator-go) is:
- Running
- Running with a correct config (specific config can be found at the begging of every `./scenarios/_test.py`)
- If you run chain-simulator on different port, you can edit it in `./scenarios/config.py`

### 5) RUN
`pytest scenarios/` - to run all scenarios
`pytest scenarios/_17.py` - to run a specific scenario

79 changes: 79 additions & 0 deletions testing-suite/staking-v4/chain_commander.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import requests
import json

from config import *
from get_info import *
from constants import *
import time


def send_egld_to_address(egld_amount, erd_address):
details = {
'address': f'{erd_address}',
'balance': f'{egld_amount}'
}

details_list = [details]
json_structure = json.dumps(details_list)
response = requests.post(f"{DEFAULT_PROXY}/simulator/set-state", data=json_structure)
response.raise_for_status()

return response.text


def add_blocks(nr_of_blocks):
req = requests.post(f"{DEFAULT_PROXY}/simulator/generate-blocks/{nr_of_blocks}")
return req.text


def add_blocks_until_epoch_reached(epoch_to_be_reached: int):
req = requests.post(f"{DEFAULT_PROXY}/simulator/generate-blocks-until-epoch-reached/{str(epoch_to_be_reached)}")
return req.text


def add_blocks_until_tx_fully_executed(tx_hash) -> str:
print("Checking: ", tx_hash)
counter = 0

while counter < MAX_NUM_OF_BLOCKS_UNTIL_TX_SHOULD_BE_EXECUTED:
add_blocks(1)

time.sleep(WAIT_UNTIL_API_REQUEST_IN_SEC)
if get_status_of_tx(tx_hash) == "pending":
counter += 1
else:
print("Tx fully executed after", counter, " blocks.")
return get_status_of_tx(tx_hash)


def is_chain_online() -> bool:
flag = False

while not flag:
time.sleep(1)
try:
response = requests.get(f"{DEFAULT_PROXY}/network/status/0")
print(response)
flag = True
except requests.exceptions.ConnectionError:
print("Chain not started jet")

return flag


def force_reset_validator_statistics():
req = requests.post(f"{DEFAULT_PROXY}/simulator/force-reset-validator-statistics")
print(req.text)

return req.text


def add_key(private_keys: list) -> str:
post_body = {
"privateKeysBase64": private_keys
}

json_structure = json.dumps(post_body)
req = requests.post(f"{DEFAULT_PROXY}/simulator/add-keys", data=json_structure)

return req.text
28 changes: 28 additions & 0 deletions testing-suite/staking-v4/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from pathlib import Path
from multiversx_sdk_network_providers import ProxyNetworkProvider


PROXY_PUBLIC_TESTNET = "https://testnet-gateway.multiversx.com"
PROXY_PUBLIC_DEVNET = "https://devnet-gateway.multiversx.com"
PROXY_CHAIN_SIMULATOR = "http://localhost:8085"

DEFAULT_PROXY = PROXY_CHAIN_SIMULATOR

try:
proxy_default = ProxyNetworkProvider(DEFAULT_PROXY)
except:
Exception

chain_id = "chain"

# relative path to chain-simulator
chain_simulator_path = Path("../../cmd/chainsimulator")

# config for cli flags for starting chain simulator
log_level = '"*:DEBUG,process:TRACE"'
num_validators_per_shard = "10"
num_validators_meta = "10"
num_waiting_validators_per_shard = "6"
num_waiting_validators_meta = "6"

rounds_per_epoch = "50"
17 changes: 17 additions & 0 deletions testing-suite/staking-v4/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# contracts
VALIDATOR_CONTRACT = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l"
SYSTEM_DELEGATION_MANAGER_CONTRACT = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6"
STAKING_CONTRACT = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqllls0lczs7"


# timing
WAIT_UNTIL_API_REQUEST_IN_SEC = 0.5

# chain
MAX_NUM_OF_BLOCKS_UNTIL_TX_SHOULD_BE_EXECUTED = 8

# staking_v4
EPOCH_WITH_STAKING_V3_5 = 3
EPOCH_STAKING_QUEUE_BECOMES_AUCTION_LIST = 4
EPOCH_SHUFFLING_FROM_ELIGIBLE_TO_AUCTION_LIST = 5
EPOCH_STAKING_V4_FULLY_FUNCTIONAL = 6
49 changes: 49 additions & 0 deletions testing-suite/staking-v4/core/chain_simulator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import stat

from constants import *
from config import *
import os
import signal
import subprocess
from subprocess import Popen
from threading import Thread
import threading


class ChainSimulator:
def __init__(self, path: Path) -> None:
self.path = path
self.log_level = log_level
self.num_validators_per_shard = num_validators_per_shard
self.num_validators_meta = num_validators_meta
self.num_waiting_validators_per_shard = num_waiting_validators_per_shard
self.num_waiting_validators_meta = num_waiting_validators_meta
self.rounds_per_epoch = rounds_per_epoch
self.process = None

def start(self):
command = f"./chainsimulator --log-level {self.log_level} --rounds-per-epoch {rounds_per_epoch}\
-num-validators-per-shard {self.num_validators_per_shard} \
-num-waiting-validators-per-shard {num_waiting_validators_per_shard} \
-num-validators-meta {num_validators_meta} \
-num-waiting-validators-meta {num_waiting_validators_meta}"

flag = True
while flag:
if " " in command:
command = command.replace(" ", " ")
else:
flag = False
print(command)

self.process = subprocess.Popen(command, stdout=subprocess.PIPE,
shell=True, preexec_fn=os.setsid, cwd=chain_simulator_path)

out, err = self.process.communicate()
if err:
print(err)

def stop(self) -> None:
self.process.terminate()


76 changes: 76 additions & 0 deletions testing-suite/staking-v4/core/validatorKey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import requests

from core.wallet import *
from pathlib import Path
from helpers import *
from get_info import *
from constants import *
from chain_commander import *


class ValidatorKey:
def __init__(self, path: Path) -> None:
self.path = path

def public_address(self) -> str:
f = open(self.path)
lines = f.readlines()
for line in lines:
if "BEGIN" in line:
line = line.split(" ")
address = line[-1].replace("-----", "")
if "\n" in address:
address = address.replace("\n", "")
break
return address

# is using vm-query with "getBlsKeysStatus" function
def get_status(self, owner_address: str) -> str:
owner_address = Address.from_bech32(owner_address).to_hex()
key_status_pair = get_bls_key_status([owner_address])
if key_status_pair is None:
return "no bls keys on this owner"
for key, status in key_status_pair.items():
if key == self.public_address():
return status

# is using /validator/statistics route
def get_state(self) -> str:
force_reset_validator_statistics()

# sometimes it needs a second until cache is resetting
time.sleep(1)

response = requests.get(f"{DEFAULT_PROXY}/validator/statistics")
response.raise_for_status()
parsed = response.json()

general_data = parsed.get("data")
general_statistics = general_data.get("statistics")
key_data = general_statistics.get(self.public_address())
if key_data is None:
return "Key not present in validator/statistics"
else:
status = key_data.get("validatorStatus")
return status

# using getOwner vm-query
def belongs_to(self, address: str) -> bool:
owner = get_owner([self.public_address()])
if owner == address:
return True
else:
return False

def get_private_key(self) -> str:
private_key = ""

f = open(self.path)
lines = f.readlines()
for line in lines:
if not "BEGIN" in line and not "END" in line:
private_key += line
if "\n" in private_key:
private_key = private_key.replace("\n", "")

return private_key
61 changes: 61 additions & 0 deletions testing-suite/staking-v4/core/wallet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from pathlib import Path
from config import *
import requests
import json
from multiversx_sdk_network_providers import ProxyNetworkProvider
from multiversx_sdk_wallet import UserSigner
from multiversx_sdk_core import Address
from multiversx_sdk_network_providers import accounts
from helpers import *
from constants import *


class Wallet:
def __init__(self, path: Path) -> None:
self.path = path

def public_address(self) -> str:
f = open(self.path)

lines = f.readlines()
for line in lines:
if "BEGIN" in line:
line = line.split(" ")
address = line[-1].replace("-----", "")
if "\n" in address:
address = address.replace("\n", "")
break

return address

def get_balance(self) -> int:
response = requests.get(f"{DEFAULT_PROXY}/address/{self.public_address()}/balance")
response.raise_for_status()
parsed = response.json()

general_data = parsed.get("data")
balance = general_data.get("balance")

return balance


def set_balance(self, egld_amount):
details = {
'address': f'{self.public_address()}',
'balance': f'{egld_amount}'
}

details_list = [details]
json_structure = json.dumps(details_list)
req = requests.post(f"{DEFAULT_PROXY}/simulator/set-state", data=json_structure)

return req.text

def get_signer(self) -> UserSigner:
return UserSigner.from_pem_file(self.path)

def get_address(self) -> Address:
return Address.from_bech32(self.public_address())

def get_account(self):
return proxy_default.get_account(self.get_address())
Loading

0 comments on commit 92e97bb

Please sign in to comment.