Skip to content

Commit

Permalink
Support for non-validator nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
jzvikart committed Oct 10, 2023
1 parent 4f5bab3 commit 0a342ed
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 52 deletions.
36 changes: 36 additions & 0 deletions test/integration/framework/resources/sifnoded.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash

set -euo pipefail

BASEDIR="$0"
while [ -h "$BASEDIR" ]; do
ls=$(ls -ld "$BASEDIR")
link=$(expr "$ls" : '.*-> \(.*\)$')
if expr "$link" : '/.*' > /dev/null; then
BASEDIR="$link"
else
BASEDIR="$(dirname "$BASEDIR")/$link"
fi
done
BASEDIR="$(dirname "$BASEDIR")"
BASEDIR="$(cd "$BASEDIR"; pwd)"

SERVICE_NAME="${1-}"

if [ -z "$SERVICE_NAME" ]; then
echo "Mising parameter: SERVICE_NAME"
exit 255
fi

echo $BASEDIR
echo $SERVICE_NAME

export DAEMON_NAME="sifnoded"
export DAEMON_HOME="$BASEDIR/$SERVICE_NAME"
export DAEMON_ALLOW_DOWNLOAD_BINARIES="true"
export DAEMON_RESTART_AFTER_UPGRADE="true"
export UNSAFE_SKIP_BACKUP="true"

echo "Running cosmovisor for ${SERVICE_NAME} in ${DAEMON_HOME}..."

"$BASEDIR/cosmovisor" start --home "${DAEMON_HOME}"
16 changes: 16 additions & 0 deletions test/integration/framework/resources/sifnoded@.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[Unit]
Description="sifchain node %I"
After=network-online.target

[Service]
User=sif
ExecStart=/home/sif/testnet/sifnode/sifnoded.sh %i
Restart=always
RestartSec=15
LimitNOFILE=4096
# SyslogIdentifier=sifnoded-%i
StandardOutput=append:/home/sif/testnet/sifnode/%i/sifnoded.log
StandardError=inherit

[Install]
WantedBy=multi-user.target
17 changes: 10 additions & 7 deletions test/integration/framework/src/siftool/chainbuilder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys

from siftool import common, command, environments, project
from siftool import common, command, environments, project, sifchain


def __brutally_terminate_processes(cmd):
Expand All @@ -15,13 +15,16 @@ def install_testnet(cmd: command.Command, base_dir: str, chain_id: str):
# "juniper": "clump genre baby drum canvas uncover firm liberty verb moment access draft erupt fog alter gadget elder elephant divide biology choice sentence oppose avoid",
# "ethereum_root": "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat",
# }
external_host = "147.135.105.196"
env = environments.SifnodedEnvironment(cmd, chain_id=chain_id, sifnoded_home_root=base_dir)
env.add_validator(moniker="ant")
env.add_validator(moniker="bee", pruning="nothing")
env.add_validator(moniker="cat", pruning="everything")
env.add_validator(moniker="dog", pruning="everything")
env.add_validator(moniker="node-0", external_host=external_host, pruning="default")
env.add_validator(moniker="node-1", external_host=external_host, pruning="nothing")
env.add_validator(moniker="node-2", external_host=external_host, pruning="everything")
env.add_validator(moniker="node-3", external_host=external_host, pruning="everything")
env.init(faucet_balance={sifchain.ROWAN: 10**30, sifchain.STAKE: 10**30})
env.start()
env._client_for().wait_for_block(15)
sifnoded = env._client_for()
sifnoded.wait_for_block(sifnoded.get_current_block() + 10)
__brutally_terminate_processes(cmd)


Expand All @@ -33,4 +36,4 @@ def main(*argv):


