From c48f7b7ff2dd440551576d4bc24b09a844fee11e Mon Sep 17 00:00:00 2001 From: Vladimir Avdeev Date: Mon, 19 Sep 2022 20:00:46 +0300 Subject: [PATCH] Implemented neofs-go, neo-go, neofs-authmate lib --- cli/__init__.py | 3 + cli/adm/__init__.py | 1 + cli/adm/adm.py | 24 ++ cli/adm/config.py | 24 ++ cli/adm/morph.py | 372 ++++++++++++++++++++++++++++ cli/adm/storage_config.py | 25 ++ cli/adm/subnet.py | 255 +++++++++++++++++++ cli/adm/version.py | 13 + cli/authmate/__init__.py | 1 + cli/authmate/authmate.py | 20 ++ cli/authmate/secret.py | 92 +++++++ cli/authmate/version.py | 13 + cli/cli_command.py | 58 +++++ cli/go/__init__.py | 2 + cli/go/blockchain_network_type.py | 7 + cli/go/candidate.py | 120 +++++++++ cli/go/contract.py | 358 +++++++++++++++++++++++++++ cli/go/db.py | 74 ++++++ cli/go/go.py | 44 ++++ cli/go/nep17.py | 243 ++++++++++++++++++ cli/go/node.py | 18 ++ cli/go/query.py | 128 ++++++++++ cli/go/version.py | 13 + cli/go/wallet.py | 395 ++++++++++++++++++++++++++++++ shell/__init__.py | 3 + 25 files changed, 2306 insertions(+) create mode 100644 cli/__init__.py create mode 100644 cli/adm/__init__.py create mode 100644 cli/adm/adm.py create mode 100644 cli/adm/config.py create mode 100644 cli/adm/morph.py create mode 100644 cli/adm/storage_config.py create mode 100644 cli/adm/subnet.py create mode 100644 cli/adm/version.py create mode 100644 cli/authmate/__init__.py create mode 100644 cli/authmate/authmate.py create mode 100644 cli/authmate/secret.py create mode 100644 cli/authmate/version.py create mode 100644 cli/cli_command.py create mode 100644 cli/go/__init__.py create mode 100644 cli/go/blockchain_network_type.py create mode 100644 cli/go/candidate.py create mode 100644 cli/go/contract.py create mode 100644 cli/go/db.py create mode 100644 cli/go/go.py create mode 100644 cli/go/nep17.py create mode 100644 cli/go/node.py create mode 100644 cli/go/query.py create mode 100644 cli/go/version.py create mode 100644 cli/go/wallet.py diff --git a/cli/__init__.py b/cli/__init__.py new file mode 100644 index 0000000..112c15b --- /dev/null +++ b/cli/__init__.py @@ -0,0 +1,3 @@ +from .adm import NeofsAdm +from .authmate import NeofsAuthmate +from .go import NeoGo, NetworkType diff --git a/cli/adm/__init__.py b/cli/adm/__init__.py new file mode 100644 index 0000000..1596cb3 --- /dev/null +++ b/cli/adm/__init__.py @@ -0,0 +1 @@ +from .adm import NeofsAdm diff --git a/cli/adm/adm.py b/cli/adm/adm.py new file mode 100644 index 0000000..0313b78 --- /dev/null +++ b/cli/adm/adm.py @@ -0,0 +1,24 @@ +from typing import Optional + +from shell import Shell + +from .config import NeofsAdmConfig +from .morph import NeofsAdmMorph +from .subnet import NeofsAdmMorphSubnet +from .storage_config import NeofsAdmStorageConfig +from .version import NeofsAdmVersion + + +class NeofsAdm: + config: Optional[NeofsAdmConfig] = None + morph: Optional[NeofsAdmMorph] = None + subnet: Optional[NeofsAdmMorphSubnet] = None + storage_config: Optional[NeofsAdmStorageConfig] = None + version: Optional[NeofsAdmVersion] = None + + def __init__(self, shell: Shell, neofs_adm_exec_path: str, config_file: Optional[str] = None): + self.config = NeofsAdmConfig(shell, neofs_adm_exec_path, config=config_file) + self.morph = NeofsAdmMorph(shell, neofs_adm_exec_path, config=config_file) + self.subnet = NeofsAdmMorphSubnet(shell, neofs_adm_exec_path, config=config_file) + self.storage_config = NeofsAdmStorageConfig(shell, neofs_adm_exec_path, config=config_file) + self.version = NeofsAdmVersion(shell, neofs_adm_exec_path, config=config_file) diff --git a/cli/adm/config.py b/cli/adm/config.py new file mode 100644 index 0000000..7c21bd5 --- /dev/null +++ b/cli/adm/config.py @@ -0,0 +1,24 @@ +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeofsAdmConfig(NeofsCliCommand): + def init(self, path: str = "~/.neofs/adm/config.yml") -> CommandResult: + """Initialize basic neofs-adm configuration file. + + Args: + path (str): path to config (default ~/.neofs/adm/config.yml) + + + Returns: + str: Command string + + """ + return self._execute( + "config init", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) diff --git a/cli/adm/morph.py b/cli/adm/morph.py new file mode 100644 index 0000000..4fa2c9b --- /dev/null +++ b/cli/adm/morph.py @@ -0,0 +1,372 @@ +from typing import Optional + +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeofsAdmMorph(NeofsCliCommand): + def deposit_notary( + self, + rpc_endpoint: str, + account: str, + gas: str, + storage_wallet: Optional[str] = None, + till: Optional[str] = None, + ) -> CommandResult: + """Deposit GAS for notary service. + + Args: + account (str): wallet account address + gas (str): amount of GAS to deposit + rpc_endpoint (str): N3 RPC node endpoint + storage_wallet (str): path to storage node wallet + till (str): notary deposit duration in blocks + + + Returns: + str: Command string + + """ + return self._execute( + "morph deposit-notary", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def dump_balances( + self, + rpc_endpoint: str, + alphabet: Optional[str] = None, + proxy: Optional[str] = None, + script_hash: Optional[str] = None, + storage: Optional[str] = None, + ) -> CommandResult: + """Dump GAS balances + + Args: + alphabet (str): dump balances of alphabet contracts + proxy (str): dump balances of the proxy contract + rpc_endpoint (str): N3 RPC node endpoint + script_hash (str): use script-hash format for addresses + storage (str): dump balances of storage nodes from the current netmap + + + Returns: + str: Command string + + """ + return self._execute( + "morph dump-balances", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def dump_config(self, rpc_endpoint: str) -> CommandResult: + """Section for morph network configuration commands. + + Args: + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + "morph dump-config", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def dump_containers( + self, + rpc_endpoint: str, + cid: Optional[str] = None, + container_contract: Optional[str] = None, + dump: str = './testlib_dump_container', + ) -> CommandResult: + """Dump NeoFS containers to file. + + Args: + cid (str): containers to dump + container_contract (str): container contract hash (for networks without NNS) + dump (str): file where to save dumped containers + (default: ./testlib_dump_container) + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + "morph dump-containers", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def dump_hashes(self, rpc_endpoint: str) -> CommandResult: + """Dump deployed contract hashes. + + Args: + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + "morph dump-hashes", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def force_new_epoch( + self, rpc_endpoint: Optional[str] = None, alphabet: Optional[str] = None + ) -> CommandResult: + """Create new NeoFS epoch event in the side chain + + Args: + alphabet (str): path to alphabet wallets dir + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + "morph force-new-epoch", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def generate_alphabet(self, rpc_endpoint: str, alphabet_wallets: str, size: int = 7) -> CommandResult: + """Generate alphabet wallets for consensus nodes of the morph network + + Args: + alphabet_wallets (str): path to alphabet wallets dir + size (int): amount of alphabet wallets to generate (default 7) + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + "morph generate-alphabet", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def generate_storage_wallet( + self, + rpc_endpoint: str, + alphabet_wallets: str, + storage_wallet: str, + initial_gas: Optional[str] = None, + ) -> CommandResult: + """Generate storage node wallet for the morph network + + Args: + alphabet_wallets (str): path to alphabet wallets dir + initial_gas (str): initial amount of GAS to transfer + rpc_endpoint (str): N3 RPC node endpoint + storage_wallet (str): path to new storage node wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph generate-storage-wallet", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def init( + self, + rpc_endpoint: str, + alphabet_wallets: str, + contracts: str, + protocol: str, + container_alias_fee: int = 500, + container_fee: int = 1000, + epoch_duration: int = 240, + homomorphic_disabled: bool = False, + local_dump: Optional[str] = None, + max_object_size: int = 67108864, + ) -> CommandResult: + """Section for morph network configuration commands. + + Args: + alphabet_wallets (str): path to alphabet wallets dir + container_alias_fee (int): container alias fee (default 500) + container_fee (int): container registration fee (default 1000) + contracts (str): path to archive with compiled NeoFS contracts + (default fetched from latest github release) + epoch_duration (int): amount of side chain blocks in one NeoFS epoch + (default 240) + homomorphic_disabled: (bool): disable object homomorphic hashing + local_dump (str): path to the blocks dump file + max_object_size (int): max single object size in bytes (default 67108864) + protocol (str): path to the consensus node configuration + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + "morph init", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def refill_gas( + self, + rpc_endpoint: str, + alphabet_wallets: str, + storage_wallet: str, + gas: Optional[str] = None, + ) -> CommandResult: + """Refill GAS of storage node's wallet in the morph network + + Args: + alphabet_wallets (str): path to alphabet wallets dir + gas (str): additional amount of GAS to transfer + rpc_endpoint (str): N3 RPC node endpoint + storage_wallet (str): path to new storage node wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph refill-gas", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def restore_containers( + self, rpc_endpoint: str, alphabet_wallets: str, cid: str, dump: str + ) -> CommandResult: + """Restore NeoFS containers from file. + + Args: + alphabet_wallets (str): path to alphabet wallets dir + cid (str): containers to restore + dump (str): file to restore containers from + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + "morph restore-containers", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def set_policy( + self, + rpc_endpoint: str, + alphabet_wallets: str, + exec_fee_factor: Optional[int] = None, + storage_price: Optional[int] = None, + fee_per_byte: Optional[int] = None, + ) -> CommandResult: + """Set global policy values + + Args: + alphabet_wallets (str): path to alphabet wallets dir + exec_fee_factor (int): ExecFeeFactor= + storage_price (int): StoragePrice= + fee_per_byte (int): FeePerByte= + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + non_param_attribute = "" + if exec_fee_factor: + non_param_attribute += f"ExecFeeFactor={exec_fee_factor} " + if storage_price: + non_param_attribute += f"StoragePrice={storage_price} " + if fee_per_byte: + non_param_attribute += f"FeePerByte={fee_per_byte} " + return self._execute( + f"morph restore-containers {non_param_attribute}", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self", "exec_fee_factor", "storage_price", "fee_per_byte"] + }, + ) + + def update_contracts( + self, rpc_endpoint: str, alphabet_wallets: str, contracts: Optional[str] = None + ) -> CommandResult: + """Update NeoFS contracts. + + Args: + alphabet_wallets (str): path to alphabet wallets dir + contracts (str): path to archive with compiled NeoFS contracts + (default fetched from latest github release) + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + "morph update-contracts", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) diff --git a/cli/adm/storage_config.py b/cli/adm/storage_config.py new file mode 100644 index 0000000..031bd0f --- /dev/null +++ b/cli/adm/storage_config.py @@ -0,0 +1,25 @@ +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeofsAdmStorageConfig(NeofsCliCommand): + def set(self, account: str, wallet: str) -> CommandResult: + """Initialize basic neofs-adm configuration file. + + Args: + account (str): wallet account + wallet (str): path to wallet + + + Returns: + str: Command string + + """ + return self._execute( + "storage-config", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) diff --git a/cli/adm/subnet.py b/cli/adm/subnet.py new file mode 100644 index 0000000..e19c468 --- /dev/null +++ b/cli/adm/subnet.py @@ -0,0 +1,255 @@ +from typing import Optional + +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeofsAdmMorphSubnet(NeofsCliCommand): + def create(self, rpc_endpoint: str, address: str, wallet: str, notary: bool = False) -> CommandResult: + """Create NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + notary (bool): Flag to create subnet in notary environment + rpc_endpoint (str): N3 RPC node endpoint + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph subnet create", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def get(self, rpc_endpoint: str, subnet: str) -> CommandResult: + """Read information about the NeoFS subnet. + + Args: + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + + + Returns: + str: Command string + + """ + return self._execute( + "morph subnet get", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def remove( + self, rpc_endpoint: str, wallet: str, subnet: str, address: Optional[str] = None + ) -> CommandResult: + """Remove NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph subnet remove", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def admin_add( + self, + rpc_endpoint: str, + wallet: str, + admin: str, + subnet: str, + client: Optional[str] = None, + group: Optional[str] = None, + address: Optional[str] = None, + ) -> CommandResult: + """Add admin to the NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + admin (str): Hex-encoded public key of the admin + client (str): Add client admin instead of node one + group (str): Client group ID in text format (needed with --client only) + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph subnet admin add", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def admin_remove( + self, + rpc_endpoint: str, + wallet: str, + admin: str, + subnet: str, + client: Optional[str] = None, + address: Optional[str] = None, + ) -> CommandResult: + """Remove admin of the NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + admin (str): Hex-encoded public key of the admin + client (str): Remove client admin instead of node one + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph subnet admin remove", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def client_add( + self, + rpc_endpoint: str, + wallet: str, + subnet: str, + client: Optional[str] = None, + group: Optional[str] = None, + address: Optional[str] = None, + ) -> CommandResult: + """Add client to the NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + client (str): Add client admin instead of node one + group (str): Client group ID in text format (needed with --client only) + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph subnet client add", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def client_remove( + self, + rpc_endpoint: str, + wallet: str, + client: str, + group: str, + subnet: str, + address: Optional[str] = None, + ) -> CommandResult: + """Remove client of the NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + client (str): Remove client admin instead of node one + group (str): ID of the client group to work with + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph subnet client remove", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def node_add(self, rpc_endpoint: str, wallet: str, node: str, subnet: str) -> CommandResult: + """Add node to the NeoFS subnet. + + Args: + node (str): Hex-encoded public key of the node + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph subnet node add", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def node_remove(self, rpc_endpoint: str, wallet: str, node: str, subnet: str) -> CommandResult: + """Remove node from the NeoFS subnet. + + Args: + node (str): Hex-encoded public key of the node + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + "morph subnet node remove", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) diff --git a/cli/adm/version.py b/cli/adm/version.py new file mode 100644 index 0000000..6a2aedd --- /dev/null +++ b/cli/adm/version.py @@ -0,0 +1,13 @@ +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeofsAdmVersion(NeofsCliCommand): + def get(self) -> CommandResult: + """Application version + + Returns: + str: Command string + + """ + return self._execute("", version=True) diff --git a/cli/authmate/__init__.py b/cli/authmate/__init__.py new file mode 100644 index 0000000..112b6a9 --- /dev/null +++ b/cli/authmate/__init__.py @@ -0,0 +1 @@ +from .authmate import NeofsAuthmate diff --git a/cli/authmate/authmate.py b/cli/authmate/authmate.py new file mode 100644 index 0000000..58ee873 --- /dev/null +++ b/cli/authmate/authmate.py @@ -0,0 +1,20 @@ +from typing import Optional + +from shell import Shell + +from .secret import NeofsAuthmateSecret +from .version import NeofsAuthmateVersion + + +class NeofsAuthmate: + secret: Optional[NeofsAuthmateSecret] = None + version: Optional[NeofsAuthmateVersion] = None + + def __init__( + self, + shell: Shell, + neofs_authmate_exec_path: str, + ): + + self.secret = NeofsAuthmateSecret(shell, neofs_authmate_exec_path) + self.version = NeofsAuthmateVersion(shell, neofs_authmate_exec_path) diff --git a/cli/authmate/secret.py b/cli/authmate/secret.py new file mode 100644 index 0000000..66d7e81 --- /dev/null +++ b/cli/authmate/secret.py @@ -0,0 +1,92 @@ +from typing import Optional + +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeofsAuthmateSecret(NeofsCliCommand): + def obtain( + self, + wallet: str, + peer: str, + gate_wallet: str, + access_key_id: str, + address: Optional[str] = None, + gate_address: Optional[str] = None, + ) -> CommandResult: + """Obtain a secret from NeoFS network + + Args: + wallet (str): path to the wallet + address (str): address of wallet account + peer (str): address of neofs peer to connect to + gate_wallet (str): path to the wallet + gate_address (str): address of wallet account + access_key_id (str): access key id for s3 + + Returns: + str: Command string + + """ + return self._execute( + "obtain-secret", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def issue( + self, + wallet: str, + peer: str, + bearer_rules: str, + gate_public_key: str, + address: Optional[str] = None, + container_id: Optional[str] = None, + container_friendly_name: Optional[str] = None, + container_placement_policy: Optional[str] = None, + session_tokens: Optional[str] = None, + lifetime: Optional[str] = None, + container_policy: Optional[str] = None, + aws_cli_credentials: Optional[str] = None, + ) -> CommandResult: + """Obtain a secret from NeoFS network + + Args: + wallet (str): path to the wallet + address (str): address of wallet account + peer (str): address of a neofs peer to connect to + bearer_rules (str): rules for bearer token as plain json string + gate_public_key (str): public 256r1 key of a gate (use flags repeatedly for + multiple gates) + container_id (str): auth container id to put the secret into + container_friendly_name (str): friendly name of auth container to put the + secret into + container_placement_policy (str): placement policy of auth container to put the + secret into + (default: "REP 2 IN X CBF 3 SELECT 2 FROM * AS X") + session_tokens (str): create session tokens with rules, if the rules are + set as 'none', no session tokens will be created + lifetime (str): Lifetime of tokens. For example 50h30m + (note: max time unit is an hour so to set a day you + should use 24h). It will be ceil rounded to the + nearest amount of epoch. (default: 720h0m0s) + container_policy (str): mapping AWS storage class to NeoFS storage policy as + plain json string or path to json file + aws_cli_credentials (str): path to the aws cli credential file + + + Returns: + str: Command string + + """ + return self._execute( + "issue-secret", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) diff --git a/cli/authmate/version.py b/cli/authmate/version.py new file mode 100644 index 0000000..4432b2d --- /dev/null +++ b/cli/authmate/version.py @@ -0,0 +1,13 @@ +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeofsAuthmateVersion(NeofsCliCommand): + def get(self) -> CommandResult: + """Application version + + Returns: + str: Command string + + """ + return self._execute("", version=True) diff --git a/cli/cli_command.py b/cli/cli_command.py new file mode 100644 index 0000000..a622324 --- /dev/null +++ b/cli/cli_command.py @@ -0,0 +1,58 @@ +from typing import Optional + +from shell import CommandResult, Shell + + +class NeofsCliCommand: + + WALLET_SOURCE_ERROR_MSG = 'Provide either wallet or wallet_config to specify wallet location' + + neofs_cli_exec: Optional[str] = None + __base_params: Optional[str] = None + map_params = { + "json_mode": "json", + "await_mode": "await", + "hash_type": "hash", + "doc_type": "type", + } + + def __init__(self, shell: Shell, neofs_cli_exec: str, **base_params): + self.shell = shell + self.neofs_cli_exec = neofs_cli_exec + self.__base_params = " ".join( + [f"--{param} {value}" for param, value in base_params.items() if value] + ) + + def _format_command(self, command: str, **params) -> str: + param_str = [] + for param, value in params.items(): + if param in self.map_params.keys(): + param = self.map_params[param] + param = param.replace("_", "-") + if not value: + continue + if isinstance(value, bool): + param_str.append(f"--{param}") + elif isinstance(value, int): + param_str.append(f"--{param} {value}") + elif isinstance(value, list): + for value_item in value: + val_str = str(value_item).replace("'", "\\'") + param_str.append(f"--{param} '{val_str}'") + elif isinstance(value, dict): + param_str.append( + f'--{param} \'{",".join(f"{key}={val}" for key, val in value.items())}\'' + ) + else: + if "'" in str(value): + value_str = str(value).replace('"', '\\"') + param_str.append(f'--{param} "{value_str}"') + else: + param_str.append(f"--{param} '{value}'") + + param_str = " ".join(param_str) + + return f'{self.neofs_cli_exec} {self.__base_params} {command or ""} {param_str}' + + def _execute(self, command: Optional[str], **params) -> CommandResult: + return self.shell.exec(self._format_command(command, **params)) diff --git a/cli/go/__init__.py b/cli/go/__init__.py new file mode 100644 index 0000000..d3fa193 --- /dev/null +++ b/cli/go/__init__.py @@ -0,0 +1,2 @@ +from .blockchain_network_type import NetworkType +from .go import NeoGo diff --git a/cli/go/blockchain_network_type.py b/cli/go/blockchain_network_type.py new file mode 100644 index 0000000..9129f88 --- /dev/null +++ b/cli/go/blockchain_network_type.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class NetworkType(Enum): + PRIVATE = "privnet" + MAIN = "mainnet" + TEST = "testnet" diff --git a/cli/go/candidate.py b/cli/go/candidate.py new file mode 100644 index 0000000..99a42bf --- /dev/null +++ b/cli/go/candidate.py @@ -0,0 +1,120 @@ +from typing import Optional + +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeoGoCandidate(NeofsCliCommand): + + def register( + self, + address: str, + rpc_endpoint: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + gas: Optional[float] = None, + timeout: int = 10, + ) -> CommandResult: + """ register as a new candidate + + Args: + address (str): Address to register + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + gas (float): network fee to add to the transaction (prioritizing it) + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet candidate register", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def unregister( + self, + address: str, + rpc_endpoint: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + gas: Optional[float] = None, + timeout: int = 10, + ) -> CommandResult: + """ unregister self as a candidate + + Args: + address (str): Address to unregister + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + gas (float): network fee to add to the transaction (prioritizing it) + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet candidate unregister", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def vote( + self, + candidate: str, + rpc_endpoint: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + gas: Optional[float] = None, + timeout: int = 10, + ) -> CommandResult: + """ Votes for a validator by calling "vote" method of a NEO native + contract. Do not provide candidate argument to perform unvoting. + + + Args: + candidate (str): Public key of candidate to vote for + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + gas (float): network fee to add to the transaction (prioritizing it) + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet candidate vote", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) diff --git a/cli/go/contract.py b/cli/go/contract.py new file mode 100644 index 0000000..5797e06 --- /dev/null +++ b/cli/go/contract.py @@ -0,0 +1,358 @@ +from typing import Optional + +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeoGoContract(NeofsCliCommand): + def compile( + self, + input_file: str, + out: str, + manifest: str, + config: str, + no_standards: bool = False, + no_events: bool = False, + no_permissions: bool = False, + bindings: Optional[str] = None, + ) -> CommandResult: + """compile a smart contract to a .nef file + + Args: + input_file (str): Input file for the smart contract to be compiled + out (str): Output of the compiled contract + manifest (str): Emit contract manifest (*.manifest.json) file into separate + file using configuration input file (*.yml) + config (str): Configuration input file (*.yml) + no_standards (bool): do not check compliance with supported standards + no_events (bool): do not check emitted events with the manifest + no_permissions (bool): do not check if invoked contracts are allowed in manifest + bindings (str): output file for smart-contract bindings configuration + + Returns: + str: Command string + + """ + return self._execute( + "contract compile", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def deploy( + self, + address: str, + input_file: str, + sysgas: float, + manifest: str, + rpc_endpoint: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + gas: Optional[float] = None, + out: Optional[str] = None, + force: bool = False, + timeout: int = 10, + + ) -> CommandResult: + """deploy a smart contract (.nef with description) + + Args: + wallet (str): wallet to use to get the key for transaction signing; + conflicts with wallet_config + wallet_config (str): path to wallet config to use to get the key for transaction + signing; conflicts with wallet + address (str): address to use as transaction signee (and gas source) + gas (float): network fee to add to the transaction (prioritizing it) + sysgas (float): system fee to add to transaction (compensating for execution) + out (str): file to put JSON transaction to + force (bool): Do not ask for a confirmation + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + input_file (str): Input file for the smart contract (*.nef) + manifest (str): Emit contract manifest (*.manifest.json) file into separate + file using configuration input file (*.yml) + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "contract deploy", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def generate_wrapper( + self, + out: str, + hash: str, + config: Optional[str] = None, + manifest: Optional[str] = None, + ) -> CommandResult: + """generate wrapper to use in other contracts + + Args: + config (str): Configuration file to use + manifest (str): Read contract manifest (*.manifest.json) file + out (str): Output of the compiled contract + hash (str): Smart-contract hash + + Returns: + str: Command string + + """ + return self._execute( + "contract generate-wrapper", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def invokefunction( + self, + address: str, + scripthash: str, + wallet: Optional[str] = None, + method: Optional[str] = None, + arguments: Optional[str] = None, + multisig_hash: Optional[str] = None, + wallet_config: Optional[str] = None, + gas: Optional[float] = None, + sysgas: Optional[float] = None, + out: Optional[str] = None, + force: bool = False, + rpc_endpoint: Optional[str] = None, + timeout: int = 10, + ) -> CommandResult: + """Executes given (as a script hash) deployed script with the given method, + arguments and signers. Sender is included in the list of signers by default + with None witness scope. If you'd like to change default sender's scope, + specify it via signers parameter. See testinvokefunction documentation for + the details about parameters. It differs from testinvokefunction in that this + command sends an invocation transaction to the network. + + Args: + scripthash (str): Function hash + method (str): Call method + arguments (str): Method arguments + multisig_hash (str): Multisig hash + wallet (str): wallet to use to get the key for transaction signing; + conflicts with wallet_config + wallet_config (str): path to wallet config to use to get the key for transaction + signing; conflicts with wallet + address (str): address to use as transaction signee (and gas source) + gas (float): network fee to add to the transaction (prioritizing it) + sysgas (float): system fee to add to transaction (compensating for execution) + out (str): file to put JSON transaction to + force (bool): force-push the transaction in case of bad VM state after + test script invocation + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + multisig_hash = f"-- {multisig_hash}" or "" + return self._execute( + "contract invokefunction " + f"{scripthash} {method or ''} {arguments or ''} {multisig_hash}", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self", "scripthash", "method", "arguments", "multisig_hash"] + }, + ) + + def testinvokefunction( + self, + scripthash: str, + wallet: Optional[str] = None, + method: Optional[str] = None, + arguments: Optional[str] = None, + multisig_hash: Optional[str] = None, + rpc_endpoint: Optional[str] = None, + timeout: int = 10, + ) -> CommandResult: + """Executes given (as a script hash) deployed script with the given method, + arguments and signers (sender is not included by default). If no method is given + "" is passed to the script, if no arguments are given, an empty array is + passed, if no signers are given no array is passed. If signers are specified, + the first one of them is treated as a sender. All of the given arguments are + encapsulated into array before invoking the script. The script thus should + follow the regular convention of smart contract arguments (method string and + an array of other arguments). + + See more information and samples in `neo-go contract testinvokefunction --help` + + Args: + scripthash (str): Function hash + method (str): Call method + arguments (str): Method arguments + multisig_hash (str): Multisig hash + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + multisig_hash = f"-- {multisig_hash}" or "" + return self._execute( + "contract testinvokefunction " + f"{scripthash} {method or ''} {arguments or ''} {multisig_hash}", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self", "scripthash", "method", "arguments", "multisig_hash"] + }, + ) + + def testinvokescript( + self, + input_file: str, + rpc_endpoint: Optional[str] = None, + timeout: int = 10, + ) -> CommandResult: + """Executes given compiled AVM instructions in NEF format with the given set of + signers not included sender by default. See testinvokefunction documentation + for the details about parameters. + + + Args: + input_file (str): Input location of the .nef file that needs to be invoked + conflicts with wallet_config + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + return self._execute( + f"contract testinvokescript", + **{ + 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: + """initialize a new smart-contract in a directory with boiler plate code + + Args: + name (str): name of the smart-contract to be initialized + skip_details (bool): skip filling in the projects and contract details + + Returns: + str: Command string + + """ + return self._execute( + "contract init", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def inspect( + self, + input_file: Optional[str] = None, + compile: Optional[str] = None, + ) -> CommandResult: + """creates a user readable dump of the program instructions + + Args: + input_file (str): input file of the program (either .go or .nef) + compile (str): compile input file (it should be go code then) + + Returns: + str: Command string + + """ + return self._execute( + "contract inspect", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def calc_hash( + self, + input_file: str, + manifest: str, + sender: Optional[str] = None, + ) -> CommandResult: + """calculates hash of a contract after deployment + + Args: + input_file (str): path to NEF file + sender (str): sender script hash or address + manifest (str): path to manifest file + + Returns: + str: Command string + + """ + return self._execute( + "contract calc-hash", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def add_group( + self, + manifest: str, + address: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + sender: Optional[str] = None, + nef: Optional[str] = None, + ) -> CommandResult: + """adds group to the manifest + + Args: + wallet (str): wallet to use to get the key for transaction signing; + conflicts with wallet_config + wallet_config (str): path to wallet config to use to get the key for transaction + signing; conflicts with wallet + sender (str): deploy transaction sender + address (str): account to sign group with + nef (str): path to the NEF file + manifest (str): path to the manifest + + + Returns: + str: Command string + + """ + return self._execute( + "contract manifest add-group", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) diff --git a/cli/go/db.py b/cli/go/db.py new file mode 100644 index 0000000..1faf28b --- /dev/null +++ b/cli/go/db.py @@ -0,0 +1,74 @@ +from typing import Optional + +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + +from .blockchain_network_type import NetworkType + + +class NeoGoDb(NeofsCliCommand): + def dump( + self, + config_path: str, + out: str, + network: NetworkType = NetworkType.PRIVATE, + count: int = 0, + start: int = 0, + ) -> CommandResult: + """ dump blocks (starting with block #1) to the file + + Args: + config_path (str): path to config + network (NetworkType): Select network type (default: private) + count (int): number of blocks to be processed (default or 0: all chain) + (default: 0) + start (int): block number to start from (default: 0) (default: 0) + out (srt): Output file (stdout if not given) + + Returns: + str: Command string + + """ + return self._execute( + "db dump", + **{network.value: True}, + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def restore( + self, + config_path: str, + input_file: str, + network: NetworkType = NetworkType.PRIVATE, + count: int = 0, + dump: Optional[str] = None, + incremental: bool = False, + ) -> CommandResult: + """ dump blocks (starting with block #1) to the file + + Args: + config_path (str): path to config + network (NetworkType): Select network type (default: private) + count (int): number of blocks to be processed (default or 0: all chain) + (default: 0) + input_file (str): Input file (stdin if not given) + dump (str): directory for storing JSON dumps + incremental (bool): use if dump is incremental + + Returns: + str: Command string + + """ + return self._execute( + "db restore", + **{network.value: True}, + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) diff --git a/cli/go/go.py b/cli/go/go.py new file mode 100644 index 0000000..635d42e --- /dev/null +++ b/cli/go/go.py @@ -0,0 +1,44 @@ +from typing import Optional + +from shell import Shell + +from .candidate import NeoGoCandidate +from .contract import NeoGoContract +from .db import NeoGoDb +from .nep17 import NeoGoNep17 +from .node import NeoGoNode +from .query import NeoGoQuery +from .version import NeoGoVersion +from .wallet import NeoGoWallet + + +class NeoGo: + neo_go_exec_path: Optional[str] = None + config_path: Optional[str] = None + candidate: Optional[NeoGoCandidate] = None + contract: Optional[NeoGoContract] = None + db: Optional[NeoGoDb] = None + nep17: Optional[NeoGoNep17] = None + node: Optional[NeoGoNode] = None + query: Optional[NeoGoQuery] = None + version: Optional[NeoGoVersion] = None + wallet: Optional[NeoGoWallet] = None + + def __init__( + self, + shell: Shell, + neo_go_exec_path: Optional[str] = None, + config_path: Optional[str] = None, + ): + self.candidate = NeoGoCandidate( + shell, neo_go_exec_path, config_path=config_path + ) + self.contract = NeoGoContract( + self.neo_go_exec_path, config_path=config_path + ) + self.db = NeoGoDb(shell, neo_go_exec_path, config_path=config_path) + self.nep17 = NeoGoNep17(shell, neo_go_exec_path, config_path=config_path) + self.node = NeoGoNode(shell, neo_go_exec_path, config_path=config_path) + self.query = NeoGoQuery(shell, neo_go_exec_path, config_path=config_path) + self.version = NeoGoVersion(shell, neo_go_exec_path, config_path=config_path) + self.wallet = NeoGoWallet(shell, neo_go_exec_path, config_path=config_path) diff --git a/cli/go/nep17.py b/cli/go/nep17.py new file mode 100644 index 0000000..62c936d --- /dev/null +++ b/cli/go/nep17.py @@ -0,0 +1,243 @@ +from typing import List, Optional + +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeoGoNep17(NeofsCliCommand): + def balance( + self, + address: str, + token: str, + rpc_endpoint: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + timeout: int = 10, + ) -> CommandResult: + """Get address balance + + Args: + address (str): Address to use + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + token (str): Token to use (hash or name (for NEO/GAS or imported tokens)) + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet nep17 balance", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def import_token( + self, + address: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + token: Optional[str] = None, + rpc_endpoint: Optional[str] = None, + timeout: int = 10, + ) -> CommandResult: + """import NEP-17 token to a wallet + + Args: + address (str): Token contract address or hash in LE + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + token (str): Token to use (hash or name (for NEO/GAS or imported tokens)) + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet nep17 import", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def info( + self, + token: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + ) -> CommandResult: + """print imported NEP-17 token info + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + token (str): Token to use (hash or name (for NEO/GAS or imported tokens)) + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet nep17 info", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def remove( + self, + token: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + force: bool = False, + ) -> CommandResult: + """remove NEP-17 token from the wallet + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + token (str): Token to use (hash or name (for NEO/GAS or imported tokens)) + force (bool): Do not ask for a confirmation + + Returns: + str: Command string + + """ + return self._execute( + "wallet nep17 remove", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def transfer( + self, + token: str, + to_address: str, + sysgas: float, + rpc_endpoint: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + out: Optional[str] = None, + from_address: Optional[str] = None, + force: bool = False, + gas: Optional[float] = None, + amount: float = 0, + timeout: int = 10, + ) -> CommandResult: + """Transfers specified NEP-17 token amount with optional 'data' parameter and cosigners + list attached to the transfer. See 'contract testinvokefunction' documentation + for the details about 'data' parameter and cosigners syntax. If no 'data' is + given then default nil value will be used. If no cosigners are given then the + sender with CalledByEntry scope will be used as the only signer. + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + out (str): file to put JSON transaction to + from_address (str): Address to send an asset from + to_address (str): Address to send an asset to + token (str): Token to use (hash or name (for NEO/GAS or imported tokens)) + force (bool): Do not ask for a confirmation + gas (float): network fee to add to the transaction (prioritizing it) + sysgas (float): system fee to add to transaction (compensating for execution) + force (bool): Do not ask for a confirmation + amount (float) Amount of asset to send + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet nep17 transfer", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def multitransfer( + self, + token: str, + to_address: List[str], + sysgas: float, + rpc_endpoint: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + out: Optional[str] = None, + from_address: Optional[str] = None, + force: bool = False, + gas: Optional[float] = None, + amount: float = 0, + + timeout: int = 10, + ) -> CommandResult: + """transfer NEP-17 tokens to multiple recipients + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + out (str): file to put JSON transaction to + from_address (str): Address to send an asset from + to_address (str): Address to send an asset to + token (str): Token to use (hash or name (for NEO/GAS or imported tokens)) + force (bool): Do not ask for a confirmation + gas (float): network fee to add to the transaction (prioritizing it) + sysgas (float): system fee to add to transaction (compensating for execution) + force (bool): Do not ask for a confirmation + amount (float) Amount of asset to send + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet nep17 multitransfer", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) diff --git a/cli/go/node.py b/cli/go/node.py new file mode 100644 index 0000000..363dc9b --- /dev/null +++ b/cli/go/node.py @@ -0,0 +1,18 @@ +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + +from .blockchain_network_type import NetworkType + + +class NeoGoNode(NeofsCliCommand): + def start(self, network: NetworkType = NetworkType.PRIVATE) -> CommandResult: + """Start a NEO node + + Args: + network (NetworkType): Select network type (default: private) + + Returns: + str: Command string + + """ + return self._execute("start", **{network.value: True}) diff --git a/cli/go/query.py b/cli/go/query.py new file mode 100644 index 0000000..bdcff77 --- /dev/null +++ b/cli/go/query.py @@ -0,0 +1,128 @@ +from typing import Optional + +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeoGoQuery(NeofsCliCommand): + def candidates( + self, + rpc_endpoint: str, + timeout: int = 10, + ) -> CommandResult: + """Get candidates and votes + + Args: + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + return self._execute( + "query candidates", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def committee( + self, + rpc_endpoint: str, + timeout: int = 10, + ) -> CommandResult: + """Get committee list + + Args: + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + return self._execute( + "query committee", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def height( + self, + rpc_endpoint: str, + timeout: int = 10, + ) -> CommandResult: + """Get node height + + Args: + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + return self._execute( + "query height", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) + + def tx( + self, + tx_hash: str, + rpc_endpoint: str, + timeout: int = 10, + ) -> CommandResult: + """Query transaction status + + Args: + tx_hash (str): Hash of transaction + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + return self._execute( + f"query tx {tx_hash}", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self", "hash"] + }, + ) + + def voter( + self, + rpc_endpoint: str, + timeout: int = 10, + ) -> CommandResult: + """Print NEO holder account state + + Args: + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + return self._execute( + "query voter", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + }, + ) diff --git a/cli/go/version.py b/cli/go/version.py new file mode 100644 index 0000000..beb3cfa --- /dev/null +++ b/cli/go/version.py @@ -0,0 +1,13 @@ +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeoGoVersion(NeofsCliCommand): + def get(self) -> CommandResult: + """Application version + + Returns: + str: Command string + + """ + return self._execute("", version=True) diff --git a/cli/go/wallet.py b/cli/go/wallet.py new file mode 100644 index 0000000..3d20c25 --- /dev/null +++ b/cli/go/wallet.py @@ -0,0 +1,395 @@ +from typing import Optional + +from cli.cli_command import NeofsCliCommand +from shell import CommandResult + + +class NeoGoWallet(NeofsCliCommand): + def claim( + self, + address: str, + rpc_endpoint: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + timeout: int = 10, + ) -> CommandResult: + """claim GAS + + Args: + address (str): Address to claim GAS for + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet claim", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def init( + self, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + account: bool = False, + ) -> CommandResult: + """create a new wallet + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + account (bool): Create a new account + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet init", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def convert( + self, + out: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + ) -> CommandResult: + """convert addresses from existing NEO2 NEP6-wallet to NEO3 format + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + out (str): where to write converted wallet + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet convert", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def create( + self, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + ) -> CommandResult: + """add an account to the existing wallet + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet create", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def dump( + self, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + decrypt: bool = False, + ) -> CommandResult: + """check and dump an existing NEO wallet + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + decrypt (bool): Decrypt encrypted keys. + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet dump", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def dump_keys( + self, + address: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + ) -> CommandResult: + """check and dump an existing NEO wallet + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + address (str): address to print public keys for + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet dump-keys", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def export( + self, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + decrypt: bool = False, + ) -> CommandResult: + """export keys for address + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + decrypt (bool): Decrypt encrypted keys. + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet export", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def import_wif( + self, + wif: str, + name: str, + contract: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + ) -> CommandResult: + """import WIF of a standard signature contract + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + wif (str): WIF to import + name (str): Optional account name + contract (str): Verification script for custom contracts + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet import", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def import_multisig( + self, + wif: str, + name: Optional[str] = None, + min_number: int = 0, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + ) -> CommandResult: + """import multisig contract + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + wif (str): WIF to import + name (str): Optional account name + min_number (int): Minimal number of signatures (default: 0) + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet import-multisig", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def import_deployed( + self, + wif: str, + rpc_endpoint: str, + name: Optional[str] = None, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + contract: Optional[str] = None, + + timeout: int = 10, + ) -> CommandResult: + """import multisig contract + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + wif (str): WIF to import + name (str): Optional account name + contract (str): Contract hash or address + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet import-deployed", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def remove( + self, + address: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + force: bool = False, + ) -> CommandResult: + """check and dump an existing NEO wallet + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + address (str): Account address or hash in LE form to be removed + force (bool): Do not ask for a confirmation + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet remove", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) + + def sign( + self, + input_file: str, + address: str, + rpc_endpoint: str, + wallet: Optional[str] = None, + wallet_config: Optional[str] = None, + out: Optional[str] = None, + timeout: int = 10, + ) -> CommandResult: + """import multisig contract + + Args: + wallet (str): Target location of the wallet file ('-' to read from stdin); + conflicts with --wallet-config flag. + wallet_config (str): Target location of the wallet config file; + conflicts with --wallet flag. + out (str): file to put JSON transaction to + input_file (str): file with JSON transaction + address (str): Address to use + rpc_endpoint (str): RPC node address + timeout (int): Timeout for the operation (default: 10s) + + Returns: + str: Command string + + """ + assert bool(wallet) ^ bool(wallet_config), self.WALLET_SOURCE_ERROR_MSG + + return self._execute( + "wallet sign", + **{ + param: param_value + for param, param_value in locals().items() + if param not in ["self"] + } + ) diff --git a/shell/__init__.py b/shell/__init__.py index e69de29..b867f00 100644 --- a/shell/__init__.py +++ b/shell/__init__.py @@ -0,0 +1,3 @@ +from .interfaces import CommandResult, Shell +from .local_shell import LocalShell +from .ssh_shell import SSHShell