From 2bfa2bf3069ba4a6dee0c7f25a9eea88d95574de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 19:23:09 +0000 Subject: [PATCH 01/10] Bump cryptography from 43.0.3 to 44.0.1 Bumps [cryptography](https://github.com/pyca/cryptography) from 43.0.3 to 44.0.1. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/43.0.3...44.0.1) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5580833f..1ce47ad6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,4 +20,4 @@ trezor==0.13.8 ledgerblue==0.1.48 snet-contracts==0.2.1 lighthouseweb3==0.1.4 -cryptography==43.0.3 +cryptography==44.0.1 From 1af4d484fc09b4182cb7e74886e1e36e15c72547 Mon Sep 17 00:00:00 2001 From: kiruxaspb Date: Fri, 28 Feb 2025 19:28:10 +0300 Subject: [PATCH 02/10] Refactor CLI for migration --- .gitignore | 4 +-- README.md | 23 ++++++++++---- requirements.txt | 2 +- setup.py | 2 +- snet/cli/arguments.py | 44 +++++++++++++------------- snet/cli/commands/commands.py | 8 ++--- snet/cli/commands/mpe_account.py | 16 +++++----- snet/cli/commands/mpe_channel.py | 6 ++-- snet/cli/commands/mpe_client.py | 4 +-- snet/cli/commands/mpe_treasurer.py | 8 ++--- snet/cli/config.py | 2 +- snet/cli/identity.py | 7 ++-- snet/cli/metadata/service.py | 2 +- snet/cli/resources/service_schema.json | 2 +- snet/cli/utils/agix2cogs.py | 24 -------------- snet/cli/utils/token2cogs.py | 24 ++++++++++++++ version.py | 2 +- 17 files changed, 95 insertions(+), 85 deletions(-) delete mode 100644 snet/cli/utils/agix2cogs.py create mode 100644 snet/cli/utils/token2cogs.py diff --git a/.gitignore b/.gitignore index b2a9e7e6..ac764b82 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,8 @@ venv/ .idea/ __pycache__ blockchain/node_modules -snet.egg-info/ -snet.cli.egg-info/ +snet_egg-info/ +snet_cli.egg-info/ snet/cli/resources/node_modules snet/cli/resources/proto/*.py build/ diff --git a/README.md b/README.md index 1c215855..a5330f73 100644 --- a/README.md +++ b/README.md @@ -122,18 +122,27 @@ Backward compatibility for other Python versions is not guaranteed. --- -* Clone the git repository +* + +* Install the package in development mode + +### Clone the git repository + ```bash -$ git clone https://github.com/singnet/snet-cli.git -$ cd snet-cli/packages/snet_cli +git clone https://github.com/singnet/snet-cli.git +cd snet-cli ``` -* - -* Install the package in development/editable mode +```bash +pip install -r requirements.txt +``` + +```bash +pip3 install . +``` ```bash -$ pip3 install -e . +snet -h ``` ### Building the Documentation in Markdown files diff --git a/requirements.txt b/requirements.txt index 5580833f..cdbf5c29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,6 +18,6 @@ jsonschema==4.0.0 eth-account==0.9.0 trezor==0.13.8 ledgerblue==0.1.48 -snet-contracts==0.2.1 +snet-contracts==0.2.2 lighthouseweb3==0.1.4 cryptography==43.0.3 diff --git a/setup.py b/setup.py index 26f55ed8..14d766d3 100644 --- a/setup.py +++ b/setup.py @@ -67,7 +67,7 @@ def run(self): }, entry_points={ 'console_scripts': [ - 'snet = snet.cli:main', + 'snet = snet.cli:main' ], } ) diff --git a/snet/cli/arguments.py b/snet/cli/arguments.py index 1bc5d45f..380b0c7e 100644 --- a/snet/cli/arguments.py +++ b/snet/cli/arguments.py @@ -14,7 +14,7 @@ from snet.cli.commands.sdk_command import SDKCommand from snet.cli.config import Config, get_session_keys, get_session_network_keys_removable from snet.cli.identity import get_identity_types -from snet.cli.utils.agix2cogs import stragix2cogs +from snet.cli.utils.token2cogs import strtoken2cogs from snet.cli.utils.utils import type_converter @@ -62,7 +62,7 @@ def add_root_options(parser, config): title="snet commands", metavar="COMMAND") subparsers.required = True - p = subparsers.add_parser("account", help="AGIX account") + p = subparsers.add_parser("account", help="ASI(FET) account") add_mpe_account_options(p) p = subparsers.add_parser("channel", help="Interact with SingularityNET payment channels") @@ -236,7 +236,7 @@ def add_contract_options(parser): contracts = get_all_abi_contract_files() for path in contracts: - if "MultiPartyEscrow" in str(path) or "Registry" in str(path) or "SingularityNetToken" in str(path): + if "MultiPartyEscrow" in str(path) or "Registry" in str(path) or "FetchToken" in str(path): contract_name = re.search( r"([^.]*)\.json", os.path.basename(path)).group(1) contract_p = subparsers.add_parser( @@ -584,32 +584,32 @@ def add_p_snt_address_opt(p): p.set_defaults(fn="print_account") add_eth_call_arguments(p) - p = subparsers.add_parser("balance", help="Print balance of AGIX tokens and balance of MPE wallet") - p.set_defaults(fn="print_agix_and_mpe_balances") + p = subparsers.add_parser("balance", help="Print balance of ASI(FET) tokens and balance of MPE wallet") + p.set_defaults(fn="print_token_and_mpe_balances") p.add_argument("--account", default=None, help="Account to print balance for (default is the current identity)") add_p_snt_address_opt(p) add_p_mpe_address_opt(p) add_eth_call_arguments(p) - p = subparsers.add_parser("deposit", help="Deposit AGIX tokens to MPE wallet") + p = subparsers.add_parser("deposit", help="Deposit ASI(FET) tokens to MPE wallet") p.set_defaults(fn="deposit_to_mpe") - p.add_argument("amount", type=stragix2cogs, help="Amount of AGIX tokens to deposit in MPE wallet", metavar="AMOUNT") + p.add_argument("amount", type=strtoken2cogs, help="Amount of ASI(FET) tokens to deposit in MPE wallet", metavar="AMOUNT") add_p_snt_address_opt(p) add_p_mpe_address_opt(p) add_transaction_arguments(p) - p = subparsers.add_parser("withdraw", help="Withdraw AGIX tokens from MPE wallet") + p = subparsers.add_parser("withdraw", help="Withdraw ASI(FET) tokens from MPE wallet") p.set_defaults(fn="withdraw_from_mpe") - p.add_argument("amount", type=stragix2cogs, help="Amount of AGIX tokens to withdraw from MPE wallet", metavar="AMOUNT") + p.add_argument("amount", type=strtoken2cogs, help="Amount of ASI(FET) tokens to withdraw from MPE wallet", metavar="AMOUNT") add_p_mpe_address_opt(p) add_transaction_arguments(p) - p = subparsers.add_parser("transfer", help="Transfer AGIX tokens inside MPE wallet") + p = subparsers.add_parser("transfer", help="Transfer ASI(FET) tokens inside MPE wallet") p.set_defaults(fn="transfer_in_mpe") p.add_argument("receiver", help="Address of the receiver", metavar="RECEIVER") p.add_argument("amount", - type=stragix2cogs, - help="Amount of AGIX tokens to be transferred to another account inside MPE wallet", + type=strtoken2cogs, + help="Amount of ASI(FET) tokens to be transferred to another account inside MPE wallet", metavar="AMOUNT") add_p_mpe_address_opt(p) add_transaction_arguments(p) @@ -664,8 +664,8 @@ def add_p_open_channel_basic(p): add_group_name(p) p.add_argument("amount", - type=stragix2cogs, - help="Amount of AGIX tokens to put in the new channel", + type=strtoken2cogs, + help="Amount of ASI(FET) tokens to put in the new channel", metavar="AMOUNT") # p.add_argument("group_name", # default=None, @@ -709,8 +709,8 @@ def add_p_set_for_extend_add(_p): title="Expiration and amount") add_p_expiration(expiration_amount_g, is_optional=True) expiration_amount_g.add_argument("--amount", - type=stragix2cogs, - help="Amount of AGIX tokens to add to the channel") + type=strtoken2cogs, + help="Amount of ASI(FET) tokens to add to the channel") add_p_mpe_address_opt(p) add_transaction_arguments(p) @@ -920,8 +920,8 @@ def add_mpe_service_options(parser): nargs='*', help="Endpoints for the first group") p.add_argument("--fixed-price", - type=stragix2cogs, - help="Set fixed price in AGIX token for all methods") + type=strtoken2cogs, + help="Set fixed price in ASI(FET) token for all methods") p.add_argument("--encoding", default="proto", @@ -950,8 +950,8 @@ def add_mpe_service_options(parser): p.add_argument("group_name", help="group name for fixed price method") p.add_argument("price", - type=stragix2cogs, - help="Set fixed price in AGIX token for all methods", + type=strtoken2cogs, + help="Set fixed price in ASI(FET) token for all methods", metavar="PRICE") add_p_metadata_file_opt(p) @@ -970,8 +970,8 @@ def add_mpe_service_options(parser): ) p.add_argument("price", - type=stragix2cogs, - help="Set fixed price in AGIX token for all methods", + type=strtoken2cogs, + help="Set fixed price in ASI(FET) token for all methods", metavar="PRICE") add_p_metadata_file_opt(p) diff --git a/snet/cli/commands/commands.py b/snet/cli/commands/commands.py index aa00f3c1..64b80b91 100644 --- a/snet/cli/commands/commands.py +++ b/snet/cli/commands/commands.py @@ -166,8 +166,8 @@ def get_identity(self): return RpcIdentityProvider(self.w3, self.get_wallet_index()) if identity_type == "mnemonic": return MnemonicIdentityProvider(self.w3, self.config.get_session_field("mnemonic"), self.get_wallet_index()) - if identity_type == "trezor": - return TrezorIdentityProvider(self.w3, self.get_wallet_index()) + # if identity_type == "trezor": + # return TrezorIdentityProvider(self.w3, self.get_wallet_index()) if identity_type == "ledger": return LedgerIdentityProvider(self.w3, self.get_wallet_index()) if identity_type == "key": @@ -358,8 +358,8 @@ def populate_contract_address(self, rez, key): w3=self.w3, contract_name="Registry") rez[key]['default_multipartyescrow_at'] = read_default_contract_address( w3=self.w3, contract_name="MultiPartyEscrow") - rez[key]['default_singularitynettoken_at'] = read_default_contract_address( - w3=self.w3, contract_name="SingularityNetToken") + rez[key]['default_fetchtoken_at'] = read_default_contract_address( + w3=self.w3, contract_name="FetchToken") except Exception as e: pass return diff --git a/snet/cli/commands/mpe_account.py b/snet/cli/commands/mpe_account.py index c5d29ea5..805bb3cd 100644 --- a/snet/cli/commands/mpe_account.py +++ b/snet/cli/commands/mpe_account.py @@ -1,5 +1,5 @@ from snet.cli.commands.commands import BlockchainCommand -from snet.cli.utils.agix2cogs import cogs2stragix +from snet.cli.utils.token2cogs import cogs2strtoken class MPEAccountCommand(BlockchainCommand): @@ -8,31 +8,31 @@ def print_account(self): self.check_ident() self._printout(self.ident.address) - def print_agix_and_mpe_balances(self): - """ Print balance of ETH, AGIX, and MPE wallet """ + def print_token_and_mpe_balances(self): + """ Print balance of ETH, ASI(FET), and MPE wallet """ self.check_ident() if self.args.account: account = self.args.account else: account = self.ident.address eth_wei = self.w3.eth.get_balance(account) - agix_cogs = self.call_contract_command("SingularityNetToken", "balanceOf", [account]) + token_cogs = self.call_contract_command("FetchToken", "balanceOf", [account]) mpe_cogs = self.call_contract_command("MultiPartyEscrow", "balances", [account]) # we cannot use _pprint here because it doesn't conserve order yet self._printout(" account: %s"%account) self._printout(" ETH: %s"%self.w3.from_wei(eth_wei, 'ether')) - self._printout(" AGIX: %s"%cogs2stragix(agix_cogs)) - self._printout(" MPE: %s"%cogs2stragix(mpe_cogs)) + self._printout(" ASI(FET): %s"%cogs2strtoken(token_cogs)) + self._printout(" MPE: %s"%cogs2strtoken(mpe_cogs)) def deposit_to_mpe(self): self.check_ident() amount = self.args.amount mpe_address = self.get_mpe_address() - already_approved = self.call_contract_command("SingularityNetToken", "allowance", [self.ident.address, mpe_address]) + already_approved = self.call_contract_command("FetchToken", "allowance", [self.ident.address, mpe_address]) if already_approved < amount: - self.transact_contract_command("SingularityNetToken", "approve", [mpe_address, amount]) + self.transact_contract_command("FetchToken", "approve", [mpe_address, amount]) self.transact_contract_command("MultiPartyEscrow", "deposit", [amount]) def withdraw_from_mpe(self): diff --git a/snet/cli/commands/mpe_channel.py b/snet/cli/commands/mpe_channel.py index 53fae089..62fdad3f 100644 --- a/snet/cli/commands/mpe_channel.py +++ b/snet/cli/commands/mpe_channel.py @@ -14,7 +14,7 @@ from snet.cli.commands.commands import OrganizationCommand from snet.cli.metadata.service import mpe_service_metadata_from_json, load_mpe_service_metadata from snet.cli.metadata.organization import OrganizationMetadata -from snet.cli.utils.agix2cogs import cogs2stragix +from snet.cli.utils.token2cogs import cogs2strtoken from snet.cli.utils.ipfs_utils import get_from_ipfs_and_checkhash from snet.cli.utils.utils import abi_decode_struct_to_dict, abi_get_element_by_name, \ compile_proto, type_converter, bytesuri_to_hash, get_file_from_filecoin, download_and_safe_extract_proto @@ -286,7 +286,7 @@ def _open_channel_for_org(self, metadata): "MultiPartyEscrow", "balances", [self.ident.address]) if mpe_cogs < self.args.amount: raise Exception( - "insufficient funds. You MPE balance is %s AGIX " % cogs2stragix(mpe_cogs)) + "insufficient funds. You MPE balance is %s ASI(FET) " % cogs2strtoken(mpe_cogs)) group_id = base64.b64decode(metadata.get_group_id_by_group_name(self.args.group_name)) if not group_id: @@ -496,7 +496,7 @@ def _print_channels(self, channels, filters: list[str] = None): for channel_id in channels_ids: channel = self._get_channel_state_from_blockchain(channel_id) channel["channel_id"] = channel_id - channel["value"] = cogs2stragix(channel["value"]) + channel["value"] = cogs2strtoken(channel["value"]) channel["group_id"] = base64.b64encode(channel["groupId"]).decode("ascii") self._printout(self._convert_channel_dict_to_str(channel, filters)) diff --git a/snet/cli/commands/mpe_client.py b/snet/cli/commands/mpe_client.py index 49bb90ad..760cfff4 100644 --- a/snet/cli/commands/mpe_client.py +++ b/snet/cli/commands/mpe_client.py @@ -6,7 +6,7 @@ from eth_account.messages import encode_defunct from snet.cli.commands.mpe_channel import MPEChannelCommand -from snet.cli.utils.agix2cogs import cogs2stragix +from snet.cli.utils.token2cogs import cogs2strtoken from snet.cli.utils.proto_utils import import_protobuf_from_dir, switch_to_json_payload_encoding from snet.cli.utils.utils import open_grpc_channel, rgetattr, RESOURCES_PATH @@ -303,7 +303,7 @@ def call_server_statelessly_with_params(self, params, group_name): server_state = self._get_channel_state_from_server(grpc_channel, channel_id) proceed = self.args.yes or input( - "Price for this call will be %s AGIX (use -y to remove this warning). Proceed? (y/n): " % (cogs2stragix(price))) == "y" + "Price for this call will be %s ASI(FET) (use -y to remove this warning). Proceed? (y/n): " % (cogs2strtoken(price))) == "y" if not proceed: self._error("Cancelled") diff --git a/snet/cli/commands/mpe_treasurer.py b/snet/cli/commands/mpe_treasurer.py index a0855d46..57c9c298 100644 --- a/snet/cli/commands/mpe_treasurer.py +++ b/snet/cli/commands/mpe_treasurer.py @@ -4,7 +4,7 @@ from snet.cli.utils.proto_utils import import_protobuf_from_dir from snet.cli.utils.utils import compile_proto, open_grpc_channel, int4bytes_big, RESOURCES_PATH from snet.cli.commands.mpe_client import MPEClientCommand -from snet.cli.utils.agix2cogs import cogs2stragix +from snet.cli.utils.token2cogs import cogs2strtoken class MPETreasurerCommand(MPEClientCommand): @@ -93,13 +93,13 @@ def _call_StartClaim(self, grpc_channel, channel_id, channel_nonce): def print_unclaimed(self): grpc_channel = open_grpc_channel(self.args.endpoint) payments = self._call_GetListUnclaimed(grpc_channel) - self._printout("# channel_id channel_nonce signed_amount (AGIX)") + self._printout("# channel_id channel_nonce signed_amount (ASI(FET))") total = 0 for p in payments: self._printout("%i %i %s" % ( - p["channel_id"], p["nonce"], cogs2stragix(p["amount"]))) + p["channel_id"], p["nonce"], cogs2strtoken(p["amount"]))) total += p["amount"] - self._printout("# total_unclaimed_in_AGIX = %s" % cogs2stragix(total)) + self._printout("# total_unclaimed_in_AGIX = %s" % cogs2strtoken(total)) def _blockchain_claim(self, payments): for payment in payments: diff --git a/snet/cli/config.py b/snet/cli/config.py index 9c62382c..7747fc36 100644 --- a/snet/cli/config.py +++ b/snet/cli/config.py @@ -289,8 +289,8 @@ def first_identity_message_and_exit(is_sdk=False): " - 'key' (uses a required hex-encoded private key for signing)\n" " - 'ledger' (yields to a required ledger nano s device for signing using a given wallet\n" " index)\n" - " - 'trezor' (yields to a required trezor device for signing using a given wallet index)\n" "\n") + # " - 'trezor' (yields to a required trezor device for signing using a given wallet index)\n" exit(1) diff --git a/snet/cli/identity.py b/snet/cli/identity.py index 0eb4cd4e..67ba62f8 100644 --- a/snet/cli/identity.py +++ b/snet/cli/identity.py @@ -325,8 +325,8 @@ def get_kws_for_identity_type(identity_type): return [("mnemonic", SECRET)] elif identity_type == "key": return [("private_key", SECRET)] - elif identity_type == "trezor": - return [] + # elif identity_type == "trezor": + # return [] elif identity_type == "ledger": return [] elif identity_type == "keystore": @@ -337,7 +337,8 @@ def get_kws_for_identity_type(identity_type): def get_identity_types(): - return ["rpc", "mnemonic", "key", "trezor", "ledger", "keystore"] + # temporary fully removed: trezor + return ["rpc", "mnemonic", "key", "ledger", "keystore"] def sign_transaction_with_private_key(w3, private_key, transaction): diff --git a/snet/cli/metadata/service.py b/snet/cli/metadata/service.py index 6184afd9..39bd7754 100644 --- a/snet/cli/metadata/service.py +++ b/snet/cli/metadata/service.py @@ -21,7 +21,7 @@ Possible pricing models: 1. Fixed price price_model - "fixed_price" - price_in_cogs - unique fixed price in cogs for all method (1 AGIX = 10^8 cogs) + price_in_cogs - unique fixed price in cogs for all method (1 ASI(FET) = 10^18 cogs) (other pricing models can be easily supported) groups [] - group is the number of endpoints which shares same payment channel; grouping strategy is defined by service provider; diff --git a/snet/cli/resources/service_schema.json b/snet/cli/resources/service_schema.json index 8ab85338..a477d141 100644 --- a/snet/cli/resources/service_schema.json +++ b/snet/cli/resources/service_schema.json @@ -56,7 +56,7 @@ "enum": [ "fixed_price", "method_price" ] }, "price_in_cogs": { - "description": "Price in AGIX tokens for all methods", + "description": "Price in ASI(FET) tokens for all methods", "type": "number" }, "default": { diff --git a/snet/cli/utils/agix2cogs.py b/snet/cli/utils/agix2cogs.py deleted file mode 100644 index 382750fe..00000000 --- a/snet/cli/utils/agix2cogs.py +++ /dev/null @@ -1,24 +0,0 @@ -""" Safe conversion between agix(string) and cogs(int) """ -import decimal - -AGIX_TOKEN_DECIMALS = 8 - - -def stragix2cogs(stragix): - if type(stragix) != str: - raise Exception("Parameter should be string") - - # in case user write something stupid we set very big precision - decimal.getcontext().prec = 1000 - cogs_decimal = decimal.Decimal(stragix) * 10 ** AGIX_TOKEN_DECIMALS - cogs_int = int(cogs_decimal) - if cogs_int != cogs_decimal: - raise Exception("AGIX token has only %i decimals" % AGIX_TOKEN_DECIMALS) - return cogs_int - - -def cogs2stragix(cogs_int): - # presicison should be higer then INITIAL_SUPPLY + 1, we set it to 1000 be consistent with stragix2cogs - decimal.getcontext().prec = 1000 - agix_decimal = decimal.Decimal(cogs_int) / 10 ** AGIX_TOKEN_DECIMALS - return format(agix_decimal, 'f') diff --git a/snet/cli/utils/token2cogs.py b/snet/cli/utils/token2cogs.py new file mode 100644 index 00000000..d72b5efd --- /dev/null +++ b/snet/cli/utils/token2cogs.py @@ -0,0 +1,24 @@ +""" Safe conversion between token(string) and cogs(int) """ +import decimal + +TOKEN_DECIMALS = 18 + + +def strtoken2cogs(str_token): + if type(str_token) != str: + raise Exception("Parameter should be string") + + # in case user write something stupid we set very big precision + decimal.getcontext().prec = 1000 + cogs_decimal = decimal.Decimal(str_token) * 10 ** TOKEN_DECIMALS + cogs_int = int(cogs_decimal) + if cogs_int != cogs_decimal: + raise Exception("ASI(FET) token has only %i decimals" % TOKEN_DECIMALS) + return cogs_int + + +def cogs2strtoken(cogs_int): + # presicison should be higer then INITIAL_SUPPLY + 1, we set it to 1000 be consistent with strtoken2cogs + decimal.getcontext().prec = 1000 + token_decimal = decimal.Decimal(cogs_int) / 10 ** TOKEN_DECIMALS + return format(token_decimal, 'f') diff --git a/version.py b/version.py index 54499df3..528787cf 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -__version__ = "2.4.1" +__version__ = "3.0.0" From 838dbbf7855a22516cb5d9c6b78d3ed01abe7ca2 Mon Sep 17 00:00:00 2001 From: kiruxaspb Date: Fri, 28 Feb 2025 19:30:30 +0300 Subject: [PATCH 03/10] Fix markdown --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index a5330f73..48d5c198 100644 --- a/README.md +++ b/README.md @@ -122,9 +122,7 @@ Backward compatibility for other Python versions is not guaranteed. --- -* - -* Install the package in development mode +## Install the package in development mode ### Clone the git repository From f07d14660978a87ba24a1f0e95a5e6e2a929001d Mon Sep 17 00:00:00 2001 From: kiruxaspb Date: Mon, 3 Mar 2025 19:45:08 +0300 Subject: [PATCH 04/10] change agix to asi(fet) --- snet/cli/commands/mpe_treasurer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snet/cli/commands/mpe_treasurer.py b/snet/cli/commands/mpe_treasurer.py index 57c9c298..4043c269 100644 --- a/snet/cli/commands/mpe_treasurer.py +++ b/snet/cli/commands/mpe_treasurer.py @@ -99,7 +99,7 @@ def print_unclaimed(self): self._printout("%i %i %s" % ( p["channel_id"], p["nonce"], cogs2strtoken(p["amount"]))) total += p["amount"] - self._printout("# total_unclaimed_in_AGIX = %s" % cogs2strtoken(total)) + self._printout("# total_unclaimed_in_ASI(FET) = %s" % cogs2strtoken(total)) def _blockchain_claim(self, payments): for payment in payments: From 80e72137c27a0bf42e7604976e868cb291a1c6ad Mon Sep 17 00:00:00 2001 From: Arondondon Date: Tue, 4 Mar 2025 12:58:07 +0300 Subject: [PATCH 05/10] Changed archive with .proto files from .tar to .tar.gz --- snet/cli/utils/ipfs_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snet/cli/utils/ipfs_utils.py b/snet/cli/utils/ipfs_utils.py index f627075d..49fccfe5 100644 --- a/snet/cli/utils/ipfs_utils.py +++ b/snet/cli/utils/ipfs_utils.py @@ -54,7 +54,7 @@ def publish_proto_in_ipfs(ipfs_client, protodir): files.sort() tarbytes = io.BytesIO() - tar = tarfile.open(fileobj=tarbytes, mode="w") + tar = tarfile.open(fileobj=tarbytes, mode="w:gz") for f in files: tar.add(f, os.path.basename(f)) tar.close() @@ -79,7 +79,7 @@ def publish_proto_in_filecoin(filecoin_client, protodir): tarbytes = io.BytesIO() - with tarfile.open(fileobj=tarbytes, mode="w") as tar: + with tarfile.open(fileobj=tarbytes, mode="w:gz") as tar: for f in files: tar.add(f, os.path.basename(f)) tarbytes.seek(0) From d926cac7b46635bdd1895b1e46f0fda5b3dcc1ab Mon Sep 17 00:00:00 2001 From: Arondondon Date: Tue, 4 Mar 2025 15:47:57 +0300 Subject: [PATCH 06/10] Fixed generating service stubs with training import. --- snet/cli/commands/mpe_channel.py | 6 +- snet/cli/resources/proto/training.proto | 182 ++++++++++-------- .../resources/proto/training/training.proto | 128 ++++++++++++ snet/cli/utils/utils.py | 23 ++- 4 files changed, 252 insertions(+), 87 deletions(-) create mode 100644 snet/cli/resources/proto/training/training.proto diff --git a/snet/cli/commands/mpe_channel.py b/snet/cli/commands/mpe_channel.py index 62fdad3f..063dc039 100644 --- a/snet/cli/commands/mpe_channel.py +++ b/snet/cli/commands/mpe_channel.py @@ -17,7 +17,8 @@ from snet.cli.utils.token2cogs import cogs2strtoken from snet.cli.utils.ipfs_utils import get_from_ipfs_and_checkhash from snet.cli.utils.utils import abi_decode_struct_to_dict, abi_get_element_by_name, \ - compile_proto, type_converter, bytesuri_to_hash, get_file_from_filecoin, download_and_safe_extract_proto + compile_proto, type_converter, bytesuri_to_hash, get_file_from_filecoin, download_and_safe_extract_proto, \ + check_training_in_proto # we inherit MPEServiceCommand because we need _get_service_metadata_from_registry @@ -602,9 +603,10 @@ def _init_or_update_service_if_needed(self, metadata, service_registration): os.makedirs(spec_dir, mode=0o700) service_api_source = metadata.get("service_api_source") or metadata.get("model_ipfs_hash") download_and_safe_extract_proto(service_api_source, spec_dir, self._get_ipfs_client()) + training_added = check_training_in_proto(spec_dir) # compile .proto files - if not compile_proto(Path(spec_dir), service_dir): + if not compile_proto(Path(spec_dir), service_dir, add_training = training_added): raise Exception("Fail to compile %s/*.proto" % spec_dir) # save service_metadata.json in channel_dir diff --git a/snet/cli/resources/proto/training.proto b/snet/cli/resources/proto/training.proto index 86b3074c..8582c666 100644 --- a/snet/cli/resources/proto/training.proto +++ b/snet/cli/resources/proto/training.proto @@ -1,112 +1,128 @@ syntax = "proto3"; -import "google/protobuf/descriptor.proto"; +import "google/protobuf/descriptor.proto"; // Required for indicators to work package training; -option go_package = "../training"; -//Please note that the AI developers need to provide a server implementation of the gprc server of this proto. -message ModelDetails { - //This Id will be generated when you invoke the create_model method and hence doesnt need to be filled when you - //invoke the create model - string model_id = 1; - //define the training method name - string grpc_method_name = 2; - //define the grpc service name , under which the method is defined - string grpc_service_name = 3; - string description = 4; +option go_package = "github.com/singnet/snet-daemon/v5/training"; - string status = 6; - string updated_date = 7; - //List of all the addresses that will have access to this model - repeated string address_list = 8; - // this is optional - string training_data_link = 9; - string model_name = 10; +// Methods that the service provider must implement +service Model { + // Free + // Can pass the address of the model creator + rpc create_model(NewModel) returns (ModelID) {} - string organization_id = 11; - string service_id = 12 ; - string group_id = 13; + // Free + rpc validate_model_price(ValidateRequest) returns (PriceInBaseUnit) {} - //set this to true if you want your model to be used by other AI consumers - bool is_publicly_accessible = 14; + // Paid + rpc upload_and_validate(stream UploadInput) returns (StatusResponse) {} -} + // Paid + rpc validate_model(ValidateRequest) returns (StatusResponse) {} -message AuthorizationDetails { - uint64 current_block = 1; - //Signer can fill in any message here - string message = 2; - //signature of the following message: - //("user specified message", user_address, current_block_number) - bytes signature = 3; - string signer_address = 4; + // Free, one signature for both train_model_price & train_model methods + rpc train_model_price(ModelID) returns (PriceInBaseUnit) {} -} + // Paid + rpc train_model(ModelID) returns (StatusResponse) {} -enum Status { - CREATED = 0; - IN_PROGRESS = 1; - ERRORED = 2; - COMPLETED = 3; - DELETED = 4; -} + // Free + rpc delete_model(ModelID) returns (StatusResponse) { + // After model deletion, the status becomes DELETED in etcd + } -message CreateModelRequest { - AuthorizationDetails authorization = 1; - ModelDetails model_details = 2; + // Free + rpc get_model_status(ModelID) returns (StatusResponse) {} } -//the signer address will get to know all the models associated with this address. -message AccessibleModelsRequest { - string grpc_method_name = 1; - string grpc_service_name = 2; - AuthorizationDetails authorization = 3; -} +message ModelResponse { + string model_id = 1; + Status status = 2; + string created_date = 3; + string updated_date = 4; + string name = 5; + string description = 6; + string grpc_method_name = 7; + string grpc_service_name = 8; -message AccessibleModelsResponse { - repeated ModelDetails list_of_models = 1; -} + // List of all addresses that will have access to this model + repeated string address_list = 9; + + // Access to the model is granted only for use and viewing + bool is_public = 10; + + string training_data_link = 11; -message ModelDetailsRequest { - ModelDetails model_details = 1 ; - AuthorizationDetails authorization = 2; + string created_by_address = 12; + string updated_by_address = 13; } -//helps determine which service end point to call for model training -//format is of type "packageName/serviceName/MethodName", Example :"/example_service.Calculator/estimate_add" -//Daemon will invoke the model training end point , when the below method option is specified -message TrainingMethodOption { - string trainingMethodIndicator = 1; +// Used as input for new_model requests +// The service provider decides whether to use these fields; returning model_id is mandatory +message NewModel { + string name = 1; + string description = 2; + string grpc_method_name = 3; + string grpc_service_name = 4; + + // List of all addresses that will have access to this model + repeated string address_list = 5; + + // Set this to true if you want your model to be accessible by other AI consumers + bool is_public = 6; + + // These parameters will be passed by the daemon + string organization_id = 7; + string service_id = 8; + string group_id = 9; } -extend google.protobuf.MethodOptions { - TrainingMethodOption my_method_option = 9999197; +// This structure must be used by the service provider +message ModelID { + string model_id = 1; } -message UpdateModelRequest { - ModelDetails update_model_details = 1 ; - AuthorizationDetails authorization = 2; +// This structure must be used by the service provider +// Used in the train_model_price method to get the training/validation price +message PriceInBaseUnit { + uint64 price = 1; // cogs, weis, afet, aasi, etc. } +enum Status { + CREATED = 0; + VALIDATING = 1; + VALIDATED = 2; + TRAINING = 3; + READY_TO_USE = 4; // After training is completed + ERRORED = 5; + DELETED = 6; +} -message ModelDetailsResponse { +message StatusResponse { Status status = 1; - ModelDetails model_details = 2; - } -service Model { - - // The AI developer needs to Implement this service and Daemon will call these - // There will be no cost borne by the consumer in calling these methods, - // Pricing will apply when you actually call the training methods defined. - // AI consumer will call all these methods - rpc create_model(CreateModelRequest) returns (ModelDetailsResponse) {} - rpc delete_model(UpdateModelRequest) returns (ModelDetailsResponse) {} - rpc get_model_status(ModelDetailsRequest) returns (ModelDetailsResponse) {} - - // Daemon will implement , however the AI developer should skip implementing these and just provide dummy code. - rpc update_model_access(UpdateModelRequest) returns (ModelDetailsResponse) {} - rpc get_all_models(AccessibleModelsRequest) returns (AccessibleModelsResponse) {} +message UploadInput { + string model_id = 1; + bytes data = 2; + string file_name = 3; + uint64 file_size = 4; // in bytes + uint64 batch_size = 5; + uint64 batch_number = 6; + uint64 batch_count = 7; +} +message ValidateRequest { + string model_id = 2; + string training_data_link = 3; +} -} \ No newline at end of file +extend google.protobuf.MethodOptions { + string default_model_id = 50001; + uint64 max_models_per_user = 50002; // max models per method & user + uint64 dataset_max_size_mb = 50003; // max size of dataset + uint64 dataset_max_count_files = 50004; // maximum number of files in the dataset + uint64 dataset_max_size_single_file_mb = 50005; // maximum size of a single file in the dataset + string dataset_files_type = 50006; // allowed files types in dataset. string with array or single value, example: jpg, png, mp3 + string dataset_type = 50007; // string with array or single value, example: zip, tar.gz, tar + string dataset_description = 50008; // additional free-form requirements +} diff --git a/snet/cli/resources/proto/training/training.proto b/snet/cli/resources/proto/training/training.proto new file mode 100644 index 00000000..8582c666 --- /dev/null +++ b/snet/cli/resources/proto/training/training.proto @@ -0,0 +1,128 @@ +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; // Required for indicators to work +package training; +option go_package = "github.com/singnet/snet-daemon/v5/training"; + +// Methods that the service provider must implement +service Model { + + // Free + // Can pass the address of the model creator + rpc create_model(NewModel) returns (ModelID) {} + + // Free + rpc validate_model_price(ValidateRequest) returns (PriceInBaseUnit) {} + + // Paid + rpc upload_and_validate(stream UploadInput) returns (StatusResponse) {} + + // Paid + rpc validate_model(ValidateRequest) returns (StatusResponse) {} + + // Free, one signature for both train_model_price & train_model methods + rpc train_model_price(ModelID) returns (PriceInBaseUnit) {} + + // Paid + rpc train_model(ModelID) returns (StatusResponse) {} + + // Free + rpc delete_model(ModelID) returns (StatusResponse) { + // After model deletion, the status becomes DELETED in etcd + } + + // Free + rpc get_model_status(ModelID) returns (StatusResponse) {} +} + +message ModelResponse { + string model_id = 1; + Status status = 2; + string created_date = 3; + string updated_date = 4; + string name = 5; + string description = 6; + string grpc_method_name = 7; + string grpc_service_name = 8; + + // List of all addresses that will have access to this model + repeated string address_list = 9; + + // Access to the model is granted only for use and viewing + bool is_public = 10; + + string training_data_link = 11; + + string created_by_address = 12; + string updated_by_address = 13; +} + +// Used as input for new_model requests +// The service provider decides whether to use these fields; returning model_id is mandatory +message NewModel { + string name = 1; + string description = 2; + string grpc_method_name = 3; + string grpc_service_name = 4; + + // List of all addresses that will have access to this model + repeated string address_list = 5; + + // Set this to true if you want your model to be accessible by other AI consumers + bool is_public = 6; + + // These parameters will be passed by the daemon + string organization_id = 7; + string service_id = 8; + string group_id = 9; +} + +// This structure must be used by the service provider +message ModelID { + string model_id = 1; +} + +// This structure must be used by the service provider +// Used in the train_model_price method to get the training/validation price +message PriceInBaseUnit { + uint64 price = 1; // cogs, weis, afet, aasi, etc. +} + +enum Status { + CREATED = 0; + VALIDATING = 1; + VALIDATED = 2; + TRAINING = 3; + READY_TO_USE = 4; // After training is completed + ERRORED = 5; + DELETED = 6; +} + +message StatusResponse { + Status status = 1; +} + +message UploadInput { + string model_id = 1; + bytes data = 2; + string file_name = 3; + uint64 file_size = 4; // in bytes + uint64 batch_size = 5; + uint64 batch_number = 6; + uint64 batch_count = 7; +} + +message ValidateRequest { + string model_id = 2; + string training_data_link = 3; +} + +extend google.protobuf.MethodOptions { + string default_model_id = 50001; + uint64 max_models_per_user = 50002; // max models per method & user + uint64 dataset_max_size_mb = 50003; // max size of dataset + uint64 dataset_max_count_files = 50004; // maximum number of files in the dataset + uint64 dataset_max_size_single_file_mb = 50005; // maximum size of a single file in the dataset + string dataset_files_type = 50006; // allowed files types in dataset. string with array or single value, example: jpg, png, mp3 + string dataset_type = 50007; // string with array or single value, example: zip, tar.gz, tar + string dataset_description = 50008; // additional free-form requirements +} diff --git a/snet/cli/utils/utils.py b/snet/cli/utils/utils.py index b2b38479..0efc8d20 100644 --- a/snet/cli/utils/utils.py +++ b/snet/cli/utils/utils.py @@ -146,7 +146,7 @@ def get_cli_version(): return distribution("snet.cli").version -def compile_proto(entry_path, codegen_dir, proto_file=None): +def compile_proto(entry_path, codegen_dir, proto_file=None, add_training=False): try: if not os.path.exists(codegen_dir): os.makedirs(codegen_dir) @@ -157,6 +157,10 @@ def compile_proto(entry_path, codegen_dir, proto_file=None): "-I{}".format(proto_include) ] + if add_training: + training_include = RESOURCES_PATH.joinpath("proto", "training") + compiler_args.append("-I{}".format(training_include)) + compiler_args.insert(0, "protoc") compiler_args.append("--python_out={}".format(codegen_dir)) compiler_args.append("--grpc_python_out={}".format(codegen_dir)) @@ -167,6 +171,9 @@ def compile_proto(entry_path, codegen_dir, proto_file=None): else: compiler_args.extend([str(p) for p in entry_path.glob("**/*.proto")]) + if add_training: + compiler_args.append(str(training_include.joinpath("training.proto"))) + if not compiler(compiler_args): return True else: @@ -346,4 +353,16 @@ def download_and_safe_extract_proto(service_api_source, protodir, ipfs_client): os.remove(fullname) print("%s removed." % fullname) # now it is safe to call extractall - f.extractall(protodir) \ No newline at end of file + f.extractall(protodir) + + +def check_training_in_proto(protodir) -> bool: + files = os.listdir(protodir) + for file in files: + if ".proto" not in file: + continue + with open(protodir.joinpath(file), "r") as f: + proto_text = f.read() + if 'import "training.proto";' in proto_text: + return True + return False \ No newline at end of file From a453fe5ba97e9278afccb2f78645d7dbe3f3c2b2 Mon Sep 17 00:00:00 2001 From: Arondondon Date: Tue, 4 Mar 2025 18:14:36 +0300 Subject: [PATCH 07/10] Fixed generate-client-library command --- snet/cli/commands/sdk_command.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/snet/cli/commands/sdk_command.py b/snet/cli/commands/sdk_command.py index fae953ac..0f8b05d0 100644 --- a/snet/cli/commands/sdk_command.py +++ b/snet/cli/commands/sdk_command.py @@ -1,7 +1,7 @@ import os from pathlib import Path, PurePath -from snet.cli.utils.utils import compile_proto, download_and_safe_extract_proto +from snet.cli.utils.utils import compile_proto, download_and_safe_extract_proto, check_training_in_proto from snet.cli.commands.mpe_service import MPEServiceCommand @@ -28,8 +28,10 @@ def generate_client_library(self): # Receive proto files download_and_safe_extract_proto(service_api_source, library_dir_path, self._get_ipfs_client()) + training_added = check_training_in_proto(library_dir_path) + # Compile proto files - compile_proto(Path(library_dir_path), library_dir_path) + compile_proto(Path(library_dir_path), library_dir_path, add_training = training_added) self._printout( 'client libraries for service with id "{}" in org with id "{}" generated at {}'.format(library_service_id, From 854787d33d44039d508b4e1ed7680c125808c7cd Mon Sep 17 00:00:00 2001 From: Arondondon Date: Tue, 4 Mar 2025 18:58:28 +0300 Subject: [PATCH 08/10] Fixed manifest --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 11fc35c8..dc34359b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include snet/cli/resources/* +recursive-include snet/cli/resources * recursive-exclude * .git recursive-exclude * node_modules From 6b56cb4418e43776368ff769af27adf5c5fd310f Mon Sep 17 00:00:00 2001 From: Arondondon Date: Thu, 6 Mar 2025 15:59:53 +0300 Subject: [PATCH 09/10] Fixed duplicating channels in the cache --- snet/cli/commands/mpe_channel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snet/cli/commands/mpe_channel.py b/snet/cli/commands/mpe_channel.py index 063dc039..e2d5be02 100644 --- a/snet/cli/commands/mpe_channel.py +++ b/snet/cli/commands/mpe_channel.py @@ -38,7 +38,7 @@ def _get_channels_cache_file(self): def _update_channels_cache(self): channels = [] - last_read_block = get_contract_deployment_block(self.ident.w3, "MultiPartyEscrow") + last_read_block = get_contract_deployment_block(self.ident.w3, "MultiPartyEscrow") - 1 channels_file = self._get_channels_cache_file() if not channels_file.exists(): @@ -59,7 +59,7 @@ def _update_channels_cache(self): current_block_number = self.ident.w3.eth.block_number if last_read_block < current_block_number: - new_channels = self._get_all_opened_channels_from_blockchain(last_read_block, current_block_number) + new_channels = self._get_all_opened_channels_from_blockchain(last_read_block + 1, current_block_number) channels = channels + new_channels last_read_block = current_block_number From 50ee3125021f7e903844177622cbf47661953213 Mon Sep 17 00:00:00 2001 From: kiruxaspb Date: Mon, 5 May 2025 17:56:38 +0300 Subject: [PATCH 10/10] update package version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 075396ef..36cb230a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,6 +18,6 @@ jsonschema==4.0.0 eth-account==0.9.0 trezor==0.13.8 ledgerblue==0.1.48 -snet-contracts==0.2.2 +snet-contracts==1.0.0 lighthouseweb3==0.1.4 cryptography==44.0.1