if __name__ == "__main__":
main(*sys.argv)
main(*sys.argv[1:])
94 changes: 54 additions & 40 deletions test/integration/framework/src/siftool/environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ def __init__(self, cmd: command.Command, chain_id: Optional[str] = None, sifnode
self.running_processes = []
self.open_log_files = []
self._state = 0
self.sifnoded_connect_host = LOCALHOST # The hostname for building sifnoded '--node' argument
self.sifnoded = sifchain.Sifnoded(self.cmd, home=self.keyring_dir, chain_id=self.chain_id)

def add_validator(self, /, binary: Optional[str] = None, admin_name: Optional[str] = None,
def add_validator(self, /, binary: Optional[str] = None, admin_name: Optional[str] = None, is_validator: bool = True,
admin_mnemonic: Optional[Sequence[str]] = None, moniker: Optional[str] = None, home: Optional[str] = None,
external_host: Optional[str] = None, external_port: Optional[int] = None, staking_amount: Optional[int] = None,
initial_balance: Optional[cosmos.Balance] = None, commission_rate: Optional[float] = None,
Expand All @@ -37,6 +38,7 @@ def add_validator(self, /, binary: Optional[str] = None, admin_name: Optional[s
pruning_interval: Optional[int] = None, log_level: Optional[str] = None, log_file: Optional[str] = None
):
next_id = len(self.node_info)
assert is_validator or (next_id > 0), "First node must be a validator"

binary = binary if binary is not None else self.default_binary
moniker = moniker if moniker is not None else "sifnoded-{}".format(next_id)
Expand All @@ -60,6 +62,7 @@ def add_validator(self, /, binary: Optional[str] = None, admin_name: Optional[s
"home": home,
"external_host": external_host,
"external_port": external_port,
"is_validator": is_validator,
"admin_name": admin_name,
"staking_amount": staking_amount,
"initial_balance": initial_balance,
Expand All @@ -68,21 +71,25 @@ def add_validator(self, /, binary: Optional[str] = None, admin_name: Optional[s
"commission_max_change_rate": commission_max_change_rate,
"min_self_delegation": min_self_delegation,
"ports": ports,
"pruning": pruning,
"pruning_keep_recent": pruning_keep_recent,
"pruning_keep_every": pruning_keep_every,
"pruning_interval": pruning_interval,
"log_level": log_level,
"log_file": log_file,
}
if admin_mnemonic is not None:
node_info["admin_mnemonic"] = admin_mnemonic
if pruning is not None:
node_info["pruning"] = pruning
if pruning_keep_recent is not None:
node_info["pruning_keep_recent"] = pruning_keep_recent
if pruning_keep_every is not None:
node_info["pruning_keep_every"] = pruning_keep_every
if pruning_interval is not None:
node_info["pruning_interval"] = pruning_interval

next_index = len(self.node_info)
is_first = next_index == 0
peers = [] if is_first else [self.node_info[0]]

self._create_validator_home(node_info)
self._create_sifnoded_home(node_info)
self._update_configuration_files(node_info, peers)

if self._state == 1:
Expand All @@ -98,7 +105,8 @@ def add_validator(self, /, binary: Optional[str] = None, admin_name: Optional[s
sifnoded_i = self._sifnoded_for(node_info)
sifnoded_i.save_genesis_json(sifnoded.load_genesis_json())
self._sifnoded_start(node_info)
self._broadcast_create_validator_msg(node_info)
if is_validator:
self._broadcast_create_validator_msg(node_info)

self.node_info.append(node_info)

Expand Down Expand Up @@ -130,12 +138,13 @@ def init(self, faucet_balance: Optional[cosmos.Balance] = None, extra_accounts:
sifnoded0 = self._sifnoded_for(node_info0)

for node_info in self.node_info:
sifnoded = self._sifnoded_for(node_info)
admin_addr = node_info["admin_addr"]
admin_bech = sifnoded.get_val_address(admin_addr)
validator_balance = cosmos.balance_add({self.staking_denom: node_info["staking_amount"]}, node_info["initial_balance"])
sifnoded0.add_genesis_account(admin_addr, validator_balance)
sifnoded0.add_genesis_validators(admin_bech)
if node_info["is_validator"]:
sifnoded = self._sifnoded_for(node_info)
admin_addr = node_info["admin_addr"]
admin_bech = sifnoded.get_val_address(admin_addr)
validator_balance = cosmos.balance_add({self.staking_denom: node_info["staking_amount"]}, node_info["initial_balance"])
sifnoded0.add_genesis_account(admin_addr, validator_balance)
sifnoded0.add_genesis_validators(admin_bech)

admin0_addr = node_info0["admin_addr"]
# admin0_name = node_info0["admin_name"]
Expand Down Expand Up @@ -204,7 +213,8 @@ def start(self):
sifnoded0.wait_for_last_transaction_to_be_mined()

for node_info in other_validators:
self._broadcast_create_validator_msg(node_info)
if node_info["is_validator"]:
self._broadcast_create_validator_msg(node_info)

self.sifnoded = sifchain.Sifnoded(self.cmd, home=self.keyring_dir, chain_id=self.chain_id,
node=sifchain.format_node_url(self.node_info[0]["external_host"], self.node_info[0]["external_port"]),
Expand Down Expand Up @@ -271,13 +281,13 @@ def _update_configuration_files(self, node_info, peers_node_info):
app_toml = sifnoded.load_app_toml()
config_toml = sifnoded.load_config_toml()
app_toml["minimum-gas-prices"] = sif_format_amount(0.5, ROWAN)
if node_info["pruning"] is not None:
if node_info.get("pruning", None) is not None:
app_toml["pruning"] = node_info["pruning"]
if node_info["pruning_keep_recent"] is not None:
if node_info.get("pruning_keep_recent", None) is not None:
app_toml["pruning-keep-recent"] = str(node_info["pruning_keep_recent"])
if node_info["pruning_keep_every"] is not None:
if node_info.get("pruning_keep_every", None) is not None:
app_toml["pruning-keep-every"] = str(node_info["pruning_keep_every"])
if node_info["pruning_interval"] is not None:
if node_info.get("pruning_interval", None) is not None:
app_toml["pruning-interval"] = str(node_info["pruning_interval"])
app_toml['api']['enable'] = True
app_toml["api"]["address"] = sifchain.format_node_url(ANY_ADDR, node_info["ports"]["api"]) # default "tcp://0.0.0.0:1317"
Expand Down Expand Up @@ -315,13 +325,13 @@ def _sifnoded_for(self, node_info: JsonDict, to_node_info: Optional[JsonDict] =
binary = node_info["binary"]
home = node_info["home"]
to_node_info = to_node_info if to_node_info is not None else node_info
node = sifchain.format_node_url(to_node_info["external_host"], to_node_info["external_port"])
node = sifchain.format_node_url(self.sifnoded_connect_host, to_node_info["external_port"])
return sifchain.Sifnoded(self.cmd, binary=binary, home=home, chain_id=self.chain_id, node=node)

# Returns a Sifnoded that uses a default binary with the "keyring" profile and pointing to specific validator.
def _client_for(self, node_index: int = 0) -> sifchain.Sifnoded:
to_node_info = self.node_info[node_index]
node = sifchain.format_node_url(to_node_info["external_host"], to_node_info["external_port"])
node = sifchain.format_node_url(self.sifnoded_connect_host, to_node_info["external_port"])
return sifchain.Sifnoded(self.cmd, home=self.keyring_dir, chain_id=self.chain_id, node=node)

def _sifnoded_start(self, node_info: JsonDict):
Expand Down Expand Up @@ -378,27 +388,31 @@ def _broadcast_create_validator_msg(self, node_info: JsonDict):
assert float(new_validator["commission"]["commission_rates"]["max_rate"]) == commission_max_rate
assert float(new_validator["commission"]["commission_rates"]["max_change_rate"]) == commission_max_change_rate

def _create_validator_home(self, node_info: JsonDict):
# Create admin account. We want this account both in validator's home and in self.keyring_dir:
# - it has to be in validator's home because "set-genesis-oracle-admin" requires it and there is no separate
# "--keyring-dir" CLI option. Otherwise, we would prefer all accounts to be separated from validator home.
# - because it is also in self.keyring_dir all admin names have to be unique.
admin_name = node_info["admin_name"]
admin_mnemonic = node_info.get("admin_mnemonic", None)
sifnoded = sifchain.Sifnoded(self.cmd, home=self.keyring_dir)
admin_acct, admin_mnemonic = sifnoded._keys_add(admin_name, mnemonic=admin_mnemonic) # Creates if none given
admin_addr = admin_acct["address"]
node_info["admin_addr"] = admin_addr

sifnoded_i = self._sifnoded_for(node_info)
def _create_sifnoded_home(self, node_info: JsonDict):
is_validator = node_info["is_validator"]
moniker = node_info["moniker"]
sifnoded_i.init(moniker)
admin_account_copy = sifnoded_i.keys_add(admin_name, mnemonic=admin_mnemonic)
assert admin_account_copy["address"] == admin_addr
node_id = sifnoded_i.tendermint_show_node_id() # Taken from ${sifnoded_home}/config/node_key.json
pubkey = sifnoded_i.tendermint_show_validator() # Taken from ${sifnoded_home}/config/priv_validator_key.json
node_info["node_id"] = node_id
node_info["pubkey"] = pubkey
sifnoded_i = self._sifnoded_for(node_info)

if is_validator:
# Create admin account. We want this account both in validator's home and in self.keyring_dir:
# - it has to be in validator's home because "set-genesis-oracle-admin" requires it and there is no separate
# "--keyring-dir" CLI option. Otherwise, we would prefer all accounts to be separated from validator home.
# - because it is also in self.keyring_dir all admin names have to be unique.
admin_name = node_info["admin_name"]
admin_mnemonic = node_info.get("admin_mnemonic", None)
sifnoded_client = sifchain.Sifnoded(self.cmd, home=self.keyring_dir)
admin_acct, admin_mnemonic = sifnoded_client._keys_add(admin_name, mnemonic=admin_mnemonic) # Creates if none given
admin_addr = admin_acct["address"]
sifnoded_i.init(moniker)
admin_account_copy = sifnoded_i.keys_add(admin_name, mnemonic=admin_mnemonic)
assert admin_account_copy["address"] == admin_addr
node_id = sifnoded_i.tendermint_show_node_id() # Taken from ${sifnoded_home}/config/node_key.json
pubkey = sifnoded_i.tendermint_show_validator() # Taken from ${sifnoded_home}/config/priv_validator_key.json
node_info["admin_addr"] = admin_addr
node_info["node_id"] = node_id
node_info["pubkey"] = pubkey
else:
sifnoded_i.init(moniker)

def close(self):
for p in self.running_processes:
Expand Down
2 changes: 1 addition & 1 deletion test/integration/framework/src/siftool/sifchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ def _rpc_get(self, host, port, relative_url):
log.debug("Result for {}: {} bytes".format(url, len(http_result_payload)))
return json.loads(http_result_payload.decode("UTF-8"))

def wait_for_last_transaction_to_be_mined(self, count: int = 1, disable_log: bool = True, timeout: int = 90):
def wait_for_last_transaction_to_be_mined(self, count: int = 1, timeout: int = 90):
log.debug("Waiting for last sifnode transaction to be mined...")
start_time = time.time()
initial_block = self.get_current_block()
Expand Down
17 changes: 13 additions & 4 deletions test/integration/framework/test/test_environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def test_transfer(env):


def assert_validators_working(env):
expected_monikers = [node_info["moniker"] for node_info in env.node_info]
expected_monikers = [node_info["moniker"] for node_info in env.node_info if node_info["is_validator"]]
assert set(get_validators(env)) == set(expected_monikers)
number_of_nodes = len(env.node_info)
for i in range(number_of_nodes):
Expand Down Expand Up @@ -52,13 +52,13 @@ def teardown_method(self):
prj = project.Project(self.cmd, project_dir())
prj.pkill()

def test_environment_setup_basic_1(self):
def test_environment_setup_basic_1_validator(self):
env = environments.SifnodedEnvironment(self.cmd, sifnoded_home_root=self.sifnoded_home_root)
env.add_validator()
env.start()
assert_validators_working(env)

def test_environment_setup_basic_4(self):
def test_environment_setup_basic_4_validators(self):
env = environments.SifnodedEnvironment(self.cmd, chain_id="cownet-2", sifnoded_home_root=self.sifnoded_home_root)
assert len(env.node_info) == 0
env.add_validator(moniker="ant")
Expand All @@ -68,7 +68,16 @@ def test_environment_setup_basic_4(self):
assert len(env.node_info) == 4
env.start()
assert_validators_working(env)
# env._sifnoded_for(env.node_info[0]).wait_for_block(15)

def test_environment_setup_mix_of_nodes_and_validators(self):
env = environments.SifnodedEnvironment(self.cmd, chain_id="cownet-2", sifnoded_home_root=self.sifnoded_home_root)
assert len(env.node_info) == 0
env.add_validator(moniker="ant", is_validator=True)
env.add_validator(moniker="bee", is_validator=False)
env.add_validator(moniker="cat", is_validator=True)
assert len(env.node_info) == 3
env.start()
assert_validators_working(env)

def test_add_validator_before_and_after_start(self):
env = environments.SifnodedEnvironment(self.cmd, sifnoded_home_root=self.sifnoded_home_root)
Expand Down

0 comments on commit 0a342ed

Please sign in to comment.