Add keywords helpers

Signed-off-by: Vladimir Avdeev <v.avdeev@yadro.com>
This commit is contained in:
Vladimir Avdeev 2022-10-26 12:40:57 +03:00 committed by Vladimir Avdeev
parent f907de52cf
commit aebec54495
18 changed files with 637 additions and 125 deletions

View file

@ -83,10 +83,13 @@ Detailed information about registering entrypoints can be found at [setuptools d
## Library structure ## Library structure
The library provides the following primary components: The library provides the following primary components:
* `blockchain` - Contains helpers that allow to interact with neo blockchain, smart contracts, gas transfers, etc.
* `cli` - wrappers on top of neoFS command-line tools. These wrappers execute on a shell and provide type-safe interface for interacting with the tools. * `cli` - wrappers on top of neoFS command-line tools. These wrappers execute on a shell and provide type-safe interface for interacting with the tools.
* `hosting` - management of infrastructure (docker, virtual machines, services where neoFS is hosted). The library provides host implementation for docker environment (when neoFS services are running as docker containers). Support for other hosts is provided via plugins. * `hosting` - management of infrastructure (docker, virtual machines, services where neoFS is hosted). The library provides host implementation for docker environment (when neoFS services are running as docker containers). Support for other hosts is provided via plugins.
* `reporter` - abstraction on top of test reporting tool like Allure. Components of the library will report their steps and attach artifacts to the configured reporter instance. * `reporter` - abstraction on top of test reporting tool like Allure. Components of the library will report their steps and attach artifacts to the configured reporter instance.
* `shell` - shells that can be used to execute commands. Currently library provides local shell (on machine that runs the code) or SSH shell that connects to a remote machine via SSH. * `shell` - shells that can be used to execute commands. Currently library provides local shell (on machine that runs the code) or SSH shell that connects to a remote machine via SSH.
* `utils` - Support functions.
## Contributing ## Contributing
Any contributions to the library should conform to the [contribution guideline](https://github.com/nspcc-dev/neofs-testlib/blob/master/CONTRIBUTING.md). Any contributions to the library should conform to the [contribution guideline](https://github.com/nspcc-dev/neofs-testlib/blob/master/CONTRIBUTING.md).

View file

@ -19,6 +19,7 @@ dependencies = [
"allure-python-commons>=2.9.45", "allure-python-commons>=2.9.45",
"docker>=4.4.0", "docker>=4.4.0",
"importlib_metadata>=5.0; python_version < '3.10'", "importlib_metadata>=5.0; python_version < '3.10'",
"neo-mamba==0.10.0",
"paramiko>=2.10.3", "paramiko>=2.10.3",
"pexpect>=4.8.0", "pexpect>=4.8.0",
"requests>=2.28.0", "requests>=2.28.0",

View file

@ -1,6 +1,7 @@
allure-python-commons==2.9.45 allure-python-commons==2.9.45
docker==4.4.0 docker==4.4.0
importlib_metadata==5.0.0 importlib_metadata==5.0.0
neo-mamba==0.10.0
paramiko==2.10.3 paramiko==2.10.3
pexpect==4.8.0 pexpect==4.8.0
requests==2.28.1 requests==2.28.1

View file

@ -0,0 +1,2 @@
from neofs_testlib.blockchain.multisig import Multisig
from neofs_testlib.blockchain.rpc_client import RPCClient

View file

@ -0,0 +1,53 @@
from typing import List
from neofs_testlib.cli import NeoGo
class Multisig:
def __init__(self, neogo: NeoGo, invoke_tx_file: str, block_period: int):
self.neogo = neogo
self.invoke_tx_file = invoke_tx_file
self.block_period = block_period
def create_and_send(
self,
contract_hash: str,
contract_args: str,
multisig_hash: str,
wallets: List[str],
passwords: List[str],
address: str,
endpoint: str,
) -> None:
if not len(wallets):
raise AttributeError("Got empty wallets list")
self.neogo.contract.invokefunction(
address=address,
rpc_endpoint=endpoint,
wallet=wallets[0],
wallet_password=passwords[0],
out=None if len(wallets) == 1 else self.invoke_tx_file,
scripthash=contract_hash,
arguments=contract_args,
multisig_hash=multisig_hash,
)
if len(wallets) > 1:
# sign with rest of wallets except the last one
for wallet in wallets[1:-1]:
self.neogo.wallet.sign(
wallet=wallet,
input_file=self.invoke_tx_file,
out=self.invoke_tx_file,
address=address,
)
# sign tx with last wallet and push it to blockchain
self.neogo.wallet.sign(
wallet=wallets[-1],
input_file=self.invoke_tx_file,
out=self.invoke_tx_file,
address=address,
rpc_endpoint=endpoint,
)

View file

@ -0,0 +1,156 @@
import json
from time import sleep
from typing import List, Optional
from cli import NeoGo
from shell import Shell
from utils.converters import process_b64_bytearray
from neofs_testlib.blockchain import Multisig
class RoleDesignation:
def __init__(
self,
shell: Shell,
neo_go_exec_path: str,
block_period: int,
designate_contract: str,
):
self.neogo = NeoGo(shell, neo_go_exec_path)
self.block_period = block_period
self.designate_contract = designate_contract
def set_notary_nodes(
self,
addr: str,
pubkeys: List[str],
script_hash: str,
wallet: str,
passwd: str,
endpoint: str,
) -> str:
keys = [f"bytes:{k}" for k in pubkeys]
keys_str = " ".join(keys)
out = self.neogo.contract.invokefunction(
address=addr,
scripthash=self.designate_contract,
wallet=wallet,
wallet_password=passwd,
rpc_endpoint=endpoint,
arguments=f"designateAsRole int:32 [ {keys_str} ] -- {script_hash}",
force=True,
)
sleep(self.block_period)
return out.stdout.split(" ")[-1]
def set_inner_ring(
self,
addr: str,
pubkeys: List[str],
script_hash: str,
wallet: str,
passwd: str,
endpoint: str,
) -> str:
keys = [f"bytes:{k}" for k in pubkeys]
keys_str = " ".join(keys)
out = self.neogo.contract.invokefunction(
address=addr,
scripthash=self.designate_contract,
wallet=wallet,
wallet_password=passwd,
rpc_endpoint=endpoint,
arguments=f"designateAsRole int:16 [ {keys_str} ] -- {script_hash}",
force=True,
)
sleep(self.block_period)
return out.stdout.split(" ")[-1]
def set_oracles(
self,
addr: str,
pubkeys: List[str],
script_hash: str,
wallet: str,
passwd: str,
endpoint: str,
) -> str:
keys = [f"bytes:{k}" for k in pubkeys]
keys_str = " ".join(keys)
out = self.neogo.contract.invokefunction(
address=addr,
scripthash=self.designate_contract,
wallet=wallet,
wallet_password=passwd,
rpc_endpoint=endpoint,
arguments=f"designateAsRole int:8 [ {keys_str} ] -- {script_hash}",
force=True,
)
sleep(self.block_period)
return out.stdout.split(" ")[-1]
def set_notary_nodes_multisig_tx(
self,
pubkeys: List[str],
script_hash: str,
wallets: List[str],
passwords: List[str],
address: str,
endpoint: str,
invoke_tx_file: str,
) -> None:
keys = [f"bytes:{k}" for k in pubkeys]
keys_str = " ".join(keys)
multisig = Multisig(
self.neogo, invoke_tx_file=invoke_tx_file, block_period=self.block_period
)
multisig.create_and_send(
self.designate_contract,
f"designateAsRole int:32 [ {keys_str} ]",
script_hash,
wallets,
passwords,
address,
endpoint,
)
sleep(self.block_period)
def set_inner_ring_multisig_tx(
self,
pubkeys: List[str],
script_hash: str,
wallets: List[str],
passwords: List[str],
address: str,
endpoint: str,
invoke_tx_file: str,
) -> None:
keys = [f"bytes:{k}" for k in pubkeys]
keys_str = " ".join(keys)
multisig = Multisig(
self.neogo, invoke_tx_file=invoke_tx_file, block_period=self.block_period
)
multisig.create_and_send(
self.designate_contract,
f"designateAsRole int:16 [ {keys_str} ]",
script_hash,
wallets,
passwords,
address,
endpoint,
)
sleep(self.block_period)
def check_candidates(self, contract_hash: str, endpoint: str) -> Optional[List[str]]:
out = self.neogo.contract.testinvokefunction(
scripthash=contract_hash,
method="innerRingCandidates",
rpc_endpoint=endpoint,
)
output_dict = json.loads(out.stdout.replace("\n", ""))
candidates = output_dict["stack"][0]["value"]
if len(candidates) == 0:
return None
# TODO: return a list of keys
return [process_b64_bytearray(candidate["value"][0]["value"]) for candidate in candidates]

View file

@ -0,0 +1,80 @@
import json
import logging
from typing import Any, Dict, List, Optional
import requests
logger = logging.getLogger("neofs.testlib.blockchain")
class NeoRPCException(Exception):
pass
class RPCClient:
def __init__(self, endpoint, timeout: int = 10):
self.endpoint = endpoint
self.timeout = timeout
def get_raw_transaction(self, tx_hash):
return self._call_endpoint("getrawtransaction", params=[tx_hash])
def send_raw_transaction(self, raw_tx: str):
return self._call_endpoint("sendrawtransaction", params=[raw_tx])
def get_storage(self, sc_hash: str, storage_key: str):
return self._call_endpoint("getstorage", params=[sc_hash, storage_key])
def invoke_function(
self,
sc_hash: str,
function: str,
params: Optional[List] = None,
signers: Optional[List] = None,
) -> Dict[str, Any]:
return self._call_endpoint(
"invokefunction", params=[sc_hash, function, params or [], signers or []]
)
def get_transaction_height(self, txid: str):
return self._call_endpoint("gettransactionheight", params=[txid])
def get_nep17_transfers(self, address, timestamps=None):
params = [address]
if timestamps:
params.append(timestamps)
return self._call_endpoint("getnep17transfers", params)
def get_nep17_balances(self, address):
return self._call_endpoint("getnep17balances", [address, 0])
def get_application_log(self, tx_hash):
return self._call_endpoint("getapplicationlog", params=[tx_hash])
def get_contract_state(self, contract_id):
"""
`contract_id` might be contract name, script hash or number
"""
return self._call_endpoint("getcontractstate", params=[contract_id])
def _call_endpoint(self, method, params=None) -> Dict[str, Any]:
payload = _build_payload(method, params)
logger.info(payload)
try:
response = requests.post(self.endpoint, data=payload, timeout=self.timeout)
response.raise_for_status()
if response.status_code == 200:
if "result" in response.json():
return response.json()["result"]
return response.json()
except Exception as exc:
raise NeoRPCException(
f"Could not call method {method} "
f"with endpoint: {self.endpoint}: {exc}"
f"\nRequest sent: {payload}"
) from exc
def _build_payload(method, params: Optional[List] = None):
payload = json.dumps({"jsonrpc": "2.0", "method": method, "params": params or [], "id": 1})
return payload.replace("'", '"')

View file

@ -1,11 +1,12 @@
from typing import Optional from typing import Optional
from neofs_testlib.shell import CommandResult, Shell from neofs_testlib.shell import CommandOptions, CommandResult, InteractiveInput, Shell
class CliCommand: class CliCommand:
WALLET_SOURCE_ERROR_MSG = "Provide either wallet or wallet_config to specify wallet location" WALLET_SOURCE_ERROR_MSG = "Provide either wallet or wallet_config to specify wallet location"
WALLET_PASSWD_ERROR_MSG = "Provide either wallet_password or wallet_config to specify password"
cli_exec_path: Optional[str] = None cli_exec_path: Optional[str] = None
__base_params: Optional[str] = None __base_params: Optional[str] = None
@ -14,6 +15,8 @@ class CliCommand:
"await_mode": "await", "await_mode": "await",
"hash_type": "hash", "hash_type": "hash",
"doc_type": "type", "doc_type": "type",
"to_address": "to",
"from_address": "from",
} }
def __init__(self, shell: Shell, cli_exec_path: str, **base_params): def __init__(self, shell: Shell, cli_exec_path: str, **base_params):
@ -26,6 +29,9 @@ class CliCommand:
def _format_command(self, command: str, **params) -> str: def _format_command(self, command: str, **params) -> str:
param_str = [] param_str = []
for param, value in params.items(): for param, value in params.items():
if param == "post_data":
param_str.append(value)
continue
if param in self.map_params.keys(): if param in self.map_params.keys():
param = self.map_params[param] param = self.map_params[param]
param = param.replace("_", "-") param = param.replace("_", "-")
@ -56,3 +62,11 @@ class CliCommand:
def _execute(self, command: Optional[str], **params) -> CommandResult: def _execute(self, command: Optional[str], **params) -> CommandResult:
return self.shell.exec(self._format_command(command, **params)) return self.shell.exec(self._format_command(command, **params))
def _execute_with_password(self, command: Optional[str], password, **params) -> CommandResult:
return self.shell.exec(
self._format_command(command, **params),
options=CommandOptions(
interactive_inputs=[InteractiveInput(prompt_pattern="assword", input=password)]
),
)

View file

@ -11,6 +11,7 @@ class NeoGoCandidate(CliCommand):
rpc_endpoint: str, rpc_endpoint: str,
wallet: Optional[str] = None, wallet: Optional[str] = None,
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
wallet_password: Optional[str] = None,
gas: Optional[float] = None, gas: Optional[float] = None,
timeout: int = 10, timeout: int = 10,
) -> CommandResult: ) -> CommandResult:
@ -21,6 +22,7 @@ class NeoGoCandidate(CliCommand):
wallet: Target location of the wallet file ('-' to read from stdin); wallet: Target location of the wallet file ('-' to read from stdin);
conflicts with --wallet-config flag. conflicts with --wallet-config flag.
wallet_config: Target location of the wallet config file; conflicts with --wallet flag. wallet_config: Target location of the wallet config file; conflicts with --wallet flag.
wallet_password: Wallet password.
gas: Network fee to add to the transaction (prioritizing it). gas: Network fee to add to the transaction (prioritizing it).
rpc_endpoint: RPC node address. rpc_endpoint: RPC node address.
timeout: Timeout for the operation (default: 10s). timeout: Timeout for the operation (default: 10s).
@ -29,15 +31,20 @@ class NeoGoCandidate(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
return self._execute(
"wallet candidate register",
**{
param: param_value param: param_value
for param, param_value in locals().items() for param, param_value in locals().items()
if param not in ["self"] if param not in ["self", "wallet_password"]
}, }
exec_param["timeout"] = f"{timeout}s"
if wallet_password is not None:
return self._execute_with_password(
"wallet candidate register", wallet_password, **exec_param
) )
if wallet_config:
return self._execute("wallet candidate register", **exec_param)
raise Exception(self.WALLET_PASSWD_ERROR_MSG)
def unregister( def unregister(
self, self,
@ -45,6 +52,7 @@ class NeoGoCandidate(CliCommand):
rpc_endpoint: str, rpc_endpoint: str,
wallet: Optional[str] = None, wallet: Optional[str] = None,
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
wallet_password: Optional[str] = None,
gas: Optional[float] = None, gas: Optional[float] = None,
timeout: int = 10, timeout: int = 10,
) -> CommandResult: ) -> CommandResult:
@ -55,6 +63,7 @@ class NeoGoCandidate(CliCommand):
wallet: Target location of the wallet file ('-' to read from stdin); wallet: Target location of the wallet file ('-' to read from stdin);
conflicts with --wallet-config flag. conflicts with --wallet-config flag.
wallet_config: Target location of the wallet config file; conflicts with --wallet flag. wallet_config: Target location of the wallet config file; conflicts with --wallet flag.
wallet_password: Wallet password.
gas: Network fee to add to the transaction (prioritizing it). gas: Network fee to add to the transaction (prioritizing it).
rpc_endpoint: RPC node address. rpc_endpoint: RPC node address.
timeout: Timeout for the operation (default: 10s). timeout: Timeout for the operation (default: 10s).
@ -63,22 +72,29 @@ class NeoGoCandidate(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
return self._execute(
"wallet candidate unregister",
**{
param: param_value param: param_value
for param, param_value in locals().items() for param, param_value in locals().items()
if param not in ["self"] if param not in ["self", "wallet_password"]
}, }
exec_param["timeout"] = f"{timeout}s"
if wallet_password is not None:
return self._execute_with_password(
"wallet candidate unregister", wallet_password, **exec_param
) )
if wallet_config:
return self._execute("wallet candidate unregister", **exec_param)
raise Exception(self.WALLET_PASSWD_ERROR_MSG)
def vote( def vote(
self, self,
address: str,
candidate: str, candidate: str,
rpc_endpoint: str, rpc_endpoint: str,
wallet: Optional[str] = None, wallet: Optional[str] = None,
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
wallet_password: Optional[str] = None,
gas: Optional[float] = None, gas: Optional[float] = None,
timeout: int = 10, timeout: int = 10,
) -> CommandResult: ) -> CommandResult:
@ -88,10 +104,12 @@ class NeoGoCandidate(CliCommand):
candidate argument to perform unvoting. candidate argument to perform unvoting.
Args: Args:
address: Address to vote from
candidate: Public key of candidate to vote for. candidate: Public key of candidate to vote for.
wallet: Target location of the wallet file ('-' to read from stdin); wallet: Target location of the wallet file ('-' to read from stdin);
conflicts with --wallet-config flag. conflicts with --wallet-config flag.
wallet_config: Target location of the wallet config file; conflicts with --wallet flag. wallet_config: Target location of the wallet config file; conflicts with --wallet flag.
wallet_password: Wallet password.
gas: Network fee to add to the transaction (prioritizing it). gas: Network fee to add to the transaction (prioritizing it).
rpc_endpoint: RPC node address. rpc_endpoint: RPC node address.
timeout: Timeout for the operation (default: 10s). timeout: Timeout for the operation (default: 10s).
@ -100,12 +118,17 @@ class NeoGoCandidate(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
return self._execute(
"wallet candidate vote",
**{
param: param_value param: param_value
for param, param_value in locals().items() for param, param_value in locals().items()
if param not in ["self"] if param not in ["self", "wallet_password"]
}, }
exec_param["timeout"] = f"{timeout}s"
if wallet_password is not None:
return self._execute_with_password(
"wallet candidate vote", wallet_password, **exec_param
) )
if wallet_config:
return self._execute("wallet candidate vote", **exec_param)
raise Exception(self.WALLET_PASSWD_ERROR_MSG)

View file

@ -45,11 +45,12 @@ class NeoGoContract(CliCommand):
self, self,
address: str, address: str,
input_file: str, input_file: str,
sysgas: float,
manifest: str, manifest: str,
rpc_endpoint: str, rpc_endpoint: str,
sysgas: Optional[float] = None,
wallet: Optional[str] = None, wallet: Optional[str] = None,
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
wallet_password: Optional[str] = None,
gas: Optional[float] = None, gas: Optional[float] = None,
out: Optional[str] = None, out: Optional[str] = None,
force: bool = False, force: bool = False,
@ -62,6 +63,7 @@ class NeoGoContract(CliCommand):
conflicts with wallet_config. conflicts with wallet_config.
wallet_config: Path to wallet config to use to get the key for transaction signing; wallet_config: Path to wallet config to use to get the key for transaction signing;
conflicts with wallet. conflicts with wallet.
wallet_password: Wallet password.
address: Address to use as transaction signee (and gas source). address: Address to use as transaction signee (and gas source).
gas: Network fee to add to the transaction (prioritizing it). gas: Network fee to add to the transaction (prioritizing it).
sysgas: System fee to add to transaction (compensating for execution). sysgas: System fee to add to transaction (compensating for execution).
@ -77,15 +79,26 @@ class NeoGoContract(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
return self._execute(
"contract deploy",
**{
param: param_value param: param_value
for param, param_value in locals().items() for param, param_value in locals().items()
if param not in ["self"] if param not in ["self", "wallet_password"]
}, }
exec_param["timeout"] = f"{timeout}s"
if wallet_password is not None:
return self._execute_with_password(
"contract deploy",
wallet_password,
**exec_param,
) )
if wallet_config:
return self._execute(
"contract deploy",
**exec_param,
)
raise Exception(self.WALLET_PASSWD_ERROR_MSG)
def generate_wrapper( def generate_wrapper(
self, self,
@ -116,13 +129,14 @@ class NeoGoContract(CliCommand):
def invokefunction( def invokefunction(
self, self,
address: str,
scripthash: str, scripthash: str,
address: Optional[str] = None,
wallet: Optional[str] = None, wallet: Optional[str] = None,
method: Optional[str] = None, method: Optional[str] = None,
arguments: Optional[str] = None, arguments: Optional[str] = None,
multisig_hash: Optional[str] = None, multisig_hash: Optional[str] = None,
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
wallet_password: Optional[str] = None,
gas: Optional[float] = None, gas: Optional[float] = None,
sysgas: Optional[float] = None, sysgas: Optional[float] = None,
out: Optional[str] = None, out: Optional[str] = None,
@ -147,6 +161,7 @@ class NeoGoContract(CliCommand):
conflicts with wallet_config. conflicts with wallet_config.
wallet_config: Path to wallet config to use to get the key for transaction signing; wallet_config: Path to wallet config to use to get the key for transaction signing;
conflicts with wallet. conflicts with wallet.
wallet_password: Wallet password.
address: Address to use as transaction signee (and gas source). address: Address to use as transaction signee (and gas source).
gas: Network fee to add to the transaction (prioritizing it). gas: Network fee to add to the transaction (prioritizing it).
sysgas: System fee to add to transaction (compensating for execution). sysgas: System fee to add to transaction (compensating for execution).
@ -158,21 +173,40 @@ class NeoGoContract(CliCommand):
Returns: Returns:
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
multisig_hash = f"-- {multisig_hash}" or "" multisig_hash = f"-- {multisig_hash}" or ""
return self._execute( post_data = f"{scripthash} {method or ''} {arguments or ''} {multisig_hash}"
"contract invokefunction " exec_param = {
f"{scripthash} {method or ''} {arguments or ''} {multisig_hash}",
**{
param: param_value param: param_value
for param, param_value in locals().items() for param, param_value in locals().items()
if param not in ["self", "scripthash", "method", "arguments", "multisig_hash"] if param
}, not in [
"self",
"scripthash",
"method",
"arguments",
"multisig_hash",
"wallet_password",
]
}
exec_param["timeout"] = f"{timeout}s"
exec_param["post_data"] = post_data
if wallet_password is not None:
return self._execute_with_password(
"contract invokefunction", wallet_password, **exec_param
) )
if wallet_config:
return self._execute("contract invokefunction", **exec_param)
raise Exception(self.WALLET_PASSWD_ERROR_MSG)
def testinvokefunction( def testinvokefunction(
self, self,
scripthash: str, scripthash: str,
wallet: Optional[str] = None, wallet: Optional[str] = None,
wallet_password: Optional[str] = None,
method: Optional[str] = None, method: Optional[str] = None,
arguments: Optional[str] = None, arguments: Optional[str] = None,
multisig_hash: Optional[str] = None, multisig_hash: Optional[str] = None,
@ -192,6 +226,8 @@ class NeoGoContract(CliCommand):
Args: Args:
scripthash: Function hash. scripthash: Function hash.
wallet: Wallet to use for testinvoke.
wallet_password: Wallet password.
method: Call method. method: Call method.
arguments: Method arguments. arguments: Method arguments.
multisig_hash: Multisig hash. multisig_hash: Multisig hash.
@ -201,17 +237,30 @@ class NeoGoContract(CliCommand):
Returns: Returns:
Command's result. Command's result.
""" """
multisig_hash = f"-- {multisig_hash}" or "" multisig_hash = f"-- {multisig_hash}" if multisig_hash else ""
return self._execute( post_data = f"{scripthash} {method or ''} {arguments or ''} {multisig_hash}"
"contract testinvokefunction " exec_param = {
f"{scripthash} {method or ''} {arguments or ''} {multisig_hash}",
**{
param: param_value param: param_value
for param, param_value in locals().items() for param, param_value in locals().items()
if param not in ["self", "scripthash", "method", "arguments", "multisig_hash"] if param
}, not in [
"self",
"scripthash",
"method",
"arguments",
"multisig_hash",
"wallet_password",
]
}
exec_param["timeout"] = f"{timeout}s"
exec_param["post_data"] = post_data
if wallet_password is not None:
return self._execute_with_password(
"contract testinvokefunction", wallet_password, **exec_param
) )
return self._execute("contract testinvokefunction", **exec_param)
def testinvokescript( def testinvokescript(
self, self,
input_file: str, input_file: str,
@ -231,13 +280,13 @@ class NeoGoContract(CliCommand):
Returns: Returns:
Command's result. Command's result.
""" """
exec_param = {
param: param_value for param, param_value in locals().items() if param not in ["self"]
}
exec_param["timeout"] = f"{timeout}s"
return self._execute( return self._execute(
f"contract testinvokescript", "contract testinvokescript",
**{ **exec_param,
param: param_value
for param, param_value in locals().items()
if param not in ["self"]
},
) )
def init(self, name: str, skip_details: bool = False) -> CommandResult: def init(self, name: str, skip_details: bool = False) -> CommandResult:
@ -313,14 +362,18 @@ class NeoGoContract(CliCommand):
address: str, address: str,
wallet: Optional[str] = None, wallet: Optional[str] = None,
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
wallet_password: Optional[str] = None,
sender: Optional[str] = None, sender: Optional[str] = None,
nef: Optional[str] = None, nef: Optional[str] = None,
) -> CommandResult: ) -> CommandResult:
"""Adds group to the manifest. """Adds group to the manifest.
Args: Args:
wallet: Wallet to use to get the key for transaction signing; conflicts with wallet_config. wallet: Wallet to use to get the key for transaction signing;
wallet_config: Path to wallet config to use to get the key for transaction signing; conflicts with wallet. conflicts with wallet_config.
wallet_config: Path to wallet config to use to get the key for transaction signing;
conflicts with wallet.
wallet_password: Wallet password.
sender: Deploy transaction sender. sender: Deploy transaction sender.
address: Account to sign group with. address: Account to sign group with.
nef: Path to the NEF file. nef: Path to the NEF file.
@ -329,11 +382,17 @@ class NeoGoContract(CliCommand):
Returns: Returns:
Command's result. Command's result.
""" """
return self._execute( assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
"contract manifest add-group", exec_param = {
**{
param: param_value param: param_value
for param, param_value in locals().items() for param, param_value in locals().items()
if param not in ["self"] if param not in ["self", "wallet_password"]
}, }
if wallet_password is not None:
return self._execute_with_password(
"contract manifest add-group", wallet_password, **exec_param
) )
if wallet_config:
return self._execute("contract manifest add-group", **exec_param)
raise Exception(self.WALLET_PASSWD_ERROR_MSG)

View file

@ -24,7 +24,7 @@ class NeoGo:
def __init__( def __init__(
self, self,
shell: Shell, shell: Shell,
neo_go_exec_path: Optional[str] = None, neo_go_exec_path: str,
config_path: Optional[str] = None, config_path: Optional[str] = None,
): ):
self.candidate = NeoGoCandidate(shell, neo_go_exec_path, config_path=config_path) self.candidate = NeoGoCandidate(shell, neo_go_exec_path, config_path=config_path)

View file

@ -29,14 +29,13 @@ class NeoGoNep17(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
param: param_value for param, param_value in locals().items() if param not in ["self"]
}
exec_param["timeout"] = f"{timeout}s"
return self._execute( return self._execute(
"wallet nep17 balance", "wallet nep17 balance",
**{ **exec_param,
param: param_value
for param, param_value in locals().items()
if param not in ["self"]
},
) )
def import_token( def import_token(
@ -63,14 +62,13 @@ class NeoGoNep17(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
param: param_value for param, param_value in locals().items() if param not in ["self"]
}
exec_param["timeout"] = f"{timeout}s"
return self._execute( return self._execute(
"wallet nep17 import", "wallet nep17 import",
**{ **exec_param,
param: param_value
for param, param_value in locals().items()
if param not in ["self"]
},
) )
def info( def info(
@ -133,10 +131,11 @@ class NeoGoNep17(CliCommand):
self, self,
token: str, token: str,
to_address: str, to_address: str,
sysgas: float,
rpc_endpoint: str, rpc_endpoint: str,
sysgas: Optional[float] = None,
wallet: Optional[str] = None, wallet: Optional[str] = None,
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
wallet_password: Optional[str] = None,
out: Optional[str] = None, out: Optional[str] = None,
from_address: Optional[str] = None, from_address: Optional[str] = None,
force: bool = False, force: bool = False,
@ -156,6 +155,7 @@ class NeoGoNep17(CliCommand):
wallet: Target location of the wallet file ('-' to read from stdin); wallet: Target location of the wallet file ('-' to read from stdin);
conflicts with --wallet-config flag. conflicts with --wallet-config flag.
wallet_config: Target location of the wallet config file; conflicts with --wallet flag. wallet_config: Target location of the wallet config file; conflicts with --wallet flag.
wallet_password: Wallet password.
out: File to put JSON transaction to. out: File to put JSON transaction to.
from_address: Address to send an asset from. from_address: Address to send an asset from.
to_address: Address to send an asset to. to_address: Address to send an asset to.
@ -172,15 +172,26 @@ class NeoGoNep17(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
return self._execute(
"wallet nep17 transfer",
**{
param: param_value param: param_value
for param, param_value in locals().items() for param, param_value in locals().items()
if param not in ["self"] if param not in ["self", "wallet_password"]
}, }
exec_param["timeout"] = f"{timeout}s"
if wallet_password is not None:
return self._execute_with_password(
"wallet nep17 transfer",
wallet_password,
**exec_param,
) )
if wallet_config:
return self._execute(
"wallet nep17 transfer",
**exec_param,
)
raise Exception(self.WALLET_PASSWD_ERROR_MSG)
def multitransfer( def multitransfer(
self, self,
@ -219,12 +230,11 @@ class NeoGoNep17(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
param: param_value for param, param_value in locals().items() if param not in ["self"]
}
exec_param["timeout"] = f"{timeout}s"
return self._execute( return self._execute(
"wallet nep17 multitransfer", "wallet nep17 multitransfer",
**{ **exec_param,
param: param_value
for param, param_value in locals().items()
if param not in ["self"]
},
) )

View file

@ -3,7 +3,7 @@ from neofs_testlib.shell import CommandResult
class NeoGoQuery(CliCommand): class NeoGoQuery(CliCommand):
def candidates(self, rpc_endpoint: str, timeout: int = 10) -> CommandResult: def candidates(self, rpc_endpoint: str, timeout: str = "10s") -> CommandResult:
"""Get candidates and votes. """Get candidates and votes.
Args: Args:
@ -22,7 +22,7 @@ class NeoGoQuery(CliCommand):
}, },
) )
def committee(self, rpc_endpoint: str, timeout: int = 10) -> CommandResult: def committee(self, rpc_endpoint: str, timeout: str = "10s") -> CommandResult:
"""Get committee list. """Get committee list.
Args: Args:
@ -41,7 +41,7 @@ class NeoGoQuery(CliCommand):
}, },
) )
def height(self, rpc_endpoint: str, timeout: int = 10) -> CommandResult: def height(self, rpc_endpoint: str, timeout: str = "10s") -> CommandResult:
"""Get node height. """Get node height.
Args: Args:
@ -60,7 +60,7 @@ class NeoGoQuery(CliCommand):
}, },
) )
def tx(self, tx_hash: str, rpc_endpoint: str, timeout: int = 10) -> CommandResult: def tx(self, tx_hash: str, rpc_endpoint: str, timeout: str = "10s") -> CommandResult:
"""Query transaction status. """Query transaction status.
Args: Args:
@ -80,7 +80,7 @@ class NeoGoQuery(CliCommand):
}, },
) )
def voter(self, rpc_endpoint: str, timeout: int = 10) -> CommandResult: def voter(self, rpc_endpoint: str, timeout: str = "10s") -> CommandResult:
"""Print NEO holder account state. """Print NEO holder account state.
Args: Args:

View file

@ -27,14 +27,13 @@ class NeoGoWallet(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
param: param_value for param, param_value in locals().items() if param not in ["self"]
}
exec_param["timeout"] = f"{timeout}s"
return self._execute( return self._execute(
"wallet claim", "wallet claim",
**{ **exec_param,
param: param_value
for param, param_value in locals().items()
if param not in ["self"]
},
) )
def init( def init(
@ -293,14 +292,13 @@ class NeoGoWallet(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
param: param_value for param, param_value in locals().items() if param not in ["self"]
}
exec_param["timeout"] = f"{timeout}s"
return self._execute( return self._execute(
"wallet import-deployed", "wallet import-deployed",
**{ **exec_param,
param: param_value
for param, param_value in locals().items()
if param not in ["self"]
},
) )
def remove( def remove(
@ -337,9 +335,10 @@ class NeoGoWallet(CliCommand):
self, self,
input_file: str, input_file: str,
address: str, address: str,
rpc_endpoint: str, rpc_endpoint: Optional[str] = None,
wallet: Optional[str] = None, wallet: Optional[str] = None,
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
wallet_password: Optional[str] = None,
out: Optional[str] = None, out: Optional[str] = None,
timeout: int = 10, timeout: int = 10,
) -> CommandResult: ) -> CommandResult:
@ -356,6 +355,7 @@ class NeoGoWallet(CliCommand):
wallet: Target location of the wallet file ('-' to read from stdin); wallet: Target location of the wallet file ('-' to read from stdin);
conflicts with --wallet-config flag. conflicts with --wallet-config flag.
wallet_config: Target location of the wallet config file; conflicts with --wallet flag. wallet_config: Target location of the wallet config file; conflicts with --wallet flag.
wallet_password: Wallet password.
out: File to put JSON transaction to. out: File to put JSON transaction to.
input_file: File with JSON transaction. input_file: File with JSON transaction.
address: Address to use. address: Address to use.
@ -366,12 +366,16 @@ class NeoGoWallet(CliCommand):
Command's result. Command's result.
""" """
assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG
exec_param = {
return self._execute(
"wallet sign",
**{
param: param_value param: param_value
for param, param_value in locals().items() for param, param_value in locals().items()
if param not in ["self"] if param not in ["self", "wallet_password"]
}, }
) exec_param["timeout"] = f"{timeout}s"
if wallet_password is not None:
return self._execute_with_password("wallet sign", wallet_password, **exec_param)
if wallet_config:
return self._execute("wallet sign", **exec_param)
raise Exception(self.WALLET_PASSWD_ERROR_MSG)

View file

@ -1,3 +1,3 @@
from neofs_testlib.shell.interfaces import CommandOptions, CommandResult, Shell from neofs_testlib.shell.interfaces import CommandOptions, CommandResult, InteractiveInput, Shell
from neofs_testlib.shell.local_shell import LocalShell from neofs_testlib.shell.local_shell import LocalShell
from neofs_testlib.shell.ssh_shell import SSHShell from neofs_testlib.shell.ssh_shell import SSHShell

View file

View file

@ -0,0 +1,69 @@
import base64
import binascii
import json
import base58
from neo3 import wallet as neo3_wallet
def str_to_ascii_hex(input: str) -> str:
b = binascii.hexlify(input.encode())
return str(b)[2:-1]
def ascii_hex_to_str(input: str) -> bytes:
return bytes.fromhex(input)
# Two functions below do parsing of Base64-encoded byte arrays which
# tests receive from Neo node RPC calls.
def process_b64_bytearray_reverse(data: str) -> bytes:
"""
This function decodes input data from base64, reverses the byte
array and returns its string representation.
"""
arr = bytearray(base64.standard_b64decode(data))
arr.reverse()
return binascii.b2a_hex(arr)
def process_b64_bytearray(data: str) -> bytes:
"""
This function decodes input data from base64 and returns the
bytearray string representation.
"""
arr = bytearray(base64.standard_b64decode(data))
return binascii.b2a_hex(arr)
def contract_hash_to_address(chash: str) -> str:
"""
This function accepts contract hash in BE, then translates in to LE,
prepends NEO wallet prefix and encodes to base58. It is equal to
`UInt160ToString` method in NEO implementations.
"""
be = bytearray(bytes.fromhex(chash))
be.reverse()
return base58.b58encode_check(b"\x35" + bytes(be)).decode()
def get_contract_hash_from_manifest(manifest_path: str) -> str:
with open(manifest_path) as m:
data = json.load(m)
# cut off '0x' and return the hash
return data["abi"]["hash"][2:]
def get_wif_from_private_key(priv_key: bytes) -> str:
wif_version = b"\x80"
compressed_flag = b"\x01"
wif = base58.b58encode_check(wif_version + priv_key + compressed_flag)
return wif.decode("utf-8")
def load_wallet(path: str, passwd: str = "") -> neo3_wallet.Wallet:
with open(path, "r") as wallet_file:
wlt_data = wallet_file.read()
return neo3_wallet.Wallet.from_json(json.loads(wlt_data), password=passwd)

View file

@ -0,0 +1,37 @@
import json
import logging
from neo3 import wallet as neo3_wallet
logger = logging.getLogger("neofs.testlib.utils")
def init_wallet(wallet_path: str, wallet_password: str):
"""
Create new wallet and new account.
Args:
wallet_path: The path to the wallet to save wallet.
wallet_password: The password for new wallet.
"""
wallet = neo3_wallet.Wallet()
account = neo3_wallet.Account.create_new(wallet_password)
wallet.account_add(account)
with open(wallet_path, "w") as out:
json.dump(wallet.to_json(), out)
logger.info(f"Init new wallet: {wallet_path}, address: {account.address}")
def get_last_address_from_wallet(wallet_path: str, wallet_password: str):
"""
Extracting the last address from the given wallet.
Args:
wallet_path: The path to the wallet to extract address from.
wallet_password: The password for the given wallet.
Returns:
The address for the wallet.
"""
with open(wallet_path) as wallet_file:
wallet = neo3_wallet.Wallet.from_json(json.load(wallet_file), password=wallet_password)
address = wallet.accounts[-1].address
logger.info(f"got address: {address}")
return address