diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..daa53e37 --- /dev/null +++ b/.clang-format @@ -0,0 +1,26 @@ +--- +Language: Cpp +BasedOnStyle: Google +AlignOperands: true +BreakStringLiterals: true +ColumnLimit: 80 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +FixNamespaceComments: true +IndentCaseLabels: true +IndentWidth: 4 +MaxEmptyLinesToKeep: 1 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: true +UseTab: Never +--- +Language: Proto +BasedOnStyle: LLVM \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index bf920b8a..e61f9d5d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,12 +1,19 @@ { "python.linting.pylintEnabled": false, - "python.linting.pylamaEnabled": true, + "python.linting.pylamaEnabled": false, "python.linting.enabled": true, "files.associations": { - "*.h": "c", - ".clang-tidy": "yaml", - "string_view": "c", - "regex": "c" + "*.h": "c", + ".clang-tidy": "yaml", + "string_view": "c", + "regex": "c", + "system_error": "c", + "array": "c", + "functional": "c", + "tuple": "c", + "type_traits": "c", + "utility": "c" }, "C_Cpp.dimInactiveRegions": false, + "python.linting.mypyEnabled": true, } diff --git a/LICENSE b/LICENSE deleted file mode 100644 index ae3ac373..00000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - nanopb notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2019 Hedera Hashgraph LLC - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/Makefile b/Makefile index 390fe555..22e7c050 100755 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ DFEFINES += PB_FIELD_32BIT=1 # vendor/printf DEFINES += PRINTF_DISABLE_SUPPORT_FLOAT PRINTF_DISABLE_SUPPORT_EXPONENTIAL PRINTF_DISABLE_SUPPORT_PTRDIFF_T -DEFINES += PRINTF_NTOA_BUFFER_SIZE=9U PRINTF_FTOA_BUFFER_SIZE=0 +DEFINES += PRINTF_FTOA_BUFFER_SIZE=0 # endif # U2F @@ -160,39 +160,24 @@ DEFINES += PB_NO_ERRMSG=1 SOURCE_FILES += $(NANOPB_CORE) CFLAGS += "-I$(NANOPB_DIR)" -# Build rule for proto files -SOURCE_FILES += proto/BasicTypes.pb.c -SOURCE_FILES += proto/Wrappers.pb.c -SOURCE_FILES += proto/CryptoCreate.pb.c -SOURCE_FILES += proto/Transfer.pb.c -SOURCE_FILES += proto/TransactionBody.pb.c -SOURCE_FILES += proto/TokenAssociate.pb.c -SOURCE_FILES += proto/TokenMint.pb.c -SOURCE_FILES += proto/TokenBurn.pb.c +PB_FILES = $(wildcard proto/*.proto) +C_PB_FILES = $(patsubst %.proto,%.pb.c,$(PB_FILES)) +PYTHON_PB_FILES = $(patsubst %.proto,%_pb2.py,$(PB_FILES)) -proto/BasicTypes.pb.c: proto/BasicTypes.proto - $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. proto/BasicTypes.proto +# Build rule for C proto files +SOURCE_FILES += $(C_PB_FILES) +$(C_PB_FILES): %.pb.c: $(PB_FILES) + $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. $*.proto -proto/Wrappers.pb.c: proto/Wrappers.proto - $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. proto/Wrappers.proto +# Build rule for Python proto files +$(PYTHON_PB_FILES): %_pb2.py: $(PB_FILES) + $(PROTOC) $(PROTOC_OPTS) --python_out=. --mypy_out=. $*.proto -proto/CryptoCreate.pb.c: proto/BasicTypes.proto - $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. proto/CryptoCreate.proto - -proto/Transfer.pb.c: proto/BasicTypes.proto - $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. proto/Transfer.proto - -proto/TransactionBody.pb.c: proto/BasicTypes.proto - $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. proto/TransactionBody.proto - -proto/TokenAssociate.pb.c: proto/BasicTypes.proto - $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. proto/TokenAssociate.proto - -proto/TokenMint.pb.c: proto/BasicTypes.proto - $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. proto/TokenMint.proto - -proto/TokenBurn.pb.c: proto/BasicTypes.proto - $(PROTOC) $(PROTOC_OPTS) --nanopb_out=. proto/TokenBurn.proto +.PHONY: python_pb clean_python_pb +c_pb: $(C_PB_FILES) +python_pb: $(PYTHON_PB_FILES) +clean_python_pb: + rm -f $(PYTHON_PB_FILES) # target to also clean generated proto c files .SILENT : cleanall diff --git a/README.md b/README.md deleted file mode 100644 index 647ef3c9..00000000 --- a/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Hedera Ledger App - -Hedera™ Hashgraph BOLOS application for Ledger Nano S and Nano X. - -## Development - -### Prerequisite - -- Docker - -### Compile - -``` -docker run -v $PWD:/app --platform linux/amd64 -it \ - ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest \ - make -``` - -### Check - -``` -docker run -v $PWD:/app --platform linux/amd64 -it \ - ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest \ - scan-build --use-cc=clang -analyze-headers -enable-checker security \ - -enable-checker unix -enable-checker valist -o scan-build \ - --status-bugs make default -``` diff --git a/proto/BasicTypes.proto b/proto/BasicTypes.proto deleted file mode 100644 index a691136d..00000000 --- a/proto/BasicTypes.proto +++ /dev/null @@ -1,43 +0,0 @@ -syntax = "proto3"; - -import "nanopb.proto"; - -message HederaKey { - oneof key { - bytes ed25519 = 2 [(nanopb).max_size = 32]; - } -} - -message HederaShardID { - uint64 shardNum = 1; -} - -message HederaRealmID { - uint64 shardNum = 1; - uint64 realmNum = 2; -} - -message HederaAccountID { - uint64 shardNum = 1; - uint64 realmNum = 2; - uint64 accountNum = 3; -} - -message HederaTokenID { - uint64 shardNum = 1; - uint64 realmNum = 2; - uint64 tokenNum = 3; -} - -message HederaTimestamp { - uint64 seconds = 1; - uint32 nanos = 2; -} - -message HederaDuration { - uint64 seconds = 1; -} - -message HederaTransactionID { - HederaAccountID accountID = 2; -} diff --git a/proto/CryptoCreate.proto b/proto/CryptoCreate.proto deleted file mode 100644 index da04ce64..00000000 --- a/proto/CryptoCreate.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax = "proto3"; - -message HederaCryptoCreateTransactionBody { - uint64 initialBalance = 2; -} diff --git a/proto/TokenAssociate.proto b/proto/TokenAssociate.proto deleted file mode 100644 index 4bfad268..00000000 --- a/proto/TokenAssociate.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto3"; - -import "nanopb.proto"; -import "proto/BasicTypes.proto"; - -message HederaTokenAssociateTransactionBody { - HederaAccountID account = 1; - - repeated HederaTokenID tokens = 2 [(nanopb).max_count = 1]; -} diff --git a/proto/TokenBurn.proto b/proto/TokenBurn.proto deleted file mode 100644 index 10f42aaf..00000000 --- a/proto/TokenBurn.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto3"; - -import "proto/Wrappers.proto"; -import "proto/BasicTypes.proto"; - -message HederaTokenBurnTransactionBody { - HederaTokenID token = 1; - - uint64 amount = 2; - - // TODO(nft): repeated int64 serialNumbers = 3; - - UInt32Value expected_decimals = 4; -} diff --git a/proto/TokenMint.proto b/proto/TokenMint.proto deleted file mode 100644 index 2638adee..00000000 --- a/proto/TokenMint.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto3"; - -import "proto/Wrappers.proto"; -import "proto/BasicTypes.proto"; - -message HederaTokenMintTransactionBody { - HederaTokenID token = 1; - - uint64 amount = 2; - - // TODO(nft): repeated bytes metadata = 3; - - UInt32Value expected_decimals = 4; -} diff --git a/proto/TransactionBody.proto b/proto/TransactionBody.proto deleted file mode 100644 index 3fa24cf2..00000000 --- a/proto/TransactionBody.proto +++ /dev/null @@ -1,23 +0,0 @@ -syntax = "proto3"; - -import "nanopb.proto"; -import "proto/BasicTypes.proto"; -import "proto/CryptoCreate.proto"; -import "proto/Transfer.proto"; -import "proto/TokenAssociate.proto"; -import "proto/TokenMint.proto"; -import "proto/TokenBurn.proto"; - -message HederaTransactionBody { - HederaTransactionID transactionID = 1; - uint64 transactionFee = 3; - string memo = 6 [(nanopb).max_size = 100]; - - oneof data { - HederaCryptoCreateTransactionBody cryptoCreateAccount = 11; - HederaCryptoTransferTransactionBody cryptoTransfer = 14; - HederaTokenAssociateTransactionBody tokenAssociate = 40; - HederaTokenMintTransactionBody tokenMint = 37; - HederaTokenBurnTransactionBody tokenBurn = 38; - } -} diff --git a/proto/Transfer.proto b/proto/Transfer.proto deleted file mode 100644 index 783ae912..00000000 --- a/proto/Transfer.proto +++ /dev/null @@ -1,32 +0,0 @@ -syntax = "proto3"; - -import "nanopb.proto"; -import "proto/Wrappers.proto"; -import "proto/BasicTypes.proto"; - -message HederaAccountAmount { - HederaAccountID accountID = 1; - sint64 amount = 2; -} - -message HederaTransferList { - repeated HederaAccountAmount accountAmounts = 1 [(nanopb).max_count = 2]; -} - -message HederaTokenTransferList { - HederaTokenID token = 1; - - repeated HederaAccountAmount transfers = 2 [(nanopb).max_count = 2]; - - /** - * If present, the number of decimals this fungible token type is expected to have. The transfer - * will fail with UNEXPECTED_TOKEN_DECIMALS if the actual decimals differ. - */ - UInt32Value expected_decimals = 4; -} - -message HederaCryptoTransferTransactionBody { - HederaTransferList transfers = 1; - - repeated HederaTokenTransferList tokenTransfers = 2 [(nanopb).max_count = 1]; -} diff --git a/proto/Wrappers.proto b/proto/Wrappers.proto deleted file mode 100644 index 0fe25353..00000000 --- a/proto/Wrappers.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax = "proto3"; - -message UInt32Value { - uint32 value = 1; -} diff --git a/proto/basic_types.proto b/proto/basic_types.proto new file mode 100644 index 00000000..6aec15af --- /dev/null +++ b/proto/basic_types.proto @@ -0,0 +1,594 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2022 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; +import "proto/timestamp.proto"; +import "proto/wrappers.proto"; + +/** + * Each shard has a nonnegative shard number. Each realm within a given shard + * has a nonnegative realm number (that number might be reused in other + * shards). And each account, file, and smart contract instance within a given + * realm has a nonnegative number (which might be reused in other realms). + * Every account, file, and smart contract instance is within exactly one + * realm. So a FileID is a triplet of numbers, like 0.1.2 for entity number 2 + * within realm 1 within shard 0. Each realm maintains a single counter for + * assigning numbers, so if there is a file with ID 0.1.2, then there won't be + * an account or smart contract instance with ID 0.1.2. + * + * Everything is partitioned into realms so that each Solidity smart contract + * can access everything in just a single realm, locking all those entities + * while it's running, but other smart contracts could potentially run in + * other realms in parallel. So realms allow Solidity to be parallelized + * somewhat, even though the language itself assumes everything is serial. + */ +message ShardID { + /** + * the shard number (nonnegative) + */ + int64 shardNum = 1; +} + +/** + * The ID for a realm. Within a given shard, every realm has a unique ID. Each + * account, file, and contract instance belongs to exactly one realm. + */ +message RealmID { + /** + * The shard number (nonnegative) + */ + int64 shardNum = 1; + + /** + * The realm number (nonnegative) + */ + int64 realmNum = 2; +} + +/** + * The ID for an a cryptocurrency account + */ +message AccountID { + /** + * The shard number (nonnegative) + */ + int64 shardNum = 1; + + /** + * The realm number (nonnegative) + */ + int64 realmNum = 2; + + /** + * The account number unique within its realm which can be either a + * non-negative integer or an alias public key. For any AccountID fields in + * the query response, transaction record or transaction receipt only + * accountNum will be populated. + */ + oneof account { + /** + * A non-negative account number unique within its realm + */ + int64 accountNum = 3; + + /** + * The public key bytes to be used as the account's alias. The public key + * bytes are the result of serializing a protobuf Key message for any + * primitive key type. Currently only primitive key bytes are supported as + * an alias (ThresholdKey, KeyList, ContractID, and delegatable_contract_id + * are not supported) + * + * At most one account can ever have a given alias and it is used for + * account creation if it was automatically created using a crypto + * transfer. It will be null if an account is created normally. It is + * immutable once it is set for an account. + * + * If a transaction auto-creates the account, any further transfers to that + * alias will simply be deposited in that account, without creating + * anything, and with no creation fee being charged. + */ + bytes alias = 4 [ (nanopb).max_size = 32 ]; + } +} + +/** + * The ID for a file + */ +message FileID { + /** + * The shard number (nonnegative) + */ + int64 shardNum = 1; + + /** + * The realm number (nonnegative) + */ + int64 realmNum = 2; + + /** + * A nonnegative File number unique within its realm + */ + int64 fileNum = 3; +} + +/** + * The ID for a smart contract instance + */ +message ContractID { + /** + * The shard number (nonnegative) + */ + int64 shardNum = 1; + + /** + * The realm number (nonnegative) + */ + int64 realmNum = 2; + + oneof contract { + /** + * A nonnegative number unique within a given shard and realm + */ + int64 contractNum = 3; + + /** + * The 20-byte EVM address of the contract to call. + * + * Every contract has an EVM address determined by its + * shard.realm.num id. This address is as follows:
  1. The + * first 4 bytes are the big-endian representation of the shard.
  2. + *
  3. The next 8 bytes are the big-endian representation of the + * realm.
  4. The final 8 bytes are the big-endian representation of + * the number.
  5. + *
+ * + * Contracts created via CREATE2 have an additional, primary address + * that is derived from the EIP-1014 + * specification, and does not have a simple relation to a + * shard.realm.num id. + * + * (Please do note that CREATE2 contracts can also be referenced by the + * three-part EVM address described above.) + */ + bytes evm_address = 4 [ (nanopb).max_size = 20 ]; + } +} + +/** + * The ID for a transaction. This is used for retrieving receipts and records + * for a transaction, for appending to a file right after creating it, for + * instantiating a smart contract with bytecode in a file just created, and + * internally by the network for detecting when duplicate transactions are + * submitted. A user might get a transaction processed faster by submitting it + * to N nodes, each with a different node account, but all with the same + * TransactionID. Then, the transaction will take effect when the first of all + * those nodes submits the transaction and it reaches consensus. The other + * transactions will not take effect. So this could make the transaction take + * effect faster, if any given node might be slow. However, the full + * transaction fee is charged for each transaction, so the total fee is N times + * as much if the transaction is sent to N nodes. + * + * Applicable to Scheduled Transactions: + * - The ID of a Scheduled Transaction has transactionValidStart and + * accountIDs inherited from the ScheduleCreate transaction that created it. + * That is to say that they are equal + * - The scheduled property is true for Scheduled Transactions + * - transactionValidStart, accountID and scheduled properties should be + * omitted + */ +message TransactionID { + /** + * The transaction is invalid if consensusTimestamp < + * transactionID.transactionStartValid + */ + Timestamp transactionValidStart = 1; + + /** + * The Account ID that paid for this transaction + */ + AccountID accountID = 2; + + /** + * Whether the Transaction is of type Scheduled or no + */ + bool scheduled = 3; + + /** + * The identifier for an internal transaction that was spawned as part + * of handling a user transaction. (These internal transactions share the + * transactionValidStart and accountID of the user transaction, so a + * nonce is necessary to give them a unique TransactionID.) + * + * An example is when a "parent" ContractCreate or ContractCall transaction + * calls one or more HTS precompiled contracts; each of the "child" + * transactions spawned for a precompile has a id with a different nonce. + */ + int32 nonce = 4; +} + +/** + * An account, and the amount that it sends or receives during a cryptocurrency + * or token transfer. + */ +message AccountAmount { + /** + * The Account ID that sends/receives cryptocurrency or tokens + */ + AccountID accountID = 1; + + /** + * The amount of tinybars (for Crypto transfers) or in the lowest + * denomination (for Token transfers) that the account sends(negative) or + * receives(positive) + */ + sint64 amount = 2; + + /** + * If true then the transfer is expected to be an approved allowance and the + * accountID is expected to be the owner. The default is false (omitted). + */ + bool is_approval = 3; +} + +/** + * A list of accounts and amounts to transfer out of each account (negative) or + * into it (positive). + */ +message TransferList { + /** + * Multiple list of AccountAmount pairs, each of which has an account and + * an amount to transfer into it (positive) or out of it (negative) + * Limited to 2 for a transfer between two accounts + */ + repeated AccountAmount accountAmounts = 1 [ (nanopb).max_count = 2 ]; +} + +/** + * A sender account, a receiver account, and the serial number of an NFT of a + * Token with NON_FUNGIBLE_UNIQUE type. When minting NFTs the sender will be + * the default AccountID instance (0.0.0) and when burning NFTs, the receiver + * will be the default AccountID instance. + */ +message NftTransfer { + /** + * The accountID of the sender + */ + AccountID senderAccountID = 1; + + /** + * The accountID of the receiver + */ + AccountID receiverAccountID = 2; + + /** + * The serial number of the NFT + */ + int64 serialNumber = 3; + + /** + * If true then the transfer is expected to be an approved allowance and the + * senderAccountID is expected to be the owner. The default is false + * (omitted). + */ + bool is_approval = 4; +} + +/** + * A list of token IDs and amounts representing the transferred out (negative) + * or into (positive) amounts, represented in the lowest denomination of the + * token + */ +message TokenTransferList { + /** + * The ID of the token + */ + TokenID token = 1; + + /** + * Applicable to tokens of type FUNGIBLE_COMMON. Multiple list of + * AccountAmounts, each of which has an account and amount + * Limited to 2 for 1 allowed transfer (reciprocal subtraction of balance + + * actual transfer) + */ + repeated AccountAmount transfers = 2 [ (nanopb).max_count = 2 ]; + + /** + * Applicable to tokens of type NON_FUNGIBLE_UNIQUE. Multiple list of + * NftTransfers, each of which has a sender and receiver account, including + * the serial number of the NFT + * Limited to 1 here + */ + repeated NftTransfer nftTransfers = 3 [ (nanopb).max_count = 1 ]; + + /** + * If present, the number of decimals this fungible token type is expected to + * have. The transfer will fail with UNEXPECTED_TOKEN_DECIMALS if the actual + * decimals differ. + */ + UInt32Value expected_decimals = 4; +} + +/** + * A rational number, used to set the amount of a value transfer to collect as + * a custom fee + */ +message Fraction { + /** + * The rational's numerator + */ + int64 numerator = 1; + + /** + * The rational's denominator; a zero value will result in + * FRACTION_DIVIDES_BY_ZERO + */ + int64 denominator = 2; +} + +/** + * Unique identifier for a token + */ +message TokenID { + /** + * A nonnegative shard number + */ + int64 shardNum = 1; + + /** + * A nonnegative realm number + */ + int64 realmNum = 2; + + /** + * A nonnegative token number + */ + int64 tokenNum = 3; +} + +/** + * A Key can be a public key from either the Ed25519 or ECDSA(secp256k1) + * signature schemes, where in the ECDSA(secp256k1) case we require the 33-byte + * compressed form of the public key. We call these public keys primitive + * keys. + * + * If an account has primitive key associated to it, then the corresponding + * private key must sign any transaction to transfer cryptocurrency out of it. + * + * A Key can also be the ID of a smart contract instance, which is then + * authorized to perform any precompiled contract action that requires this key + * to sign. + * + * Note that when a Key is a smart contract ID, it doesn't mean the + * contract with that ID will actually create a cryptographic signature. It + * only means that when the contract calls a precompiled contract, the + * resulting "child transaction" will be authorized to perform any action + * controlled by the Key. + * + * A Key can be a "threshold key", which means a list of M keys, any N of which + * must sign in order for the threshold signature to be considered valid. The + * keys within a threshold signature may themselves be threshold signatures, to + * allow complex signature requirements. + * + * A Key can be a "key list" where all keys in the list must sign unless + * specified otherwise in the documentation for a specific transaction type + * (e.g. FileDeleteTransactionBody). Their use is dependent on context. For + * example, a Hedera file is created with a list of keys, where all of them + * must sign a transaction to create or modify the file, but only one of them + * is needed to sign a transaction to delete the file. So it's a single list + * that sometimes acts as a 1-of-M threshold key, and sometimes acts as an + * M-of-M threshold key. A key list is always an M-of-M, unless specified + * otherwise in documentation. A key list can have nested key lists or + * threshold keys. Nested key lists are always M-of-M. A key list can have + * repeated primitive public keys, but all repeated keys are only required to + * sign once. + * + * A Key can contain a ThresholdKey or KeyList, which in turn contain a Key, so + * this mutual recursion would allow nesting arbitrarily deep. A ThresholdKey + * which contains a list of primitive keys has 3 levels: ThresholdKey -> + * KeyList + * -> Key. A KeyList which contains several primitive keys has 2 levels: + * KeyList + * -> Key. A Key with 2 levels of nested ThresholdKeys has 7 levels: Key -> + * ThresholdKey -> KeyList -> Key -> ThresholdKey -> KeyList -> Key. + * + * Each Key should not have more than 46 levels, which implies 15 levels of + * nested ThresholdKeys. + */ +message Key { + oneof key { + /** + * smart contract instance that is authorized as if it had signed with a + * key + */ + ContractID contractID = 1; + + /** + * Ed25519 public key bytes + */ + bytes ed25519 = 2 [ (nanopb).max_size = 32 ]; + + /** + * (NOT SUPPORTED) RSA-3072 public key bytes + */ + bytes RSA_3072 = 3 [ (nanopb).max_size = 32 ]; + + /** + * (NOT SUPPORTED) ECDSA with the p-384 curve public key bytes + */ + bytes ECDSA_384 = 4 [ (nanopb).max_size = 32 ]; + + /** + * a threshold N followed by a list of M keys, any N of which are required + * to form a valid signature + * Leaving this in causes a circular dependecy + */ + // ThresholdKey thresholdKey = 5; + + /** + * A list of Keys of the Key type. + * Leaving this in causes a circular dependency + */ + // KeyList keyList = 6; + + /** + * Compressed ECDSA(secp256k1) public key bytes + */ + bytes ECDSA_secp256k1 = 7 [ (nanopb).max_size = 32 ]; + + /** + * A smart contract that, if the recipient of the active message frame, + * should be treated as having signed. (Note this does not mean the code + * being executed in the frame will belong to the given contract, since + * it could be running another contract's code via delegatecall. + * So setting this key is a more permissive version of setting the + * contractID key, which also requires the code in the active message frame + * belong to the the contract with the given id.) + */ + ContractID delegatable_contract_id = 8; + } +} + +/** + * A set of public keys that are used together to form a threshold signature. + * If the threshold is N and there are M keys, then this is an N of M threshold + * signature. If an account is associated with ThresholdKeys, then a + * transaction to move cryptocurrency out of it must be signed by a list of M + * signatures, where at most M-N of them are blank, and the other at least N of + * them are valid signatures corresponding to at least N of the public keys + * listed here. + */ +message ThresholdKey { + /** + * A valid signature set must have at least this many signatures + */ + uint32 threshold = 1; + + /** + * List of all the keys that can sign + */ + KeyList keys = 2; +} + +/** + * A list of keys that requires all keys (M-of-M) to sign unless otherwise + * specified in documentation. A KeyList may contain repeated keys, but all + * repeated keys are only required to sign once. + */ +message KeyList { + /** + * list of keys + * Limited to 1 here (because we don't have malloc!) + */ + repeated Key keys = 1 [ (nanopb).max_count = 1 ]; +} + +/** + * A number of transferable units of a certain token. + * + * The transferable unit of a token is its smallest denomination, as given by + * the token's decimals property---each minted token contains + * 10decimals transferable units. For example, we could + * think of the cent as the transferable unit of the US dollar + * (decimals=2); and the tinybar as the transferable unit of hbar + * (decimals=8). + * + * Transferable units are not directly comparable across different tokens. + */ +message TokenBalance { + /** + * A unique token id + */ + TokenID tokenId = 1; + + /** + * Number of transferable units of the identified token. For token of type + * FUNGIBLE_COMMON - balance in the smallest denomination. For token of type + * NON_FUNGIBLE_UNIQUE - the number of NFTs held by the account + */ + uint64 balance = 2; + + /** + * Tokens divide into 10decimals pieces + */ + uint32 decimals = 3; +} + +/** + * A sequence of token balances + * Limited to 1 here + */ +message TokenBalances { + repeated TokenBalance tokenBalances = 1 [ (nanopb).max_count = 1 ]; +} + +/* A token - account association */ +message TokenAssociation { + TokenID token_id = 1; // The token involved in the association + AccountID account_id = 2; // The account involved in the association +} + +/** + * Staking metadata for an account or a contract returned in CryptoGetInfo or + * ContractGetInfo queries + */ +message StakingInfo { + /** + * If true, this account or contract declined to receive a staking reward. + */ + bool decline_reward = 1; + + /** + * The staking period during which either the staking settings for this + * account or contract changed (such as starting staking or changing + * staked_node_id) or the most recent reward was earned, whichever is later. + * If this account or contract is not currently staked to a node, then this + * field is not set. + */ + Timestamp stake_period_start = 2; + + /** + * The amount in tinybars that will be received in the next reward situation. + */ + int64 pending_reward = 3; + + /** + * The total of balance of all accounts staked to this account or contract. + */ + int64 staked_to_me = 4; + + /** + * ID of the account or node to which this account or contract is staking. + */ + oneof staked_id { + /** + * The account to which this account or contract is staking. + */ + AccountID staked_account_id = 5; + + /** + * The ID of the node this account or contract is staked to. + */ + int64 staked_node_id = 6; + } +} \ No newline at end of file diff --git a/proto/basic_types_pb2.py b/proto/basic_types_pb2.py new file mode 100644 index 00000000..1861ee2f --- /dev/null +++ b/proto/basic_types_pb2.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/basic_types.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 +from proto import timestamp_pb2 as proto_dot_timestamp__pb2 +from proto import wrappers_pb2 as proto_dot_wrappers__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17proto/basic_types.proto\x12\x06Hedera\x1a\x0cnanopb.proto\x1a\x15proto/timestamp.proto\x1a\x14proto/wrappers.proto\"\x1b\n\x07ShardID\x12\x10\n\x08shardNum\x18\x01 \x01(\x03\"-\n\x07RealmID\x12\x10\n\x08shardNum\x18\x01 \x01(\x03\x12\x10\n\x08realmNum\x18\x02 \x01(\x03\"h\n\tAccountID\x12\x10\n\x08shardNum\x18\x01 \x01(\x03\x12\x10\n\x08realmNum\x18\x02 \x01(\x03\x12\x14\n\naccountNum\x18\x03 \x01(\x03H\x00\x12\x16\n\x05\x61lias\x18\x04 \x01(\x0c\x42\x05\x92?\x02\x08 H\x00\x42\t\n\x07\x61\x63\x63ount\"=\n\x06\x46ileID\x12\x10\n\x08shardNum\x18\x01 \x01(\x03\x12\x10\n\x08realmNum\x18\x02 \x01(\x03\x12\x0f\n\x07\x66ileNum\x18\x03 \x01(\x03\"q\n\nContractID\x12\x10\n\x08shardNum\x18\x01 \x01(\x03\x12\x10\n\x08realmNum\x18\x02 \x01(\x03\x12\x15\n\x0b\x63ontractNum\x18\x03 \x01(\x03H\x00\x12\x1c\n\x0b\x65vm_address\x18\x04 \x01(\x0c\x42\x05\x92?\x02\x08\x14H\x00\x42\n\n\x08\x63ontract\"\x89\x01\n\rTransactionID\x12\x30\n\x15transactionValidStart\x18\x01 \x01(\x0b\x32\x11.Hedera.Timestamp\x12$\n\taccountID\x18\x02 \x01(\x0b\x32\x11.Hedera.AccountID\x12\x11\n\tscheduled\x18\x03 \x01(\x08\x12\r\n\x05nonce\x18\x04 \x01(\x05\"Z\n\rAccountAmount\x12$\n\taccountID\x18\x01 \x01(\x0b\x32\x11.Hedera.AccountID\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x12\x12\x13\n\x0bis_approval\x18\x03 \x01(\x08\"D\n\x0cTransferList\x12\x34\n\x0e\x61\x63\x63ountAmounts\x18\x01 \x03(\x0b\x32\x15.Hedera.AccountAmountB\x05\x92?\x02\x10\x02\"\x92\x01\n\x0bNftTransfer\x12*\n\x0fsenderAccountID\x18\x01 \x01(\x0b\x32\x11.Hedera.AccountID\x12,\n\x11receiverAccountID\x18\x02 \x01(\x0b\x32\x11.Hedera.AccountID\x12\x14\n\x0cserialNumber\x18\x03 \x01(\x03\x12\x13\n\x0bis_approval\x18\x04 \x01(\x08\"\xc6\x01\n\x11TokenTransferList\x12\x1e\n\x05token\x18\x01 \x01(\x0b\x32\x0f.Hedera.TokenID\x12/\n\ttransfers\x18\x02 \x03(\x0b\x32\x15.Hedera.AccountAmountB\x05\x92?\x02\x10\x02\x12\x30\n\x0cnftTransfers\x18\x03 \x03(\x0b\x32\x13.Hedera.NftTransferB\x05\x92?\x02\x10\x01\x12.\n\x11\x65xpected_decimals\x18\x04 \x01(\x0b\x32\x13.Hedera.UInt32Value\"2\n\x08\x46raction\x12\x11\n\tnumerator\x18\x01 \x01(\x03\x12\x13\n\x0b\x64\x65nominator\x18\x02 \x01(\x03\"?\n\x07TokenID\x12\x10\n\x08shardNum\x18\x01 \x01(\x03\x12\x10\n\x08realmNum\x18\x02 \x01(\x03\x12\x10\n\x08tokenNum\x18\x03 \x01(\x03\"\xe0\x01\n\x03Key\x12(\n\ncontractID\x18\x01 \x01(\x0b\x32\x12.Hedera.ContractIDH\x00\x12\x18\n\x07\x65\x64\x32\x35\x35\x31\x39\x18\x02 \x01(\x0c\x42\x05\x92?\x02\x08 H\x00\x12\x19\n\x08RSA_3072\x18\x03 \x01(\x0c\x42\x05\x92?\x02\x08 H\x00\x12\x1a\n\tECDSA_384\x18\x04 \x01(\x0c\x42\x05\x92?\x02\x08 H\x00\x12 \n\x0f\x45\x43\x44SA_secp256k1\x18\x07 \x01(\x0c\x42\x05\x92?\x02\x08 H\x00\x12\x35\n\x17\x64\x65legatable_contract_id\x18\x08 \x01(\x0b\x32\x12.Hedera.ContractIDH\x00\x42\x05\n\x03key\"@\n\x0cThresholdKey\x12\x11\n\tthreshold\x18\x01 \x01(\r\x12\x1d\n\x04keys\x18\x02 \x01(\x0b\x32\x0f.Hedera.KeyList\"+\n\x07KeyList\x12 \n\x04keys\x18\x01 \x03(\x0b\x32\x0b.Hedera.KeyB\x05\x92?\x02\x10\x01\"S\n\x0cTokenBalance\x12 \n\x07tokenId\x18\x01 \x01(\x0b\x32\x0f.Hedera.TokenID\x12\x0f\n\x07\x62\x61lance\x18\x02 \x01(\x04\x12\x10\n\x08\x64\x65\x63imals\x18\x03 \x01(\r\"C\n\rTokenBalances\x12\x32\n\rtokenBalances\x18\x01 \x03(\x0b\x32\x14.Hedera.TokenBalanceB\x05\x92?\x02\x10\x01\"\\\n\x10TokenAssociation\x12!\n\x08token_id\x18\x01 \x01(\x0b\x32\x0f.Hedera.TokenID\x12%\n\naccount_id\x18\x02 \x01(\x0b\x32\x11.Hedera.AccountID\"\xd9\x01\n\x0bStakingInfo\x12\x16\n\x0e\x64\x65\x63line_reward\x18\x01 \x01(\x08\x12-\n\x12stake_period_start\x18\x02 \x01(\x0b\x32\x11.Hedera.Timestamp\x12\x16\n\x0epending_reward\x18\x03 \x01(\x03\x12\x14\n\x0cstaked_to_me\x18\x04 \x01(\x03\x12.\n\x11staked_account_id\x18\x05 \x01(\x0b\x32\x11.Hedera.AccountIDH\x00\x12\x18\n\x0estaked_node_id\x18\x06 \x01(\x03H\x00\x42\x0b\n\tstaked_idb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.basic_types_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _ACCOUNTID.fields_by_name['alias']._options = None + _ACCOUNTID.fields_by_name['alias']._serialized_options = b'\222?\002\010 ' + _CONTRACTID.fields_by_name['evm_address']._options = None + _CONTRACTID.fields_by_name['evm_address']._serialized_options = b'\222?\002\010\024' + _TRANSFERLIST.fields_by_name['accountAmounts']._options = None + _TRANSFERLIST.fields_by_name['accountAmounts']._serialized_options = b'\222?\002\020\002' + _TOKENTRANSFERLIST.fields_by_name['transfers']._options = None + _TOKENTRANSFERLIST.fields_by_name['transfers']._serialized_options = b'\222?\002\020\002' + _TOKENTRANSFERLIST.fields_by_name['nftTransfers']._options = None + _TOKENTRANSFERLIST.fields_by_name['nftTransfers']._serialized_options = b'\222?\002\020\001' + _KEY.fields_by_name['ed25519']._options = None + _KEY.fields_by_name['ed25519']._serialized_options = b'\222?\002\010 ' + _KEY.fields_by_name['RSA_3072']._options = None + _KEY.fields_by_name['RSA_3072']._serialized_options = b'\222?\002\010 ' + _KEY.fields_by_name['ECDSA_384']._options = None + _KEY.fields_by_name['ECDSA_384']._serialized_options = b'\222?\002\010 ' + _KEY.fields_by_name['ECDSA_secp256k1']._options = None + _KEY.fields_by_name['ECDSA_secp256k1']._serialized_options = b'\222?\002\010 ' + _KEYLIST.fields_by_name['keys']._options = None + _KEYLIST.fields_by_name['keys']._serialized_options = b'\222?\002\020\001' + _TOKENBALANCES.fields_by_name['tokenBalances']._options = None + _TOKENBALANCES.fields_by_name['tokenBalances']._serialized_options = b'\222?\002\020\001' + _SHARDID._serialized_start=94 + _SHARDID._serialized_end=121 + _REALMID._serialized_start=123 + _REALMID._serialized_end=168 + _ACCOUNTID._serialized_start=170 + _ACCOUNTID._serialized_end=274 + _FILEID._serialized_start=276 + _FILEID._serialized_end=337 + _CONTRACTID._serialized_start=339 + _CONTRACTID._serialized_end=452 + _TRANSACTIONID._serialized_start=455 + _TRANSACTIONID._serialized_end=592 + _ACCOUNTAMOUNT._serialized_start=594 + _ACCOUNTAMOUNT._serialized_end=684 + _TRANSFERLIST._serialized_start=686 + _TRANSFERLIST._serialized_end=754 + _NFTTRANSFER._serialized_start=757 + _NFTTRANSFER._serialized_end=903 + _TOKENTRANSFERLIST._serialized_start=906 + _TOKENTRANSFERLIST._serialized_end=1104 + _FRACTION._serialized_start=1106 + _FRACTION._serialized_end=1156 + _TOKENID._serialized_start=1158 + _TOKENID._serialized_end=1221 + _KEY._serialized_start=1224 + _KEY._serialized_end=1448 + _THRESHOLDKEY._serialized_start=1450 + _THRESHOLDKEY._serialized_end=1514 + _KEYLIST._serialized_start=1516 + _KEYLIST._serialized_end=1559 + _TOKENBALANCE._serialized_start=1561 + _TOKENBALANCE._serialized_end=1644 + _TOKENBALANCES._serialized_start=1646 + _TOKENBALANCES._serialized_end=1713 + _TOKENASSOCIATION._serialized_start=1715 + _TOKENASSOCIATION._serialized_end=1807 + _STAKINGINFO._serialized_start=1810 + _STAKINGINFO._serialized_end=2027 +# @@protoc_insertion_point(module_scope) diff --git a/proto/basic_types_pb2.pyi b/proto/basic_types_pb2.pyi new file mode 100644 index 00000000..9707db1c --- /dev/null +++ b/proto/basic_types_pb2.pyi @@ -0,0 +1,862 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import proto.timestamp_pb2 +import proto.wrappers_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class ShardID(google.protobuf.message.Message): + """* + Each shard has a nonnegative shard number. Each realm within a given shard + has a nonnegative realm number (that number might be reused in other + shards). And each account, file, and smart contract instance within a given + realm has a nonnegative number (which might be reused in other realms). + Every account, file, and smart contract instance is within exactly one + realm. So a FileID is a triplet of numbers, like 0.1.2 for entity number 2 + within realm 1 within shard 0. Each realm maintains a single counter for + assigning numbers, so if there is a file with ID 0.1.2, then there won't be + an account or smart contract instance with ID 0.1.2. + + Everything is partitioned into realms so that each Solidity smart contract + can access everything in just a single realm, locking all those entities + while it's running, but other smart contracts could potentially run in + other realms in parallel. So realms allow Solidity to be parallelized + somewhat, even though the language itself assumes everything is serial. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SHARDNUM_FIELD_NUMBER: builtins.int + shardNum: builtins.int + """* + the shard number (nonnegative) + """ + def __init__( + self, + *, + shardNum: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["shardNum", b"shardNum"]) -> None: ... + +global___ShardID = ShardID + +@typing_extensions.final +class RealmID(google.protobuf.message.Message): + """* + The ID for a realm. Within a given shard, every realm has a unique ID. Each + account, file, and contract instance belongs to exactly one realm. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SHARDNUM_FIELD_NUMBER: builtins.int + REALMNUM_FIELD_NUMBER: builtins.int + shardNum: builtins.int + """* + The shard number (nonnegative) + """ + realmNum: builtins.int + """* + The realm number (nonnegative) + """ + def __init__( + self, + *, + shardNum: builtins.int = ..., + realmNum: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["realmNum", b"realmNum", "shardNum", b"shardNum"]) -> None: ... + +global___RealmID = RealmID + +@typing_extensions.final +class AccountID(google.protobuf.message.Message): + """* + The ID for an a cryptocurrency account + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SHARDNUM_FIELD_NUMBER: builtins.int + REALMNUM_FIELD_NUMBER: builtins.int + ACCOUNTNUM_FIELD_NUMBER: builtins.int + ALIAS_FIELD_NUMBER: builtins.int + shardNum: builtins.int + """* + The shard number (nonnegative) + """ + realmNum: builtins.int + """* + The realm number (nonnegative) + """ + accountNum: builtins.int + """* + A non-negative account number unique within its realm + """ + alias: builtins.bytes + """* + The public key bytes to be used as the account's alias. The public key + bytes are the result of serializing a protobuf Key message for any + primitive key type. Currently only primitive key bytes are supported as + an alias (ThresholdKey, KeyList, ContractID, and delegatable_contract_id + are not supported) + + At most one account can ever have a given alias and it is used for + account creation if it was automatically created using a crypto + transfer. It will be null if an account is created normally. It is + immutable once it is set for an account. + + If a transaction auto-creates the account, any further transfers to that + alias will simply be deposited in that account, without creating + anything, and with no creation fee being charged. + """ + def __init__( + self, + *, + shardNum: builtins.int = ..., + realmNum: builtins.int = ..., + accountNum: builtins.int = ..., + alias: builtins.bytes = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["account", b"account", "accountNum", b"accountNum", "alias", b"alias"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["account", b"account", "accountNum", b"accountNum", "alias", b"alias", "realmNum", b"realmNum", "shardNum", b"shardNum"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["account", b"account"]) -> typing_extensions.Literal["accountNum", "alias"] | None: ... + +global___AccountID = AccountID + +@typing_extensions.final +class FileID(google.protobuf.message.Message): + """* + The ID for a file + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SHARDNUM_FIELD_NUMBER: builtins.int + REALMNUM_FIELD_NUMBER: builtins.int + FILENUM_FIELD_NUMBER: builtins.int + shardNum: builtins.int + """* + The shard number (nonnegative) + """ + realmNum: builtins.int + """* + The realm number (nonnegative) + """ + fileNum: builtins.int + """* + A nonnegative File number unique within its realm + """ + def __init__( + self, + *, + shardNum: builtins.int = ..., + realmNum: builtins.int = ..., + fileNum: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["fileNum", b"fileNum", "realmNum", b"realmNum", "shardNum", b"shardNum"]) -> None: ... + +global___FileID = FileID + +@typing_extensions.final +class ContractID(google.protobuf.message.Message): + """* + The ID for a smart contract instance + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SHARDNUM_FIELD_NUMBER: builtins.int + REALMNUM_FIELD_NUMBER: builtins.int + CONTRACTNUM_FIELD_NUMBER: builtins.int + EVM_ADDRESS_FIELD_NUMBER: builtins.int + shardNum: builtins.int + """* + The shard number (nonnegative) + """ + realmNum: builtins.int + """* + The realm number (nonnegative) + """ + contractNum: builtins.int + """* + A nonnegative number unique within a given shard and realm + """ + evm_address: builtins.bytes + """* + The 20-byte EVM address of the contract to call. + + Every contract has an EVM address determined by its + shard.realm.num id. This address is as follows:
  1. The + first 4 bytes are the big-endian representation of the shard.
  2. +
  3. The next 8 bytes are the big-endian representation of the + realm.
  4. The final 8 bytes are the big-endian representation of + the number.
  5. +
+ + Contracts created via CREATE2 have an additional, primary address + that is derived from the EIP-1014 + specification, and does not have a simple relation to a + shard.realm.num id. + + (Please do note that CREATE2 contracts can also be referenced by the + three-part EVM address described above.) + """ + def __init__( + self, + *, + shardNum: builtins.int = ..., + realmNum: builtins.int = ..., + contractNum: builtins.int = ..., + evm_address: builtins.bytes = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["contract", b"contract", "contractNum", b"contractNum", "evm_address", b"evm_address"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["contract", b"contract", "contractNum", b"contractNum", "evm_address", b"evm_address", "realmNum", b"realmNum", "shardNum", b"shardNum"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["contract", b"contract"]) -> typing_extensions.Literal["contractNum", "evm_address"] | None: ... + +global___ContractID = ContractID + +@typing_extensions.final +class TransactionID(google.protobuf.message.Message): + """* + The ID for a transaction. This is used for retrieving receipts and records + for a transaction, for appending to a file right after creating it, for + instantiating a smart contract with bytecode in a file just created, and + internally by the network for detecting when duplicate transactions are + submitted. A user might get a transaction processed faster by submitting it + to N nodes, each with a different node account, but all with the same + TransactionID. Then, the transaction will take effect when the first of all + those nodes submits the transaction and it reaches consensus. The other + transactions will not take effect. So this could make the transaction take + effect faster, if any given node might be slow. However, the full + transaction fee is charged for each transaction, so the total fee is N times + as much if the transaction is sent to N nodes. + + Applicable to Scheduled Transactions: + - The ID of a Scheduled Transaction has transactionValidStart and + accountIDs inherited from the ScheduleCreate transaction that created it. + That is to say that they are equal + - The scheduled property is true for Scheduled Transactions + - transactionValidStart, accountID and scheduled properties should be + omitted + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRANSACTIONVALIDSTART_FIELD_NUMBER: builtins.int + ACCOUNTID_FIELD_NUMBER: builtins.int + SCHEDULED_FIELD_NUMBER: builtins.int + NONCE_FIELD_NUMBER: builtins.int + @property + def transactionValidStart(self) -> proto.timestamp_pb2.Timestamp: + """* + The transaction is invalid if consensusTimestamp < + transactionID.transactionStartValid + """ + @property + def accountID(self) -> global___AccountID: + """* + The Account ID that paid for this transaction + """ + scheduled: builtins.bool + """* + Whether the Transaction is of type Scheduled or no + """ + nonce: builtins.int + """* + The identifier for an internal transaction that was spawned as part + of handling a user transaction. (These internal transactions share the + transactionValidStart and accountID of the user transaction, so a + nonce is necessary to give them a unique TransactionID.) + + An example is when a "parent" ContractCreate or ContractCall transaction + calls one or more HTS precompiled contracts; each of the "child" + transactions spawned for a precompile has a id with a different nonce. + """ + def __init__( + self, + *, + transactionValidStart: proto.timestamp_pb2.Timestamp | None = ..., + accountID: global___AccountID | None = ..., + scheduled: builtins.bool = ..., + nonce: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["accountID", b"accountID", "transactionValidStart", b"transactionValidStart"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["accountID", b"accountID", "nonce", b"nonce", "scheduled", b"scheduled", "transactionValidStart", b"transactionValidStart"]) -> None: ... + +global___TransactionID = TransactionID + +@typing_extensions.final +class AccountAmount(google.protobuf.message.Message): + """* + An account, and the amount that it sends or receives during a cryptocurrency + or token transfer. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ACCOUNTID_FIELD_NUMBER: builtins.int + AMOUNT_FIELD_NUMBER: builtins.int + IS_APPROVAL_FIELD_NUMBER: builtins.int + @property + def accountID(self) -> global___AccountID: + """* + The Account ID that sends/receives cryptocurrency or tokens + """ + amount: builtins.int + """* + The amount of tinybars (for Crypto transfers) or in the lowest + denomination (for Token transfers) that the account sends(negative) or + receives(positive) + """ + is_approval: builtins.bool + """* + If true then the transfer is expected to be an approved allowance and the + accountID is expected to be the owner. The default is false (omitted). + """ + def __init__( + self, + *, + accountID: global___AccountID | None = ..., + amount: builtins.int = ..., + is_approval: builtins.bool = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["accountID", b"accountID"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["accountID", b"accountID", "amount", b"amount", "is_approval", b"is_approval"]) -> None: ... + +global___AccountAmount = AccountAmount + +@typing_extensions.final +class TransferList(google.protobuf.message.Message): + """* + A list of accounts and amounts to transfer out of each account (negative) or + into it (positive). + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ACCOUNTAMOUNTS_FIELD_NUMBER: builtins.int + @property + def accountAmounts(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AccountAmount]: + """* + Multiple list of AccountAmount pairs, each of which has an account and + an amount to transfer into it (positive) or out of it (negative) + Limited to 2 for a transfer between two accounts + """ + def __init__( + self, + *, + accountAmounts: collections.abc.Iterable[global___AccountAmount] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["accountAmounts", b"accountAmounts"]) -> None: ... + +global___TransferList = TransferList + +@typing_extensions.final +class NftTransfer(google.protobuf.message.Message): + """* + A sender account, a receiver account, and the serial number of an NFT of a + Token with NON_FUNGIBLE_UNIQUE type. When minting NFTs the sender will be + the default AccountID instance (0.0.0) and when burning NFTs, the receiver + will be the default AccountID instance. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SENDERACCOUNTID_FIELD_NUMBER: builtins.int + RECEIVERACCOUNTID_FIELD_NUMBER: builtins.int + SERIALNUMBER_FIELD_NUMBER: builtins.int + IS_APPROVAL_FIELD_NUMBER: builtins.int + @property + def senderAccountID(self) -> global___AccountID: + """* + The accountID of the sender + """ + @property + def receiverAccountID(self) -> global___AccountID: + """* + The accountID of the receiver + """ + serialNumber: builtins.int + """* + The serial number of the NFT + """ + is_approval: builtins.bool + """* + If true then the transfer is expected to be an approved allowance and the + senderAccountID is expected to be the owner. The default is false + (omitted). + """ + def __init__( + self, + *, + senderAccountID: global___AccountID | None = ..., + receiverAccountID: global___AccountID | None = ..., + serialNumber: builtins.int = ..., + is_approval: builtins.bool = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["receiverAccountID", b"receiverAccountID", "senderAccountID", b"senderAccountID"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["is_approval", b"is_approval", "receiverAccountID", b"receiverAccountID", "senderAccountID", b"senderAccountID", "serialNumber", b"serialNumber"]) -> None: ... + +global___NftTransfer = NftTransfer + +@typing_extensions.final +class TokenTransferList(google.protobuf.message.Message): + """* + A list of token IDs and amounts representing the transferred out (negative) + or into (positive) amounts, represented in the lowest denomination of the + token + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TOKEN_FIELD_NUMBER: builtins.int + TRANSFERS_FIELD_NUMBER: builtins.int + NFTTRANSFERS_FIELD_NUMBER: builtins.int + EXPECTED_DECIMALS_FIELD_NUMBER: builtins.int + @property + def token(self) -> global___TokenID: + """* + The ID of the token + """ + @property + def transfers(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AccountAmount]: + """* + Applicable to tokens of type FUNGIBLE_COMMON. Multiple list of + AccountAmounts, each of which has an account and amount + Limited to 2 for 1 allowed transfer (reciprocal subtraction of balance + + actual transfer) + """ + @property + def nftTransfers(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___NftTransfer]: + """* + Applicable to tokens of type NON_FUNGIBLE_UNIQUE. Multiple list of + NftTransfers, each of which has a sender and receiver account, including + the serial number of the NFT + Limited to 1 here + """ + @property + def expected_decimals(self) -> proto.wrappers_pb2.UInt32Value: + """* + If present, the number of decimals this fungible token type is expected to + have. The transfer will fail with UNEXPECTED_TOKEN_DECIMALS if the actual + decimals differ. + """ + def __init__( + self, + *, + token: global___TokenID | None = ..., + transfers: collections.abc.Iterable[global___AccountAmount] | None = ..., + nftTransfers: collections.abc.Iterable[global___NftTransfer] | None = ..., + expected_decimals: proto.wrappers_pb2.UInt32Value | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["expected_decimals", b"expected_decimals", "token", b"token"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["expected_decimals", b"expected_decimals", "nftTransfers", b"nftTransfers", "token", b"token", "transfers", b"transfers"]) -> None: ... + +global___TokenTransferList = TokenTransferList + +@typing_extensions.final +class Fraction(google.protobuf.message.Message): + """* + A rational number, used to set the amount of a value transfer to collect as + a custom fee + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NUMERATOR_FIELD_NUMBER: builtins.int + DENOMINATOR_FIELD_NUMBER: builtins.int + numerator: builtins.int + """* + The rational's numerator + """ + denominator: builtins.int + """* + The rational's denominator; a zero value will result in + FRACTION_DIVIDES_BY_ZERO + """ + def __init__( + self, + *, + numerator: builtins.int = ..., + denominator: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["denominator", b"denominator", "numerator", b"numerator"]) -> None: ... + +global___Fraction = Fraction + +@typing_extensions.final +class TokenID(google.protobuf.message.Message): + """* + Unique identifier for a token + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SHARDNUM_FIELD_NUMBER: builtins.int + REALMNUM_FIELD_NUMBER: builtins.int + TOKENNUM_FIELD_NUMBER: builtins.int + shardNum: builtins.int + """* + A nonnegative shard number + """ + realmNum: builtins.int + """* + A nonnegative realm number + """ + tokenNum: builtins.int + """* + A nonnegative token number + """ + def __init__( + self, + *, + shardNum: builtins.int = ..., + realmNum: builtins.int = ..., + tokenNum: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["realmNum", b"realmNum", "shardNum", b"shardNum", "tokenNum", b"tokenNum"]) -> None: ... + +global___TokenID = TokenID + +@typing_extensions.final +class Key(google.protobuf.message.Message): + """* + A Key can be a public key from either the Ed25519 or ECDSA(secp256k1) + signature schemes, where in the ECDSA(secp256k1) case we require the 33-byte + compressed form of the public key. We call these public keys primitive + keys. + + If an account has primitive key associated to it, then the corresponding + private key must sign any transaction to transfer cryptocurrency out of it. + + A Key can also be the ID of a smart contract instance, which is then + authorized to perform any precompiled contract action that requires this key + to sign. + + Note that when a Key is a smart contract ID, it doesn't mean the + contract with that ID will actually create a cryptographic signature. It + only means that when the contract calls a precompiled contract, the + resulting "child transaction" will be authorized to perform any action + controlled by the Key. + + A Key can be a "threshold key", which means a list of M keys, any N of which + must sign in order for the threshold signature to be considered valid. The + keys within a threshold signature may themselves be threshold signatures, to + allow complex signature requirements. + + A Key can be a "key list" where all keys in the list must sign unless + specified otherwise in the documentation for a specific transaction type + (e.g. FileDeleteTransactionBody). Their use is dependent on context. For + example, a Hedera file is created with a list of keys, where all of them + must sign a transaction to create or modify the file, but only one of them + is needed to sign a transaction to delete the file. So it's a single list + that sometimes acts as a 1-of-M threshold key, and sometimes acts as an + M-of-M threshold key. A key list is always an M-of-M, unless specified + otherwise in documentation. A key list can have nested key lists or + threshold keys. Nested key lists are always M-of-M. A key list can have + repeated primitive public keys, but all repeated keys are only required to + sign once. + + A Key can contain a ThresholdKey or KeyList, which in turn contain a Key, so + this mutual recursion would allow nesting arbitrarily deep. A ThresholdKey + which contains a list of primitive keys has 3 levels: ThresholdKey -> + KeyList + -> Key. A KeyList which contains several primitive keys has 2 levels: + KeyList + -> Key. A Key with 2 levels of nested ThresholdKeys has 7 levels: Key -> + ThresholdKey -> KeyList -> Key -> ThresholdKey -> KeyList -> Key. + + Each Key should not have more than 46 levels, which implies 15 levels of + nested ThresholdKeys. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CONTRACTID_FIELD_NUMBER: builtins.int + ED25519_FIELD_NUMBER: builtins.int + RSA_3072_FIELD_NUMBER: builtins.int + ECDSA_384_FIELD_NUMBER: builtins.int + ECDSA_SECP256K1_FIELD_NUMBER: builtins.int + DELEGATABLE_CONTRACT_ID_FIELD_NUMBER: builtins.int + @property + def contractID(self) -> global___ContractID: + """* + smart contract instance that is authorized as if it had signed with a + key + """ + ed25519: builtins.bytes + """* + Ed25519 public key bytes + """ + RSA_3072: builtins.bytes + """* + (NOT SUPPORTED) RSA-3072 public key bytes + """ + ECDSA_384: builtins.bytes + """* + (NOT SUPPORTED) ECDSA with the p-384 curve public key bytes + """ + ECDSA_secp256k1: builtins.bytes + """KeyList keyList = 6; + + * + Compressed ECDSA(secp256k1) public key bytes + """ + @property + def delegatable_contract_id(self) -> global___ContractID: + """* + A smart contract that, if the recipient of the active message frame, + should be treated as having signed. (Note this does not mean the code + being executed in the frame will belong to the given contract, since + it could be running another contract's code via delegatecall. + So setting this key is a more permissive version of setting the + contractID key, which also requires the code in the active message frame + belong to the the contract with the given id.) + """ + def __init__( + self, + *, + contractID: global___ContractID | None = ..., + ed25519: builtins.bytes = ..., + RSA_3072: builtins.bytes = ..., + ECDSA_384: builtins.bytes = ..., + ECDSA_secp256k1: builtins.bytes = ..., + delegatable_contract_id: global___ContractID | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["ECDSA_384", b"ECDSA_384", "ECDSA_secp256k1", b"ECDSA_secp256k1", "RSA_3072", b"RSA_3072", "contractID", b"contractID", "delegatable_contract_id", b"delegatable_contract_id", "ed25519", b"ed25519", "key", b"key"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["ECDSA_384", b"ECDSA_384", "ECDSA_secp256k1", b"ECDSA_secp256k1", "RSA_3072", b"RSA_3072", "contractID", b"contractID", "delegatable_contract_id", b"delegatable_contract_id", "ed25519", b"ed25519", "key", b"key"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["key", b"key"]) -> typing_extensions.Literal["contractID", "ed25519", "RSA_3072", "ECDSA_384", "ECDSA_secp256k1", "delegatable_contract_id"] | None: ... + +global___Key = Key + +@typing_extensions.final +class ThresholdKey(google.protobuf.message.Message): + """* + A set of public keys that are used together to form a threshold signature. + If the threshold is N and there are M keys, then this is an N of M threshold + signature. If an account is associated with ThresholdKeys, then a + transaction to move cryptocurrency out of it must be signed by a list of M + signatures, where at most M-N of them are blank, and the other at least N of + them are valid signatures corresponding to at least N of the public keys + listed here. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + THRESHOLD_FIELD_NUMBER: builtins.int + KEYS_FIELD_NUMBER: builtins.int + threshold: builtins.int + """* + A valid signature set must have at least this many signatures + """ + @property + def keys(self) -> global___KeyList: + """* + List of all the keys that can sign + """ + def __init__( + self, + *, + threshold: builtins.int = ..., + keys: global___KeyList | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["keys", b"keys"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["keys", b"keys", "threshold", b"threshold"]) -> None: ... + +global___ThresholdKey = ThresholdKey + +@typing_extensions.final +class KeyList(google.protobuf.message.Message): + """* + A list of keys that requires all keys (M-of-M) to sign unless otherwise + specified in documentation. A KeyList may contain repeated keys, but all + repeated keys are only required to sign once. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEYS_FIELD_NUMBER: builtins.int + @property + def keys(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Key]: + """* + list of keys + Limited to 1 here (because we don't have malloc!) + """ + def __init__( + self, + *, + keys: collections.abc.Iterable[global___Key] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["keys", b"keys"]) -> None: ... + +global___KeyList = KeyList + +@typing_extensions.final +class TokenBalance(google.protobuf.message.Message): + """* + A number of transferable units of a certain token. + + The transferable unit of a token is its smallest denomination, as given by + the token's decimals property---each minted token contains + 10decimals transferable units. For example, we could + think of the cent as the transferable unit of the US dollar + (decimals=2); and the tinybar as the transferable unit of hbar + (decimals=8). + + Transferable units are not directly comparable across different tokens. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TOKENID_FIELD_NUMBER: builtins.int + BALANCE_FIELD_NUMBER: builtins.int + DECIMALS_FIELD_NUMBER: builtins.int + @property + def tokenId(self) -> global___TokenID: + """* + A unique token id + """ + balance: builtins.int + """* + Number of transferable units of the identified token. For token of type + FUNGIBLE_COMMON - balance in the smallest denomination. For token of type + NON_FUNGIBLE_UNIQUE - the number of NFTs held by the account + """ + decimals: builtins.int + """* + Tokens divide into 10decimals pieces + """ + def __init__( + self, + *, + tokenId: global___TokenID | None = ..., + balance: builtins.int = ..., + decimals: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["tokenId", b"tokenId"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["balance", b"balance", "decimals", b"decimals", "tokenId", b"tokenId"]) -> None: ... + +global___TokenBalance = TokenBalance + +@typing_extensions.final +class TokenBalances(google.protobuf.message.Message): + """* + A sequence of token balances + Limited to 1 here + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TOKENBALANCES_FIELD_NUMBER: builtins.int + @property + def tokenBalances(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TokenBalance]: ... + def __init__( + self, + *, + tokenBalances: collections.abc.Iterable[global___TokenBalance] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["tokenBalances", b"tokenBalances"]) -> None: ... + +global___TokenBalances = TokenBalances + +@typing_extensions.final +class TokenAssociation(google.protobuf.message.Message): + """A token - account association""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TOKEN_ID_FIELD_NUMBER: builtins.int + ACCOUNT_ID_FIELD_NUMBER: builtins.int + @property + def token_id(self) -> global___TokenID: + """The token involved in the association""" + @property + def account_id(self) -> global___AccountID: + """The account involved in the association""" + def __init__( + self, + *, + token_id: global___TokenID | None = ..., + account_id: global___AccountID | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["account_id", b"account_id", "token_id", b"token_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["account_id", b"account_id", "token_id", b"token_id"]) -> None: ... + +global___TokenAssociation = TokenAssociation + +@typing_extensions.final +class StakingInfo(google.protobuf.message.Message): + """* + Staking metadata for an account or a contract returned in CryptoGetInfo or + ContractGetInfo queries + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DECLINE_REWARD_FIELD_NUMBER: builtins.int + STAKE_PERIOD_START_FIELD_NUMBER: builtins.int + PENDING_REWARD_FIELD_NUMBER: builtins.int + STAKED_TO_ME_FIELD_NUMBER: builtins.int + STAKED_ACCOUNT_ID_FIELD_NUMBER: builtins.int + STAKED_NODE_ID_FIELD_NUMBER: builtins.int + decline_reward: builtins.bool + """* + If true, this account or contract declined to receive a staking reward. + """ + @property + def stake_period_start(self) -> proto.timestamp_pb2.Timestamp: + """* + The staking period during which either the staking settings for this + account or contract changed (such as starting staking or changing + staked_node_id) or the most recent reward was earned, whichever is later. + If this account or contract is not currently staked to a node, then this + field is not set. + """ + pending_reward: builtins.int + """* + The amount in tinybars that will be received in the next reward situation. + """ + staked_to_me: builtins.int + """* + The total of balance of all accounts staked to this account or contract. + """ + @property + def staked_account_id(self) -> global___AccountID: + """* + The account to which this account or contract is staking. + """ + staked_node_id: builtins.int + """* + The ID of the node this account or contract is staked to. + """ + def __init__( + self, + *, + decline_reward: builtins.bool = ..., + stake_period_start: proto.timestamp_pb2.Timestamp | None = ..., + pending_reward: builtins.int = ..., + staked_to_me: builtins.int = ..., + staked_account_id: global___AccountID | None = ..., + staked_node_id: builtins.int = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["stake_period_start", b"stake_period_start", "staked_account_id", b"staked_account_id", "staked_id", b"staked_id", "staked_node_id", b"staked_node_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["decline_reward", b"decline_reward", "pending_reward", b"pending_reward", "stake_period_start", b"stake_period_start", "staked_account_id", b"staked_account_id", "staked_id", b"staked_id", "staked_node_id", b"staked_node_id", "staked_to_me", b"staked_to_me"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["staked_id", b"staked_id"]) -> typing_extensions.Literal["staked_account_id", "staked_node_id"] | None: ... + +global___StakingInfo = StakingInfo diff --git a/proto/crypto_create.proto b/proto/crypto_create.proto new file mode 100644 index 00000000..dbe42cec --- /dev/null +++ b/proto/crypto_create.proto @@ -0,0 +1,160 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; +import "proto/basic_types.proto"; +import "proto/duration.proto"; + +/* + * Create a new account. After the account is created, the AccountID for it is + * in the receipt. It can also be retrieved with a GetByKey query. Threshold + * values can be defined, and records are generated and stored for 25 hours for + * any transfer that exceeds the thresholds. This account is charged for each + * record generated, so the thresholds are useful for limiting record + * generation to happen only for large transactions. + * + * The Key field is the key used to sign transactions for this account. If the + * account has receiverSigRequired set to true, then all cryptocurrency + * transfers must be signed by this account's key, both for transfers in and + * out. If it is false, then only transfers out have to be signed by it. When + * the account is created, the payer account is charged enough hbars so that + * the new account will not expire for the next autoRenewPeriod seconds. When + * it reaches the expiration time, the new account will then be automatically + * charged to renew for another autoRenewPeriod seconds. If it does not have + * enough hbars to renew for that long, then the remaining hbars are used to + * extend its expiration as long as possible. If it is has a zero balance when + * it expires, then it is deleted. This transaction must be signed by the payer + * account. If receiverSigRequired is false, then the transaction does not have + * to be signed by the keys in the keys field. If it is true, then it must be + * signed by them, in addition to the keys of the payer account. + * + * An entity (account, file, or smart contract instance) must be created in a + * particular realm. If the realmID is left null, then a new realm will be + * created with the given admin key. If a new realm has a null adminKey, then + * anyone can create/modify/delete entities in that realm. But if an admin key + * is given, then any transaction to create/modify/delete an entity in that + * realm must be signed by that key, though anyone can still call functions on + * smart contract instances that exist in that realm. A realm ceases to exist + * when everything within it has expired and no longer exists. + * + * The current API ignores shardID, realmID, and newRealmAdminKey, and creates + * everything in shard 0 and realm 0, with a null key. Future versions of the + * API will support multiple realms and multiple shards. + */ +message CryptoCreateTransactionBody { + /** + * The key that must sign each transfer out of the account. If + * receiverSigRequired is true, then it must also sign any transfer into the + * account. + */ + Key key = 1; + + /** + * The initial number of tinybars to put into the account + */ + uint64 initialBalance = 2; + + /** + * [Deprecated] ID of the account to which this account is proxy staked. If + * proxyAccountID is null, or is an invalid account, or is an account that + * isn't a node, then this account is automatically proxy staked to a node + * chosen by the network, but without earning payments. If the proxyAccountID + * account refuses to accept proxy staking , or if it is not currently + * running a node, then it will behave as if proxyAccountID was null. + */ + AccountID proxyAccountID = 3 [ deprecated = true ]; + + /** + * [Deprecated]. The threshold amount (in tinybars) for which an account + * record is created for any send/withdraw transaction + */ + uint64 sendRecordThreshold = 6 [ deprecated = true ]; + + /** + * [Deprecated]. The threshold amount (in tinybars) for which an account + * record is created for any receive/deposit transaction + */ + uint64 receiveRecordThreshold = 7 [ deprecated = true ]; + + /** + * If true, this account's key must sign any transaction depositing into this + * account (in addition to all withdrawals) + */ + bool receiverSigRequired = 8; + + /** + * The account is charged to extend its expiration date every this many + * seconds. If it doesn't have enough balance, it extends as long as + * possible. If it is empty when it expires, then it is deleted. + */ + Duration autoRenewPeriod = 9; + + /** + * The shard in which this account is created + */ + ShardID shardID = 10; + + /** + * The realm in which this account is created (leave this null to create a + * new realm) + */ + RealmID realmID = 11; + + /** + * If realmID is null, then this the admin key for the new realm that will be + * created + */ + Key newRealmAdminKey = 12; + + /** + * The memo associated with the account (UTF-8 encoding max 100 bytes) + */ + string memo = 13 [ (nanopb).max_size = 100 ]; + + /** + * The maximum number of tokens that an Account can be implicitly associated + * with. Defaults to 0 and up to a maximum value of 1000. + */ + int32 max_automatic_token_associations = 14; + + /** + * ID of the account or node to which this account is staking. + */ + oneof staked_id { + /** + * ID of the account to which this account is staking. + */ + AccountID staked_account_id = 15; + + /** + * ID of the node this account is staked to. + */ + int64 staked_node_id = 16; + } + + /** + * If true, the account declines receiving a staking reward. The default + * value is false. + */ + bool decline_reward = 17; +} \ No newline at end of file diff --git a/proto/crypto_create_pb2.py b/proto/crypto_create_pb2.py new file mode 100644 index 00000000..6f2009d0 --- /dev/null +++ b/proto/crypto_create_pb2.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/crypto_create.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 +from proto import basic_types_pb2 as proto_dot_basic__types__pb2 +from proto import duration_pb2 as proto_dot_duration__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19proto/crypto_create.proto\x12\x06Hedera\x1a\x0cnanopb.proto\x1a\x17proto/basic_types.proto\x1a\x14proto/duration.proto\"\xa4\x04\n\x1b\x43ryptoCreateTransactionBody\x12\x18\n\x03key\x18\x01 \x01(\x0b\x32\x0b.Hedera.Key\x12\x16\n\x0einitialBalance\x18\x02 \x01(\x04\x12-\n\x0eproxyAccountID\x18\x03 \x01(\x0b\x32\x11.Hedera.AccountIDB\x02\x18\x01\x12\x1f\n\x13sendRecordThreshold\x18\x06 \x01(\x04\x42\x02\x18\x01\x12\"\n\x16receiveRecordThreshold\x18\x07 \x01(\x04\x42\x02\x18\x01\x12\x1b\n\x13receiverSigRequired\x18\x08 \x01(\x08\x12)\n\x0f\x61utoRenewPeriod\x18\t \x01(\x0b\x32\x10.Hedera.Duration\x12 \n\x07shardID\x18\n \x01(\x0b\x32\x0f.Hedera.ShardID\x12 \n\x07realmID\x18\x0b \x01(\x0b\x32\x0f.Hedera.RealmID\x12%\n\x10newRealmAdminKey\x18\x0c \x01(\x0b\x32\x0b.Hedera.Key\x12\x13\n\x04memo\x18\r \x01(\tB\x05\x92?\x02\x08\x64\x12(\n max_automatic_token_associations\x18\x0e \x01(\x05\x12.\n\x11staked_account_id\x18\x0f \x01(\x0b\x32\x11.Hedera.AccountIDH\x00\x12\x18\n\x0estaked_node_id\x18\x10 \x01(\x03H\x00\x12\x16\n\x0e\x64\x65\x63line_reward\x18\x11 \x01(\x08\x42\x0b\n\tstaked_idb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.crypto_create_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _CRYPTOCREATETRANSACTIONBODY.fields_by_name['proxyAccountID']._options = None + _CRYPTOCREATETRANSACTIONBODY.fields_by_name['proxyAccountID']._serialized_options = b'\030\001' + _CRYPTOCREATETRANSACTIONBODY.fields_by_name['sendRecordThreshold']._options = None + _CRYPTOCREATETRANSACTIONBODY.fields_by_name['sendRecordThreshold']._serialized_options = b'\030\001' + _CRYPTOCREATETRANSACTIONBODY.fields_by_name['receiveRecordThreshold']._options = None + _CRYPTOCREATETRANSACTIONBODY.fields_by_name['receiveRecordThreshold']._serialized_options = b'\030\001' + _CRYPTOCREATETRANSACTIONBODY.fields_by_name['memo']._options = None + _CRYPTOCREATETRANSACTIONBODY.fields_by_name['memo']._serialized_options = b'\222?\002\010d' + _CRYPTOCREATETRANSACTIONBODY._serialized_start=99 + _CRYPTOCREATETRANSACTIONBODY._serialized_end=647 +# @@protoc_insertion_point(module_scope) diff --git a/proto/crypto_create_pb2.pyi b/proto/crypto_create_pb2.pyi new file mode 100644 index 00000000..2d472669 --- /dev/null +++ b/proto/crypto_create_pb2.pyi @@ -0,0 +1,181 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import proto.basic_types_pb2 +import proto.duration_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class CryptoCreateTransactionBody(google.protobuf.message.Message): + """ + Create a new account. After the account is created, the AccountID for it is + in the receipt. It can also be retrieved with a GetByKey query. Threshold + values can be defined, and records are generated and stored for 25 hours for + any transfer that exceeds the thresholds. This account is charged for each + record generated, so the thresholds are useful for limiting record + generation to happen only for large transactions. + + The Key field is the key used to sign transactions for this account. If the + account has receiverSigRequired set to true, then all cryptocurrency + transfers must be signed by this account's key, both for transfers in and + out. If it is false, then only transfers out have to be signed by it. When + the account is created, the payer account is charged enough hbars so that + the new account will not expire for the next autoRenewPeriod seconds. When + it reaches the expiration time, the new account will then be automatically + charged to renew for another autoRenewPeriod seconds. If it does not have + enough hbars to renew for that long, then the remaining hbars are used to + extend its expiration as long as possible. If it is has a zero balance when + it expires, then it is deleted. This transaction must be signed by the payer + account. If receiverSigRequired is false, then the transaction does not have + to be signed by the keys in the keys field. If it is true, then it must be + signed by them, in addition to the keys of the payer account. + + An entity (account, file, or smart contract instance) must be created in a + particular realm. If the realmID is left null, then a new realm will be + created with the given admin key. If a new realm has a null adminKey, then + anyone can create/modify/delete entities in that realm. But if an admin key + is given, then any transaction to create/modify/delete an entity in that + realm must be signed by that key, though anyone can still call functions on + smart contract instances that exist in that realm. A realm ceases to exist + when everything within it has expired and no longer exists. + + The current API ignores shardID, realmID, and newRealmAdminKey, and creates + everything in shard 0 and realm 0, with a null key. Future versions of the + API will support multiple realms and multiple shards. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + INITIALBALANCE_FIELD_NUMBER: builtins.int + PROXYACCOUNTID_FIELD_NUMBER: builtins.int + SENDRECORDTHRESHOLD_FIELD_NUMBER: builtins.int + RECEIVERECORDTHRESHOLD_FIELD_NUMBER: builtins.int + RECEIVERSIGREQUIRED_FIELD_NUMBER: builtins.int + AUTORENEWPERIOD_FIELD_NUMBER: builtins.int + SHARDID_FIELD_NUMBER: builtins.int + REALMID_FIELD_NUMBER: builtins.int + NEWREALMADMINKEY_FIELD_NUMBER: builtins.int + MEMO_FIELD_NUMBER: builtins.int + MAX_AUTOMATIC_TOKEN_ASSOCIATIONS_FIELD_NUMBER: builtins.int + STAKED_ACCOUNT_ID_FIELD_NUMBER: builtins.int + STAKED_NODE_ID_FIELD_NUMBER: builtins.int + DECLINE_REWARD_FIELD_NUMBER: builtins.int + @property + def key(self) -> proto.basic_types_pb2.Key: + """* + The key that must sign each transfer out of the account. If + receiverSigRequired is true, then it must also sign any transfer into the + account. + """ + initialBalance: builtins.int + """* + The initial number of tinybars to put into the account + """ + @property + def proxyAccountID(self) -> proto.basic_types_pb2.AccountID: + """* + [Deprecated] ID of the account to which this account is proxy staked. If + proxyAccountID is null, or is an invalid account, or is an account that + isn't a node, then this account is automatically proxy staked to a node + chosen by the network, but without earning payments. If the proxyAccountID + account refuses to accept proxy staking , or if it is not currently + running a node, then it will behave as if proxyAccountID was null. + """ + sendRecordThreshold: builtins.int + """* + [Deprecated]. The threshold amount (in tinybars) for which an account + record is created for any send/withdraw transaction + """ + receiveRecordThreshold: builtins.int + """* + [Deprecated]. The threshold amount (in tinybars) for which an account + record is created for any receive/deposit transaction + """ + receiverSigRequired: builtins.bool + """* + If true, this account's key must sign any transaction depositing into this + account (in addition to all withdrawals) + """ + @property + def autoRenewPeriod(self) -> proto.duration_pb2.Duration: + """* + The account is charged to extend its expiration date every this many + seconds. If it doesn't have enough balance, it extends as long as + possible. If it is empty when it expires, then it is deleted. + """ + @property + def shardID(self) -> proto.basic_types_pb2.ShardID: + """* + The shard in which this account is created + """ + @property + def realmID(self) -> proto.basic_types_pb2.RealmID: + """* + The realm in which this account is created (leave this null to create a + new realm) + """ + @property + def newRealmAdminKey(self) -> proto.basic_types_pb2.Key: + """* + If realmID is null, then this the admin key for the new realm that will be + created + """ + memo: builtins.str + """* + The memo associated with the account (UTF-8 encoding max 100 bytes) + """ + max_automatic_token_associations: builtins.int + """* + The maximum number of tokens that an Account can be implicitly associated + with. Defaults to 0 and up to a maximum value of 1000. + """ + @property + def staked_account_id(self) -> proto.basic_types_pb2.AccountID: + """* + ID of the account to which this account is staking. + """ + staked_node_id: builtins.int + """* + ID of the node this account is staked to. + """ + decline_reward: builtins.bool + """* + If true, the account declines receiving a staking reward. The default + value is false. + """ + def __init__( + self, + *, + key: proto.basic_types_pb2.Key | None = ..., + initialBalance: builtins.int = ..., + proxyAccountID: proto.basic_types_pb2.AccountID | None = ..., + sendRecordThreshold: builtins.int = ..., + receiveRecordThreshold: builtins.int = ..., + receiverSigRequired: builtins.bool = ..., + autoRenewPeriod: proto.duration_pb2.Duration | None = ..., + shardID: proto.basic_types_pb2.ShardID | None = ..., + realmID: proto.basic_types_pb2.RealmID | None = ..., + newRealmAdminKey: proto.basic_types_pb2.Key | None = ..., + memo: builtins.str = ..., + max_automatic_token_associations: builtins.int = ..., + staked_account_id: proto.basic_types_pb2.AccountID | None = ..., + staked_node_id: builtins.int = ..., + decline_reward: builtins.bool = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["autoRenewPeriod", b"autoRenewPeriod", "key", b"key", "newRealmAdminKey", b"newRealmAdminKey", "proxyAccountID", b"proxyAccountID", "realmID", b"realmID", "shardID", b"shardID", "staked_account_id", b"staked_account_id", "staked_id", b"staked_id", "staked_node_id", b"staked_node_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["autoRenewPeriod", b"autoRenewPeriod", "decline_reward", b"decline_reward", "initialBalance", b"initialBalance", "key", b"key", "max_automatic_token_associations", b"max_automatic_token_associations", "memo", b"memo", "newRealmAdminKey", b"newRealmAdminKey", "proxyAccountID", b"proxyAccountID", "realmID", b"realmID", "receiveRecordThreshold", b"receiveRecordThreshold", "receiverSigRequired", b"receiverSigRequired", "sendRecordThreshold", b"sendRecordThreshold", "shardID", b"shardID", "staked_account_id", b"staked_account_id", "staked_id", b"staked_id", "staked_node_id", b"staked_node_id"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["staked_id", b"staked_id"]) -> typing_extensions.Literal["staked_account_id", "staked_node_id"] | None: ... + +global___CryptoCreateTransactionBody = CryptoCreateTransactionBody diff --git a/proto/crypto_transfer.proto b/proto/crypto_transfer.proto new file mode 100644 index 00000000..9b59bf3d --- /dev/null +++ b/proto/crypto_transfer.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; +import "proto/basic_types.proto"; + +/** + * Transfers cryptocurrency among two or more accounts by making the desired + * adjustments to their balances. Each transfer list can specify up to 10 + * adjustments. Each negative amount is withdrawn from the corresponding + * account (a sender), and each positive one is added to the corresponding + * account (a receiver). The amounts list must sum to zero. Each amount is a + * number of tinybars (there are 100,000,000 tinybars in one hbar). If any + * sender account fails to have sufficient hbars, then the entire transaction + * fails, and none of those transfers occur, though the transaction fee is + * still charged. This transaction must be signed by the keys for all the + * sending accounts, and for any receiving accounts that have + * receiverSigRequired == true. The signatures are in the same order as the + * accounts, skipping those accounts that don't need a signature. + */ +message CryptoTransferTransactionBody { + /** + * The desired hbar balance adjustments + */ + TransferList transfers = 1; + + /** + * The desired token unit balance adjustments; if any custom fees are + * assessed, the ledger will try to deduct them from the payer of this + * CryptoTransfer, resolving the transaction to + * INSUFFICIENT_PAYER_BALANCE_FOR_CUSTOM_FEE if this is not possible + * Limited to 1 here + */ + repeated TokenTransferList tokenTransfers = 2 [ (nanopb).max_count = 1 ]; +} \ No newline at end of file diff --git a/proto/crypto_transfer_pb2.py b/proto/crypto_transfer_pb2.py new file mode 100644 index 00000000..9c40c036 --- /dev/null +++ b/proto/crypto_transfer_pb2.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/crypto_transfer.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 +from proto import basic_types_pb2 as proto_dot_basic__types__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bproto/crypto_transfer.proto\x12\x06Hedera\x1a\x0cnanopb.proto\x1a\x17proto/basic_types.proto\"\x82\x01\n\x1d\x43ryptoTransferTransactionBody\x12\'\n\ttransfers\x18\x01 \x01(\x0b\x32\x14.Hedera.TransferList\x12\x38\n\x0etokenTransfers\x18\x02 \x03(\x0b\x32\x19.Hedera.TokenTransferListB\x05\x92?\x02\x10\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.crypto_transfer_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _CRYPTOTRANSFERTRANSACTIONBODY.fields_by_name['tokenTransfers']._options = None + _CRYPTOTRANSFERTRANSACTIONBODY.fields_by_name['tokenTransfers']._serialized_options = b'\222?\002\020\001' + _CRYPTOTRANSFERTRANSACTIONBODY._serialized_start=79 + _CRYPTOTRANSFERTRANSACTIONBODY._serialized_end=209 +# @@protoc_insertion_point(module_scope) diff --git a/proto/crypto_transfer_pb2.pyi b/proto/crypto_transfer_pb2.pyi new file mode 100644 index 00000000..831e8333 --- /dev/null +++ b/proto/crypto_transfer_pb2.pyi @@ -0,0 +1,64 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import proto.basic_types_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class CryptoTransferTransactionBody(google.protobuf.message.Message): + """* + Transfers cryptocurrency among two or more accounts by making the desired + adjustments to their balances. Each transfer list can specify up to 10 + adjustments. Each negative amount is withdrawn from the corresponding + account (a sender), and each positive one is added to the corresponding + account (a receiver). The amounts list must sum to zero. Each amount is a + number of tinybars (there are 100,000,000 tinybars in one hbar). If any + sender account fails to have sufficient hbars, then the entire transaction + fails, and none of those transfers occur, though the transaction fee is + still charged. This transaction must be signed by the keys for all the + sending accounts, and for any receiving accounts that have + receiverSigRequired == true. The signatures are in the same order as the + accounts, skipping those accounts that don't need a signature. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRANSFERS_FIELD_NUMBER: builtins.int + TOKENTRANSFERS_FIELD_NUMBER: builtins.int + @property + def transfers(self) -> proto.basic_types_pb2.TransferList: + """* + The desired hbar balance adjustments + """ + @property + def tokenTransfers(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[proto.basic_types_pb2.TokenTransferList]: + """* + The desired token unit balance adjustments; if any custom fees are + assessed, the ledger will try to deduct them from the payer of this + CryptoTransfer, resolving the transaction to + INSUFFICIENT_PAYER_BALANCE_FOR_CUSTOM_FEE if this is not possible + Limited to 1 here + """ + def __init__( + self, + *, + transfers: proto.basic_types_pb2.TransferList | None = ..., + tokenTransfers: collections.abc.Iterable[proto.basic_types_pb2.TokenTransferList] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["transfers", b"transfers"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["tokenTransfers", b"tokenTransfers", "transfers", b"transfers"]) -> None: ... + +global___CryptoTransferTransactionBody = CryptoTransferTransactionBody diff --git a/proto/crypto_update.proto b/proto/crypto_update.proto new file mode 100644 index 00000000..a60c27bc --- /dev/null +++ b/proto/crypto_update.proto @@ -0,0 +1,157 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; +import "proto/basic_types.proto"; +import "proto/duration.proto"; +import "proto/timestamp.proto"; +import "proto/wrappers.proto"; + +/** + * Change properties for the given account. Any null field is ignored (left + * unchanged). This transaction must be signed by the existing key for this + * account. If the transaction is changing the key field, then the transaction + * must be signed by both the old key (from before the change) and the new key. + * The old key must sign for security. The new key must sign as a safeguard to + * avoid accidentally changing to an invalid key, and then having no way to + * recover. + */ +message CryptoUpdateTransactionBody { + /** + * The account ID which is being updated in this transaction + */ + AccountID accountIDToUpdate = 2; + + /** + * The new key + */ + Key key = 3; + + /** + * [Deprecated] ID of the account to which this account is proxy staked. If + * proxyAccountID is null, or is an invalid account, or is an account that + * isn't a node, then this account is automatically proxy staked to a node + * chosen by the network, but without earning payments. If the proxyAccountID + * account refuses to accept proxy staking , or if it is not currently running + * a node, then it will behave as if proxyAccountID was null. + */ + AccountID proxyAccountID = 4 [ deprecated = true ]; + + /** + * [Deprecated]. Payments earned from proxy staking are shared between the + * node and this account, with proxyFraction / 10000 going to this account + */ + int32 proxyFraction = 5 [ deprecated = true ]; + + oneof sendRecordThresholdField { + /** + * [Deprecated]. The new threshold amount (in tinybars) for which an account + * record is created for any send/withdraw transaction + */ + uint64 sendRecordThreshold = 6 [ deprecated = true ]; + + /** + * [Deprecated]. The new threshold amount (in tinybars) for which an account + * record is created for any send/withdraw transaction + */ + UInt64Value sendRecordThresholdWrapper = 11 [ deprecated = true ]; + } + + oneof receiveRecordThresholdField { + /** + * [Deprecated]. The new threshold amount (in tinybars) for which an account + * record is created for any receive/deposit transaction. + */ + uint64 receiveRecordThreshold = 7 [ deprecated = true ]; + + /** + * [Deprecated]. The new threshold amount (in tinybars) for which an account + * record is created for any receive/deposit transaction. + */ + UInt64Value receiveRecordThresholdWrapper = 12 [ deprecated = true ]; + } + + /** + * The duration in which it will automatically extend the expiration period. + * If it doesn't have enough balance, it extends as long as possible. If it is + * empty when it expires, then it is deleted. + */ + Duration autoRenewPeriod = 8; + + /** + * The new expiration time to extend to (ignored if equal to or before the + * current one) + */ + Timestamp expirationTime = 9; + + oneof receiverSigRequiredField { + /** + * [Deprecated] Do NOT use this field to set a false value because the + * server cannot distinguish from the default value. Use + * receiverSigRequiredWrapper field for this purpose. + */ + bool receiverSigRequired = 10 [ deprecated = true ]; + + /** + * If true, this account's key must sign any transaction depositing into + * this account (in addition to all withdrawals) + */ + BoolValue receiverSigRequiredWrapper = 13; + } + + /** + * If set, the new memo to be associated with the account (UTF-8 encoding max + * 100 bytes) + */ + StringValue memo = 14; + + /** + * The maximum number of tokens that an Account can be implicitly associated + * with. Up to a 1000 including implicit and explicit associations. + */ + Int32Value max_automatic_token_associations = 15; + + /** + * ID of the account or node to which this account is staking. + */ + oneof staked_id { + /** + * ID of the new account to which this account is staking. If set to the + * sentinel 0.0.0 AccountID, this field removes this account's + * staked account ID. + */ + AccountID staked_account_id = 16; + + /** + * ID of the new node this account is staked to. If set to the sentinel + * -1, this field removes this account's staked node ID. + */ + int64 staked_node_id = 17; + } + + /** + * If true, the account declines receiving a staking reward. The default value + * is false. + */ + BoolValue decline_reward = 18; +} \ No newline at end of file diff --git a/proto/crypto_update_pb2.py b/proto/crypto_update_pb2.py new file mode 100644 index 00000000..1936fb2f --- /dev/null +++ b/proto/crypto_update_pb2.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/crypto_update.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 +from proto import basic_types_pb2 as proto_dot_basic__types__pb2 +from proto import duration_pb2 as proto_dot_duration__pb2 +from proto import timestamp_pb2 as proto_dot_timestamp__pb2 +from proto import wrappers_pb2 as proto_dot_wrappers__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19proto/crypto_update.proto\x12\x06Hedera\x1a\x0cnanopb.proto\x1a\x17proto/basic_types.proto\x1a\x14proto/duration.proto\x1a\x15proto/timestamp.proto\x1a\x14proto/wrappers.proto\"\xe5\x06\n\x1b\x43ryptoUpdateTransactionBody\x12,\n\x11\x61\x63\x63ountIDToUpdate\x18\x02 \x01(\x0b\x32\x11.Hedera.AccountID\x12\x18\n\x03key\x18\x03 \x01(\x0b\x32\x0b.Hedera.Key\x12-\n\x0eproxyAccountID\x18\x04 \x01(\x0b\x32\x11.Hedera.AccountIDB\x02\x18\x01\x12\x19\n\rproxyFraction\x18\x05 \x01(\x05\x42\x02\x18\x01\x12!\n\x13sendRecordThreshold\x18\x06 \x01(\x04\x42\x02\x18\x01H\x00\x12=\n\x1asendRecordThresholdWrapper\x18\x0b \x01(\x0b\x32\x13.Hedera.UInt64ValueB\x02\x18\x01H\x00\x12$\n\x16receiveRecordThreshold\x18\x07 \x01(\x04\x42\x02\x18\x01H\x01\x12@\n\x1dreceiveRecordThresholdWrapper\x18\x0c \x01(\x0b\x32\x13.Hedera.UInt64ValueB\x02\x18\x01H\x01\x12)\n\x0f\x61utoRenewPeriod\x18\x08 \x01(\x0b\x32\x10.Hedera.Duration\x12)\n\x0e\x65xpirationTime\x18\t \x01(\x0b\x32\x11.Hedera.Timestamp\x12!\n\x13receiverSigRequired\x18\n \x01(\x08\x42\x02\x18\x01H\x02\x12\x37\n\x1areceiverSigRequiredWrapper\x18\r \x01(\x0b\x32\x11.Hedera.BoolValueH\x02\x12!\n\x04memo\x18\x0e \x01(\x0b\x32\x13.Hedera.StringValue\x12<\n max_automatic_token_associations\x18\x0f \x01(\x0b\x32\x12.Hedera.Int32Value\x12.\n\x11staked_account_id\x18\x10 \x01(\x0b\x32\x11.Hedera.AccountIDH\x03\x12\x18\n\x0estaked_node_id\x18\x11 \x01(\x03H\x03\x12)\n\x0e\x64\x65\x63line_reward\x18\x12 \x01(\x0b\x32\x11.Hedera.BoolValueB\x1a\n\x18sendRecordThresholdFieldB\x1d\n\x1breceiveRecordThresholdFieldB\x1a\n\x18receiverSigRequiredFieldB\x0b\n\tstaked_idb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.crypto_update_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['proxyAccountID']._options = None + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['proxyAccountID']._serialized_options = b'\030\001' + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['proxyFraction']._options = None + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['proxyFraction']._serialized_options = b'\030\001' + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['sendRecordThreshold']._options = None + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['sendRecordThreshold']._serialized_options = b'\030\001' + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['sendRecordThresholdWrapper']._options = None + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['sendRecordThresholdWrapper']._serialized_options = b'\030\001' + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['receiveRecordThreshold']._options = None + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['receiveRecordThreshold']._serialized_options = b'\030\001' + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['receiveRecordThresholdWrapper']._options = None + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['receiveRecordThresholdWrapper']._serialized_options = b'\030\001' + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['receiverSigRequired']._options = None + _CRYPTOUPDATETRANSACTIONBODY.fields_by_name['receiverSigRequired']._serialized_options = b'\030\001' + _CRYPTOUPDATETRANSACTIONBODY._serialized_start=144 + _CRYPTOUPDATETRANSACTIONBODY._serialized_end=1013 +# @@protoc_insertion_point(module_scope) diff --git a/proto/crypto_update_pb2.pyi b/proto/crypto_update_pb2.pyi new file mode 100644 index 00000000..e1ca7bc6 --- /dev/null +++ b/proto/crypto_update_pb2.pyi @@ -0,0 +1,187 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import proto.basic_types_pb2 +import proto.duration_pb2 +import proto.timestamp_pb2 +import proto.wrappers_pb2 +import sys +import typing + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class CryptoUpdateTransactionBody(google.protobuf.message.Message): + """* + Change properties for the given account. Any null field is ignored (left + unchanged). This transaction must be signed by the existing key for this + account. If the transaction is changing the key field, then the transaction + must be signed by both the old key (from before the change) and the new key. + The old key must sign for security. The new key must sign as a safeguard to + avoid accidentally changing to an invalid key, and then having no way to + recover. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ACCOUNTIDTOUPDATE_FIELD_NUMBER: builtins.int + KEY_FIELD_NUMBER: builtins.int + PROXYACCOUNTID_FIELD_NUMBER: builtins.int + PROXYFRACTION_FIELD_NUMBER: builtins.int + SENDRECORDTHRESHOLD_FIELD_NUMBER: builtins.int + SENDRECORDTHRESHOLDWRAPPER_FIELD_NUMBER: builtins.int + RECEIVERECORDTHRESHOLD_FIELD_NUMBER: builtins.int + RECEIVERECORDTHRESHOLDWRAPPER_FIELD_NUMBER: builtins.int + AUTORENEWPERIOD_FIELD_NUMBER: builtins.int + EXPIRATIONTIME_FIELD_NUMBER: builtins.int + RECEIVERSIGREQUIRED_FIELD_NUMBER: builtins.int + RECEIVERSIGREQUIREDWRAPPER_FIELD_NUMBER: builtins.int + MEMO_FIELD_NUMBER: builtins.int + MAX_AUTOMATIC_TOKEN_ASSOCIATIONS_FIELD_NUMBER: builtins.int + STAKED_ACCOUNT_ID_FIELD_NUMBER: builtins.int + STAKED_NODE_ID_FIELD_NUMBER: builtins.int + DECLINE_REWARD_FIELD_NUMBER: builtins.int + @property + def accountIDToUpdate(self) -> proto.basic_types_pb2.AccountID: + """* + The account ID which is being updated in this transaction + """ + @property + def key(self) -> proto.basic_types_pb2.Key: + """* + The new key + """ + @property + def proxyAccountID(self) -> proto.basic_types_pb2.AccountID: + """* + [Deprecated] ID of the account to which this account is proxy staked. If + proxyAccountID is null, or is an invalid account, or is an account that + isn't a node, then this account is automatically proxy staked to a node + chosen by the network, but without earning payments. If the proxyAccountID + account refuses to accept proxy staking , or if it is not currently running + a node, then it will behave as if proxyAccountID was null. + """ + proxyFraction: builtins.int + """* + [Deprecated]. Payments earned from proxy staking are shared between the + node and this account, with proxyFraction / 10000 going to this account + """ + sendRecordThreshold: builtins.int + """* + [Deprecated]. The new threshold amount (in tinybars) for which an account + record is created for any send/withdraw transaction + """ + @property + def sendRecordThresholdWrapper(self) -> proto.wrappers_pb2.UInt64Value: + """* + [Deprecated]. The new threshold amount (in tinybars) for which an account + record is created for any send/withdraw transaction + """ + receiveRecordThreshold: builtins.int + """* + [Deprecated]. The new threshold amount (in tinybars) for which an account + record is created for any receive/deposit transaction. + """ + @property + def receiveRecordThresholdWrapper(self) -> proto.wrappers_pb2.UInt64Value: + """* + [Deprecated]. The new threshold amount (in tinybars) for which an account + record is created for any receive/deposit transaction. + """ + @property + def autoRenewPeriod(self) -> proto.duration_pb2.Duration: + """* + The duration in which it will automatically extend the expiration period. + If it doesn't have enough balance, it extends as long as possible. If it is + empty when it expires, then it is deleted. + """ + @property + def expirationTime(self) -> proto.timestamp_pb2.Timestamp: + """* + The new expiration time to extend to (ignored if equal to or before the + current one) + """ + receiverSigRequired: builtins.bool + """* + [Deprecated] Do NOT use this field to set a false value because the + server cannot distinguish from the default value. Use + receiverSigRequiredWrapper field for this purpose. + """ + @property + def receiverSigRequiredWrapper(self) -> proto.wrappers_pb2.BoolValue: + """* + If true, this account's key must sign any transaction depositing into + this account (in addition to all withdrawals) + """ + @property + def memo(self) -> proto.wrappers_pb2.StringValue: + """* + If set, the new memo to be associated with the account (UTF-8 encoding max + 100 bytes) + """ + @property + def max_automatic_token_associations(self) -> proto.wrappers_pb2.Int32Value: + """* + The maximum number of tokens that an Account can be implicitly associated + with. Up to a 1000 including implicit and explicit associations. + """ + @property + def staked_account_id(self) -> proto.basic_types_pb2.AccountID: + """* + ID of the new account to which this account is staking. If set to the + sentinel 0.0.0 AccountID, this field removes this account's + staked account ID. + """ + staked_node_id: builtins.int + """* + ID of the new node this account is staked to. If set to the sentinel + -1, this field removes this account's staked node ID. + """ + @property + def decline_reward(self) -> proto.wrappers_pb2.BoolValue: + """* + If true, the account declines receiving a staking reward. The default value + is false. + """ + def __init__( + self, + *, + accountIDToUpdate: proto.basic_types_pb2.AccountID | None = ..., + key: proto.basic_types_pb2.Key | None = ..., + proxyAccountID: proto.basic_types_pb2.AccountID | None = ..., + proxyFraction: builtins.int = ..., + sendRecordThreshold: builtins.int = ..., + sendRecordThresholdWrapper: proto.wrappers_pb2.UInt64Value | None = ..., + receiveRecordThreshold: builtins.int = ..., + receiveRecordThresholdWrapper: proto.wrappers_pb2.UInt64Value | None = ..., + autoRenewPeriod: proto.duration_pb2.Duration | None = ..., + expirationTime: proto.timestamp_pb2.Timestamp | None = ..., + receiverSigRequired: builtins.bool = ..., + receiverSigRequiredWrapper: proto.wrappers_pb2.BoolValue | None = ..., + memo: proto.wrappers_pb2.StringValue | None = ..., + max_automatic_token_associations: proto.wrappers_pb2.Int32Value | None = ..., + staked_account_id: proto.basic_types_pb2.AccountID | None = ..., + staked_node_id: builtins.int = ..., + decline_reward: proto.wrappers_pb2.BoolValue | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["accountIDToUpdate", b"accountIDToUpdate", "autoRenewPeriod", b"autoRenewPeriod", "decline_reward", b"decline_reward", "expirationTime", b"expirationTime", "key", b"key", "max_automatic_token_associations", b"max_automatic_token_associations", "memo", b"memo", "proxyAccountID", b"proxyAccountID", "receiveRecordThreshold", b"receiveRecordThreshold", "receiveRecordThresholdField", b"receiveRecordThresholdField", "receiveRecordThresholdWrapper", b"receiveRecordThresholdWrapper", "receiverSigRequired", b"receiverSigRequired", "receiverSigRequiredField", b"receiverSigRequiredField", "receiverSigRequiredWrapper", b"receiverSigRequiredWrapper", "sendRecordThreshold", b"sendRecordThreshold", "sendRecordThresholdField", b"sendRecordThresholdField", "sendRecordThresholdWrapper", b"sendRecordThresholdWrapper", "staked_account_id", b"staked_account_id", "staked_id", b"staked_id", "staked_node_id", b"staked_node_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["accountIDToUpdate", b"accountIDToUpdate", "autoRenewPeriod", b"autoRenewPeriod", "decline_reward", b"decline_reward", "expirationTime", b"expirationTime", "key", b"key", "max_automatic_token_associations", b"max_automatic_token_associations", "memo", b"memo", "proxyAccountID", b"proxyAccountID", "proxyFraction", b"proxyFraction", "receiveRecordThreshold", b"receiveRecordThreshold", "receiveRecordThresholdField", b"receiveRecordThresholdField", "receiveRecordThresholdWrapper", b"receiveRecordThresholdWrapper", "receiverSigRequired", b"receiverSigRequired", "receiverSigRequiredField", b"receiverSigRequiredField", "receiverSigRequiredWrapper", b"receiverSigRequiredWrapper", "sendRecordThreshold", b"sendRecordThreshold", "sendRecordThresholdField", b"sendRecordThresholdField", "sendRecordThresholdWrapper", b"sendRecordThresholdWrapper", "staked_account_id", b"staked_account_id", "staked_id", b"staked_id", "staked_node_id", b"staked_node_id"]) -> None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["receiveRecordThresholdField", b"receiveRecordThresholdField"]) -> typing_extensions.Literal["receiveRecordThreshold", "receiveRecordThresholdWrapper"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["receiverSigRequiredField", b"receiverSigRequiredField"]) -> typing_extensions.Literal["receiverSigRequired", "receiverSigRequiredWrapper"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["sendRecordThresholdField", b"sendRecordThresholdField"]) -> typing_extensions.Literal["sendRecordThreshold", "sendRecordThresholdWrapper"] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing_extensions.Literal["staked_id", b"staked_id"]) -> typing_extensions.Literal["staked_account_id", "staked_node_id"] | None: ... + +global___CryptoUpdateTransactionBody = CryptoUpdateTransactionBody diff --git a/proto/duration.proto b/proto/duration.proto new file mode 100644 index 00000000..9d7514c7 --- /dev/null +++ b/proto/duration.proto @@ -0,0 +1,34 @@ + +syntax = "proto3"; + +package Hedera; +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; + +/** + * A length of time in seconds. + */ +message Duration { + /** + * The number of seconds + */ + int64 seconds = 1; +} \ No newline at end of file diff --git a/proto/duration_pb2.py b/proto/duration_pb2.py new file mode 100644 index 00000000..7dae9331 --- /dev/null +++ b/proto/duration_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/duration.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14proto/duration.proto\x12\x06Hedera\x1a\x0cnanopb.proto\"\x1b\n\x08\x44uration\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.duration_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _DURATION._serialized_start=46 + _DURATION._serialized_end=73 +# @@protoc_insertion_point(module_scope) diff --git a/proto/duration_pb2.pyi b/proto/duration_pb2.pyi new file mode 100644 index 00000000..656be12d --- /dev/null +++ b/proto/duration_pb2.pyi @@ -0,0 +1,37 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class Duration(google.protobuf.message.Message): + """* + A length of time in seconds. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SECONDS_FIELD_NUMBER: builtins.int + seconds: builtins.int + """* + The number of seconds + """ + def __init__( + self, + *, + seconds: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["seconds", b"seconds"]) -> None: ... + +global___Duration = Duration diff --git a/proto/timestamp.proto b/proto/timestamp.proto new file mode 100644 index 00000000..4c898e45 --- /dev/null +++ b/proto/timestamp.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2022 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; + +/** + * An exact date and time. This is the same data structure as the protobuf + * Timestamp.proto (see the comments in + * https://github.com/google/protobuf + * /blob/master/src/google/protobuf/timestamp.proto) + */ +message Timestamp { + /** + * Number of complete seconds since the start of the epoch + */ + int64 seconds = 1; + + /** + * Number of nanoseconds since the start of the last second + */ + int32 nanos = 2; +} + +/** + * An exact date and time, with a resolution of one second (no nanoseconds). + */ +message TimestampSeconds { + /** + * Number of complete seconds since the start of the epoch + */ + int64 seconds = 1; +} diff --git a/proto/timestamp_pb2.py b/proto/timestamp_pb2.py new file mode 100644 index 00000000..ee8d3d1e --- /dev/null +++ b/proto/timestamp_pb2.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/timestamp.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15proto/timestamp.proto\x12\x06Hedera\x1a\x0cnanopb.proto\"+\n\tTimestamp\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x12\r\n\x05nanos\x18\x02 \x01(\x05\"#\n\x10TimestampSeconds\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.timestamp_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _TIMESTAMP._serialized_start=47 + _TIMESTAMP._serialized_end=90 + _TIMESTAMPSECONDS._serialized_start=92 + _TIMESTAMPSECONDS._serialized_end=127 +# @@protoc_insertion_point(module_scope) diff --git a/proto/timestamp_pb2.pyi b/proto/timestamp_pb2.pyi new file mode 100644 index 00000000..883e7eb1 --- /dev/null +++ b/proto/timestamp_pb2.pyi @@ -0,0 +1,68 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class Timestamp(google.protobuf.message.Message): + """* + An exact date and time. This is the same data structure as the protobuf + Timestamp.proto (see the comments in + https://github.com/google/protobuf + /blob/master/src/google/protobuf/timestamp.proto) + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SECONDS_FIELD_NUMBER: builtins.int + NANOS_FIELD_NUMBER: builtins.int + seconds: builtins.int + """* + Number of complete seconds since the start of the epoch + """ + nanos: builtins.int + """* + Number of nanoseconds since the start of the last second + """ + def __init__( + self, + *, + seconds: builtins.int = ..., + nanos: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["nanos", b"nanos", "seconds", b"seconds"]) -> None: ... + +global___Timestamp = Timestamp + +@typing_extensions.final +class TimestampSeconds(google.protobuf.message.Message): + """* + An exact date and time, with a resolution of one second (no nanoseconds). + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SECONDS_FIELD_NUMBER: builtins.int + seconds: builtins.int + """* + Number of complete seconds since the start of the epoch + """ + def __init__( + self, + *, + seconds: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["seconds", b"seconds"]) -> None: ... + +global___TimestampSeconds = TimestampSeconds diff --git a/proto/token_associate.proto b/proto/token_associate.proto new file mode 100644 index 00000000..2a28fce5 --- /dev/null +++ b/proto/token_associate.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; +import "proto/basic_types.proto"; + +/** + * Associates the provided account with the provided tokens. Must be signed by + * the provided Account's key. If the provided account is not found, the + * transaction will resolve to INVALID_ACCOUNT_ID. If the provided account has + * been deleted, the transaction will resolve to ACCOUNT_DELETED. If any of the + * provided tokens is not found, the transaction will resolve to + * INVALID_TOKEN_REF. If any of the provided tokens has been deleted, the + * transaction will resolve to TOKEN_WAS_DELETED. If an association between the + * provided account and any of the tokens already exists, the transaction will + * resolve to TOKEN_ALREADY_ASSOCIATED_TO_ACCOUNT. If the provided account's + * associations count exceed the constraint of maximum token associations per + * account, the transaction will resolve to TOKENS_PER_ACCOUNT_LIMIT_EXCEEDED. + * On success, associations between the provided account and tokens are made and + * the account is ready to interact with the tokens. + */ +message TokenAssociateTransactionBody { + /** + * The account to be associated with the provided tokens + */ + AccountID account = 1; + + /** + * The tokens to be associated with the provided account. In the case of + * NON_FUNGIBLE_UNIQUE Type, once an account is associated, it can hold any + * number of NFTs (serial numbers) of that token type + * Limited to 1 here (no access to malloc for dynamic decode!) + */ + repeated TokenID tokens = 2 [ (nanopb).max_count = 1 ]; +} \ No newline at end of file diff --git a/proto/token_associate_pb2.py b/proto/token_associate_pb2.py new file mode 100644 index 00000000..21ee64af --- /dev/null +++ b/proto/token_associate_pb2.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/token_associate.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 +from proto import basic_types_pb2 as proto_dot_basic__types__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bproto/token_associate.proto\x12\x06Hedera\x1a\x0cnanopb.proto\x1a\x17proto/basic_types.proto\"k\n\x1dTokenAssociateTransactionBody\x12\"\n\x07\x61\x63\x63ount\x18\x01 \x01(\x0b\x32\x11.Hedera.AccountID\x12&\n\x06tokens\x18\x02 \x03(\x0b\x32\x0f.Hedera.TokenIDB\x05\x92?\x02\x10\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.token_associate_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _TOKENASSOCIATETRANSACTIONBODY.fields_by_name['tokens']._options = None + _TOKENASSOCIATETRANSACTIONBODY.fields_by_name['tokens']._serialized_options = b'\222?\002\020\001' + _TOKENASSOCIATETRANSACTIONBODY._serialized_start=78 + _TOKENASSOCIATETRANSACTIONBODY._serialized_end=185 +# @@protoc_insertion_point(module_scope) diff --git a/proto/token_associate_pb2.pyi b/proto/token_associate_pb2.pyi new file mode 100644 index 00000000..bd01a227 --- /dev/null +++ b/proto/token_associate_pb2.pyi @@ -0,0 +1,64 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import proto.basic_types_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class TokenAssociateTransactionBody(google.protobuf.message.Message): + """* + Associates the provided account with the provided tokens. Must be signed by + the provided Account's key. If the provided account is not found, the + transaction will resolve to INVALID_ACCOUNT_ID. If the provided account has + been deleted, the transaction will resolve to ACCOUNT_DELETED. If any of the + provided tokens is not found, the transaction will resolve to + INVALID_TOKEN_REF. If any of the provided tokens has been deleted, the + transaction will resolve to TOKEN_WAS_DELETED. If an association between the + provided account and any of the tokens already exists, the transaction will + resolve to TOKEN_ALREADY_ASSOCIATED_TO_ACCOUNT. If the provided account's + associations count exceed the constraint of maximum token associations per + account, the transaction will resolve to TOKENS_PER_ACCOUNT_LIMIT_EXCEEDED. + On success, associations between the provided account and tokens are made and + the account is ready to interact with the tokens. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ACCOUNT_FIELD_NUMBER: builtins.int + TOKENS_FIELD_NUMBER: builtins.int + @property + def account(self) -> proto.basic_types_pb2.AccountID: + """* + The account to be associated with the provided tokens + """ + @property + def tokens(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[proto.basic_types_pb2.TokenID]: + """* + The tokens to be associated with the provided account. In the case of + NON_FUNGIBLE_UNIQUE Type, once an account is associated, it can hold any + number of NFTs (serial numbers) of that token type + Limited to 1 here (no access to malloc for dynamic decode!) + """ + def __init__( + self, + *, + account: proto.basic_types_pb2.AccountID | None = ..., + tokens: collections.abc.Iterable[proto.basic_types_pb2.TokenID] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["account", b"account"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["account", b"account", "tokens", b"tokens"]) -> None: ... + +global___TokenAssociateTransactionBody = TokenAssociateTransactionBody diff --git a/proto/token_burn.proto b/proto/token_burn.proto new file mode 100644 index 00000000..d89dda89 --- /dev/null +++ b/proto/token_burn.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; +import "proto/basic_types.proto"; + +/** + * Burns tokens from the Token's treasury Account. If no Supply Key is defined, + * the transaction will resolve to TOKEN_HAS_NO_SUPPLY_KEY. The operation + * decreases the Total Supply of the Token. Total supply cannot go below zero. + * The amount provided must be in the lowest denomination possible. Example: + * Token A has 2 decimals. In order to burn 100 tokens, one must provide amount + * of 10000. In order to burn 100.55 tokens, one must provide amount of 10055. + * For non fungible tokens the transaction body accepts serialNumbers list of + * integers as a parameter. + * + * If neither the amount nor the serialNumbers get filled, a + * INVALID_TOKEN_BURN_AMOUNT response code will be returned. If both amount and + * serialNumbers get filled, a INVALID_TRANSACTION_BODY response code will be + * returned. + * If the serialNumbers' list count is greater than the batch size limit global + * dynamic property, a BATCH_SIZE_LIMIT_EXCEEDED response code will be returned. + * If the serialNumbers list contains a non-positive integer as a serial number, + * a INVALID_NFT_ID response code will be returned. + */ +message TokenBurnTransactionBody { + /** + * The token for which to burn tokens. If token does not exist, transaction + * results in INVALID_TOKEN_ID + */ + TokenID token = 1; + + /** + * Applicable to tokens of type FUNGIBLE_COMMON. The amount to burn from the + * Treasury Account. Amount must be a positive non-zero number, not bigger + * than the token balance of the treasury account (0; balance], represented in + * the lowest denomination. + */ + uint64 amount = 2; + + /** + * Applicable to tokens of type NON_FUNGIBLE_UNIQUE. The list of serial + * numbers to be burned. + * Limited to 1 here + */ + repeated int64 serialNumbers = 3 [ (nanopb).max_count = 1 ]; +} \ No newline at end of file diff --git a/proto/token_burn_pb2.py b/proto/token_burn_pb2.py new file mode 100644 index 00000000..05efdf3f --- /dev/null +++ b/proto/token_burn_pb2.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/token_burn.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 +from proto import basic_types_pb2 as proto_dot_basic__types__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16proto/token_burn.proto\x12\x06Hedera\x1a\x0cnanopb.proto\x1a\x17proto/basic_types.proto\"h\n\x18TokenBurnTransactionBody\x12\x1e\n\x05token\x18\x01 \x01(\x0b\x32\x0f.Hedera.TokenID\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x1c\n\rserialNumbers\x18\x03 \x03(\x03\x42\x05\x92?\x02\x10\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.token_burn_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _TOKENBURNTRANSACTIONBODY.fields_by_name['serialNumbers']._options = None + _TOKENBURNTRANSACTIONBODY.fields_by_name['serialNumbers']._serialized_options = b'\222?\002\020\001' + _TOKENBURNTRANSACTIONBODY._serialized_start=73 + _TOKENBURNTRANSACTIONBODY._serialized_end=177 +# @@protoc_insertion_point(module_scope) diff --git a/proto/token_burn_pb2.pyi b/proto/token_burn_pb2.pyi new file mode 100644 index 00000000..dc77477a --- /dev/null +++ b/proto/token_burn_pb2.pyi @@ -0,0 +1,77 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import proto.basic_types_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class TokenBurnTransactionBody(google.protobuf.message.Message): + """* + Burns tokens from the Token's treasury Account. If no Supply Key is defined, + the transaction will resolve to TOKEN_HAS_NO_SUPPLY_KEY. The operation + decreases the Total Supply of the Token. Total supply cannot go below zero. + The amount provided must be in the lowest denomination possible. Example: + Token A has 2 decimals. In order to burn 100 tokens, one must provide amount + of 10000. In order to burn 100.55 tokens, one must provide amount of 10055. + For non fungible tokens the transaction body accepts serialNumbers list of + integers as a parameter. + + If neither the amount nor the serialNumbers get filled, a + INVALID_TOKEN_BURN_AMOUNT response code will be returned. If both amount and + serialNumbers get filled, a INVALID_TRANSACTION_BODY response code will be + returned. + If the serialNumbers' list count is greater than the batch size limit global + dynamic property, a BATCH_SIZE_LIMIT_EXCEEDED response code will be returned. + If the serialNumbers list contains a non-positive integer as a serial number, + a INVALID_NFT_ID response code will be returned. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TOKEN_FIELD_NUMBER: builtins.int + AMOUNT_FIELD_NUMBER: builtins.int + SERIALNUMBERS_FIELD_NUMBER: builtins.int + @property + def token(self) -> proto.basic_types_pb2.TokenID: + """* + The token for which to burn tokens. If token does not exist, transaction + results in INVALID_TOKEN_ID + """ + amount: builtins.int + """* + Applicable to tokens of type FUNGIBLE_COMMON. The amount to burn from the + Treasury Account. Amount must be a positive non-zero number, not bigger + than the token balance of the treasury account (0; balance], represented in + the lowest denomination. + """ + @property + def serialNumbers(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """* + Applicable to tokens of type NON_FUNGIBLE_UNIQUE. The list of serial + numbers to be burned. + Limited to 1 here + """ + def __init__( + self, + *, + token: proto.basic_types_pb2.TokenID | None = ..., + amount: builtins.int = ..., + serialNumbers: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["token", b"token"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["amount", b"amount", "serialNumbers", b"serialNumbers", "token", b"token"]) -> None: ... + +global___TokenBurnTransactionBody = TokenBurnTransactionBody diff --git a/proto/token_dissociate.proto b/proto/token_dissociate.proto new file mode 100644 index 00000000..4e75f3c7 --- /dev/null +++ b/proto/token_dissociate.proto @@ -0,0 +1,56 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; +import "proto/basic_types.proto"; + +/** + * Dissociates the provided account with the provided tokens. Must be signed by + * the provided Account's key. If the provided account is not found, the + * transaction will resolve to INVALID_ACCOUNT_ID. If the provided account has + * been deleted, the transaction will resolve to ACCOUNT_DELETED. If any of the + * provided tokens is not found, the transaction will resolve to + * INVALID_TOKEN_REF. If any of the provided tokens has been deleted, the + * transaction will resolve to TOKEN_WAS_DELETED. If an association between the + * provided account and any of the tokens does not exist, the transaction will + * resolve to TOKEN_NOT_ASSOCIATED_TO_ACCOUNT. If a token has not been deleted + * and has not expired, and the user has a nonzero balance, the transaction will + * resolve to TRANSACTION_REQUIRES_ZERO_TOKEN_BALANCES. If a fungible + * token has expired, the user can disassociate even if their token balance + * is not zero. If a non fungible token has expired, the user can + * not disassociate if their token balance is not zero. The transaction + * will resolve to TRANSACTION_REQUIRED_ZERO_TOKEN_BALANCES. On success, + * associations between the provided account and tokens are removed. + */ +message TokenDissociateTransactionBody { + /** + * The account to be dissociated with the provided tokens + */ + AccountID account = 1; + + /** + * The tokens to be dissociated with the provided account + * Limited to 1 here + */ + repeated TokenID tokens = 2 [ (nanopb).max_count = 1 ]; +} \ No newline at end of file diff --git a/proto/token_dissociate_pb2.py b/proto/token_dissociate_pb2.py new file mode 100644 index 00000000..b5809df2 --- /dev/null +++ b/proto/token_dissociate_pb2.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/token_dissociate.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 +from proto import basic_types_pb2 as proto_dot_basic__types__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cproto/token_dissociate.proto\x12\x06Hedera\x1a\x0cnanopb.proto\x1a\x17proto/basic_types.proto\"l\n\x1eTokenDissociateTransactionBody\x12\"\n\x07\x61\x63\x63ount\x18\x01 \x01(\x0b\x32\x11.Hedera.AccountID\x12&\n\x06tokens\x18\x02 \x03(\x0b\x32\x0f.Hedera.TokenIDB\x05\x92?\x02\x10\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.token_dissociate_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _TOKENDISSOCIATETRANSACTIONBODY.fields_by_name['tokens']._options = None + _TOKENDISSOCIATETRANSACTIONBODY.fields_by_name['tokens']._serialized_options = b'\222?\002\020\001' + _TOKENDISSOCIATETRANSACTIONBODY._serialized_start=79 + _TOKENDISSOCIATETRANSACTIONBODY._serialized_end=187 +# @@protoc_insertion_point(module_scope) diff --git a/proto/token_dissociate_pb2.pyi b/proto/token_dissociate_pb2.pyi new file mode 100644 index 00000000..e5a665e6 --- /dev/null +++ b/proto/token_dissociate_pb2.pyi @@ -0,0 +1,65 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import proto.basic_types_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class TokenDissociateTransactionBody(google.protobuf.message.Message): + """* + Dissociates the provided account with the provided tokens. Must be signed by + the provided Account's key. If the provided account is not found, the + transaction will resolve to INVALID_ACCOUNT_ID. If the provided account has + been deleted, the transaction will resolve to ACCOUNT_DELETED. If any of the + provided tokens is not found, the transaction will resolve to + INVALID_TOKEN_REF. If any of the provided tokens has been deleted, the + transaction will resolve to TOKEN_WAS_DELETED. If an association between the + provided account and any of the tokens does not exist, the transaction will + resolve to TOKEN_NOT_ASSOCIATED_TO_ACCOUNT. If a token has not been deleted + and has not expired, and the user has a nonzero balance, the transaction will + resolve to TRANSACTION_REQUIRES_ZERO_TOKEN_BALANCES. If a fungible + token has expired, the user can disassociate even if their token balance + is not zero. If a non fungible token has expired, the user can + not disassociate if their token balance is not zero. The transaction + will resolve to TRANSACTION_REQUIRED_ZERO_TOKEN_BALANCES. On success, + associations between the provided account and tokens are removed. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ACCOUNT_FIELD_NUMBER: builtins.int + TOKENS_FIELD_NUMBER: builtins.int + @property + def account(self) -> proto.basic_types_pb2.AccountID: + """* + The account to be dissociated with the provided tokens + """ + @property + def tokens(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[proto.basic_types_pb2.TokenID]: + """* + The tokens to be dissociated with the provided account + Limited to 1 here + """ + def __init__( + self, + *, + account: proto.basic_types_pb2.AccountID | None = ..., + tokens: collections.abc.Iterable[proto.basic_types_pb2.TokenID] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["account", b"account"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["account", b"account", "tokens", b"tokens"]) -> None: ... + +global___TokenDissociateTransactionBody = TokenDissociateTransactionBody diff --git a/proto/token_mint.proto b/proto/token_mint.proto new file mode 100644 index 00000000..1c81866a --- /dev/null +++ b/proto/token_mint.proto @@ -0,0 +1,65 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; +import "proto/basic_types.proto"; + +/** + * Mints tokens to the Token's treasury Account. If no Supply Key is defined, + * the transaction will resolve to TOKEN_HAS_NO_SUPPLY_KEY. The operation + * increases the Total Supply of the Token. The maximum total supply a token can + * have is 2^63-1. The amount provided must be in the lowest denomination + * possible. Example: Token A has 2 decimals. In order to mint 100 tokens, one + * must provide amount of 10000. In order to mint 100.55 tokens, one must + * provide amount of 10055. If both amount and metadata list get filled, a + * INVALID_TRANSACTION_BODY response code will be returned. If the metadata list + * contains metadata which is too large, a METADATA_TOO_LONG response code will + * be returned. + * If neither the amount nor the metadata list get filled, a + * INVALID_TOKEN_MINT_AMOUNT response code will be returned. If the metadata + * list count is greater than the batch size limit global dynamic property, a + * BATCH_SIZE_LIMIT_EXCEEDED response code will be returned. + */ +message TokenMintTransactionBody { + /** + * The token for which to mint tokens. If token does not exist, transaction + * results in INVALID_TOKEN_ID + */ + TokenID token = 1; + + /** + * Applicable to tokens of type FUNGIBLE_COMMON. The amount to mint to the + * Treasury Account. Amount must be a positive non-zero number represented in + * the lowest denomination of the token. The new supply must be lower than + * 2^63. + */ + uint64 amount = 2; + + /** + * Applicable to tokens of type NON_FUNGIBLE_UNIQUE. A list of metadata that + * are being created. Maximum allowed size of each metadata is 100 bytes + * Limited to 1 metadata chunk (no access to malloc) + */ + repeated bytes metadata = 3 + [ (nanopb).max_size = 100, (nanopb).max_count = 1 ]; +} \ No newline at end of file diff --git a/proto/token_mint_pb2.py b/proto/token_mint_pb2.py new file mode 100644 index 00000000..0ea6629c --- /dev/null +++ b/proto/token_mint_pb2.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/token_mint.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 +from proto import basic_types_pb2 as proto_dot_basic__types__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16proto/token_mint.proto\x12\x06Hedera\x1a\x0cnanopb.proto\x1a\x17proto/basic_types.proto\"h\n\x18TokenMintTransactionBody\x12\x1e\n\x05token\x18\x01 \x01(\x0b\x32\x0f.Hedera.TokenID\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x1c\n\x08metadata\x18\x03 \x03(\x0c\x42\n\x92?\x02\x08\x64\x92?\x02\x10\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.token_mint_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _TOKENMINTTRANSACTIONBODY.fields_by_name['metadata']._options = None + _TOKENMINTTRANSACTIONBODY.fields_by_name['metadata']._serialized_options = b'\222?\002\010d\222?\002\020\001' + _TOKENMINTTRANSACTIONBODY._serialized_start=73 + _TOKENMINTTRANSACTIONBODY._serialized_end=177 +# @@protoc_insertion_point(module_scope) diff --git a/proto/token_mint_pb2.pyi b/proto/token_mint_pb2.pyi new file mode 100644 index 00000000..2cece623 --- /dev/null +++ b/proto/token_mint_pb2.pyi @@ -0,0 +1,74 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import proto.basic_types_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class TokenMintTransactionBody(google.protobuf.message.Message): + """* + Mints tokens to the Token's treasury Account. If no Supply Key is defined, + the transaction will resolve to TOKEN_HAS_NO_SUPPLY_KEY. The operation + increases the Total Supply of the Token. The maximum total supply a token can + have is 2^63-1. The amount provided must be in the lowest denomination + possible. Example: Token A has 2 decimals. In order to mint 100 tokens, one + must provide amount of 10000. In order to mint 100.55 tokens, one must + provide amount of 10055. If both amount and metadata list get filled, a + INVALID_TRANSACTION_BODY response code will be returned. If the metadata list + contains metadata which is too large, a METADATA_TOO_LONG response code will + be returned. + If neither the amount nor the metadata list get filled, a + INVALID_TOKEN_MINT_AMOUNT response code will be returned. If the metadata + list count is greater than the batch size limit global dynamic property, a + BATCH_SIZE_LIMIT_EXCEEDED response code will be returned. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TOKEN_FIELD_NUMBER: builtins.int + AMOUNT_FIELD_NUMBER: builtins.int + METADATA_FIELD_NUMBER: builtins.int + @property + def token(self) -> proto.basic_types_pb2.TokenID: + """* + The token for which to mint tokens. If token does not exist, transaction + results in INVALID_TOKEN_ID + """ + amount: builtins.int + """* + Applicable to tokens of type FUNGIBLE_COMMON. The amount to mint to the + Treasury Account. Amount must be a positive non-zero number represented in + the lowest denomination of the token. The new supply must be lower than + 2^63. + """ + @property + def metadata(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: + """* + Applicable to tokens of type NON_FUNGIBLE_UNIQUE. A list of metadata that + are being created. Maximum allowed size of each metadata is 100 bytes + Limited to 1 metadata chunk (no access to malloc) + """ + def __init__( + self, + *, + token: proto.basic_types_pb2.TokenID | None = ..., + amount: builtins.int = ..., + metadata: collections.abc.Iterable[builtins.bytes] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["token", b"token"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["amount", b"amount", "metadata", b"metadata", "token", b"token"]) -> None: ... + +global___TokenMintTransactionBody = TokenMintTransactionBody diff --git a/proto/transaction_body.proto b/proto/transaction_body.proto new file mode 100644 index 00000000..73a5ca6b --- /dev/null +++ b/proto/transaction_body.proto @@ -0,0 +1,117 @@ +syntax = "proto3"; + +package Hedera; + +/*- + * ‌ + * Hedera Network Services Protobuf + * ​ + * Copyright (C) 2018 - 2021 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import "nanopb.proto"; +import "proto/basic_types.proto"; +import "proto/crypto_create.proto"; +import "proto/crypto_transfer.proto"; +import "proto/crypto_update.proto"; +import "proto/duration.proto"; +import "proto/token_associate.proto"; +import "proto/token_burn.proto"; +import "proto/token_dissociate.proto"; +import "proto/token_mint.proto"; + +/** + * A single transaction. All transaction types are possible here. + */ +message TransactionBody { + /** + * The ID for this transaction, which includes the payer's account (the + * account paying the transaction fee). If two transactions have the same + * transactionID, they won't both have an effect + */ + TransactionID transactionID = 1; + + /** + * The account of the node that submits the client's transaction to the + * network + */ + AccountID nodeAccountID = 2; + + /** + * The maximum transaction fee the client is willing to pay + */ + uint64 transactionFee = 3; + + /** + * The transaction is invalid if consensusTimestamp > + * transactionID.transactionValidStart + transactionValidDuration + */ + Duration transactionValidDuration = 4; + + /** + * Should a record of this transaction be generated? (A receipt is always + * generated, but the record is optional) + */ + bool generateRecord = 5 [ deprecated = true ]; + + /** + * Any notes or descriptions that should be put into the record (max length + * 100) + */ + string memo = 6 [ (nanopb).max_size = 100 ]; + + /** + * The choices here are arranged by service in roughly lexicographical order. + * The field ordinals are non-sequential, and a result of the historical order + * of implementation. + * Limited to supported transactions here + */ + oneof data { + /** + * Create a new cryptocurrency account + */ + CryptoCreateTransactionBody cryptoCreateAccount = 11; + + /** + * Transfer amount between accounts + */ + CryptoTransferTransactionBody cryptoTransfer = 14; + + /** + * Modify information such as the expiration date for an account + */ + CryptoUpdateTransactionBody cryptoUpdateAccount = 15; + + /** + * Mints new tokens to a token's treasury account + */ + TokenMintTransactionBody tokenMint = 37; + + /** + * Burns tokens from a token's treasury account + */ + TokenBurnTransactionBody tokenBurn = 38; + + /** + * Associate tokens to an account + */ + TokenAssociateTransactionBody tokenAssociate = 40; + + /** + * Dissociate tokens from an account + */ + TokenDissociateTransactionBody tokenDissociate = 41; + } +} \ No newline at end of file diff --git a/proto/transaction_body_pb2.py b/proto/transaction_body_pb2.py new file mode 100644 index 00000000..e24c5440 --- /dev/null +++ b/proto/transaction_body_pb2.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/transaction_body.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 +from proto import basic_types_pb2 as proto_dot_basic__types__pb2 +from proto import crypto_create_pb2 as proto_dot_crypto__create__pb2 +from proto import crypto_transfer_pb2 as proto_dot_crypto__transfer__pb2 +from proto import crypto_update_pb2 as proto_dot_crypto__update__pb2 +from proto import duration_pb2 as proto_dot_duration__pb2 +from proto import token_associate_pb2 as proto_dot_token__associate__pb2 +from proto import token_burn_pb2 as proto_dot_token__burn__pb2 +from proto import token_dissociate_pb2 as proto_dot_token__dissociate__pb2 +from proto import token_mint_pb2 as proto_dot_token__mint__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cproto/transaction_body.proto\x12\x06Hedera\x1a\x0cnanopb.proto\x1a\x17proto/basic_types.proto\x1a\x19proto/crypto_create.proto\x1a\x1bproto/crypto_transfer.proto\x1a\x19proto/crypto_update.proto\x1a\x14proto/duration.proto\x1a\x1bproto/token_associate.proto\x1a\x16proto/token_burn.proto\x1a\x1cproto/token_dissociate.proto\x1a\x16proto/token_mint.proto\"\xa9\x05\n\x0fTransactionBody\x12,\n\rtransactionID\x18\x01 \x01(\x0b\x32\x15.Hedera.TransactionID\x12(\n\rnodeAccountID\x18\x02 \x01(\x0b\x32\x11.Hedera.AccountID\x12\x16\n\x0etransactionFee\x18\x03 \x01(\x04\x12\x32\n\x18transactionValidDuration\x18\x04 \x01(\x0b\x32\x10.Hedera.Duration\x12\x1a\n\x0egenerateRecord\x18\x05 \x01(\x08\x42\x02\x18\x01\x12\x13\n\x04memo\x18\x06 \x01(\tB\x05\x92?\x02\x08\x64\x12\x42\n\x13\x63ryptoCreateAccount\x18\x0b \x01(\x0b\x32#.Hedera.CryptoCreateTransactionBodyH\x00\x12?\n\x0e\x63ryptoTransfer\x18\x0e \x01(\x0b\x32%.Hedera.CryptoTransferTransactionBodyH\x00\x12\x42\n\x13\x63ryptoUpdateAccount\x18\x0f \x01(\x0b\x32#.Hedera.CryptoUpdateTransactionBodyH\x00\x12\x35\n\ttokenMint\x18% \x01(\x0b\x32 .Hedera.TokenMintTransactionBodyH\x00\x12\x35\n\ttokenBurn\x18& \x01(\x0b\x32 .Hedera.TokenBurnTransactionBodyH\x00\x12?\n\x0etokenAssociate\x18( \x01(\x0b\x32%.Hedera.TokenAssociateTransactionBodyH\x00\x12\x41\n\x0ftokenDissociate\x18) \x01(\x0b\x32&.Hedera.TokenDissociateTransactionBodyH\x00\x42\x06\n\x04\x64\x61tab\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.transaction_body_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _TRANSACTIONBODY.fields_by_name['generateRecord']._options = None + _TRANSACTIONBODY.fields_by_name['generateRecord']._serialized_options = b'\030\001' + _TRANSACTIONBODY.fields_by_name['memo']._options = None + _TRANSACTIONBODY.fields_by_name['memo']._serialized_options = b'\222?\002\010d' + _TRANSACTIONBODY._serialized_start=292 + _TRANSACTIONBODY._serialized_end=973 +# @@protoc_insertion_point(module_scope) diff --git a/proto/transaction_body_pb2.pyi b/proto/transaction_body_pb2.pyi new file mode 100644 index 00000000..e2374bdb --- /dev/null +++ b/proto/transaction_body_pb2.pyi @@ -0,0 +1,136 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import proto.basic_types_pb2 +import proto.crypto_create_pb2 +import proto.crypto_transfer_pb2 +import proto.crypto_update_pb2 +import proto.duration_pb2 +import proto.token_associate_pb2 +import proto.token_burn_pb2 +import proto.token_dissociate_pb2 +import proto.token_mint_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class TransactionBody(google.protobuf.message.Message): + """* + A single transaction. All transaction types are possible here. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRANSACTIONID_FIELD_NUMBER: builtins.int + NODEACCOUNTID_FIELD_NUMBER: builtins.int + TRANSACTIONFEE_FIELD_NUMBER: builtins.int + TRANSACTIONVALIDDURATION_FIELD_NUMBER: builtins.int + GENERATERECORD_FIELD_NUMBER: builtins.int + MEMO_FIELD_NUMBER: builtins.int + CRYPTOCREATEACCOUNT_FIELD_NUMBER: builtins.int + CRYPTOTRANSFER_FIELD_NUMBER: builtins.int + CRYPTOUPDATEACCOUNT_FIELD_NUMBER: builtins.int + TOKENMINT_FIELD_NUMBER: builtins.int + TOKENBURN_FIELD_NUMBER: builtins.int + TOKENASSOCIATE_FIELD_NUMBER: builtins.int + TOKENDISSOCIATE_FIELD_NUMBER: builtins.int + @property + def transactionID(self) -> proto.basic_types_pb2.TransactionID: + """* + The ID for this transaction, which includes the payer's account (the + account paying the transaction fee). If two transactions have the same + transactionID, they won't both have an effect + """ + @property + def nodeAccountID(self) -> proto.basic_types_pb2.AccountID: + """* + The account of the node that submits the client's transaction to the + network + """ + transactionFee: builtins.int + """* + The maximum transaction fee the client is willing to pay + """ + @property + def transactionValidDuration(self) -> proto.duration_pb2.Duration: + """* + The transaction is invalid if consensusTimestamp > + transactionID.transactionValidStart + transactionValidDuration + """ + generateRecord: builtins.bool + """* + Should a record of this transaction be generated? (A receipt is always + generated, but the record is optional) + """ + memo: builtins.str + """* + Any notes or descriptions that should be put into the record (max length + 100) + """ + @property + def cryptoCreateAccount(self) -> proto.crypto_create_pb2.CryptoCreateTransactionBody: + """* + Create a new cryptocurrency account + """ + @property + def cryptoTransfer(self) -> proto.crypto_transfer_pb2.CryptoTransferTransactionBody: + """* + Transfer amount between accounts + """ + @property + def cryptoUpdateAccount(self) -> proto.crypto_update_pb2.CryptoUpdateTransactionBody: + """* + Modify information such as the expiration date for an account + """ + @property + def tokenMint(self) -> proto.token_mint_pb2.TokenMintTransactionBody: + """* + Mints new tokens to a token's treasury account + """ + @property + def tokenBurn(self) -> proto.token_burn_pb2.TokenBurnTransactionBody: + """* + Burns tokens from a token's treasury account + """ + @property + def tokenAssociate(self) -> proto.token_associate_pb2.TokenAssociateTransactionBody: + """* + Associate tokens to an account + """ + @property + def tokenDissociate(self) -> proto.token_dissociate_pb2.TokenDissociateTransactionBody: + """* + Dissociate tokens from an account + """ + def __init__( + self, + *, + transactionID: proto.basic_types_pb2.TransactionID | None = ..., + nodeAccountID: proto.basic_types_pb2.AccountID | None = ..., + transactionFee: builtins.int = ..., + transactionValidDuration: proto.duration_pb2.Duration | None = ..., + generateRecord: builtins.bool = ..., + memo: builtins.str = ..., + cryptoCreateAccount: proto.crypto_create_pb2.CryptoCreateTransactionBody | None = ..., + cryptoTransfer: proto.crypto_transfer_pb2.CryptoTransferTransactionBody | None = ..., + cryptoUpdateAccount: proto.crypto_update_pb2.CryptoUpdateTransactionBody | None = ..., + tokenMint: proto.token_mint_pb2.TokenMintTransactionBody | None = ..., + tokenBurn: proto.token_burn_pb2.TokenBurnTransactionBody | None = ..., + tokenAssociate: proto.token_associate_pb2.TokenAssociateTransactionBody | None = ..., + tokenDissociate: proto.token_dissociate_pb2.TokenDissociateTransactionBody | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["cryptoCreateAccount", b"cryptoCreateAccount", "cryptoTransfer", b"cryptoTransfer", "cryptoUpdateAccount", b"cryptoUpdateAccount", "data", b"data", "nodeAccountID", b"nodeAccountID", "tokenAssociate", b"tokenAssociate", "tokenBurn", b"tokenBurn", "tokenDissociate", b"tokenDissociate", "tokenMint", b"tokenMint", "transactionID", b"transactionID", "transactionValidDuration", b"transactionValidDuration"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["cryptoCreateAccount", b"cryptoCreateAccount", "cryptoTransfer", b"cryptoTransfer", "cryptoUpdateAccount", b"cryptoUpdateAccount", "data", b"data", "generateRecord", b"generateRecord", "memo", b"memo", "nodeAccountID", b"nodeAccountID", "tokenAssociate", b"tokenAssociate", "tokenBurn", b"tokenBurn", "tokenDissociate", b"tokenDissociate", "tokenMint", b"tokenMint", "transactionFee", b"transactionFee", "transactionID", b"transactionID", "transactionValidDuration", b"transactionValidDuration"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["data", b"data"]) -> typing_extensions.Literal["cryptoCreateAccount", "cryptoTransfer", "cryptoUpdateAccount", "tokenMint", "tokenBurn", "tokenAssociate", "tokenDissociate"] | None: ... + +global___TransactionBody = TransactionBody diff --git a/proto/wrappers.proto b/proto/wrappers.proto new file mode 100644 index 00000000..d7f95475 --- /dev/null +++ b/proto/wrappers.proto @@ -0,0 +1,108 @@ + +syntax = "proto3"; + +package Hedera; +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Wrappers for primitive (non-message) types. These types are useful +// for embedding primitives in the `google.protobuf.Any` type and for places +// where we need to distinguish between the absence of a primitive +// typed field and its default value. +// +// These wrappers have no meaningful use within repeated fields as they lack +// the ability to detect presence on individual elements. +// These wrappers have no meaningful use within a map or a oneof since +// individual entries of a map or fields of a oneof can already detect presence. +import "nanopb.proto"; + +// Wrapper message for `double`. +// +// The JSON representation for `DoubleValue` is JSON number. +message DoubleValue { + // The double value. + double value = 1; +} + +// Wrapper message for `float`. +// +// The JSON representation for `FloatValue` is JSON number. +message FloatValue { + // The float value. + float value = 1; +} + +// Wrapper message for `int64`. +// +// The JSON representation for `Int64Value` is JSON string. +message Int64Value { + // The int64 value. + int64 value = 1; +} + +// Wrapper message for `uint64`. +// +// The JSON representation for `UInt64Value` is JSON string. +message UInt64Value { + // The uint64 value. + uint64 value = 1; +} + +// Wrapper message for `int32`. +// +// The JSON representation for `Int32Value` is JSON number. +message Int32Value { + // The int32 value. + int32 value = 1; +} + +// Wrapper message for `uint32`. +// +// The JSON representation for `UInt32Value` is JSON number. +message UInt32Value { + // The uint32 value. + uint32 value = 1; +} + +// Wrapper message for `bool`. +// +// The JSON representation for `BoolValue` is JSON `true` and `false`. +message BoolValue { + // The bool value. + bool value = 1; +} + +// Wrapper message for `string`. +// +// The JSON representation for `StringValue` is JSON string. +message StringValue { + // The string value. + string value = 1; +} \ No newline at end of file diff --git a/proto/wrappers_pb2.py b/proto/wrappers_pb2.py new file mode 100644 index 00000000..7961d1ed --- /dev/null +++ b/proto/wrappers_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/wrappers.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import nanopb_pb2 as nanopb__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14proto/wrappers.proto\x12\x06Hedera\x1a\x0cnanopb.proto\"\x1c\n\x0b\x44oubleValue\x12\r\n\x05value\x18\x01 \x01(\x01\"\x1b\n\nFloatValue\x12\r\n\x05value\x18\x01 \x01(\x02\"\x1b\n\nInt64Value\x12\r\n\x05value\x18\x01 \x01(\x03\"\x1c\n\x0bUInt64Value\x12\r\n\x05value\x18\x01 \x01(\x04\"\x1b\n\nInt32Value\x12\r\n\x05value\x18\x01 \x01(\x05\"\x1c\n\x0bUInt32Value\x12\r\n\x05value\x18\x01 \x01(\r\"\x1a\n\tBoolValue\x12\r\n\x05value\x18\x01 \x01(\x08\"\x1c\n\x0bStringValue\x12\r\n\x05value\x18\x01 \x01(\tb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.wrappers_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _DOUBLEVALUE._serialized_start=46 + _DOUBLEVALUE._serialized_end=74 + _FLOATVALUE._serialized_start=76 + _FLOATVALUE._serialized_end=103 + _INT64VALUE._serialized_start=105 + _INT64VALUE._serialized_end=132 + _UINT64VALUE._serialized_start=134 + _UINT64VALUE._serialized_end=162 + _INT32VALUE._serialized_start=164 + _INT32VALUE._serialized_end=191 + _UINT32VALUE._serialized_start=193 + _UINT32VALUE._serialized_end=221 + _BOOLVALUE._serialized_start=223 + _BOOLVALUE._serialized_end=249 + _STRINGVALUE._serialized_start=251 + _STRINGVALUE._serialized_end=279 +# @@protoc_insertion_point(module_scope) diff --git a/proto/wrappers_pb2.pyi b/proto/wrappers_pb2.pyi new file mode 100644 index 00000000..57f539f5 --- /dev/null +++ b/proto/wrappers_pb2.pyi @@ -0,0 +1,212 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. All rights reserved. +https://developers.google.com/protocol-buffers/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class DoubleValue(google.protobuf.message.Message): + """Wrapper message for `double`. + + The JSON representation for `DoubleValue` is JSON number. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUE_FIELD_NUMBER: builtins.int + value: builtins.float + """The double value.""" + def __init__( + self, + *, + value: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["value", b"value"]) -> None: ... + +global___DoubleValue = DoubleValue + +@typing_extensions.final +class FloatValue(google.protobuf.message.Message): + """Wrapper message for `float`. + + The JSON representation for `FloatValue` is JSON number. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUE_FIELD_NUMBER: builtins.int + value: builtins.float + """The float value.""" + def __init__( + self, + *, + value: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["value", b"value"]) -> None: ... + +global___FloatValue = FloatValue + +@typing_extensions.final +class Int64Value(google.protobuf.message.Message): + """Wrapper message for `int64`. + + The JSON representation for `Int64Value` is JSON string. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUE_FIELD_NUMBER: builtins.int + value: builtins.int + """The int64 value.""" + def __init__( + self, + *, + value: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["value", b"value"]) -> None: ... + +global___Int64Value = Int64Value + +@typing_extensions.final +class UInt64Value(google.protobuf.message.Message): + """Wrapper message for `uint64`. + + The JSON representation for `UInt64Value` is JSON string. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUE_FIELD_NUMBER: builtins.int + value: builtins.int + """The uint64 value.""" + def __init__( + self, + *, + value: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["value", b"value"]) -> None: ... + +global___UInt64Value = UInt64Value + +@typing_extensions.final +class Int32Value(google.protobuf.message.Message): + """Wrapper message for `int32`. + + The JSON representation for `Int32Value` is JSON number. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUE_FIELD_NUMBER: builtins.int + value: builtins.int + """The int32 value.""" + def __init__( + self, + *, + value: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["value", b"value"]) -> None: ... + +global___Int32Value = Int32Value + +@typing_extensions.final +class UInt32Value(google.protobuf.message.Message): + """Wrapper message for `uint32`. + + The JSON representation for `UInt32Value` is JSON number. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUE_FIELD_NUMBER: builtins.int + value: builtins.int + """The uint32 value.""" + def __init__( + self, + *, + value: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["value", b"value"]) -> None: ... + +global___UInt32Value = UInt32Value + +@typing_extensions.final +class BoolValue(google.protobuf.message.Message): + """Wrapper message for `bool`. + + The JSON representation for `BoolValue` is JSON `true` and `false`. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUE_FIELD_NUMBER: builtins.int + value: builtins.bool + """The bool value.""" + def __init__( + self, + *, + value: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["value", b"value"]) -> None: ... + +global___BoolValue = BoolValue + +@typing_extensions.final +class StringValue(google.protobuf.message.Message): + """Wrapper message for `string`. + + The JSON representation for `StringValue` is JSON string. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + VALUE_FIELD_NUMBER: builtins.int + value: builtins.str + """The string value.""" + def __init__( + self, + *, + value: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["value", b"value"]) -> None: ... + +global___StringValue = StringValue diff --git a/src/debug.c b/src/debug.c index 2877a8b3..a3f515a6 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,15 +1,14 @@ -#include "os.h" #include "debug.h" +#include "os.h" + // This symbol is defined by the link script to be at the start of the stack // area. extern unsigned long app_stack_canary; -#define STACK_CANARY (*((volatile uint32_t*) &app_stack_canary)) +#define STACK_CANARY (*((volatile uint32_t *)&app_stack_canary)) -void debug_init_stack_canary() { - STACK_CANARY = 0xDEADBEEF; -} +void debug_init_stack_canary() { STACK_CANARY = 0xDEADBEEF; } void debug_check_stack_canary() { if (STACK_CANARY != 0xDEADBEEF) { @@ -17,6 +16,4 @@ void debug_check_stack_canary() { } } -uint32_t debug_get_stack_canary() { - return STACK_CANARY; -} +uint32_t debug_get_stack_canary() { return STACK_CANARY; } diff --git a/src/get_app_configuration.c b/src/get_app_configuration.c index 9d63f358..6c784fd8 100644 --- a/src/get_app_configuration.c +++ b/src/get_app_configuration.c @@ -1,19 +1,14 @@ -#include - #include #include +#include #include "errors.h" #include "io.h" void handle_get_app_configuration( - uint8_t p1, - uint8_t p2, - const uint8_t* const buffer, - uint16_t len, - /* out */ volatile const unsigned int* const flags, - /* out */ volatile const unsigned int* const tx -) { + uint8_t p1, uint8_t p2, const uint8_t *const buffer, uint16_t len, + /* out */ volatile const unsigned int *const flags, + /* out */ volatile const unsigned int *const tx) { UNUSED(p1); UNUSED(p2); UNUSED(buffer); @@ -22,12 +17,12 @@ void handle_get_app_configuration( UNUSED(tx); // storage allowed? - G_io_apdu_buffer[0] = 0; + G_io_apdu_buffer[ 0 ] = 0; // version - G_io_apdu_buffer[1] = APPVERSION_M; - G_io_apdu_buffer[2] = APPVERSION_N; - G_io_apdu_buffer[3] = APPVERSION_P; + G_io_apdu_buffer[ 1 ] = APPVERSION_M; + G_io_apdu_buffer[ 2 ] = APPVERSION_N; + G_io_apdu_buffer[ 3 ] = APPVERSION_P; io_exchange_with_code(EXCEPTION_OK, 4); } diff --git a/src/get_public_key.c b/src/get_public_key.c index 4f28796d..b9c43829 100644 --- a/src/get_public_key.c +++ b/src/get_public_key.c @@ -1,46 +1,44 @@ +#include "get_public_key.h" + #include #include #include -#include "globals.h" -#include "printf.h" -#include "globals.h" #include "debug.h" #include "errors.h" +#include "globals.h" #include "handlers.h" #include "hedera.h" #include "io.h" -#include "utils.h" +#include "printf.h" #include "ui.h" -#include "get_public_key.h" +#include "utils.h" static struct get_public_key_context_t { uint32_t key_index; // Lines on the UI Screen - char ui_approve_l2[DISPLAY_SIZE + 1]; + char ui_approve_l2[ DISPLAY_SIZE + 1 ]; cx_ecfp_public_key_t public; // Public Key Compare uint8_t display_index; - uint8_t full_key[KEY_SIZE + 1]; - uint8_t partial_key[DISPLAY_SIZE + 1]; + uint8_t full_key[ KEY_SIZE + 1 ]; + uint8_t partial_key[ DISPLAY_SIZE + 1 ]; } ctx; #if defined(TARGET_NANOS) static const bagl_element_t ui_get_public_key_compare[] = { - UI_BACKGROUND(), - UI_ICON_LEFT(LEFT_ICON_ID, BAGL_GLYPH_ICON_LEFT), + UI_BACKGROUND(), UI_ICON_LEFT(LEFT_ICON_ID, BAGL_GLYPH_ICON_LEFT), UI_ICON_RIGHT(RIGHT_ICON_ID, BAGL_GLYPH_ICON_RIGHT), // <= => // Public Key // // UI_TEXT(LINE_1_ID, 0, 12, 128, "Public Key"), - UI_TEXT(LINE_2_ID, 0, 26, 128, ctx.partial_key) -}; + UI_TEXT(LINE_2_ID, 0, 26, 128, ctx.partial_key)}; static const bagl_element_t ui_get_public_key_approve[] = { UI_BACKGROUND(), @@ -55,17 +53,11 @@ static const bagl_element_t ui_get_public_key_approve[] = { }; void shift_partial_key() { - memmove( - ctx.partial_key, - ctx.full_key + ctx.display_index, - DISPLAY_SIZE - ); + memmove(ctx.partial_key, ctx.full_key + ctx.display_index, DISPLAY_SIZE); } static unsigned int ui_get_public_key_compare_button( - unsigned int button_mask, - unsigned int button_mask_counter -) { + unsigned int button_mask, unsigned int button_mask_counter) { UNUSED(button_mask_counter); switch (button_mask) { case BUTTON_LEFT: // Left @@ -76,7 +68,8 @@ static unsigned int ui_get_public_key_compare_button( break; case BUTTON_RIGHT: // Right case BUTTON_EVT_FAST | BUTTON_RIGHT: - if (ctx.display_index < KEY_SIZE - DISPLAY_SIZE) ctx.display_index++; + if (ctx.display_index < KEY_SIZE - DISPLAY_SIZE) + ctx.display_index++; shift_partial_key(); UX_REDISPLAY(); break; @@ -87,14 +80,12 @@ static unsigned int ui_get_public_key_compare_button( return 0; } -static const bagl_element_t* ui_prepro_get_public_key_compare( - const bagl_element_t* element -) { - if (element->component.userid == LEFT_ICON_ID - && ctx.display_index == 0) +static const bagl_element_t *ui_prepro_get_public_key_compare( + const bagl_element_t *element) { + if (element->component.userid == LEFT_ICON_ID && ctx.display_index == 0) return NULL; // Hide Left Arrow at Left Edge - if (element->component.userid == RIGHT_ICON_ID - && ctx.display_index == KEY_SIZE - DISPLAY_SIZE) + if (element->component.userid == RIGHT_ICON_ID && + ctx.display_index == KEY_SIZE - DISPLAY_SIZE) return NULL; // Hide Right Arrow at Right Edge return element; } @@ -102,22 +93,17 @@ static const bagl_element_t* ui_prepro_get_public_key_compare( void compare_pk() { // init partial key str from full str memmove(ctx.partial_key, ctx.full_key, DISPLAY_SIZE); - ctx.partial_key[DISPLAY_SIZE] = '\0'; - + ctx.partial_key[ DISPLAY_SIZE ] = '\0'; + // init display index ctx.display_index = 0; // Display compare with button mask - UX_DISPLAY( - ui_get_public_key_compare, - ui_prepro_get_public_key_compare - ); + UX_DISPLAY(ui_get_public_key_compare, ui_prepro_get_public_key_compare); } static unsigned int ui_get_public_key_approve_button( - unsigned int button_mask, - unsigned int button_mask_counter -) { + unsigned int button_mask, unsigned int button_mask_counter) { UNUSED(button_mask_counter); switch (button_mask) { case BUTTON_EVT_RELEASED | BUTTON_LEFT: // REJECT @@ -145,65 +131,30 @@ unsigned int io_seproxyhal_touch_pk_ok(const bagl_element_t *e) { } unsigned int io_seproxyhal_touch_pk_cancel(const bagl_element_t *e) { - io_exchange_with_code(EXCEPTION_USER_REJECTED, 0); - ui_idle(); - return 0; + io_exchange_with_code(EXCEPTION_USER_REJECTED, 0); + ui_idle(); + return 0; } -UX_STEP_NOCB( - ux_approve_pk_flow_1_step, - bn, - { - "Export Public", - ctx.ui_approve_l2 - } -); - -UX_STEP_VALID( - ux_approve_pk_flow_2_step, - pb, - io_seproxyhal_touch_pk_ok(NULL), - { - &C_icon_validate_14, - "Approve" - } -); - -UX_STEP_VALID( - ux_approve_pk_flow_3_step, - pb, - io_seproxyhal_touch_pk_cancel(NULL), - { - &C_icon_crossmark, - "Reject" - } -); - -UX_STEP_CB( - ux_compare_pk_flow_1_step, - bnnn_paging, - ui_idle(), - { - .title = "Public Key", - .text = (char*) ctx.full_key - } -); +UX_STEP_NOCB(ux_approve_pk_flow_1_step, bn, + {"Export Public", ctx.ui_approve_l2}); -UX_DEF( - ux_approve_pk_flow, - &ux_approve_pk_flow_1_step, - &ux_approve_pk_flow_2_step, - &ux_approve_pk_flow_3_step -); +UX_STEP_VALID(ux_approve_pk_flow_2_step, pb, io_seproxyhal_touch_pk_ok(NULL), + {&C_icon_validate_14, "Approve"}); -UX_DEF( - ux_compare_pk_flow, - &ux_compare_pk_flow_1_step -); +UX_STEP_VALID(ux_approve_pk_flow_3_step, pb, + io_seproxyhal_touch_pk_cancel(NULL), + {&C_icon_crossmark, "Reject"}); -void compare_pk() { - ux_flow_init(0, ux_compare_pk_flow, NULL); -} +UX_STEP_CB(ux_compare_pk_flow_1_step, bnnn_paging, ui_idle(), + {.title = "Public Key", .text = (char *)ctx.full_key}); + +UX_DEF(ux_approve_pk_flow, &ux_approve_pk_flow_1_step, + &ux_approve_pk_flow_2_step, &ux_approve_pk_flow_3_step); + +UX_DEF(ux_compare_pk_flow, &ux_compare_pk_flow_1_step); + +void compare_pk() { ux_flow_init(0, ux_compare_pk_flow, NULL); } #endif // TARGET @@ -216,17 +167,13 @@ void get_pk() { // Populate Key Hex String bin2hex(ctx.full_key, G_io_apdu_buffer, KEY_SIZE); - ctx.full_key[KEY_SIZE] = '\0'; + ctx.full_key[ KEY_SIZE ] = '\0'; } -void handle_get_public_key( - uint8_t p1, - uint8_t p2, - uint8_t* buffer, - uint16_t len, - /* out */ volatile unsigned int* flags, - /* out */ volatile unsigned int* tx -) { +void handle_get_public_key(uint8_t p1, uint8_t p2, uint8_t *buffer, + uint16_t len, + /* out */ volatile unsigned int *flags, + /* out */ volatile unsigned int *tx) { UNUSED(p2); UNUSED(len); UNUSED(tx); @@ -234,11 +181,12 @@ void handle_get_public_key( // Read Key Index ctx.key_index = U4LE(buffer, 0); - // If p1 != 0, silent mode, for use by apps that request the user's public key frequently - // Only do UI actions for p1 == 0 + // If p1 != 0, silent mode, for use by apps that request the user's public + // key frequently Only do UI actions for p1 == 0 if (p1 == 0) { // Complete "Export Public | Key #x?" - hedera_snprintf(ctx.ui_approve_l2, DISPLAY_SIZE, "Key #%u?", ctx.key_index); + hedera_snprintf(ctx.ui_approve_l2, DISPLAY_SIZE, "Key #%u?", + ctx.key_index); } // Populate context with PK diff --git a/src/get_public_key.h b/src/get_public_key.h index c208e64d..c92c2e8a 100644 --- a/src/get_public_key.h +++ b/src/get_public_key.h @@ -9,20 +9,15 @@ void compare_pk(); void shift_partial_key(); static unsigned int ui_get_public_key_compare_button( - unsigned int button_mask, - unsigned int button_mask_counter -); + unsigned int button_mask, unsigned int button_mask_counter); -static const bagl_element_t* ui_prepro_get_public_key_compare( - const bagl_element_t* element -); +static const bagl_element_t *ui_prepro_get_public_key_compare( + const bagl_element_t *element); void send_pk(); static unsigned int ui_get_public_key_approve_button( - unsigned int button_mask, - unsigned int button_mask_counter -); + unsigned int button_mask, unsigned int button_mask_counter); #elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) diff --git a/src/handlers.h b/src/handlers.h index 7d9842d6..8b4db662 100644 --- a/src/handlers.h +++ b/src/handlers.h @@ -9,14 +9,9 @@ #define INS_GET_PUBLIC_KEY 0x02 #define INS_SIGN_TRANSACTION 0x04 -typedef void handler_fn_t( - uint8_t p1, - uint8_t p2, - uint8_t* buffer, - uint16_t len, - /* out */ volatile unsigned int* flags, - /* out */ volatile unsigned int* tx -); +typedef void handler_fn_t(uint8_t p1, uint8_t p2, uint8_t* buffer, uint16_t len, + /* out */ volatile unsigned int* flags, + /* out */ volatile unsigned int* tx); extern handler_fn_t handle_get_app_configuration; extern handler_fn_t handle_get_public_key; diff --git a/src/hedera.c b/src/hedera.c index 3def8c08..36236760 100644 --- a/src/hedera.c +++ b/src/hedera.c @@ -1,60 +1,40 @@ -#include +#include "hedera.h" + #include +#include #include + #include "globals.h" -#include "hedera.h" -bool hedera_derive_keypair( - uint32_t index, - /* out */ cx_ecfp_private_key_t* secret, - /* out */ cx_ecfp_public_key_t* public -) { - static uint8_t seed[32]; - static uint32_t path[5]; +bool hedera_derive_keypair(uint32_t index, + /* out */ cx_ecfp_private_key_t *secret, + /* out */ cx_ecfp_public_key_t *public) { + static uint8_t seed[ 32 ]; + static uint32_t path[ 5 ]; static cx_ecfp_private_key_t pk; - path[0] = 44 | 0x80000000; - path[1] = 3030 | 0x80000000; - path[2] = 0x80000000; - path[3] = 0x80000000; - path[4] = index | 0x80000000; - - os_perso_derive_node_bip32_seed_key( - HDW_ED25519_SLIP10, - CX_CURVE_Ed25519, - path, - 5, - seed, - NULL, - NULL, - 0 - ); - - if (CX_OK != cx_ecfp_init_private_key_no_throw( - CX_CURVE_Ed25519, - seed, - sizeof(seed), - &pk - )) { + path[ 0 ] = 44 | 0x80000000; + path[ 1 ] = 3030 | 0x80000000; + path[ 2 ] = 0x80000000; + path[ 3 ] = 0x80000000; + path[ 4 ] = index | 0x80000000; + + os_perso_derive_node_bip32_seed_key(HDW_ED25519_SLIP10, CX_CURVE_Ed25519, + path, 5, seed, NULL, NULL, 0); + + if (CX_OK != cx_ecfp_init_private_key_no_throw(CX_CURVE_Ed25519, seed, + sizeof(seed), &pk)) { return false; } if (public) { - if (CX_OK != cx_ecfp_init_public_key_no_throw( - CX_CURVE_Ed25519, - NULL, - 0, - public - )) { + if (CX_OK != cx_ecfp_init_public_key_no_throw(CX_CURVE_Ed25519, NULL, 0, + public)) { return false; } - if (CX_OK != cx_ecfp_generate_pair_no_throw( - CX_CURVE_Ed25519, - public, - &pk, - 1 - )) { + if (CX_OK != + cx_ecfp_generate_pair_no_throw(CX_CURVE_Ed25519, public, &pk, 1)) { return false; } } @@ -69,12 +49,8 @@ bool hedera_derive_keypair( return true; } -bool hedera_sign( - uint32_t index, - const uint8_t* tx, - uint8_t tx_len, - /* out */ uint8_t* result -) { +bool hedera_sign(uint32_t index, const uint8_t *tx, uint8_t tx_len, + /* out */ uint8_t *result) { static cx_ecfp_private_key_t pk; // Get Keys @@ -87,13 +63,13 @@ bool hedera_sign( // Claims to want Hashes, but other apps use the message itself // and complain that the documentation is wrong if (CX_OK != cx_eddsa_sign_no_throw( - &pk, // private key - CX_SHA512, // hashID - tx, // hash (really message) - tx_len, // hash length (really message length) - result, // signature - 64 // signature length - )) { + &pk, // private key + CX_SHA512, // hashID + tx, // hash (really message) + tx_len, // hash length (really message length) + result, // signature + 64 // signature length + )) { return false; } diff --git a/src/hedera.h b/src/hedera.h index 0800d9d0..984219d8 100644 --- a/src/hedera.h +++ b/src/hedera.h @@ -1,6 +1,7 @@ #ifndef LEDGER_HEDERA_HEDERA_H #define LEDGER_HEDERA_HEDERA_H 1 +#include #include // Forward declare to avoid including os.h in a header file @@ -9,18 +10,13 @@ struct cx_ecfp_256_private_key_s; extern bool hedera_derive_keypair( uint32_t index, - /* out */ struct cx_ecfp_256_private_key_s* secret, - /* out */ struct cx_ecfp_256_public_key_s* public -); + /* out */ struct cx_ecfp_256_private_key_s *secret, + /* out */ struct cx_ecfp_256_public_key_s *public); -extern bool hedera_sign( - uint32_t index, - const uint8_t* tx, - uint8_t tx_len, - /* out */ uint8_t* result -); +extern bool hedera_sign(uint32_t index, const uint8_t *tx, uint8_t tx_len, + /* out */ uint8_t *result); -extern char* hedera_format_tinybar(uint64_t tinybar); -extern char* hedera_format_amount(uint64_t amount, uint8_t decimals); +extern char *hedera_format_tinybar(uint64_t tinybar); +extern char *hedera_format_amount(uint64_t amount, uint8_t decimals); #endif // LEDGER_HEDERA_HEDERA_H diff --git a/src/hedera_format.c b/src/hedera_format.c index c4443948..a50dcaee 100644 --- a/src/hedera_format.c +++ b/src/hedera_format.c @@ -1,7 +1,7 @@ -#include - #include "hedera_format.h" +#include + char* hedera_format_tinybar(uint64_t tinybar) { return hedera_format_amount(tinybar, 8); } @@ -9,7 +9,7 @@ char* hedera_format_tinybar(uint64_t tinybar) { #define BUF_SIZE 32 char* hedera_format_amount(uint64_t amount, uint8_t decimals) { - static char buf[BUF_SIZE]; + static char buf[ BUF_SIZE ]; // NOTE: format of amounts are not sensitive memset(buf, 0, BUF_SIZE); @@ -17,8 +17,8 @@ char* hedera_format_amount(uint64_t amount, uint8_t decimals) { // Quick shortcut if the amount is zero // Regardless of decimals, the output is always "0" if (amount == 0) { - buf[0] = '0'; - buf[1] = '\0'; + buf[ 0 ] = '0'; + buf[ 1 ] = '\0'; return buf; } @@ -33,15 +33,15 @@ char* hedera_format_amount(uint64_t amount, uint8_t decimals) { int digit = amount % 10; amount /= 10; - buf[i++] = '0' + digit; + buf[ i++ ] = '0' + digit; if (i == decimals) { - buf[i++] = '.'; + buf[ i++ ] = '.'; } } - if (buf[i - 1] == '.') { - buf[i++] = '0'; + if (buf[ i - 1 ] == '.') { + buf[ i++ ] = '0'; } int size = i; @@ -51,17 +51,17 @@ char* hedera_format_amount(uint64_t amount, uint8_t decimals) { while (j < i) { i -= 1; - tmp = buf[j]; - buf[j] = buf[i]; - buf[i] = tmp; + tmp = buf[ j ]; + buf[ j ] = buf[ i ]; + buf[ i ] = tmp; j += 1; } for (j = size - 1; j > 0; j--) { - if (buf[j] == '0') { + if (buf[ j ] == '0') { continue; - } else if (buf[j] == '.') { + } else if (buf[ j ] == '.') { break; } else { j += 1; @@ -70,7 +70,7 @@ char* hedera_format_amount(uint64_t amount, uint8_t decimals) { } if (j < size - 1) { - buf[j] = '\0'; + buf[ j ] = '\0'; } return buf; diff --git a/src/hedera_format.h b/src/hedera_format.h index 06fb070a..18746c34 100644 --- a/src/hedera_format.h +++ b/src/hedera_format.h @@ -3,7 +3,7 @@ #include -extern char* hedera_format_tinybar(uint64_t tinybar); -extern char* hedera_format_amount(uint64_t amount, uint8_t decimals); +extern char *hedera_format_tinybar(uint64_t tinybar); +extern char *hedera_format_amount(uint64_t amount, uint8_t decimals); #endif // LEDGER_HEDERA_HEDERA_FORMAT_H diff --git a/src/io.c b/src/io.c index 1d164801..22b348c4 100644 --- a/src/io.c +++ b/src/io.c @@ -1,17 +1,17 @@ +#include "debug.h" #include "os.h" -#include "ux.h" #include "os_io_seproxyhal.h" -#include "debug.h" +#include "ux.h" // Everything below this point is Ledger magic. And the magic isn't well- // documented, so if you want to understand it, you'll need to read the // source, which you can find in the sdk repo for your device. // Fortunately, we are not meant to understand this. -unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; +unsigned char G_io_seproxyhal_spi_buffer[ IO_SEPROXYHAL_BUFFER_SIZE_B ]; void io_seproxyhal_display(const bagl_element_t *element) { - io_seproxyhal_display_default((bagl_element_t*)element); + io_seproxyhal_display_default((bagl_element_t *)element); } unsigned char io_event(unsigned char channel) { @@ -21,7 +21,7 @@ unsigned char io_event(unsigned char channel) { debug_check_stack_canary(); // can't have more than one tag in the reply, not supported yet. - switch (G_io_seproxyhal_spi_buffer[0]) { + switch (G_io_seproxyhal_spi_buffer[ 0 ]) { case SEPROXYHAL_TAG_FINGER_EVENT: UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer); break; @@ -31,7 +31,9 @@ unsigned char io_event(unsigned char channel) { break; case SEPROXYHAL_TAG_STATUS_EVENT: - if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID && !(U4BE(G_io_seproxyhal_spi_buffer, 3) & SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) { + if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID && + !(U4BE(G_io_seproxyhal_spi_buffer, 3) & + SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) { THROW(EXCEPTION_IO_RESET); } @@ -65,7 +67,8 @@ unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { case CHANNEL_KEYBOARD: break; - // multiplexed io exchange over a SPI channel and TLV encapsulated protocol + // multiplexed io exchange over a SPI channel and TLV encapsulated + // protocol case CHANNEL_SPI: if (tx_len) { io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len); @@ -74,10 +77,10 @@ unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { reset(); } return 0; // nothing received from the master so far (it's a tx - // transaction) + // transaction) } else { return io_seproxyhal_spi_recv(G_io_apdu_buffer, - sizeof(G_io_apdu_buffer), 0); + sizeof(G_io_apdu_buffer), 0); } default: @@ -87,8 +90,8 @@ unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { } void io_exchange_with_code(uint16_t code, uint16_t tx) { - G_io_apdu_buffer[tx++] = code >> 8; - G_io_apdu_buffer[tx++] = code & 0xff; + G_io_apdu_buffer[ tx++ ] = code >> 8; + G_io_apdu_buffer[ tx++ ] = code & 0xff; io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx); } diff --git a/src/io.h b/src/io.h index c983b93f..71754812 100644 --- a/src/io.h +++ b/src/io.h @@ -1,9 +1,9 @@ #ifndef LEDGER_HEDERA_IO_H #define LEDGER_HEDERA_IO_H 1 -#include #include #include +#include extern void io_exchange_with_code(uint16_t code, uint16_t tx); diff --git a/src/main.c b/src/main.c index f632ea12..3bdc008c 100644 --- a/src/main.c +++ b/src/main.c @@ -1,10 +1,10 @@ +#include "debug.h" #include "errors.h" +#include "globals.h" #include "handlers.h" -#include "ui.h" #include "io.h" +#include "ui.h" #include "utils.h" -#include "debug.h" -#include "globals.h" // This is the main loop that reads and writes APDUs. It receives request // APDUs from the computer, looks up the corresponding command handler, and @@ -28,7 +28,8 @@ void app_main() { BEGIN_TRY { TRY { rx = tx; - tx = 0; // ensure no race in catch_other if io_exchange throws an error + tx = 0; // ensure no race in catch_other if io_exchange throws + // an error rx = io_exchange(CHANNEL_APDU | flags, rx); flags = 0; @@ -38,55 +39,44 @@ void app_main() { } // malformed APDU - if (G_io_apdu_buffer[OFFSET_CLA] != CLA) { + if (G_io_apdu_buffer[ OFFSET_CLA ] != CLA) { THROW(EXCEPTION_MALFORMED_APDU); } // APDU handler functions defined in handlers - switch (G_io_apdu_buffer[OFFSET_INS]) { + switch (G_io_apdu_buffer[ OFFSET_INS ]) { case INS_GET_APP_CONFIGURATION: // handlers -> get_app_configuration handle_get_app_configuration( - G_io_apdu_buffer[OFFSET_P1], - G_io_apdu_buffer[OFFSET_P2], - G_io_apdu_buffer + OFFSET_CDATA, - G_io_apdu_buffer[OFFSET_LC], - &flags, - &tx - ); + G_io_apdu_buffer[ OFFSET_P1 ], + G_io_apdu_buffer[ OFFSET_P2 ], + G_io_apdu_buffer + OFFSET_CDATA, + G_io_apdu_buffer[ OFFSET_LC ], &flags, &tx); break; case INS_GET_PUBLIC_KEY: // handlers -> get_public_key - handle_get_public_key( - G_io_apdu_buffer[OFFSET_P1], - G_io_apdu_buffer[OFFSET_P2], - G_io_apdu_buffer + OFFSET_CDATA, - G_io_apdu_buffer[OFFSET_LC], - &flags, - &tx - ); + handle_get_public_key(G_io_apdu_buffer[ OFFSET_P1 ], + G_io_apdu_buffer[ OFFSET_P2 ], + G_io_apdu_buffer + OFFSET_CDATA, + G_io_apdu_buffer[ OFFSET_LC ], + &flags, &tx); break; case INS_SIGN_TRANSACTION: // handlers -> sign_transaction - handle_sign_transaction( - G_io_apdu_buffer[OFFSET_P1], - G_io_apdu_buffer[OFFSET_P2], - G_io_apdu_buffer + OFFSET_CDATA, - G_io_apdu_buffer[OFFSET_LC], - &flags, - &tx - ); + handle_sign_transaction(G_io_apdu_buffer[ OFFSET_P1 ], + G_io_apdu_buffer[ OFFSET_P2 ], + G_io_apdu_buffer + OFFSET_CDATA, + G_io_apdu_buffer[ OFFSET_LC ], + &flags, &tx); break; - default: + default: THROW(EXCEPTION_UNKNOWN_INS); } } - CATCH(EXCEPTION_IO_RESET) { - THROW(EXCEPTION_IO_RESET); - } + CATCH(EXCEPTION_IO_RESET) { THROW(EXCEPTION_IO_RESET); } CATCH_OTHER(e) { // Convert exception to response code and add to APDU return switch (e & 0xF000) { @@ -100,8 +90,8 @@ void app_main() { break; } - G_io_apdu_buffer[tx++] = sw >> 8; - G_io_apdu_buffer[tx++] = sw & 0xff; + G_io_apdu_buffer[ tx++ ] = sw >> 8; + G_io_apdu_buffer[ tx++ ] = sw & 0xff; } FINALLY { // explicitly do nothing @@ -114,9 +104,7 @@ void app_main() { void app_exit(void) { // All os calls must be wrapped in a try catch context BEGIN_TRY_L(exit) { - TRY_L(exit) { - os_sched_exit(-1); - } + TRY_L(exit) { os_sched_exit(-1); } FINALLY_L(exit) { // explicitly do nothing } @@ -139,13 +127,14 @@ __attribute__((section(".boot"))) int main() { BEGIN_TRY { TRY { - // Initialize the hardware abstraction layer (HAL) in + // Initialize the hardware abstraction layer (HAL) in // the Ledger SDK io_seproxyhal_init(); #ifdef TARGET_NANOX // grab the current plane mode setting - G_io_app.plane_mode = os_setting_get(OS_SETTING_PLANEMODE, NULL, 0); + G_io_app.plane_mode = + os_setting_get(OS_SETTING_PLANEMODE, NULL, 0); #endif // TARGET_NANOX #ifdef HAVE_BLE @@ -166,9 +155,7 @@ __attribute__((section(".boot"))) int main() { // reset IO and UX before continuing continue; } - CATCH_ALL { - break; - } + CATCH_ALL { break; } FINALLY { // explicitly do nothing } diff --git a/src/printf.c b/src/printf.c index 285615dd..6a43da10 100644 --- a/src/printf.c +++ b/src/printf.c @@ -22,19 +22,19 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on -// embedded systems with a very limited resources. These routines are thread -// safe and reentrant! -// Use this instead of the bloated standard/newlib printf cause these use -// malloc for printf (and may not be thread safe). +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for +// speed on +// embedded systems with a very limited resources. These routines are +// thread safe and reentrant! Use this instead of the bloated +// standard/newlib printf cause these use malloc for printf (and may not +// be thread safe). // /////////////////////////////////////////////////////////////////////////////// -#include -#include - #include "printf.h" +#include +#include // define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the // printf_config.h header file @@ -43,19 +43,18 @@ #include "printf_config.h" #endif - // 'ntoa' conversion buffer size, this must be big enough to hold one converted // numeric number including padded zeros (dynamically created on stack) // default: 32 byte #ifndef PRINTF_NTOA_BUFFER_SIZE -#define PRINTF_NTOA_BUFFER_SIZE 32U +#define PRINTF_NTOA_BUFFER_SIZE 32U #endif // 'ftoa' conversion buffer size, this must be big enough to hold one converted // float number including padded zeros (dynamically created on stack) // default: 32 byte #ifndef PRINTF_FTOA_BUFFER_SIZE -#define PRINTF_FTOA_BUFFER_SIZE 32U +#define PRINTF_FTOA_BUFFER_SIZE 32U #endif // support for the floating point type (%f) @@ -73,13 +72,13 @@ // define the default floating point precision // default: 6 digits #ifndef PRINTF_DEFAULT_FLOAT_PRECISION -#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U #endif // define the largest float suitable to print with %f // default: 1e9 #ifndef PRINTF_MAX_FLOAT -#define PRINTF_MAX_FLOAT 1e9 +#define PRINTF_MAX_FLOAT 1e9 #endif // support for the long long types (%llu or %p) @@ -98,817 +97,872 @@ /////////////////////////////////////////////////////////////////////////////// // internal flag definitions -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_CHAR (1U << 6U) -#define FLAGS_SHORT (1U << 7U) -#define FLAGS_LONG (1U << 8U) -#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) #define FLAGS_PRECISION (1U << 10U) #define FLAGS_ADAPT_EXP (1U << 11U) - // import float.h for DBL_MAX #if defined(PRINTF_SUPPORT_FLOAT) #include #endif - // output function type -typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); - +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, + size_t maxlen); // wrapper (used as buffer) for output function type typedef struct { - void (*fct)(char character, void* arg); - void* arg; + void (*fct)(char character, void* arg); + void* arg; } out_fct_wrap_type; - // internal buffer output -static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) -{ - if (idx < maxlen) { - ((char*)buffer)[idx] = character; - } +static inline void _out_buffer(char character, void* buffer, size_t idx, + size_t maxlen) { + if (idx < maxlen) { + ((char*)buffer)[ idx ] = character; + } } - // internal null output -static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)character; (void)buffer; (void)idx; (void)maxlen; +static inline void _out_null(char character, void* buffer, size_t idx, + size_t maxlen) { + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; } - // internal _putchar wrapper -static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)buffer; (void)idx; (void)maxlen; - if (character) { - _putchar(character); - } +static inline void _out_char(char character, void* buffer, size_t idx, + size_t maxlen) { + (void)buffer; + (void)idx; + (void)maxlen; + if (character) { + _putchar(character); + } } - // internal output function wrapper -static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)idx; (void)maxlen; - if (character) { - // buffer is the output fct pointer - ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); - } +static inline void _out_fct(char character, void* buffer, size_t idx, + size_t maxlen) { + (void)idx; + (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer) + ->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } } - // internal secure strlen -// \return The length of the string (excluding the terminating 0) limited by 'maxsize' -static inline unsigned int _strnlen_s(const char* str, size_t maxsize) -{ - const char* s; - for (s = str; *s && maxsize--; ++s); - return (unsigned int)(s - str); +// \return The length of the string (excluding the terminating 0) limited by +// 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) { + const char* s; + for (s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); } - // internal test if char is a digit (0-9) // \return true if char is a digit -static inline bool _is_digit(char ch) -{ - return (ch >= '0') && (ch <= '9'); -} - +static inline bool _is_digit(char ch) { return (ch >= '0') && (ch <= '9'); } // internal ASCII string to unsigned int conversion -static unsigned int _atoi(const char** str) -{ - unsigned int i = 0U; - while (_is_digit(**str)) { - i = i * 10U + (unsigned int)(*((*str)++) - '0'); - } - return i; +static unsigned int _atoi(const char** str) { + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; } - // output the specified string in reverse, taking care of any zero-padding -static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) -{ - const size_t start_idx = idx; - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, + size_t maxlen, const char* buf, size_t len, + unsigned int width, unsigned int flags) { + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } } - } - // reverse string - while (len) { - out(buf[--len], buffer, idx++, maxlen); - } + // reverse string + while (len) { + out(buf[ --len ], buffer, idx++, maxlen); + } - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) { - out(' ', buffer, idx++, maxlen); + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } } - } - return idx; + return idx; } - // internal itoa format -static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) -{ - // pad leading zeros - if (!(flags & FLAGS_LEFT)) { - if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, + size_t maxlen, char* buf, size_t len, bool negative, + unsigned int base, unsigned int prec, + unsigned int width, unsigned int flags) { + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && + (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[ len++ ] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && + (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[ len++ ] = '0'; + } } - } - // handle hash - if (flags & FLAGS_HASH) { - if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { - len--; - if (len && (base == 16U)) { - len--; - } - } - if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'x'; - } - else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'X'; - } - else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'b'; - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - buf[len++] = '0'; + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && + ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && + (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[ len++ ] = 'x'; + } else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && + (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[ len++ ] = 'X'; + } else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[ len++ ] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[ len++ ] = '0'; + } } - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } - else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[ len++ ] = '-'; + } else if (flags & FLAGS_PLUS) { + buf[ len++ ] = '+'; // ignore the space if the '+' exists + } else if (flags & FLAGS_SPACE) { + buf[ len++ ] = ' '; + } } - } - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); } - // internal itoa for 'long' type -static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, + size_t maxlen, unsigned long value, bool negative, + unsigned long base, unsigned int prec, + unsigned int width, unsigned int flags) { + char buf[ PRINTF_NTOA_BUFFER_SIZE ]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[ len++ ] = + digit < 10 ? '0' + digit + : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, + (unsigned int)base, prec, width, flags); } - // internal itoa for 'long long' type #if defined(PRINTF_SUPPORT_LONG_LONG) -static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, + size_t maxlen, unsigned long long value, + bool negative, unsigned long long base, + unsigned int prec, unsigned int width, + unsigned int flags) { + char buf[ PRINTF_NTOA_BUFFER_SIZE ]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[ len++ ] = + digit < 10 ? '0' + digit + : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, + (unsigned int)base, prec, width, flags); } -#endif // PRINTF_SUPPORT_LONG_LONG - +#endif // PRINTF_SUPPORT_LONG_LONG #if defined(PRINTF_SUPPORT_FLOAT) #if defined(PRINTF_SUPPORT_EXPONENTIAL) -// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +// forward declaration so that _ftoa can switch to exp notation for values > +// PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, + double value, unsigned int prec, unsigned int width, + unsigned int flags); #endif - // internal ftoa for fixed decimal floating point -static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_FTOA_BUFFER_SIZE]; - size_t len = 0U; - double diff = 0.0; - - // powers of 10 - static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - - // test for special values - if (value != value) - return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); - if (value < -DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); - if (value > DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); - - // test for very large values - // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad - if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, + double value, unsigned int prec, unsigned int width, + unsigned int flags) { + char buf[ PRINTF_FTOA_BUFFER_SIZE ]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = {1, 10, 100, 1000, + 10000, 100000, 1000000, 10000000, + 100000000, 1000000000}; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, + (flags & FLAGS_PLUS) ? "fni+" : "fni", + (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which + // could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { #if defined(PRINTF_SUPPORT_EXPONENTIAL) - return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); #else - return 0U; + return 0U; #endif - } - - // test for negative - bool negative = false; - if (value < 0) { - negative = true; - value = 0 - value; - } - - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - // limit precision to 9, cause a prec >= 10 can lead to overflow errors - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { - buf[len++] = '0'; - prec--; - } - - int whole = (int)value; - double tmp = (value - whole) * pow10[prec]; - unsigned long frac = (unsigned long)tmp; - diff = tmp - frac; - - if (diff > 0.5) { - ++frac; - // handle rollover, e.g. case 0.99 with prec 1 is 1.0 - if (frac >= pow10[prec]) { - frac = 0; - ++whole; - } - } - else if (diff < 0.5) { - } - else if ((frac == 0U) || (frac & 1U)) { - // if halfway, round up if odd OR if last digit is 0 - ++frac; - } - - if (prec == 0U) { - diff = value - (double)whole; - if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++whole; - } - } - else { - unsigned int count = prec; - // now do fractional part, as an unsigned number - while (len < PRINTF_FTOA_BUFFER_SIZE) { - --count; - buf[len++] = (char)(48U + (frac % 10U)); - if (!(frac /= 10U)) { - break; - } - } - // add extra 0s - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { - buf[len++] = '0'; - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - // add decimal - buf[len++] = '.'; } - } - // do whole part, number is reversed - while (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = (char)(48 + (whole % 10)); - if (!(whole /= 10)) { - break; + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; } - } - // pad leading zeros - if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { - if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; } - while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { - buf[len++] = '0'; + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[ len++ ] = '0'; + prec--; } - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; + int whole = (int)value; + double tmp = (value - whole) * pow10[ prec ]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[ prec ]) { + frac = 0; + ++whole; + } + } else if (diff < 0.5) { + } else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[ len++ ] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[ len++ ] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[ len++ ] = '.'; + } } - else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[ len++ ] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } } - else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[ len++ ] = '0'; + } } - } - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); -} + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[ len++ ] = '-'; + } else if (flags & FLAGS_PLUS) { + buf[ len++ ] = '+'; // ignore the space if the '+' exists + } else if (flags & FLAGS_SPACE) { + buf[ len++ ] = ' '; + } + } + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} #if defined(PRINTF_SUPPORT_EXPONENTIAL) -// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) -{ - // check for NaN and special values - if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { - return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); - } - - // determine the sign - const bool negative = value < 0; - if (negative) { - value = -value; - } - - // default precision - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - - // determine the decimal exponent - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) - union { - uint64_t U; - double F; - } conv; - - conv.F = value; - int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 - conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) - // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 - int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); - // now we want to compute 10^expval but we want to be sure it won't overflow - exp2 = (int)(expval * 3.321928094887362 + 0.5); - const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; - const double z2 = z * z; - conv.U = (uint64_t)(exp2 + 1023) << 52U; - // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - // correct for rounding errors - if (value < conv.F) { - expval--; - conv.F /= 10; - } - - // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters - unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; - - // in "%g" mode, "prec" is the number of *significant figures* not decimals - if (flags & FLAGS_ADAPT_EXP) { - // do we want to fall-back to "%f" mode? - if ((value >= 1e-4) && (value < 1e6)) { - if ((int)prec > expval) { - prec = (unsigned)((int)prec - expval - 1); - } - else { - prec = 0; - } - flags |= FLAGS_PRECISION; // make sure _ftoa respects precision - // no characters in exponent - minwidth = 0U; - expval = 0; - } - else { - // we use one sigfig for the whole part - if ((prec > 0) && (flags & FLAGS_PRECISION)) { - --prec; - } - } - } - - // will everything fit? - unsigned int fwidth = width; - if (width > minwidth) { - // we didn't fall-back so subtract the characters required for the exponent - fwidth -= minwidth; - } else { - // not enough characters, so go back to default sizing - fwidth = 0U; - } - if ((flags & FLAGS_LEFT) && minwidth) { - // if we're padding on the right, DON'T pad the floating part - fwidth = 0U; - } - - // rescale the float value - if (expval) { - value /= conv.F; - } - - // output the floating part - const size_t start_idx = idx; - idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); - - // output the exponent part - if (minwidth) { - // output the exponential symbol - out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); - // output the exponent value - idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); - // might need to right-pad spaces - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); +// internal ftoa variant for exponential floating-point type, contributed by +// Martijn Jasperse +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, + double value, unsigned int prec, unsigned int width, + unsigned int flags) { + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay + // (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | + (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln + // around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see + // https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside + // 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } } - } - return idx; -} -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the + // exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, + fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, + (expval < 0) ? -expval : expval, expval < 0, 10, 0, + minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT // internal vsnprintf -static int _hedera_vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) -{ - unsigned int flags, width, precision, n; - size_t idx = 0U; - - if (!buffer) { - // use null output function - out = _out_null; - } - - while (*format) - { - // format specifier? %[flags][width][.precision][length] - if (*format != '%') { - // no - out(*format, buffer, idx++, maxlen); - format++; - continue; - } - else { - // yes, evaluate it - format++; - } - - // evaluate flags - flags = 0U; - do { - switch (*format) { - case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; - case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; - case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; - case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; - case '#': flags |= FLAGS_HASH; format++; n = 1U; break; - default : n = 0U; break; - } - } while (n); - - // evaluate width field - width = 0U; - if (_is_digit(*format)) { - width = _atoi(&format); - } - else if (*format == '*') { - const int w = va_arg(va, int); - if (w < 0) { - flags |= FLAGS_LEFT; // reverse padding - width = (unsigned int)-w; - } - else { - width = (unsigned int)w; - } - format++; - } - - // evaluate precision field - precision = 0U; - if (*format == '.') { - flags |= FLAGS_PRECISION; - format++; - if (_is_digit(*format)) { - precision = _atoi(&format); - } - else if (*format == '*') { - const int prec = (int)va_arg(va, int); - precision = prec > 0 ? (unsigned int)prec : 0U; - format++; - } - } - - // evaluate length field - switch (*format) { - case 'l' : - flags |= FLAGS_LONG; - format++; - if (*format == 'l') { - flags |= FLAGS_LONG_LONG; - format++; - } - break; - case 'h' : - flags |= FLAGS_SHORT; - format++; - if (*format == 'h') { - flags |= FLAGS_CHAR; - format++; - } - break; -#if defined(PRINTF_SUPPORT_PTRDIFF_T) - case 't' : - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; -#endif - case 'j' : - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - case 'z' : - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - default : - break; - } - - // evaluate specifier - switch (*format) { - case 'd' : - case 'i' : - case 'u' : - case 'x' : - case 'X' : - case 'o' : - case 'b' : { - // set the base - unsigned int base; - if (*format == 'x' || *format == 'X') { - base = 16U; - } - else if (*format == 'o') { - base = 8U; - } - else if (*format == 'b') { - base = 2U; - } - else { - base = 10U; - flags &= ~FLAGS_HASH; // no hash for dec format +static int _hedera_vsnprintf(out_fct_type out, char* buffer, + const size_t maxlen, const char* format, + va_list va) { + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } else { + // yes, evaluate it + format++; } - // uppercase - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } else { + width = (unsigned int)w; + } + format++; } - // no plus or space flag for u, x, X, o, b - if ((*format != 'i') && (*format != 'd')) { - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } } - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { - flags &= ~FLAGS_ZEROPAD; + // evaluate length field + switch (*format) { + case 'l': + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG + : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG + : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG + : FLAGS_LONG_LONG); + format++; + break; + default: + break; } - // convert the integer - if ((*format == 'i') || (*format == 'd')) { - // signed - if (flags & FLAGS_LONG_LONG) { + // evaluate specifier + switch (*format) { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } else if (*format == 'o') { + base = 8U; + } else if (*format == 'b') { + base = 2U; + } else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { #if defined(PRINTF_SUPPORT_LONG_LONG) - const long long value = va_arg(va, long long); - idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + const long long value = va_arg(va, long long); + idx = _ntoa_long_long( + out, buffer, idx, maxlen, + (unsigned long long)(value > 0 ? value : 0 - value), + value < 0, base, precision, width, flags); #endif - } - else if (flags & FLAGS_LONG) { - const long value = va_arg(va, long); - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); - } - else { - const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); - } - } - else { - // unsigned - if (flags & FLAGS_LONG_LONG) { + } else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long( + out, buffer, idx, maxlen, + (unsigned long)(value > 0 ? value : 0 - value), + value < 0, base, precision, width, flags); + } else { + const int value = + (flags & FLAGS_CHAR) ? (char)va_arg(va, int) + : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) + : va_arg(va, int); + idx = _ntoa_long( + out, buffer, idx, maxlen, + (unsigned int)(value > 0 ? value : 0 - value), + value < 0, base, precision, width, flags); + } + } else { + // unsigned + if (flags & FLAGS_LONG_LONG) { #if defined(PRINTF_SUPPORT_LONG_LONG) - idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); + idx = _ntoa_long_long(out, buffer, idx, maxlen, + va_arg(va, unsigned long long), + false, base, precision, width, + flags); #endif - } - else if (flags & FLAGS_LONG) { - idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); - } - else { - const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); - idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); - } - } - format++; - break; - } + } else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, + va_arg(va, unsigned long), false, base, + precision, width, flags); + } else { + const unsigned int value = + (flags & FLAGS_CHAR) + ? (unsigned char)va_arg(va, unsigned int) + : (flags & FLAGS_SHORT) + ? (unsigned short int)va_arg(va, unsigned int) + : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, + base, precision, width, flags); + } + } + format++; + break; + } #if defined(PRINTF_SUPPORT_FLOAT) - case 'f' : - case 'F' : - if (*format == 'F') flags |= FLAGS_UPPERCASE; - idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); - format++; - break; + case 'f': + case 'F': + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), + precision, width, flags); + format++; + break; #if defined(PRINTF_SUPPORT_EXPONENTIAL) - case 'e': - case 'E': - case 'g': - case 'G': - if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; - if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; - idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); - format++; - break; -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT - case 'c' : { - unsigned int l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // char output - out((char)va_arg(va, int), buffer, idx++, maxlen); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 's' : { - const char* p = va_arg(va, char*); - unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 'p' : { - width = sizeof(void*) * 2U; - flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), + precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': { + const char* p = va_arg(va, char*); + unsigned int l = + _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && + (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; #if defined(PRINTF_SUPPORT_LONG_LONG) - const bool is_ll = sizeof(uintptr_t) == sizeof(long long); - if (is_ll) { - idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); - } - else { + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, + (uintptr_t)va_arg(va, void*), false, + 16U, precision, width, flags); + } else { #endif - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); + idx = _ntoa_long( + out, buffer, idx, maxlen, + (unsigned long)((uintptr_t)va_arg(va, void*)), false, + 16U, precision, width, flags); #if defined(PRINTF_SUPPORT_LONG_LONG) - } + } #endif - format++; - break; - } - - case '%' : - out('%', buffer, idx++, maxlen); - format++; - break; - - default : - out(*format, buffer, idx++, maxlen); - format++; - break; + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } } - } - // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); - // return written chars without terminating \0 - return (int)idx; + // return written chars without terminating \0 + return (int)idx; } - /////////////////////////////////////////////////////////////////////////////// -int hedera_printf_(const char* format, ...) -{ - va_list va; - va_start(va, format); - char buffer[1]; - const int ret = _hedera_vsnprintf(_out_char, buffer, (size_t)-1, format, va); - va_end(va); - return ret; +int hedera_printf_(const char* format, ...) { + va_list va; + va_start(va, format); + char buffer[ 1 ]; + const int ret = + _hedera_vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; } - -int hedera_sprintf_(char* buffer, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = _hedera_vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); - va_end(va); - return ret; +int hedera_sprintf_(char* buffer, const char* format, ...) { + va_list va; + va_start(va, format); + const int ret = + _hedera_vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; } - -int hedera_snprintf_(char* buffer, size_t count, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = _hedera_vsnprintf(_out_buffer, buffer, count, format, va); - va_end(va); - return ret; +int hedera_snprintf_(char* buffer, size_t count, const char* format, ...) { + va_list va; + va_start(va, format); + const int ret = _hedera_vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; } - -int hedera_vprintf_(const char* format, va_list va) -{ - char buffer[1]; - return _hedera_vsnprintf(_out_char, buffer, (size_t)-1, format, va); +int hedera_vprintf_(const char* format, va_list va) { + char buffer[ 1 ]; + return _hedera_vsnprintf(_out_char, buffer, (size_t)-1, format, va); } - -int hedera_vsnprintf_(char* buffer, size_t count, const char* format, va_list va) -{ - return _hedera_vsnprintf(_out_buffer, buffer, count, format, va); +int hedera_vsnprintf_(char* buffer, size_t count, const char* format, + va_list va) { + return _hedera_vsnprintf(_out_buffer, buffer, count, format, va); } - -int hedera_fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) -{ - va_list va; - va_start(va, format); - const out_fct_wrap_type out_fct_wrap = { out, arg }; - const int ret = _hedera_vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); - va_end(va); - return ret; +int hedera_fctprintf(void (*out)(char character, void* arg), void* arg, + const char* format, ...) { + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _hedera_vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, + (size_t)-1, format, va); + va_end(va); + return ret; } diff --git a/src/printf.h b/src/printf.h index b30c54d4..d68575b4 100644 --- a/src/printf.h +++ b/src/printf.h @@ -10,10 +10,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -22,7 +22,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed +// on // embedded systems with a very limited resources. // Use this instead of bloated standard/newlib printf. // These routines are thread safe and reentrant. @@ -35,83 +36,82 @@ #include #include - #ifdef __cplusplus extern "C" { #endif - /** - * Output a character to a custom device like UART, used by the printf() function - * This function is declared here only. You have to write your custom implementation somewhere - * \param character Character to output + * Output a character to a custom device like UART, used by the printf() + * function This function is declared here only. You have to write your custom + * implementation somewhere \param character Character to output */ void _putchar(char character); - /** * Tiny printf implementation * You have to implement _putchar if you use printf() - * To avoid conflicts with the regular printf() API it is overridden by macro defines - * and internal underscore-appended functions like printf_() are used + * To avoid conflicts with the regular printf() API it is overridden by macro + * defines and internal underscore-appended functions like printf_() are used * \param format A string that specifies the format of the output - * \return The number of characters that are written into the array, not counting the terminating null character + * \return The number of characters that are written into the array, not + * counting the terminating null character */ #define hedera_printf hedera_printf_ int hedera_printf_(const char* format, ...); - /** * Tiny sprintf implementation - * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! - * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! - * \param format A string that specifies the format of the output - * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING + * (V)SNPRINTF INSTEAD! \param buffer A pointer to the buffer where to store the + * formatted string. MUST be big enough to store the output! \param format A + * string that specifies the format of the output \return The number of + * characters that are WRITTEN into the buffer, not counting the terminating + * null character */ #define hedera_sprintf hedera_sprintf_ int hedera_sprintf_(char* buffer, const char* format, ...); - /** * Tiny snprintf/vsnprintf implementation * \param buffer A pointer to the buffer where to store the formatted string - * \param count The maximum number of characters to store in the buffer, including a terminating null character - * \param format A string that specifies the format of the output - * \param va A value identifying a variable arguments list - * \return The number of characters that COULD have been written into the buffer, not counting the terminating - * null character. A value equal or larger than count indicates truncation. Only when the returned value - * is non-negative and less than count, the string has been completely written. + * \param count The maximum number of characters to store in the buffer, + * including a terminating null character \param format A string that specifies + * the format of the output \param va A value identifying a variable arguments + * list \return The number of characters that COULD have been written into the + * buffer, not counting the terminating null character. A value equal or larger + * than count indicates truncation. Only when the returned value is non-negative + * and less than count, the string has been completely written. */ -#define hedera_snprintf hedera_snprintf_ +#define hedera_snprintf hedera_snprintf_ #define hedera_vsnprintf hedera_vsnprintf_ -int hedera_snprintf_(char* buffer, size_t count, const char* format, ...); -int hedera_vsnprintf_(char* buffer, size_t count, const char* format, va_list va); - +int hedera_snprintf_(char* buffer, size_t count, const char* format, ...); +int hedera_vsnprintf_(char* buffer, size_t count, const char* format, + va_list va); /** * Tiny vprintf implementation * \param format A string that specifies the format of the output * \param va A value identifying a variable arguments list - * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + * \return The number of characters that are WRITTEN into the buffer, not + * counting the terminating null character */ #define hedera_vprintf hedera_vprintf_ int hedera_vprintf_(const char* format, va_list va); - /** * printf with output function - * You may use this as dynamic alternative to printf() with its fixed _putchar() output - * \param out An output function which takes one character and an argument pointer - * \param arg An argument pointer for user data passed to output function - * \param format A string that specifies the format of the output - * \return The number of characters that are sent to the output function, not counting the terminating null character + * You may use this as dynamic alternative to printf() with its fixed _putchar() + * output \param out An output function which takes one character and an + * argument pointer \param arg An argument pointer for user data passed to + * output function \param format A string that specifies the format of the + * output \return The number of characters that are sent to the output function, + * not counting the terminating null character */ -int hedera_fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); - +int hedera_fctprintf(void (*out)(char character, void* arg), void* arg, + const char* format, ...); #ifdef __cplusplus } #endif - -#endif // _PRINTF_H_ +#endif // _PRINTF_H_ diff --git a/src/sign_transaction.c b/src/sign_transaction.c index 9b44501e..119b0514 100644 --- a/src/sign_transaction.c +++ b/src/sign_transaction.c @@ -1,69 +1,68 @@ -#include -#include -#include -#include -#include - -#include "printf.h" -#include "globals.h" -#include "debug.h" -#include "errors.h" -#include "handlers.h" -#include "hedera.h" -#include "hedera_format.h" -#include "io.h" -#include "TransactionBody.pb.h" -#include "utils.h" -#include "ui.h" #include "sign_transaction.h" #if defined(TARGET_NANOS) -static struct sign_tx_context_t { - // ui common - uint32_t key_index; - uint8_t transfer_to_index; - uint8_t transfer_from_index; - - // Transaction Summary - char summary_line_1[DISPLAY_SIZE + 1]; - char summary_line_2[DISPLAY_SIZE + 1]; - char title[DISPLAY_SIZE + 1]; - - // Account ID: uint64_t.uint64_t.uint64_t - // Most other entities are shorter - char full[ACCOUNT_ID_SIZE + 1]; - char partial[DISPLAY_SIZE + 1]; - - // Steps correspond to parts of the transaction proto - // type is set based on proto - enum TransactionStep step; - enum TransactionType type; - - uint8_t display_index; // 1 -> Number Screens - uint8_t display_count; // Number Screens - - // Parsed transaction - HederaTransactionBody transaction; -} ctx; - -// UI Definition for Nano S +/* + * Supported Transactions: + * + * Verify: + * "Verify Account with Key #0?" (Summary) <--> "Account" (Senders) <--> Confirm +<--> Deny + * + * Create: + * "Create Account with Key #0?" (Summary) <--> Operator <--> "Stake to" +(Senders) <--> "Collect Rewards? Yes / No" (Recipients) <--> "Initial Balance" +(Amount) <--> Fee <--> Memo <--> Confirm <--> Deny + * + * Update: + * "Update Account 0.0.0 with Key #0?" (Summary) <--> Operator <--> "Stake to" +(Senders) <--> "Collect Rewards (Yes / No)" (Recipients) <--> "Updated Account" +(Amount) <--> Fee <--> Memo <--> Confirm <--> Deny + * + * Transfer: + * "Transfer with Key #0?" (Summary) <--> Operator <--> Senders <--> Recipients +<--> Amount <--> Fee <--> Memo <--> Confirm <--> Deny + * + * Associate: + * "Associate Token with Key #0?" (Summary) <--> Operator <--> "Token" (Senders) +<--> "Updating" (Amount) <--> Fee <--> Memo <--> Confirm <--> Deny + * + * Dissociate: + * "Dissociate Token with Key #0?" (Summary) <--> Operator <--> "Token" +(Senders) <--> "Updating" (Amount) <--> Fee <--> Memo <--> Confirm <--> Deny + * + * TokenMint: + * "Mint Token with Key #0?" (Summary) <--> Operator <--> "Token" (Senders) <--> +Amount <--> Fee <--> Memo <--> Confirm <--> Deny + * + * TokenBurn: + * "Burn Token with Key #0?" (Summary) <--> Operator <--> "Token" (Senders) <--> +Amount <--> Fee <--> Memo <--> Confirm <--> Deny + * + * I chose the steps for the originally supported CreateAccount and Transfer +transactions, and the additional transactions have been added since then. Steps +may be skipped or modified (as described above) from the original transfer flow. +The implementation of the steps is in the 'intermediate' screen button handlers. +These functions control iterating through the steps and control paging for +entities that overflow display on a single screen. The nano X has a paging macro +for this in its UX system. We don't have much RAM to work with, so I couldn't +define entirely separate UI elements for each flow, which lead me to using the +screens defined below as singletons. + */ + // Step 1: Transaction Summary static const bagl_element_t ui_tx_summary_step[] = { - UI_BACKGROUND(), - UI_ICON_RIGHT(RIGHT_ICON_ID, BAGL_GLYPH_ICON_RIGHT), + UI_BACKGROUND(), UI_ICON_RIGHT(RIGHT_ICON_ID, BAGL_GLYPH_ICON_RIGHT), // () >> // Line 1 // Line 2 UI_TEXT(LINE_1_ID, 0, 12, 128, ctx.summary_line_1), - UI_TEXT(LINE_2_ID, 0, 26, 128, ctx.summary_line_2) -}; + UI_TEXT(LINE_2_ID, 0, 26, 128, ctx.summary_line_2)}; // Step 2 - 7: Operator, Senders, Recipients, Amount, Fee, Memo static const bagl_element_t ui_tx_intermediate_step[] = { - UI_BACKGROUND(), - UI_ICON_LEFT(LEFT_ICON_ID, BAGL_GLYPH_ICON_LEFT), + UI_BACKGROUND(), UI_ICON_LEFT(LEFT_ICON_ID, BAGL_GLYPH_ICON_LEFT), UI_ICON_RIGHT(RIGHT_ICON_ID, BAGL_GLYPH_ICON_RIGHT), // << >> @@ -71,13 +70,11 @@ static const bagl_element_t ui_tx_intermediate_step[] = { // UI_TEXT(LINE_1_ID, 0, 12, 128, ctx.title), - UI_TEXT(LINE_2_ID, 0, 26, 128, ctx.partial) -}; + UI_TEXT(LINE_2_ID, 0, 26, 128, ctx.partial)}; // Step 8: Confirm static const bagl_element_t ui_tx_confirm_step[] = { - UI_BACKGROUND(), - UI_ICON_LEFT(LEFT_ICON_ID, BAGL_GLYPH_ICON_LEFT), + UI_BACKGROUND(), UI_ICON_LEFT(LEFT_ICON_ID, BAGL_GLYPH_ICON_LEFT), UI_ICON_RIGHT(RIGHT_ICON_ID, BAGL_GLYPH_ICON_RIGHT), // << >> @@ -85,36 +82,32 @@ static const bagl_element_t ui_tx_confirm_step[] = { // UI_TEXT(LINE_1_ID, 0, 12, 128, "Confirm"), - UI_ICON(LINE_2_ID, 0, 24, 128, BAGL_GLYPH_ICON_CHECK) -}; + UI_ICON(LINE_2_ID, 0, 24, 128, BAGL_GLYPH_ICON_CHECK)}; // Step 9: Deny static const bagl_element_t ui_tx_deny_step[] = { - UI_BACKGROUND(), - UI_ICON_LEFT(LEFT_ICON_ID, BAGL_GLYPH_ICON_LEFT), + UI_BACKGROUND(), UI_ICON_LEFT(LEFT_ICON_ID, BAGL_GLYPH_ICON_LEFT), // << () // Deny // X UI_TEXT(LINE_1_ID, 0, 12, 128, "Deny"), - UI_ICON(LINE_2_ID, 0, 24, 128, BAGL_GLYPH_ICON_CROSS) -}; + UI_ICON(LINE_2_ID, 0, 24, 128, BAGL_GLYPH_ICON_CROSS)}; -// Step 1: Transaction Summary -unsigned int ui_tx_summary_step_button( - unsigned int button_mask, - unsigned int __attribute__ ((unused)) button_mask_counter -) { - switch(button_mask) { +// Step 1: Transaction Summary --> Operator, Senders +unsigned int ui_tx_summary_step_button(unsigned int button_mask, + unsigned int __attribute__((unused)) + button_mask_counter) { + switch (button_mask) { case BUTTON_EVT_RELEASED | BUTTON_RIGHT: - if (ctx.type == Verify || ctx.type == Associate || ctx.type == TokenMint || ctx.type == TokenBurn) { + ctx.current_page = 1; + + if (ctx.type == Verify) { ctx.step = Senders; - ctx.display_index = 1; reformat_senders(); } else { ctx.step = Operator; - ctx.display_index = 1; reformat_operator(); } UX_DISPLAY(ui_tx_intermediate_step, NULL); @@ -128,97 +121,102 @@ void handle_intermediate_left_press() { // Navigate Left (scroll or return to previous step) switch (ctx.step) { case Operator: { - if (first_screen()) { // Return to Summary + if (first_screen_of_step()) { + // All: Summary <-- Operator + ctx.current_page = 1; ctx.step = Summary; - ctx.display_index = 1; UX_DISPLAY(ui_tx_summary_step, NULL); - } else { // Scroll Left - ctx.display_index--; + } else { // Scroll Left + ctx.current_page--; reformat_operator(); - UX_REDISPLAY(); } } break; case Senders: { - if (first_screen()) { // Return to Operator - if (ctx.type == Verify || ctx.type == Associate || ctx.type == TokenMint || ctx.type == TokenBurn) { + if (first_screen_of_step()) { // Return to Operator + ctx.current_page = 1; + + if (ctx.type == Verify) { + // Verify: Summary <-- Senders ctx.step = Summary; - ctx.display_index = 1; UX_DISPLAY(ui_tx_summary_step, NULL); } else { + // All (!Verify): Operator <-- Senders ctx.step = Operator; - ctx.display_index = 1; reformat_operator(); } - } else { // Scroll Left - ctx.display_index--; + } else { // Scroll Left + ctx.current_page--; reformat_senders(); } - UX_REDISPLAY(); + } break; case Recipients: { - if (first_screen()) { // Return to Senders + if (first_screen_of_step()) { + // All: Senders <-- Recipients + ctx.current_page = 1; ctx.step = Senders; - ctx.display_index = 1; reformat_senders(); - } else { // Scroll Left - ctx.display_index--; + } else { // Scroll Left + ctx.current_page--; reformat_recipients(); } - UX_REDISPLAY(); + } break; case Amount: { - if (first_screen()) { - if (ctx.type == Create) { // Return to Operator - ctx.step = Operator; - ctx.display_index = 1; - reformat_operator(); - } else if (ctx.type == Transfer || ctx.type == TokenTransfer) { // Return to Recipients - ctx.step = Recipients; - ctx.display_index = 1; - reformat_recipients(); - } else if (ctx.type == TokenMint || ctx.type == TokenBurn) { // Return to Senders + if (first_screen_of_step()) { + ctx.current_page = 1; + + if (ctx.type == TokenMint || ctx.type == TokenBurn || + ctx.type == Associate || ctx.type == Dissociate) { + // Mint, Burn, Associate, Dissociate: Senders <-- Amount ctx.step = Senders; - ctx.display_index = 1; reformat_senders(); + } else { + // All (!Mint, !Burn, !Associate, !Dissociate): Recipients + // <-- Amount + ctx.step = Recipients; + reformat_recipients(); } - } else { // Scroll left - ctx.display_index--; + } else { // Scroll left + ctx.current_page--; reformat_amount(); } - UX_REDISPLAY(); + } break; case Fee: { - if (first_screen()) { // Return to Amount + if (first_screen_of_step()) { + // All: Amount <-- Fee + ctx.current_page = 1; ctx.step = Amount; - ctx.display_index = 1; reformat_amount(); - } else { // Scroll left - ctx.display_index--; + } else { // Scroll left + ctx.current_page--; reformat_fee(); } - UX_REDISPLAY(); + } break; case Memo: { - if (first_screen()) { // Return to Fee + if (first_screen_of_step()) { + // All: Fee <-- Memo + ctx.current_page = 1; ctx.step = Fee; - ctx.display_index = 1; reformat_fee(); - } else { // Scroll Left - ctx.display_index--; + } else { // Scroll Left + ctx.current_page--; reformat_memo(); } - UX_REDISPLAY(); + } break; case Summary: case Confirm: case Deny: - // ignore left button on Summary, Confirm, and Deny screens + // Should not occur, does not handle these steps break; } } @@ -227,114 +225,107 @@ void handle_intermediate_right_press() { // Navigate Right (scroll or continue to next step) switch (ctx.step) { case Operator: { - if (last_screen()) { - if (ctx.type == Create) { // Continue to Amount - ctx.step = Amount; - ctx.display_index = 1; - reformat_amount(); - } else { // Continue to Senders - ctx.step = Senders; - ctx.display_index = 1; - reformat_senders(); - } - } else { // Scroll Right - ctx.display_index++; + if (last_screen_of_step()) { + // All: Operator --> Senders + ctx.step = Senders; + ctx.current_page = 1; + reformat_senders(); + } else { // Scroll Right + ctx.current_page++; reformat_operator(); } - UX_REDISPLAY(); + } break; case Senders: { - if (last_screen()) { - if (ctx.type == Verify || ctx.type == Associate) { // Continue to Confirm + if (last_screen_of_step()) { + ctx.current_page = 1; + + if (ctx.type == Verify) { + // Verify: Senders --> Confirm ctx.step = Confirm; UX_DISPLAY(ui_tx_confirm_step, NULL); - } else if (ctx.type == TokenMint || ctx.type == TokenBurn) { + } else if (ctx.type == TokenMint || ctx.type == TokenBurn || + ctx.type == Associate || ctx.type == Dissociate) { + // Mint, Burn: Senders --> Amount ctx.step = Amount; - ctx.display_index = 1; reformat_amount(); - } else { // Continue to Recipients + } else { + // Create, Update, Transfer: Senders --> Recipients ctx.step = Recipients; - ctx.display_index = 1; reformat_recipients(); } - } else { // Scroll Right - ctx.display_index++; + } else { // Scroll Right + ctx.current_page++; reformat_senders(); } - UX_REDISPLAY(); + } break; case Recipients: { - if (last_screen()) { // Continue to Amount + if (last_screen_of_step()) { + // All (Create, Update, Transfer): Recipients --> Amount ctx.step = Amount; - ctx.display_index = 1; + ctx.current_page = 1; reformat_amount(); - } else { // Scroll Right - ctx.display_index++; + } else { // Scroll Right + ctx.current_page++; reformat_recipients(); } - UX_REDISPLAY(); + } break; case Amount: { - if (last_screen()) { - if (ctx.type == TokenMint || ctx.type == TokenBurn) { - // Continue to Confirm - ctx.step = Confirm; - ctx.display_index = 1; - UX_DISPLAY(ui_tx_confirm_step, NULL); - } else { - // Continue to Fee - ctx.step = Fee; - ctx.display_index = 1; - reformat_fee(); - } - } else { // Scroll Right - ctx.display_index++; + if (last_screen_of_step()) { + // All: Amount --> Fee + ctx.step = Fee; + ctx.current_page = 1; + reformat_fee(); + } else { // Scroll Right + ctx.current_page++; reformat_amount(); } - UX_REDISPLAY(); + } break; case Fee: { - if (last_screen()) { // Continue to Memo + if (last_screen_of_step()) { + // All: Fee --> Memo ctx.step = Memo; - ctx.display_index = 1; + ctx.current_page = 1; reformat_memo(); - } else { // Scroll Right - ctx.display_index++; + } else { // Scroll Right + ctx.current_page++; reformat_fee(); } - UX_REDISPLAY(); + } break; case Memo: { - if (last_screen()) { // Continue to Confirm + if (last_screen_of_step()) { + // All: Memo --> Confirm ctx.step = Confirm; - ctx.display_index = 1; + ctx.current_page = 1; UX_DISPLAY(ui_tx_confirm_step, NULL); - } else { // Scroll Right - ctx.display_index++; + } else { // Scroll Right + ctx.current_page++; reformat_memo(); - UX_REDISPLAY(); } } break; case Summary: case Confirm: case Deny: - // ignore left button on Summary, Confirm, and Deny screens + // Should not occur, does not handle these steps break; } } // Step 2 - 7: Operator, Senders, Recipients, Amount, Fee, Memo -unsigned int ui_tx_intermediate_step_button( - unsigned int button_mask, - unsigned int __attribute__ ((unused)) button_mask_counter -) { - switch(button_mask) { +unsigned int ui_tx_intermediate_step_button(unsigned int button_mask, + unsigned int __attribute__((unused)) + button_mask_counter) { + switch (button_mask) { case BUTTON_EVT_RELEASED | BUTTON_LEFT: handle_intermediate_left_press(); break; @@ -342,7 +333,7 @@ unsigned int ui_tx_intermediate_step_button( handle_intermediate_right_press(); break; case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: - // Skip to confirm screen + // Skip to confirm screen on double press ctx.step = Confirm; UX_DISPLAY(ui_tx_confirm_step, NULL); break; @@ -351,23 +342,20 @@ unsigned int ui_tx_intermediate_step_button( return 0; } -unsigned int ui_tx_confirm_step_button( - unsigned int button_mask, - unsigned int __attribute__ ((unused)) button_mask_counter -) { - switch(button_mask) { +unsigned int ui_tx_confirm_step_button(unsigned int button_mask, + unsigned int __attribute__((unused)) + button_mask_counter) { + switch (button_mask) { case BUTTON_EVT_RELEASED | BUTTON_LEFT: - if (ctx.type == Verify || ctx.type == Associate) { // Return to Senders + ctx.current_page = 1; + + if (ctx.type == Verify) { + // Verify: Senders <-- Confirm ctx.step = Senders; - ctx.display_index = 1; reformat_senders(); - } else if (ctx.type == TokenMint || ctx.type == TokenBurn) { // Return to Amount - ctx.step = Amount; - ctx.display_index = 1; - reformat_amount(); - } else { // Return to Memo + } else { + // All (!Verify): Memo <-- Confirm ctx.step = Memo; - ctx.display_index = 1; reformat_memo(); } UX_DISPLAY(ui_tx_intermediate_step, NULL); @@ -387,11 +375,10 @@ unsigned int ui_tx_confirm_step_button( return 0; } -unsigned int ui_tx_deny_step_button( - unsigned int button_mask, - unsigned int __attribute__ ((unused)) button_mask_counter -) { - switch(button_mask) { +unsigned int ui_tx_deny_step_button(unsigned int button_mask, + unsigned int __attribute__((unused)) + button_mask_counter) { + switch (button_mask) { case BUTTON_EVT_RELEASED | BUTTON_LEFT: // Return to Confirm ctx.step = Confirm; @@ -409,11 +396,12 @@ unsigned int ui_tx_deny_step_button( } uint8_t num_screens(size_t length) { - // Number of screens is len / display size + 1 for overflow - if (length == 0) return 1; - + // Number of screens is (len text in chars / display size in chars) + 1 for + // overflowing text + if (length <= 0) return 1; + uint8_t screens = length / DISPLAY_SIZE; - + if (length % DISPLAY_SIZE > 0) { screens += 1; } @@ -421,146 +409,172 @@ uint8_t num_screens(size_t length) { return screens; } -void count_screens() { - ctx.display_count = num_screens(strlen(ctx.full)); -} +void count_screens_of_step() { ctx.page_count = num_screens(strlen(ctx.full)); } -void shift_display() { +void repaint() { // Slide window (partial) along full entity (full) by DISPLAY_SIZE chars explicit_bzero(ctx.partial, DISPLAY_SIZE + 1); - memmove( - ctx.partial, - ctx.full + (DISPLAY_SIZE * (ctx.display_index - 1)), - DISPLAY_SIZE - ); + memmove(ctx.partial, ctx.full + (DISPLAY_SIZE * (ctx.current_page - 1)), + DISPLAY_SIZE); + UX_REDISPLAY(); } -bool last_screen() { - return ctx.display_index == ctx.display_count; -} +bool last_screen_of_step() { return ctx.current_page == ctx.page_count; } -bool first_screen() { - return ctx.display_index == 1; -} +bool first_screen_of_step() { return ctx.current_page == 1; } void reformat_operator() { - hedera_snprintf( - ctx.full, - ACCOUNT_ID_SIZE, - "%llu.%llu.%llu", - ctx.transaction.transactionID.accountID.shardNum, - ctx.transaction.transactionID.accountID.realmNum, - ctx.transaction.transactionID.accountID.accountNum - ); - - count_screens(); - - hedera_snprintf( - ctx.title, - DISPLAY_SIZE, - "Operator (%u/%u)", - ctx.display_index, - ctx.display_count - ); - - shift_display(); + hedera_snprintf(ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.transactionID.accountID.shardNum, + ctx.transaction.transactionID.accountID.realmNum, + ctx.transaction.transactionID.accountID.account.accountNum); + + count_screens_of_step(); + + hedera_snprintf(ctx.title, DISPLAY_SIZE, "Operator (%u/%u)", + ctx.current_page, ctx.page_count); + + repaint(); } void reformat_accounts(char* title_part, uint8_t transfer_index) { - hedera_snprintf( - ctx.full, - ACCOUNT_ID_SIZE, - "%llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[transfer_index].accountID.shardNum, - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[transfer_index].accountID.realmNum, - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[transfer_index].accountID.accountNum - ); + hedera_snprintf(ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ transfer_index ] + .accountID.shardNum, + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ transfer_index ] + .accountID.realmNum, + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ transfer_index ] + .accountID.account.accountNum); + + count_screens_of_step(); + + hedera_snprintf(ctx.title, DISPLAY_SIZE, "%s (%u/%u)", title_part, + ctx.current_page, ctx.page_count); +} + +void reformat_stake_target() { + switch (ctx.type) { + case Create: { + if (ctx.transaction.data.cryptoCreateAccount.which_staked_id == + Hedera_CryptoCreateTransactionBody_staked_account_id_tag) { + // An account ID and not a Node ID + hedera_snprintf( + ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.cryptoCreateAccount.staked_id + .staked_account_id.shardNum, + ctx.transaction.data.cryptoCreateAccount.staked_id + .staked_account_id.realmNum, + ctx.transaction.data.cryptoCreateAccount.staked_id + .staked_account_id.account.accountNum); + } else if (ctx.transaction.data.cryptoCreateAccount + .which_staked_id == + Hedera_CryptoCreateTransactionBody_staked_node_id_tag) { + hedera_snprintf(ctx.full, ACCOUNT_ID_SIZE, "Node %lld", + ctx.transaction.data.cryptoCreateAccount + .staked_id.staked_node_id); + } else { + hedera_snprintf(ctx.full, DISPLAY_SIZE, "%s", "None"); + } + } break; - count_screens(); + case Update: { + if (ctx.transaction.data.cryptoUpdateAccount.which_staked_id == + Hedera_CryptoUpdateTransactionBody_staked_account_id_tag) { + // An account ID and not a Node ID + hedera_snprintf( + ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.cryptoUpdateAccount.staked_id + .staked_account_id.shardNum, + ctx.transaction.data.cryptoUpdateAccount.staked_id + .staked_account_id.realmNum, + ctx.transaction.data.cryptoUpdateAccount.staked_id + .staked_account_id.account.accountNum); + } else if (ctx.transaction.data.cryptoUpdateAccount + .which_staked_id == + Hedera_CryptoUpdateTransactionBody_staked_node_id_tag) { + hedera_snprintf(ctx.full, ACCOUNT_ID_SIZE, "Node %lld", + ctx.transaction.data.cryptoUpdateAccount + .staked_id.staked_node_id); + } else { + hedera_snprintf(ctx.full, DISPLAY_SIZE, "%s", "None"); + } + } break; - hedera_snprintf( - ctx.title, - DISPLAY_SIZE, - "%s (%u/%u)", - title_part, - ctx.display_index, - ctx.display_count - ); + default: + break; + } + + count_screens_of_step(); + + hedera_snprintf(ctx.title, DISPLAY_SIZE, "Stake To (%u/%u)", + ctx.current_page, ctx.page_count); } void reformat_token() { switch (ctx.type) { case Associate: hedera_snprintf( - ctx.full, - ACCOUNT_ID_SIZE, - "%llu.%llu.%llu", - ctx.transaction.data.tokenAssociate.tokens[0].shardNum, - ctx.transaction.data.tokenAssociate.tokens[0].realmNum, - ctx.transaction.data.tokenAssociate.tokens[0].tokenNum - ); + ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.tokenAssociate.tokens[ 0 ].shardNum, + ctx.transaction.data.tokenAssociate.tokens[ 0 ].realmNum, + ctx.transaction.data.tokenAssociate.tokens[ 0 ].tokenNum); break; - case TokenMint: + case Dissociate: hedera_snprintf( - ctx.full, - ACCOUNT_ID_SIZE, - "%llu.%llu.%llu", - ctx.transaction.data.tokenMint.token.shardNum, - ctx.transaction.data.tokenMint.token.realmNum, - ctx.transaction.data.tokenMint.token.tokenNum - ); + ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.tokenDissociate.tokens[ 0 ].shardNum, + ctx.transaction.data.tokenDissociate.tokens[ 0 ].realmNum, + ctx.transaction.data.tokenDissociate.tokens[ 0 ].tokenNum); + + break; + + case TokenMint: + hedera_snprintf(ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.tokenMint.token.shardNum, + ctx.transaction.data.tokenMint.token.realmNum, + ctx.transaction.data.tokenMint.token.tokenNum); break; case TokenBurn: - hedera_snprintf( - ctx.full, - ACCOUNT_ID_SIZE, - "%llu.%llu.%llu", - ctx.transaction.data.tokenBurn.token.shardNum, - ctx.transaction.data.tokenBurn.token.realmNum, - ctx.transaction.data.tokenBurn.token.tokenNum - ); + hedera_snprintf(ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.tokenBurn.token.shardNum, + ctx.transaction.data.tokenBurn.token.realmNum, + ctx.transaction.data.tokenBurn.token.tokenNum); break; default: - return; + break; } - count_screens(); + count_screens_of_step(); - hedera_snprintf( - ctx.title, - DISPLAY_SIZE, - "Token (%u/%u)", - ctx.display_index, - ctx.display_count - ); + hedera_snprintf(ctx.title, DISPLAY_SIZE, "Token (%u/%u)", ctx.current_page, + ctx.page_count); } -void reformat_tokens_accounts(char *title_part, uint8_t transfer_index) { - hedera_snprintf( - ctx.full, - ACCOUNT_ID_SIZE, - "%llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[transfer_index].accountID.shardNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[transfer_index].accountID.realmNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[transfer_index].accountID.accountNum - ); - - count_screens(); - - hedera_snprintf( - ctx.title, - DISPLAY_SIZE, - "%s (%u/%u)", - title_part, - ctx.display_index, - ctx.display_count - ); +void reformat_tokens_accounts(char* title_part, uint8_t transfer_index) { + hedera_snprintf(ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ transfer_index ] + .accountID.shardNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ transfer_index ] + .accountID.realmNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ transfer_index ] + .accountID.account.accountNum); + + count_screens_of_step(); + + hedera_snprintf(ctx.title, DISPLAY_SIZE, "%s (%u/%u)", title_part, + ctx.current_page, ctx.page_count); } void reformat_senders() { @@ -569,7 +583,13 @@ void reformat_senders() { reformat_accounts("Account", 0); break; + case Create: + case Update: + reformat_stake_target(); + break; + case Associate: + case Dissociate: case TokenMint: case TokenBurn: reformat_token(); @@ -587,11 +607,91 @@ void reformat_senders() { return; } - shift_display(); + repaint(); +} + +void reformat_collect_rewards() { + switch (ctx.type) { + case Create: { + bool declineRewards = + ctx.transaction.data.cryptoCreateAccount.decline_reward; + hedera_snprintf(ctx.full, MAX_MEMO_SIZE, "%s", + !declineRewards ? "Yes" : "No"); + } break; + + case Update: { + if (ctx.transaction.data.cryptoUpdateAccount.has_decline_reward) { + bool declineRewards = ctx.transaction.data.cryptoUpdateAccount + .decline_reward.value; + hedera_snprintf(ctx.full, MAX_MEMO_SIZE, "%s", + declineRewards ? "No" : "Yes"); + } else { + hedera_snprintf(ctx.full, MAX_MEMO_SIZE, "%s", "-"); + } + } break; + + default: + break; + } + + count_screens_of_step(); // 1 + + hedera_snprintf(ctx.title, DISPLAY_SIZE, "Collect Rewards?", + ctx.current_page, ctx.page_count); + + repaint(); +} + +void reformat_target_account() { + switch (ctx.type) { + case Associate: { + bool hasAccount = ctx.transaction.data.tokenAssociate.has_account; + if (hasAccount) { + hedera_snprintf( + ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.tokenAssociate.account.shardNum, + ctx.transaction.data.tokenAssociate.account.realmNum, + ctx.transaction.data.tokenAssociate.account.account + .accountNum); + } else { + hedera_snprintf( + ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.transactionID.accountID.shardNum, + ctx.transaction.transactionID.accountID.realmNum, + ctx.transaction.transactionID.accountID.account.accountNum); + } + } break; + + case Dissociate: { + bool hasAccount = ctx.transaction.data.tokenDissociate.has_account; + if (hasAccount) { + hedera_snprintf( + ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.tokenDissociate.account.shardNum, + ctx.transaction.data.tokenDissociate.account.realmNum, + ctx.transaction.data.tokenDissociate.account.account + .accountNum); + } else { + hedera_snprintf( + ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.transactionID.accountID.shardNum, + ctx.transaction.transactionID.accountID.realmNum, + ctx.transaction.transactionID.accountID.account.accountNum); + } + } break; + + default: + break; + } } void reformat_recipients() { switch (ctx.type) { + case Create: + case Update: + reformat_collect_rewards(); + break; + case TokenTransfer: reformat_tokens_accounts("Recipient", ctx.transfer_to_index); break; @@ -604,252 +704,245 @@ void reformat_recipients() { return; } - shift_display(); + repaint(); } void reformat_amount() { switch (ctx.type) { case Create: hedera_snprintf( - ctx.full, - DISPLAY_SIZE * 3, - "%s hbar", - hedera_format_tinybar(ctx.transaction.data.cryptoCreateAccount.initialBalance) - ); + ctx.full, DISPLAY_SIZE * 3, "%s hbar", + hedera_format_tinybar( + ctx.transaction.data.cryptoCreateAccount.initialBalance)); break; + case Update: { + if (ctx.transaction.data.cryptoUpdateAccount + .has_accountIDToUpdate) { + hedera_snprintf(ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.cryptoUpdateAccount + .accountIDToUpdate.shardNum, + ctx.transaction.data.cryptoUpdateAccount + .accountIDToUpdate.realmNum, + ctx.transaction.data.cryptoUpdateAccount + .accountIDToUpdate.account.accountNum); + } else { + hedera_snprintf( + ctx.full, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.transactionID.accountID.shardNum, + ctx.transaction.transactionID.accountID.realmNum, + ctx.transaction.transactionID.accountID.account.accountNum); + } + } break; + case Transfer: - hedera_snprintf( - ctx.full, - DISPLAY_SIZE * 3, - "%s hbar", - hedera_format_tinybar(ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[ctx.transfer_to_index].amount) - ); + hedera_snprintf(ctx.full, DISPLAY_SIZE * 3, "%s hbar", + hedera_format_tinybar( + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ ctx.transfer_to_index ] + .amount)); break; case TokenMint: - validate_decimals(ctx.transaction.data.tokenMint.expected_decimals.value); hedera_snprintf( - ctx.full, - DISPLAY_SIZE * 3, - "%s", + ctx.full, DISPLAY_SIZE * 3, "%s", hedera_format_amount( ctx.transaction.data.tokenMint.amount, - ctx.transaction.data.tokenMint.expected_decimals.value - ) - ); + 0)); // Must be lowest denomination without decimals break; case TokenBurn: - validate_decimals(ctx.transaction.data.tokenBurn.expected_decimals.value); hedera_snprintf( - ctx.full, - DISPLAY_SIZE * 3, - "%s", + ctx.full, DISPLAY_SIZE * 3, "%s", hedera_format_amount( ctx.transaction.data.tokenBurn.amount, - ctx.transaction.data.tokenBurn.expected_decimals.value - ) - ); + 0)); // Must be lowest denomination without decimals break; + case Associate: + case Dissociate: + reformat_target_account(); + break; + case TokenTransfer: - validate_decimals(ctx.transaction.data.cryptoTransfer.tokenTransfers[0].expected_decimals.value); hedera_snprintf( - ctx.full, - DISPLAY_SIZE * 3, - "%s", + ctx.full, DISPLAY_SIZE * 3, "%s", hedera_format_amount( - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[ctx.transfer_to_index].amount, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].expected_decimals.value - ) - ); + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ ctx.transfer_to_index ] + .amount, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .expected_decimals.value)); break; default: - return; + break; } - count_screens(); + count_screens_of_step(); - hedera_snprintf( - ctx.title, - DISPLAY_SIZE, - "%s (%u/%u)", - ctx.type == Create ? "Balance" : "Amount", - ctx.display_index, - ctx.display_count - ); - - shift_display(); + switch (ctx.type) { + case Update: + case Associate: + case Dissociate: + hedera_snprintf(ctx.title, DISPLAY_SIZE, "%s (%u/%u)", "Updating", + ctx.current_page, ctx.page_count); + break; + case Create: + hedera_snprintf(ctx.title, DISPLAY_SIZE, "%s (%u/%u)", "Balance", + ctx.current_page, ctx.page_count); + break; + default: + hedera_snprintf(ctx.title, DISPLAY_SIZE, "%s (%u/%u)", "Amount", + ctx.current_page, ctx.page_count); + break; + } + + repaint(); } void reformat_fee() { - hedera_snprintf( - ctx.full, - DISPLAY_SIZE * 3, - "%s hbar", - hedera_format_tinybar(ctx.transaction.transactionFee) - ); + hedera_snprintf(ctx.full, DISPLAY_SIZE * 3, "%s hbar", + hedera_format_tinybar(ctx.transaction.transactionFee)); - count_screens(); + count_screens_of_step(); - hedera_snprintf( - ctx.title, - DISPLAY_SIZE, - "Max Fee (%u/%u)", - ctx.display_index, - ctx.display_count - ); - - shift_display(); + hedera_snprintf(ctx.title, DISPLAY_SIZE, "Max Fee (%u/%u)", + ctx.current_page, ctx.page_count); + + repaint(); } void reformat_memo() { hedera_snprintf( - ctx.full, - MAX_MEMO_SIZE, - "%s", - strlen(ctx.transaction.memo) > 0 ? ctx.transaction.memo : "" - ); - - if (strlen(ctx.full) > MAX_MEMO_SIZE) { - // :grimacing: - THROW(EXCEPTION_MALFORMED_APDU); - } + ctx.full, MAX_MEMO_SIZE, "%s", + strlen(ctx.transaction.memo) > 0 ? ctx.transaction.memo : ""); - count_screens(); + count_screens_of_step(); - hedera_snprintf( - ctx.title, - DISPLAY_SIZE, - "Memo (%u/%u)", - ctx.display_index, - ctx.display_count - ); - - shift_display(); + hedera_snprintf(ctx.title, DISPLAY_SIZE, "Memo (%u/%u)", ctx.current_page, + ctx.page_count); + + repaint(); } void handle_transaction_body() { explicit_bzero(ctx.summary_line_1, DISPLAY_SIZE + 1); explicit_bzero(ctx.summary_line_2, DISPLAY_SIZE + 1); - explicit_bzero(ctx.full, ACCOUNT_ID_SIZE + 1); + explicit_bzero(ctx.full, DISPLAY_SIZE * 3 + 1); explicit_bzero(ctx.partial, DISPLAY_SIZE + 1); // Step 1, Unknown Type, Screen 1 of 1 ctx.step = Summary; ctx.type = Unknown; - ctx.display_index = 1; - ctx.display_count = 1; + ctx.current_page = 1; + ctx.page_count = 1; - // + // // with Key #X? - hedera_snprintf( - ctx.summary_line_2, - DISPLAY_SIZE, - "with Key #%u?", - ctx.key_index - ); + hedera_snprintf(ctx.summary_line_2, DISPLAY_SIZE, "with Key #%u?", + ctx.key_index); // Handle parsed protobuf message of transaction body switch (ctx.transaction.which_data) { - case HederaTransactionBody_cryptoCreateAccount_tag: - // Create Account Transaction + case Hedera_TransactionBody_cryptoCreateAccount_tag: ctx.type = Create; - hedera_snprintf( - ctx.summary_line_1, - DISPLAY_SIZE, - "Create Account" - ); + hedera_snprintf(ctx.summary_line_1, DISPLAY_SIZE, "Create Account"); + break; + case Hedera_TransactionBody_cryptoUpdateAccount_tag: + ctx.type = Update; + hedera_snprintf(ctx.summary_line_1, DISPLAY_SIZE, "Update Account"); break; - case HederaTransactionBody_tokenAssociate_tag: + case Hedera_TransactionBody_tokenAssociate_tag: ctx.type = Associate; - hedera_snprintf( - ctx.summary_line_1, - DISPLAY_SIZE, - "Associate Token" - ); + hedera_snprintf(ctx.summary_line_1, DISPLAY_SIZE, + "Associate Token"); + break; + case Hedera_TransactionBody_tokenDissociate_tag: + ctx.type = Dissociate; + hedera_snprintf(ctx.summary_line_1, DISPLAY_SIZE, + "Dissociate Token"); break; - case HederaTransactionBody_tokenMint_tag: + case Hedera_TransactionBody_tokenMint_tag: ctx.type = TokenMint; - hedera_snprintf( - ctx.summary_line_1, - DISPLAY_SIZE, - "Mint Token" - ); - + hedera_snprintf(ctx.summary_line_1, DISPLAY_SIZE, "Mint Token"); break; - case HederaTransactionBody_tokenBurn_tag: + case Hedera_TransactionBody_tokenBurn_tag: ctx.type = TokenBurn; - hedera_snprintf( - ctx.summary_line_1, - DISPLAY_SIZE, - "Burn Token" - ); - + hedera_snprintf(ctx.summary_line_1, DISPLAY_SIZE, "Burn Token"); break; - case HederaTransactionBody_cryptoTransfer_tag: { - validate_transfer(); - - if ( // Only 1 Account (Sender), Fee 1 Tinybar, and Value 0 Tinybar - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[0].amount == 0 && - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts_count == 1 && - ctx.transaction.transactionFee == 1 - ) { - // Verify Account Transaction + case Hedera_TransactionBody_cryptoTransfer_tag: { + if ( + /* + * "Verify Account Transaction" + * + * This is an arbitary transfer transaction that is designed + * to fail always. Transfer 0 hbar to no-one with a max fee + * of 1 tinybar. If this transaction fails with + * "insufficient fee" rather than "invalid signature", then + * we know that the signature provided by this key is indeed + * associated with the operator account for the transaction. + */ + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ 0 ] + .amount == 0 && + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts_count == 1 && + ctx.transaction.transactionFee == 1) { ctx.type = Verify; - hedera_snprintf( - ctx.summary_line_1, - DISPLAY_SIZE, - "Verify Account" - ); - } else if (ctx.transaction.data.cryptoTransfer.transfers.accountAmounts_count == 2) { - // Number of Accounts == 2 - // Some other Transfer Transaction + hedera_snprintf(ctx.summary_line_1, DISPLAY_SIZE, + "Verify Account"); + } else if (ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts_count == 2) { + // Hbar Transfer between two accounts ctx.type = Transfer; - hedera_snprintf( - ctx.summary_line_1, - DISPLAY_SIZE, - "Send Hbar" - ); + hedera_snprintf(ctx.summary_line_1, DISPLAY_SIZE, "Send Hbar"); - // Determine Sender based on amount + // Determine Sender based on transfers.accountAmounts ctx.transfer_to_index = 1; ctx.transfer_from_index = 0; - if (ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[0].amount > 0) { + if (ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ 0 ] + .amount > 0) { ctx.transfer_to_index = 0; ctx.transfer_from_index = 1; } - } else if (ctx.transaction.data.cryptoTransfer.tokenTransfers_count == 1) { + } else if (ctx.transaction.data.cryptoTransfer + .tokenTransfers_count == 1) { + // Fungible Token Transfer (two token transfers with one + // transfer each) ctx.type = TokenTransfer; + validate_token_transfer(); + hedera_snprintf( - ctx.summary_line_1, - DISPLAY_SIZE, - "Send %llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].token.shardNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].token.realmNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].token.tokenNum - ); + ctx.summary_line_1, DISPLAY_SIZE, "Send %llu.%llu.%llu", + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .token.shardNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .token.realmNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .token.tokenNum); // Determine Sender based on amount ctx.transfer_from_index = 0; ctx.transfer_to_index = 1; - if (ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[0].amount > 0) { + if (ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ 0 ] + .amount > 0) { ctx.transfer_from_index = 1; ctx.transfer_to_index = 0; } @@ -862,52 +955,13 @@ void handle_transaction_body() { default: // Unsupported THROW(EXCEPTION_MALFORMED_APDU); + break; } UX_DISPLAY(ui_tx_summary_step, NULL); } #elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) - -static struct sign_tx_context_t { - // ui common - uint32_t key_index; - uint8_t transfer_from_index; - uint8_t transfer_to_index; - - // Transaction Summary - char summary_line_1[DISPLAY_SIZE + 1]; - char summary_line_2[DISPLAY_SIZE + 1]; - char senders_title[DISPLAY_SIZE + 1]; - char amount_title[DISPLAY_SIZE + 1]; - char partial[DISPLAY_SIZE + 1]; - - enum TransactionType type; - - // Transaction Operator - char operator[DISPLAY_SIZE * 2 + 1]; - - // Transaction Senders - char senders[DISPLAY_SIZE * 2 + 1]; - - // Transaction Recipients - char recipients[DISPLAY_SIZE * 2 + 1]; - - // Transaction Amount - char amount[DISPLAY_SIZE * 2 + 1]; - - // Transaction Fee - char fee[DISPLAY_SIZE * 2 + 1]; - - // Transaction Memo - char memo[MAX_MEMO_SIZE + 1]; - - // Parsed transaction - HederaTransactionBody transaction; -} ctx; - -// UI Definition for Nano X - // Confirm Callback unsigned int io_seproxyhal_tx_approve(const bagl_element_t* e) { io_exchange_with_code(EXCEPTION_OK, 64); @@ -922,15 +976,8 @@ unsigned int io_seproxyhal_tx_reject(const bagl_element_t* e) { return 0; } -UX_STEP_NOCB( - ux_tx_flow_1_step, - bnn, - { - "Transaction Summary", - ctx.summary_line_1, - ctx.summary_line_2 - } -); +UX_STEP_NOCB(ux_tx_flow_1_step, bnn, + {"Transaction Summary", ctx.summary_line_1, ctx.summary_line_2}); UX_STEP_NOCB( ux_tx_flow_2_step, @@ -941,111 +988,75 @@ UX_STEP_NOCB( } ); -UX_STEP_NOCB( - ux_tx_flow_3_step, - bnnn_paging, - { - .title = (char*) ctx.senders_title, - .text = (char*) ctx.senders - } -); +UX_STEP_NOCB(ux_tx_flow_3_step, bnnn_paging, + {.title = (char*)ctx.senders_title, .text = (char*)ctx.senders}); -UX_STEP_NOCB( - ux_tx_flow_4_step, - bnnn_paging, - { - .title = "Recipient", - .text = (char*) ctx.recipients - } -); +UX_STEP_NOCB(ux_tx_flow_4_step, bnnn_paging, + {.title = (char*)ctx.recipients_title, + .text = (char*)ctx.recipients}); -UX_STEP_NOCB( - ux_tx_flow_5_step, - bnnn_paging, - { - .title = (char*) ctx.amount_title, - .text = (char*) ctx.amount - } -); +UX_STEP_NOCB(ux_tx_flow_5_step, bnnn_paging, + {.title = (char*)ctx.amount_title, .text = (char*)ctx.amount}); -UX_STEP_NOCB( - ux_tx_flow_6_step, - bnnn_paging, - { - .title = "Max Fee", - .text = (char*) ctx.fee - } -); +UX_STEP_NOCB(ux_tx_flow_6_step, bnnn_paging, + {.title = "Max Fee", .text = (char*)ctx.fee}); -UX_STEP_NOCB( - ux_tx_flow_7_step, - bnnn_paging, - { - .title = "Memo", - .text = (char*) ctx.memo - } -); +UX_STEP_NOCB(ux_tx_flow_7_step, bnnn_paging, + {.title = "Memo", .text = (char*)ctx.memo}); -UX_STEP_VALID( - ux_tx_flow_8_step, - pb, - io_seproxyhal_tx_approve(NULL), - { - &C_icon_validate_14, - "Confirm" - } -); +UX_STEP_VALID(ux_tx_flow_8_step, pb, io_seproxyhal_tx_approve(NULL), + {&C_icon_validate_14, "Confirm"}); -UX_STEP_VALID( - ux_tx_flow_9_step, - pb, - io_seproxyhal_tx_reject(NULL), - { - &C_icon_crossmark, - "Reject" - } -); +UX_STEP_VALID(ux_tx_flow_9_step, pb, io_seproxyhal_tx_reject(NULL), + {&C_icon_crossmark, "Reject"}); // Transfer UX Flow -UX_DEF( - ux_transfer_flow, - &ux_tx_flow_1_step, - &ux_tx_flow_2_step, - &ux_tx_flow_3_step, - &ux_tx_flow_4_step, - &ux_tx_flow_5_step, - &ux_tx_flow_6_step, - &ux_tx_flow_7_step, - &ux_tx_flow_8_step, - &ux_tx_flow_9_step -); +// Summary, Operator, Senders, Recipients, Amount, Fee, Memo, Confirm, Deny +UX_DEF(ux_transfer_flow, &ux_tx_flow_1_step, &ux_tx_flow_2_step, + &ux_tx_flow_3_step, &ux_tx_flow_4_step, &ux_tx_flow_5_step, + &ux_tx_flow_6_step, &ux_tx_flow_7_step, &ux_tx_flow_8_step, + &ux_tx_flow_9_step); // Create UX Flow -UX_DEF( - ux_create_flow, - &ux_tx_flow_1_step, - &ux_tx_flow_2_step, - &ux_tx_flow_5_step, - &ux_tx_flow_6_step, - &ux_tx_flow_7_step, - &ux_tx_flow_8_step, - &ux_tx_flow_9_step -); +// Summary, Operator, Senders (Stake To), Recipients (Collect Rewards), Amount +// (Initial Balance), Fee, Memo, Confirm, Deny +UX_DEF(ux_create_flow, &ux_tx_flow_1_step, &ux_tx_flow_2_step, + &ux_tx_flow_3_step, &ux_tx_flow_4_step, &ux_tx_flow_5_step, + &ux_tx_flow_6_step, &ux_tx_flow_7_step, &ux_tx_flow_8_step, + &ux_tx_flow_9_step); + +// Update UX Flow +// Summary, Operator, Senders (Stake To), Recipients (Collect Rewards), Amount +// (Updated Account), Fee, Memo, Confirm, Deny +UX_DEF(ux_update_flow, &ux_tx_flow_1_step, &ux_tx_flow_2_step, + &ux_tx_flow_3_step, &ux_tx_flow_4_step, &ux_tx_flow_5_step, + &ux_tx_flow_6_step, &ux_tx_flow_7_step, &ux_tx_flow_8_step, + &ux_tx_flow_9_step); // Verify UX Flow -UX_DEF( - ux_verify_flow, - &ux_tx_flow_1_step, - &ux_tx_flow_3_step, - &ux_tx_flow_8_step, - &ux_tx_flow_9_step -); +// Summary, Senders (Account), Confirm, Deny +UX_DEF(ux_verify_flow, &ux_tx_flow_1_step, &ux_tx_flow_3_step, + &ux_tx_flow_8_step, &ux_tx_flow_9_step); + +// TokenMint, TokenBurn +// Summary, Operator, Senders (Token), Amount, Fee, Memo, Confirm, Deny +UX_DEF(ux_mint_flow, &ux_tx_flow_1_step, &ux_tx_flow_2_step, &ux_tx_flow_4_step, + &ux_tx_flow_5_step, &ux_tx_flow_6_step, &ux_tx_flow_7_step, + &ux_tx_flow_8_step, &ux_tx_flow_9_step); + +// Associate, Dissociate +// Summary, Operator, Senders (Token), Recipients (Account), Fee, Memo, Confirm, +// Deny +UX_DEF(ux_associate_flow, &ux_tx_flow_1_step, &ux_tx_flow_2_step, + &ux_tx_flow_3_step, &ux_tx_flow_4_step, &ux_tx_flow_6_step, + &ux_tx_flow_7_step, &ux_tx_flow_8_step, &ux_tx_flow_9_step); void handle_transaction_body() { explicit_bzero(ctx.summary_line_1, DISPLAY_SIZE + 1); explicit_bzero(ctx.summary_line_2, DISPLAY_SIZE + 1); explicit_bzero(ctx.amount_title, DISPLAY_SIZE + 1); explicit_bzero(ctx.senders_title, DISPLAY_SIZE + 1); + explicit_bzero(ctx.recipients_title, DISPLAY_SIZE + 1); explicit_bzero(ctx.operator, DISPLAY_SIZE * 2 + 1); explicit_bzero(ctx.senders, DISPLAY_SIZE * 2 + 1); explicit_bzero(ctx.recipients, DISPLAY_SIZE * 2 + 1); @@ -1055,219 +1066,354 @@ void handle_transaction_body() { ctx.type = Unknown; - // + // // with Key #X? - hedera_snprintf( - ctx.summary_line_2, - DISPLAY_SIZE, - "with Key #%u?", - ctx.key_index - ); + hedera_snprintf(ctx.summary_line_2, DISPLAY_SIZE, "with Key #%u?", + ctx.key_index); - hedera_snprintf( - ctx.operator, - DISPLAY_SIZE * 2, - "%llu.%llu.%llu", - ctx.transaction.transactionID.accountID.shardNum, - ctx.transaction.transactionID.accountID.realmNum, - ctx.transaction.transactionID.accountID.accountNum - ); + hedera_snprintf(ctx.operator, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.transactionID.accountID.shardNum, + ctx.transaction.transactionID.accountID.realmNum, + ctx.transaction.transactionID.accountID.account); - hedera_snprintf( - ctx.fee, - DISPLAY_SIZE * 2, - "%s hbar", - hedera_format_tinybar(ctx.transaction.transactionFee) - ); + hedera_snprintf(ctx.fee, DISPLAY_SIZE * 2, "%s hbar", + hedera_format_tinybar(ctx.transaction.transactionFee)); - hedera_snprintf( - ctx.memo, - MAX_MEMO_SIZE, - "%s", - ctx.transaction.memo - ); - - hedera_sprintf( - ctx.amount_title, - "Amount" - ); - - hedera_sprintf( - ctx.senders_title, - "Sender" - ); + hedera_snprintf(ctx.memo, MAX_MEMO_SIZE, "%s", ctx.transaction.memo); + + hedera_sprintf(ctx.amount_title, "Amount"); + hedera_sprintf(ctx.senders_title, "Sender"); + hedera_sprintf(ctx.recipients_title, "Recipient"); // Handle parsed protobuf message of transaction body switch (ctx.transaction.which_data) { - case HederaTransactionBody_cryptoCreateAccount_tag: + case Hedera_TransactionBody_cryptoCreateAccount_tag: { ctx.type = Create; - // Create Account Transaction - hedera_sprintf( - ctx.summary_line_1, - "Create Account" - ); - hedera_sprintf( - ctx.amount_title, - "Balance" - ); + hedera_sprintf(ctx.summary_line_1, "Create Account"); + hedera_sprintf(ctx.senders_title, "Stake To"); + hedera_sprintf(ctx.recipients_title, "Collect Rewards?"); + hedera_sprintf(ctx.amount_title, "Balance"); + + char stake_target[ ACCOUNT_ID_SIZE ]; + + if (ctx.transaction.data.cryptoCreateAccount.which_staked_id == + Hedera_CryptoCreateTransactionBody_staked_account_id_tag) { + hedera_snprintf(stake_target, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.cryptoCreateAccount + .staked_id.staked_account_id.shardNum, + ctx.transaction.data.cryptoCreateAccount + .staked_id.staked_account_id.realmNum, + ctx.transaction.data.cryptoCreateAccount + .staked_id.staked_account_id.account); + } else if (ctx.transaction.data.cryptoCreateAccount + .which_staked_id == + Hedera_CryptoCreateTransactionBody_staked_node_id_tag) { + hedera_snprintf(stake_target, ACCOUNT_ID_SIZE, "Node %lld", + ctx.transaction.data.cryptoCreateAccount + .staked_id.staked_node_id); + } else { + hedera_snprintf(stake_target, DISPLAY_SIZE * 2, "None"); + } + + hedera_snprintf(ctx.senders, DISPLAY_SIZE * 2, "%s", stake_target); + hedera_snprintf( - ctx.amount, - DISPLAY_SIZE * 2, - "%s hbar", - hedera_format_tinybar(ctx.transaction.data.cryptoCreateAccount.initialBalance) - ); - break; + ctx.recipients, DISPLAY_SIZE * 2, "%s", + ctx.transaction.data.cryptoCreateAccount.decline_reward + ? "No" + : "Yes"); - case HederaTransactionBody_tokenAssociate_tag: + hedera_snprintf( + ctx.amount, DISPLAY_SIZE * 2, "%s hbar", + hedera_format_tinybar( + ctx.transaction.data.cryptoCreateAccount.initialBalance)); + } break; + + case Hedera_TransactionBody_cryptoUpdateAccount_tag: { + ctx.type = Update; + hedera_sprintf(ctx.summary_line_1, "Update Account"); + hedera_sprintf(ctx.senders_title, "Stake To"); + hedera_sprintf(ctx.recipients_title, "Collect Rewards"); + hedera_sprintf(ctx.amount_title, "Updating"); + + const char stake_target[ DISPLAY_SIZE * 2 ]; + + if (ctx.transaction.data.cryptoUpdateAccount.which_staked_id == + Hedera_CryptoUpdateTransactionBody_staked_account_id_tag) { + hedera_snprintf(stake_target, DISPLAY_SIZE * 2, + "%llu.%llu.%llu", + ctx.transaction.data.cryptoUpdateAccount + .staked_id.staked_account_id.shardNum, + ctx.transaction.data.cryptoUpdateAccount + .staked_id.staked_account_id.realmNum, + ctx.transaction.data.cryptoUpdateAccount + .staked_id.staked_account_id.account); + } else if (ctx.transaction.data.cryptoUpdateAccount + .which_staked_id == + Hedera_CryptoUpdateTransactionBody_staked_node_id_tag) { + hedera_snprintf(stake_target, ACCOUNT_ID_SIZE, "Node %lld", + ctx.transaction.data.cryptoUpdateAccount + .staked_id.staked_node_id); + } else { + hedera_snprintf(stake_target, DISPLAY_SIZE * 2, "None"); + } + + hedera_snprintf(ctx.senders, DISPLAY_SIZE * 2, "%s", stake_target); + + if (ctx.transaction.data.cryptoUpdateAccount.has_decline_reward) { + bool declineRewards = ctx.transaction.data.cryptoUpdateAccount + .decline_reward.value; + hedera_snprintf(ctx.recipients, DISPLAY_SIZE, "%s", + declineRewards ? "No" : "Yes"); + } else { + hedera_snprintf(ctx.recipients, DISPLAY_SIZE, "%s", "-"); + } + + if (ctx.transaction.data.cryptoUpdateAccount + .has_accountIDToUpdate) { + Hedera_AccountID updatedAccount = + ctx.transaction.data.cryptoUpdateAccount.accountIDToUpdate; + hedera_snprintf(ctx.amount, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + updatedAccount.shardNum, + updatedAccount.realmNum, + updatedAccount.account); + } else { + hedera_snprintf( + ctx.amount, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.transactionID.accountID.shardNum, + ctx.transaction.transactionID.accountID.realmNum, + ctx.transaction.transactionID.accountID.account); + } + } break; + + case Hedera_TransactionBody_tokenAssociate_tag: { ctx.type = Associate; - hedera_sprintf( - ctx.summary_line_1, - "Associate Token" - ); + hedera_sprintf(ctx.summary_line_1, "Associate Token"); + hedera_sprintf(ctx.senders_title, "Token"); + hedera_sprintf(ctx.recipients_title, "Account"); + + hedera_snprintf( + ctx.senders, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.data.tokenAssociate.tokens[ 0 ].shardNum, + ctx.transaction.data.tokenAssociate.tokens[ 0 ].realmNum, + ctx.transaction.data.tokenAssociate.tokens[ 0 ].tokenNum); + + bool hasAccount = ctx.transaction.data.tokenAssociate.has_account; + + if (hasAccount) { + hedera_snprintf( + ctx.recipients, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.tokenAssociate.account.shardNum, + ctx.transaction.data.tokenAssociate.account.realmNum, + ctx.transaction.data.tokenAssociate.account.account + .accountNum); + } else { + hedera_snprintf( + ctx.recipients, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.transactionID.accountID.shardNum, + ctx.transaction.transactionID.accountID.realmNum, + ctx.transaction.transactionID.accountID.account.accountNum); + } + } break; + + case Hedera_TransactionBody_tokenDissociate_tag: { + ctx.type = Dissociate; - hedera_sprintf( - ctx.senders_title, - "Token"); + hedera_sprintf(ctx.summary_line_1, "Dissociate Token"); + hedera_sprintf(ctx.senders_title, "Token"); + hedera_sprintf(ctx.recipients_title, "Account"); hedera_snprintf( - ctx.senders, - DISPLAY_SIZE * 2, - "%llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].token.shardNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].token.realmNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].token.tokenNum - ); + ctx.senders, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.data.tokenDissociate.tokens[ 0 ].shardNum, + ctx.transaction.data.tokenDissociate.tokens[ 0 ].realmNum, + ctx.transaction.data.tokenDissociate.tokens[ 0 ].tokenNum); - break; + bool hasAccount = ctx.transaction.data.tokenAssociate.has_account; + + if (hasAccount) { + hedera_snprintf( + ctx.recipients, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.data.tokenDissociate.account.shardNum, + ctx.transaction.data.tokenDissociate.account.realmNum, + ctx.transaction.data.tokenDissociate.account.account + .accountNum); + } else { + hedera_snprintf( + ctx.recipients, ACCOUNT_ID_SIZE, "%llu.%llu.%llu", + ctx.transaction.transactionID.accountID.shardNum, + ctx.transaction.transactionID.accountID.realmNum, + ctx.transaction.transactionID.accountID.account.accountNum); + } + } break; + + case Hedera_TransactionBody_tokenMint_tag: { + ctx.type = TokenMint; + + hedera_sprintf(ctx.summary_line_1, "Mint Token"); + + hedera_sprintf(ctx.senders_title, "Token"); + + hedera_snprintf(ctx.senders, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.data.tokenMint.token.shardNum, + ctx.transaction.data.tokenMint.token.realmNum, + ctx.transaction.data.tokenMint.token.tokenNum); + + hedera_snprintf( + ctx.amount, DISPLAY_SIZE * 2, "%s", + hedera_format_amount(ctx.transaction.data.tokenMint.amount, + 0)); // always lowest denomination of token + } break; + + case Hedera_TransactionBody_tokenBurn_tag: { + ctx.type = TokenBurn; - case HederaTransactionBody_cryptoTransfer_tag: { - validate_transfer(); + hedera_sprintf(ctx.summary_line_1, "Burn Token"); + hedera_sprintf(ctx.senders_title, "Token"); + + hedera_snprintf(ctx.senders, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.data.tokenBurn.token.shardNum, + ctx.transaction.data.tokenBurn.token.realmNum, + ctx.transaction.data.tokenBurn.token.tokenNum); + + hedera_snprintf( + ctx.amount, DISPLAY_SIZE * 2, "%s", + hedera_format_amount(ctx.transaction.data.tokenBurn.amount, + 0)); // always lowest denomination of token + } break; + + case Hedera_TransactionBody_cryptoTransfer_tag: { if ( // Only 1 Account (Sender), Fee 1 Tinybar, and Value 0 Tinybar - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[0].amount == 0 && - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts_count == 1 && - ctx.transaction.transactionFee == 1 - ) { + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ 0 ] + .amount == 0 && + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts_count == 1 && + ctx.transaction.transactionFee == 1) { // Verify Account Transaction ctx.type = Verify; - - hedera_sprintf( - ctx.summary_line_1, - "Verify Account" - ); - - hedera_sprintf( - ctx.senders_title, - "Account" - ); - - hedera_snprintf( - ctx.senders, - DISPLAY_SIZE * 2, - "%llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[0].accountID.shardNum, - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[0].accountID.realmNum, - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[0].accountID.accountNum - ); - hedera_snprintf( - ctx.amount, - DISPLAY_SIZE * 2, - "%s hbar", - hedera_format_tinybar(ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[0].amount) - ); - } else if (ctx.transaction.data.cryptoTransfer.transfers.accountAmounts_count == 2) { + hedera_sprintf(ctx.summary_line_1, "Verify Account"); + + hedera_sprintf(ctx.senders_title, "Account"); + + hedera_snprintf(ctx.senders, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ 0 ] + .accountID.shardNum, + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ 0 ] + .accountID.realmNum, + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ 0 ] + .accountID.account); + } else if (ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts_count == 2) { // Number of Accounts == 2 - // Some other Transfer Transaction + // Hbar transfer between two accounts ctx.type = Transfer; - hedera_sprintf( - ctx.summary_line_1, - "Send Hbar" - ); + hedera_sprintf(ctx.summary_line_1, "Send Hbar"); - // Determine Sender based on amount + // Determine Sender based on transfers.accountAmounts ctx.transfer_from_index = 0; ctx.transfer_to_index = 1; - if (ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[0].amount > 0) { + if (ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ 0 ] + .amount > 0) { ctx.transfer_from_index = 1; ctx.transfer_to_index = 0; } - hedera_snprintf( - ctx.senders, - DISPLAY_SIZE * 2, - "%llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[ctx.transfer_from_index].accountID.shardNum, - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[ctx.transfer_from_index].accountID.realmNum, - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[ctx.transfer_from_index].accountID.accountNum - ); + hedera_snprintf(ctx.senders, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ ctx.transfer_from_index ] + .accountID.shardNum, + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ ctx.transfer_from_index ] + .accountID.realmNum, + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ ctx.transfer_from_index ] + .accountID.account); + + hedera_snprintf(ctx.recipients, DISPLAY_SIZE * 2, + "%llu.%llu.%llu", + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ ctx.transfer_to_index ] + .accountID.shardNum, + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ ctx.transfer_to_index ] + .accountID.realmNum, + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ ctx.transfer_to_index ] + .accountID.account); hedera_snprintf( - ctx.recipients, - DISPLAY_SIZE * 2, - "%llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[ctx.transfer_to_index].accountID.shardNum, - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[ctx.transfer_to_index].accountID.realmNum, - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[ctx.transfer_to_index].accountID.accountNum - ); - - hedera_snprintf( - ctx.amount, - DISPLAY_SIZE * 2, - "%s hbar", - hedera_format_tinybar(ctx.transaction.data.cryptoTransfer.transfers.accountAmounts[ctx.transfer_to_index].amount) - ); - } else if ( ctx.transaction.data.cryptoTransfer.tokenTransfers_count == 1) { + ctx.amount, DISPLAY_SIZE * 2, "%s hbar", + hedera_format_tinybar( + ctx.transaction.data.cryptoTransfer.transfers + .accountAmounts[ ctx.transfer_to_index ] + .amount)); + } else if (ctx.transaction.data.cryptoTransfer + .tokenTransfers_count == 1) { + // Fungible Token Transfer ctx.type = TokenTransfer; + validate_token_transfer(); + hedera_snprintf( - ctx.summary_line_1, - DISPLAY_SIZE * 2, - "Send %llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].token.shardNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].token.realmNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].token.tokenNum - ); + ctx.summary_line_1, DISPLAY_SIZE * 2, "Send %llu.%llu.%llu", + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .token.shardNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .token.realmNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .token.tokenNum); // Determine Sender based on amount ctx.transfer_from_index = 0; ctx.transfer_to_index = 1; - if (ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[0].amount > 0) - { + if (ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ 0 ] + .amount > 0) { ctx.transfer_from_index = 1; ctx.transfer_to_index = 0; } hedera_snprintf( - ctx.senders, - DISPLAY_SIZE * 2, - "%llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[ctx.transfer_from_index].accountID.shardNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[ctx.transfer_from_index].accountID.realmNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[ctx.transfer_from_index].accountID.accountNum - ); + ctx.senders, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ ctx.transfer_from_index ] + .accountID.shardNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ ctx.transfer_from_index ] + .accountID.realmNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ ctx.transfer_from_index ] + .accountID.account); hedera_snprintf( - ctx.recipients, - DISPLAY_SIZE * 2, - "%llu.%llu.%llu", - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[ctx.transfer_to_index].accountID.shardNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[ctx.transfer_to_index].accountID.realmNum, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[ctx.transfer_to_index].accountID.accountNum - ); - - validate_decimals(ctx.transaction.data.cryptoTransfer.tokenTransfers[0].expected_decimals.value); + ctx.recipients, DISPLAY_SIZE * 2, "%llu.%llu.%llu", + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ ctx.transfer_to_index ] + .accountID.shardNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ ctx.transfer_to_index ] + .accountID.realmNum, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ ctx.transfer_to_index ] + .accountID.account); + hedera_snprintf( - ctx.amount, - DISPLAY_SIZE * 2, - "%s", + ctx.amount, DISPLAY_SIZE * 2, "%s", hedera_format_amount( - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers[ctx.transfer_to_index].amount, - ctx.transaction.data.cryptoTransfer.tokenTransfers[0].expected_decimals.value - ) - ); + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers[ ctx.transfer_to_index ] + .amount, + ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .expected_decimals.value)); } else { // Unsupported THROW(EXCEPTION_MALFORMED_APDU); @@ -1281,13 +1427,23 @@ void handle_transaction_body() { } switch (ctx.type) { - case Associate: case Verify: ux_flow_init(0, ux_verify_flow, NULL); break; + case TokenMint: + case TokenBurn: + ux_flow_init(0, ux_mint_flow, NULL); + break; case Create: ux_flow_init(0, ux_create_flow, NULL); break; + case Update: + ux_flow_init(0, ux_update_flow, NULL); + break; + case Associate: + case Dissociate: + ux_flow_init(0, ux_associate_flow, NULL); + break; case TokenTransfer: case Transfer: ux_flow_init(0, ux_transfer_flow, NULL); @@ -1301,14 +1457,10 @@ void handle_transaction_body() { // Sign Handler // Decodes and handles transaction message -void handle_sign_transaction( - uint8_t p1, - uint8_t p2, - uint8_t* buffer, - uint16_t len, - /* out */ volatile unsigned int* flags, - /* out */ volatile unsigned int* tx -) { +void handle_sign_transaction(uint8_t p1, uint8_t p2, uint8_t* buffer, + uint16_t len, + /* out */ volatile unsigned int* flags, + /* out */ volatile unsigned int* tx) { UNUSED(p1); UNUSED(p2); UNUSED(tx); @@ -1317,7 +1469,7 @@ void handle_sign_transaction( ctx.key_index = U4LE(buffer, 0); // Raw Tx - uint8_t raw_transaction[MAX_TX_SIZE]; + uint8_t raw_transaction[ MAX_TX_SIZE ]; int raw_transaction_length = len - 4; // Oops Oof Owie @@ -1330,27 +1482,17 @@ void handle_sign_transaction( // Sign Transaction // TODO: handle error return here (internal error?!) - if (!hedera_sign( - ctx.key_index, - raw_transaction, - raw_transaction_length, - G_io_apdu_buffer - )) { + if (!hedera_sign(ctx.key_index, raw_transaction, raw_transaction_length, + G_io_apdu_buffer)) { THROW(EXCEPTION_INTERNAL); } // Make in memory buffer into stream - pb_istream_t stream = pb_istream_from_buffer( - raw_transaction, - raw_transaction_length - ); + pb_istream_t stream = + pb_istream_from_buffer(raw_transaction, raw_transaction_length); // Decode the Transaction - if (!pb_decode( - &stream, - HederaTransactionBody_fields, - &ctx.transaction - )) { + if (!pb_decode(&stream, Hedera_TransactionBody_fields, &ctx.transaction)) { // Oh no couldn't ... THROW(EXCEPTION_MALFORMED_APDU); } @@ -1360,44 +1502,21 @@ void handle_sign_transaction( *flags |= IO_ASYNCH_REPLY; } -// Validates whether or not a transfer is legal: -// Either a transfer between two accounts -// Or a token transfer between two accounts -void validate_transfer() { - if (ctx.transaction.data.cryptoTransfer.transfers.accountAmounts_count > 2) { - // More than two accounts in a transfer - THROW(EXCEPTION_MALFORMED_APDU); - } - - if ( - ctx.transaction.data.cryptoTransfer.transfers.accountAmounts_count == 2 && - ctx.transaction.data.cryptoTransfer.tokenTransfers_count != 0 - ) { - // Can't also transfer tokens while sending hbar - THROW(EXCEPTION_MALFORMED_APDU); - } - - if (ctx.transaction.data.cryptoTransfer.tokenTransfers_count > 1) { - // More than one token transferred +void validate_decimals(uint32_t decimals) { + if (decimals >= 20) { + // We only support decimal values less than 20 THROW(EXCEPTION_MALFORMED_APDU); } - - if (ctx.transaction.data.cryptoTransfer.tokenTransfers_count == 1) { - if (ctx.transaction.data.cryptoTransfer.tokenTransfers[0].transfers_count != 2) { - // More than two accounts in a token transfer - THROW(EXCEPTION_MALFORMED_APDU); - } - - if (ctx.transaction.data.cryptoTransfer.transfers.accountAmounts_count != 0) { - // Can't also transfer Hbar if the transaction is an otherwise valid token transfer - THROW(EXCEPTION_MALFORMED_APDU); - } - } } -void validate_decimals(uint32_t decimals) { - if (decimals >= 20) { - // We only support decimal values less than 20 +void validate_token_transfer() { + // One token transfer with two accountAmounts + if (ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .transfers_count != 2) { THROW(EXCEPTION_MALFORMED_APDU); } + + // Transactions fail if not given in the right denomination + validate_decimals(ctx.transaction.data.cryptoTransfer.tokenTransfers[ 0 ] + .expected_decimals.value); } diff --git a/src/sign_transaction.h b/src/sign_transaction.h index 8496d1dc..3cb980db 100644 --- a/src/sign_transaction.h +++ b/src/sign_transaction.h @@ -1,6 +1,25 @@ #ifndef LEDGER_APP_HEDERA_SIGN_TRANSACTION_H #define LEDGER_APP_HEDERA_SIGN_TRANSACTION_H 1 +#include +#include +#include +#include +#include + +#include "basic_types.pb.h" +#include "debug.h" +#include "errors.h" +#include "globals.h" +#include "handlers.h" +#include "hedera.h" +#include "hedera_format.h" +#include "io.h" +#include "printf.h" +#include "transaction_body.pb.h" +#include "ui.h" +#include "utils.h" + enum TransactionStep { Summary = 1, Operator = 2, @@ -17,68 +36,129 @@ enum TransactionType { Unknown = -1, Verify = 0, Create = 1, - Transfer = 2, - Associate = 3, - TokenTransfer = 4, - TokenMint = 5, - TokenBurn = 6, + Update = 2, + Transfer = 3, + Associate = 4, + Dissociate = 5, + TokenTransfer = 6, + TokenMint = 7, + TokenBurn = 8, }; +void handle_transaction_body(); +void validate_decimals(uint32_t decimals); +void validate_token_transfer(); + #if defined(TARGET_NANOS) +// Transaction Context +static struct sign_tx_context_t { + // ui common + uint32_t key_index; + uint8_t transfer_to_index; + uint8_t transfer_from_index; + + // Transaction Summary + char summary_line_1[ DISPLAY_SIZE + 1 ]; + char summary_line_2[ DISPLAY_SIZE + 1 ]; + char title[ DISPLAY_SIZE + 1 ]; + + // Account ID: uint64_t.uint64_t.uint64_t + // Most other entities are shorter + char full[ ACCOUNT_ID_SIZE + 1 ]; + char partial[ DISPLAY_SIZE + 1 ]; + + // Steps correspond to parts of the transaction proto + // type is set based on proto + enum TransactionStep step; + enum TransactionType type; + + uint8_t current_page; // 1 -> Number Screens + uint8_t page_count; // Number Screens + + // Parsed transaction + Hedera_TransactionBody transaction; +} ctx; + // Forward declarations for Nano S UI // Step 1 -unsigned int ui_tx_summary_step_button( - unsigned int button_mask, - unsigned int button_mask_counter -); +unsigned int ui_tx_summary_step_button(unsigned int button_mask, + unsigned int button_mask_counter); // Step 2 - 7 void handle_intermediate_left_press(); void handle_intermediate_right_press(); -unsigned int ui_tx_intermediate_step_button( - unsigned int button_mask, - unsigned int button_mask_counter -); +unsigned int ui_tx_intermediate_step_button(unsigned int button_mask, + unsigned int button_mask_counter); // Step 8 -unsigned int ui_tx_confirm_step_button( - unsigned int button_mask, - unsigned int button_mask_counter -); +unsigned int ui_tx_confirm_step_button(unsigned int button_mask, + unsigned int button_mask_counter); // Step 9 -unsigned int ui_tx_deny_step_button( - unsigned int button_mask, - unsigned int button_mask_counter -); +unsigned int ui_tx_deny_step_button(unsigned int button_mask, + unsigned int button_mask_counter); uint8_t num_screens(size_t length); -void count_screens(); -void shift_display(); -bool first_screen(); -bool last_screen(); - -#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) -// Forward declarations for Nano X UI -void x_start_tx_loop(); -void x_continue_tx_loop(); -void x_end_tx_loop(); -unsigned int io_seproxyhal_tx_approve(const bagl_element_t* e); -unsigned int io_seproxyhal_tx_reject(const bagl_element_t* e); - -#endif // TARGET - +void count_screens_of_step(); +bool first_screen_of_step(); +bool last_screen_of_step(); void reformat_token(); -void reformat_tokens_accounts(char *title_part, uint8_t transfer_index); -void reformat_accounts(char *title_part, uint8_t transfer_index); +void reformat_tokens_accounts(char* title_part, uint8_t transfer_index); +void reformat_accounts(char* title_part, uint8_t transfer_index); +void reformat_stake_target(); +void reformat_collect_rewards(); +void reformat_target_account(); void reformat_operator(); void reformat_senders(); void reformat_recipients(); void reformat_amount(); void reformat_fee(); void reformat_memo(); -void handle_transaction_body(); -void validate_transfer(); -void validate_decimals(uint32_t decimals); -#endif //LEDGER_APP_HEDERA_SIGN_TRANSACTION_H +#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) +// Transaction Context +static struct sign_tx_context_t { + // ui common + uint32_t key_index; + uint8_t transfer_from_index; + uint8_t transfer_to_index; + + // Transaction Summary + char summary_line_1[ DISPLAY_SIZE + 1 ]; + char summary_line_2[ DISPLAY_SIZE + 1 ]; + char senders_title[ DISPLAY_SIZE + 1 ]; + char recipients_title[ DISPLAY_SIZE + 1 ]; + char amount_title[ DISPLAY_SIZE + 1 ]; + char partial[ DISPLAY_SIZE + 1 ]; + + enum TransactionType type; + + // Transaction Operator + char operator[ DISPLAY_SIZE * 2 + 1 ]; + + // Transaction Senders + char senders[ DISPLAY_SIZE * 2 + 1 ]; + + // Transaction Recipients + char recipients[ DISPLAY_SIZE * 2 + 1 ]; + + // Transaction Amount + char amount[ DISPLAY_SIZE * 2 + 1 ]; + + // Transaction Fee + char fee[ DISPLAY_SIZE * 2 + 1 ]; + + // Transaction Memo + char memo[ MAX_MEMO_SIZE + 1 ]; + + // Parsed transaction + Hedera_TransactionBody transaction; +} ctx; + +// Forward declarations for Nano X UI +unsigned int io_seproxyhal_tx_approve(const bagl_element_t* e); +unsigned int io_seproxyhal_tx_reject(const bagl_element_t* e); + +#endif // TARGET + +#endif // LEDGER_APP_HEDERA_SIGN_TRANSACTION_H diff --git a/src/ui.c b/src/ui.c index 09cf020e..7766091e 100644 --- a/src/ui.c +++ b/src/ui.c @@ -9,108 +9,81 @@ ux_state_t ux; unsigned int ux_step; unsigned int ux_step_count; -static const ux_menu_entry_t menu_main[4]; - -static const ux_menu_entry_t menu_about[3] = { - { - .menu = NULL, - .callback = NULL, - .userid = 0, - .icon = NULL, - .line1 = "Version", - .line2 = APPVERSION, - .text_x = 0, - .icon_x = 0, - }, - - { - .menu = menu_main, - .callback = NULL, - .userid = 0, - .icon = &C_icon_back, - .line1 = "Back", - .line2 = NULL, - .text_x = 61, - .icon_x = 40, - }, - - UX_MENU_END -}; - -static const ux_menu_entry_t menu_main[4] = { - { - .menu = NULL, - .callback = NULL, - .userid = 0, - .icon = NULL, - .line1 = "Awaiting", - .line2 = "Commands", - .text_x = 0, - .icon_x = 0 - }, - { - .menu = menu_about, - .callback = NULL, - .userid = 0, - .icon = NULL, - .line1 = "About", - .line2 = NULL, - .text_x = 0, - .icon_x = 0, - }, - - { - .menu = NULL, - .callback = &os_sched_exit, - .userid = 0, - .icon = &C_icon_dashboard, - .line1 = "Quit app", - .line2 = NULL, - .text_x = 50, - .icon_x = 29, - }, - - UX_MENU_END -}; +static const ux_menu_entry_t menu_main[ 4 ]; + +static const ux_menu_entry_t menu_about[ 3 ] = {{ + .menu = NULL, + .callback = NULL, + .userid = 0, + .icon = NULL, + .line1 = "Version", + .line2 = APPVERSION, + .text_x = 0, + .icon_x = 0, + }, + + { + .menu = menu_main, + .callback = NULL, + .userid = 0, + .icon = &C_icon_back, + .line1 = "Back", + .line2 = NULL, + .text_x = 61, + .icon_x = 40, + }, + + UX_MENU_END}; + +static const ux_menu_entry_t menu_main[ 4 ] = {{.menu = NULL, + .callback = NULL, + .userid = 0, + .icon = NULL, + .line1 = "Awaiting", + .line2 = "Commands", + .text_x = 0, + .icon_x = 0}, + { + .menu = menu_about, + .callback = NULL, + .userid = 0, + .icon = NULL, + .line1 = "About", + .line2 = NULL, + .text_x = 0, + .icon_x = 0, + }, + + { + .menu = NULL, + .callback = &os_sched_exit, + .userid = 0, + .icon = &C_icon_dashboard, + .line1 = "Quit app", + .line2 = NULL, + .text_x = 50, + .icon_x = 29, + }, + + UX_MENU_END}; #elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) ux_state_t G_ux; bolos_ux_params_t G_ux_params; -UX_STEP_NOCB( - ux_idle_flow_1_step, - nn, - { - "Awaiting", - "Commands" - } -); - -UX_STEP_NOCB( - ux_idle_flow_2_step, - bn, - { - "Version", - APPVERSION, - } -); - -UX_STEP_VALID( - ux_idle_flow_3_step, - pb, - os_sched_exit(-1), - { - &C_icon_dashboard_x, - "Exit" - } -); - -UX_DEF( - ux_idle_flow, - &ux_idle_flow_1_step, - &ux_idle_flow_2_step, - &ux_idle_flow_3_step -); +UX_STEP_NOCB(ux_idle_flow_1_step, nn, {"Awaiting", "Commands"}); + +UX_STEP_NOCB(ux_idle_flow_2_step, bn, + { + "Version", + APPVERSION, + }); + +UX_STEP_VALID(ux_idle_flow_3_step, pb, os_sched_exit(-1), + {&C_icon_dashboard_x, "Exit"}); + +UX_DEF(ux_idle_flow, &ux_idle_flow_1_step, &ux_idle_flow_2_step, + &ux_idle_flow_3_step); #endif // TARGET diff --git a/src/ui.h b/src/ui.h index dce88375..087a8309 100644 --- a/src/ui.h +++ b/src/ui.h @@ -1,8 +1,8 @@ #ifndef LEDGER_HEDERA_UI_H #define LEDGER_HEDERA_UI_H 1 -#include "glyphs.h" #include "globals.h" +#include "glyphs.h" #include "ux.h" #if defined(TARGET_NANOS) @@ -10,11 +10,51 @@ #include // Common UI element definitions for Nano S -#define UI_BACKGROUND() {{BAGL_RECTANGLE,0,0,0,128,32,0,0,BAGL_FILL,0,0xFFFFFF,0,0},NULL} -#define UI_ICON_LEFT(userid, glyph) {{BAGL_ICON,userid,3,12,7,7,0,0,0,0xFFFFFF,0,0,glyph},NULL} -#define UI_ICON_RIGHT(userid, glyph) {{BAGL_ICON,userid,117,13,8,6,0,0,0,0xFFFFFF,0,0,glyph},NULL} -#define UI_TEXT(userid, x, y, w, text) {{BAGL_LABELINE,userid,x,y,w,12,0,0,0,0xFFFFFF,0,BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER,0},(char *)(text)} -#define UI_ICON(userid, x, y, w, glyph) {{BAGL_ICON,userid,x,y,w,6,0,0,0,0xFFFFFF,0,BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER,glyph},NULL} +#define UI_BACKGROUND() \ + { \ + { \ + BAGL_RECTANGLE, 0, 0, 0, 128, 32, 0, 0, \ + BAGL_FILL, 0, 0xFFFFFF, 0, 0}, \ + NULL \ + } +#define UI_ICON_LEFT(userid, glyph) \ + { {BAGL_ICON, userid, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0, 0, glyph}, NULL } +#define UI_ICON_RIGHT(userid, glyph) \ + { {BAGL_ICON, userid, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0, 0, glyph}, NULL } +#define UI_TEXT(userid, x, y, w, text) \ + { \ + {BAGL_LABELINE, \ + userid, \ + x, \ + y, \ + w, \ + 12, \ + 0, \ + 0, \ + 0, \ + 0xFFFFFF, \ + 0, \ + BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, \ + 0}, \ + (char*)(text) \ + } +#define UI_ICON(userid, x, y, w, glyph) \ + { \ + {BAGL_ICON, \ + userid, \ + x, \ + y, \ + w, \ + 6, \ + 0, \ + 0, \ + 0, \ + 0xFFFFFF, \ + 0, \ + BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, \ + glyph}, \ + NULL \ + } #endif // TARGET diff --git a/src/utils.c b/src/utils.c index 1f4adf32..aab33333 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,20 +1,20 @@ #include "utils.h" -void public_key_to_bytes(unsigned char *dst, cx_ecfp_public_key_t *public) { +void public_key_to_bytes(unsigned char* dst, cx_ecfp_public_key_t* public) { for (int i = 0; i < 32; i++) { - dst[i] = public->W[64 - i]; + dst[ i ] = public->W[ 64 - i ]; } - if (public->W[32] & 1) { - dst[31] |= 0x80; + if (public->W[ 32 ] & 1) { + dst[ 31 ] |= 0x80; } } -void bin2hex(uint8_t *dst, uint8_t *data, uint64_t inlen) { - static uint8_t const hex[] = "0123456789abcdef"; - for (uint64_t i = 0; i < inlen; i++) { - dst[2*i+0] = hex[(data[i]>>4) & 0x0F]; - dst[2*i+1] = hex[(data[i]>>0) & 0x0F]; - } - dst[2*inlen] = '\0'; +void bin2hex(uint8_t* dst, uint8_t* data, uint64_t inlen) { + static uint8_t const hex[] = "0123456789abcdef"; + for (uint64_t i = 0; i < inlen; i++) { + dst[ 2 * i + 0 ] = hex[ (data[ i ] >> 4) & 0x0F ]; + dst[ 2 * i + 1 ] = hex[ (data[ i ] >> 0) & 0x0F ]; + } + dst[ 2 * inlen ] = '\0'; } diff --git a/src/utils.h b/src/utils.h index 29b00d5b..f080acb0 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,10 +1,10 @@ #ifndef LEDGER_HEDERA_UTILS_H #define LEDGER_HEDERA_UTILS_H 1 -#include #include +#include -extern void public_key_to_bytes(uint8_t *dst, cx_ecfp_public_key_t *public); -extern void bin2hex(uint8_t *dst, uint8_t *data, uint64_t inlen); +extern void public_key_to_bytes(uint8_t* dst, cx_ecfp_public_key_t* public); +extern void bin2hex(uint8_t* dst, uint8_t* data, uint64_t inlen); #endif // LEDGER_HEDERA_UTILS_H diff --git a/tests/.pdm.toml b/tests/.pdm.toml new file mode 100644 index 00000000..04b4f314 --- /dev/null +++ b/tests/.pdm.toml @@ -0,0 +1,2 @@ +[python] +path = "/home/chris/Projects/Ledger/ledger-app-hedera/tests/.venv/bin/python" diff --git a/tests/.vscode/settings.json b/tests/.vscode/settings.json new file mode 100644 index 00000000..48e647b5 --- /dev/null +++ b/tests/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.formatting.provider": "black", + "python.analysis.typeCheckingMode": "basic" +} \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/apps/__init__.py b/tests/apps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/apps/hedera.py b/tests/apps/hedera.py new file mode 100644 index 00000000..88bcb89f --- /dev/null +++ b/tests/apps/hedera.py @@ -0,0 +1,91 @@ +from typing import List, Generator, Dict +from enum import IntEnum +from contextlib import contextmanager +from time import sleep + +from ragger.backend.interface import BackendInterface, RAPDU + +from ..utils import validate_displayed_message +from .hedera_builder import hedera_transaction + + +class INS(IntEnum): + INS_GET_APP_CONFIGURATION = 0x01 + INS_GET_PUBLIC_KEY = 0x02 + INS_SIGN_TRANSACTION = 0x04 + +CLA = 0xE0 + +P1_CONFIRM = 0x00 +P1_NON_CONFIRM = 0x01 + +P2_EXTEND = 0x01 +P2_MORE = 0x02 + + +PUBLIC_KEY_LENGTH = 32 + +MAX_CHUNK_SIZE = 255 + + +STATUS_OK = 0x9000 + +class ErrorType: + EXCEPTION_USER_REJECTED = 0x6985 + + +def to_zigzag(n): + return n + n + (n < 0) + + +class HederaClient: + client: BackendInterface + + def __init__(self, client): + self._client = client + + def get_public_key_non_confirm(self, index: int) -> RAPDU: + index_b = index.to_bytes(4, "little") + return self._client.exchange(CLA, INS.INS_GET_PUBLIC_KEY, P1_NON_CONFIRM, 0, index_b) + + @contextmanager + def get_public_key_confirm(self, index: int) -> Generator[None, None, None]: + index_b = index.to_bytes(4, "little") + with self._client.exchange_async(CLA, INS.INS_GET_PUBLIC_KEY, P1_CONFIRM, 0, index_b): + sleep(0.5) + yield + + def validate(self): + self._client.right_click() + + def refuse(self): + self._client.left_click() + + def validate_screen(self, right_clicks: int): + validate_displayed_message(self._client, right_clicks) + + def get_async_response(self) -> RAPDU: + return self._client.last_async_response + + @contextmanager + def send_sign_transaction(self, + index: int, + operator_shard_num: int, + operator_realm_num: int, + operator_account_num: int, + transaction_fee: int, + memo: str, + conf: Dict) -> Generator[None, None, None]: + + transaction = hedera_transaction(operator_shard_num, + operator_realm_num, + operator_account_num, + transaction_fee, + memo, + conf) + + payload = index.to_bytes(4, "little") + transaction + + with self._client.exchange_async(CLA, INS.INS_SIGN_TRANSACTION, P1_CONFIRM, 0, payload): + sleep(0.5) + yield diff --git a/tests/apps/hedera_builder.py b/tests/apps/hedera_builder.py new file mode 100644 index 00000000..3f5da7df --- /dev/null +++ b/tests/apps/hedera_builder.py @@ -0,0 +1,319 @@ +from typing import Dict + +from proto import basic_types_pb2 as basics +from proto import crypto_create_pb2 as create +from proto import crypto_update_pb2 as update +from proto import crypto_transfer_pb2 as transfer +from proto import token_associate_pb2 as associate +from proto import token_dissociate_pb2 as dissociate +from proto import token_burn_pb2 as burn +from proto import token_mint_pb2 as mint +from proto import transaction_body_pb2 as tx_body +from proto import wrappers_pb2 as wrappers + + +def hedera_transaction( + operator_shard_num: int, + operator_realm_num: int, + operator_account_num: int, + transaction_fee: int, + memo: str, + conf: Dict, +) -> bytes: + operator = basics.AccountID( + shardNum=operator_shard_num, + realmNum=operator_realm_num, + accountNum=operator_account_num, + ) + + hedera_transaction_id = basics.TransactionID( + accountID=operator, + ) + + transaction = tx_body.TransactionBody( + transactionID=hedera_transaction_id, + transactionFee=transaction_fee, + memo=memo, + **conf, + ) + + return transaction.SerializeToString() + + +def crypto_create_account_conf(initialBalance: int) -> Dict: + crypto_create_account = create.CryptoCreateTransactionBody( + initialBalance=initialBalance, + ) + + return {"cryptoCreateAccount": crypto_create_account} + + +def crypto_create_account_stake_account_conf() -> Dict: + stake_target = basics.AccountID(shardNum=1, realmNum=2, accountNum=3) + + crypto_create_account = create.CryptoCreateTransactionBody( + initialBalance=5, staked_account_id=stake_target + ) + + return {"cryptoCreateAccount": crypto_create_account} + + +def crypto_create_account_stake_node_conf() -> Dict: + stake_target = 3 + + crypto_create_account = create.CryptoCreateTransactionBody( + initialBalance=5, staked_node_id=stake_target + ) + + return {"cryptoCreateAccount": crypto_create_account} + + +def crypto_create_account_stake_toggle_rewards_conf() -> Dict: + stake_target = basics.AccountID(shardNum=6, realmNum=6, accountNum=6) + + crypto_create_account = create.CryptoCreateTransactionBody( + initialBalance=5, staked_account_id=stake_target, decline_reward=True + ) + + return {"cryptoCreateAccount": crypto_create_account} + + +def account_update_conf() -> Dict: + stake_target = basics.AccountID(shardNum=6, realmNum=6, accountNum=6) + + crypto_update_account = update.CryptoUpdateTransactionBody( + staked_account_id=stake_target + ) + + return {"cryptoUpdateAccount": crypto_update_account} + + +def crypto_transfer_token_conf( + token_shardNum: int, + token_realmNum: int, + token_tokenNum: int, + sender_shardNum: int, + sender_realmNum: int, + sender_accountNum: int, + recipient_shardNum: int, + recipient_realmNum: int, + recipient_accountNum: int, + amount: int, + decimals: int, +) -> Dict: + + hedera_token_id = basics.TokenID( + shardNum=token_shardNum, + realmNum=token_realmNum, + tokenNum=token_tokenNum, + ) + + hedera_account_id_sender = basics.AccountID( + shardNum=sender_shardNum, + realmNum=sender_realmNum, + accountNum=sender_accountNum, + ) + + hedera_account_amount_sender = basics.AccountAmount( + accountID=hedera_account_id_sender, + amount=0, + ) + + hedera_account_id_recipient = basics.AccountID( + shardNum=recipient_shardNum, + realmNum=recipient_realmNum, + accountNum=recipient_accountNum, + ) + + hedera_account_amount_recipient = basics.AccountAmount( + accountID=hedera_account_id_recipient, + amount=amount, + ) + + hedera_transfer_list = basics.TransferList( + accountAmounts=[], + ) + + decimalsUInt32 = wrappers.UInt32Value( + value=decimals, + ) + + hedera_token_transfer_list = basics.TokenTransferList( + token=hedera_token_id, + transfers=[hedera_account_amount_recipient, hedera_account_amount_sender], + expected_decimals=decimalsUInt32, + ) + + crypto_transfer = transfer.CryptoTransferTransactionBody( + transfers=hedera_transfer_list, + tokenTransfers=[hedera_token_transfer_list], + ) + + return {"cryptoTransfer": crypto_transfer} + + +def crypto_transfer_hbar_conf( + sender_shardNum: int, + sender_realmNum: int, + sender_accountNum: int, + recipient_shardNum: int, + recipient_realmNum: int, + recipient_accountNum: int, + amount: int, +) -> Dict: + + hedera_account_id_sender = basics.AccountID( + shardNum=sender_shardNum, + realmNum=sender_realmNum, + accountNum=sender_accountNum, + ) + + hedera_account_amount_sender = basics.AccountAmount( + accountID=hedera_account_id_sender, + amount=0, + ) + + hedera_account_id_recipient = basics.AccountID( + shardNum=recipient_shardNum, + realmNum=recipient_realmNum, + accountNum=recipient_accountNum, + ) + + hedera_account_amount_recipient = basics.AccountAmount( + accountID=hedera_account_id_recipient, + amount=amount, + ) + + hedera_transfer_list = basics.TransferList( + accountAmounts=[hedera_account_amount_recipient, hedera_account_amount_sender], + ) + + crypto_transfer = transfer.CryptoTransferTransactionBody( + transfers=hedera_transfer_list, + tokenTransfers=[], + ) + + return {"cryptoTransfer": crypto_transfer} + + +def crypto_transfer_verify( + sender_shardNum: int, sender_realmNum: int, sender_accountNum: int +) -> Dict: + + hedera_account_id_sender = basics.AccountID( + shardNum=sender_shardNum, + realmNum=sender_realmNum, + accountNum=sender_accountNum, + ) + + hedera_account_amount_sender = basics.AccountAmount( + accountID=hedera_account_id_sender, + amount=0, + ) + + hedera_transfer_list = basics.TransferList( + accountAmounts=[hedera_account_amount_sender], + ) + + crypto_transfer = transfer.CryptoTransferTransactionBody( + transfers=hedera_transfer_list, + tokenTransfers=[], + ) + + return {"cryptoTransfer": crypto_transfer} + + +def token_associate_conf( + token_shardNum: int, + token_realmNum: int, + token_tokenNum: int, + sender_shardNum: int, + sender_realmNum: int, + sender_accountNum: int, +) -> Dict: + + hedera_account_id_sender = basics.AccountID( + shardNum=sender_shardNum, + realmNum=sender_realmNum, + accountNum=sender_accountNum, + ) + + hedera_token_id = basics.TokenID( + shardNum=token_shardNum, + realmNum=token_realmNum, + tokenNum=token_tokenNum, + ) + + token_associate = associate.TokenAssociateTransactionBody( + account=hedera_account_id_sender, + tokens=[hedera_token_id], + ) + + return {"tokenAssociate": token_associate} + + +def token_dissociate_conf( + token_shardNum: int, + token_realmNum: int, + token_tokenNum: int, + sender_shardNum: int, + sender_realmNum: int, + sender_accountNum: int, +) -> Dict: + + hedera_account_id_sender = basics.AccountID( + shardNum=sender_shardNum, + realmNum=sender_realmNum, + accountNum=sender_accountNum, + ) + + hedera_token_id = basics.TokenID( + shardNum=token_shardNum, + realmNum=token_realmNum, + tokenNum=token_tokenNum, + ) + + token_dissociate = dissociate.TokenDissociateTransactionBody( + account=hedera_account_id_sender, + tokens=[hedera_token_id], + ) + + return {"tokenDissociate": token_dissociate} + + +def token_burn_conf( + token_shardNum: int, + token_realmNum: int, + token_tokenNum: int, + amount: int, +) -> Dict: + hedera_token_id = basics.TokenID( + shardNum=token_shardNum, + realmNum=token_realmNum, + tokenNum=token_tokenNum, + ) + + token_burn = burn.TokenBurnTransactionBody(token=hedera_token_id, amount=amount) + + return {"tokenBurn": token_burn} + + +def token_mint_conf( + token_shardNum: int, + token_realmNum: int, + token_tokenNum: int, + amount: int, +) -> Dict: + + hedera_token_id = basics.TokenID( + shardNum=token_shardNum, + realmNum=token_realmNum, + tokenNum=token_tokenNum, + ) + + token_mint = mint.TokenMintTransactionBody( + token=hedera_token_id, + amount=amount, + ) + + return {"tokenMint": token_mint} diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..522666e0 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,99 @@ +from pathlib import Path +import pytest + +from ragger import Firmware +from ragger.backend import SpeculosBackend, LedgerCommBackend, LedgerWalletBackend + +from .utils import app_path_from_app_name + +def __str__(self): # also tried __repr__() + # Attempt to print the 'select' attribute in "pytest -v" output + return self.select + + +APPS_DIRECTORY = (Path(__file__).parent / "elfs").resolve() + +APP_NAME = "hedera" + +BACKENDS = ["speculos", "ledgercomm", "ledgerwallet"] + +FIRMWARES = [ + Firmware('nanos', '2.1'), + Firmware('nanox', '2.0.2'), + Firmware('nanosp', '1.0.3'), +] + +def pytest_addoption(parser): + parser.addoption("--backend", action="store", default="speculos") + parser.addoption("--display", action="store_true", default=False) + # Enable using --'device' in the pytest command line to restrict testing to specific devices + for fw in FIRMWARES: + parser.addoption("--"+fw.device, action="store_true", help="run on {} only".format(fw.device)) + + +@pytest.fixture(scope="session") +def backend(pytestconfig): + return pytestconfig.getoption("backend") + + +@pytest.fixture(scope="session") +def display(pytestconfig): + return pytestconfig.getoption("display") + + +# Glue to call every test that depends on the firmware once for each required firmware +def pytest_generate_tests(metafunc): + if "firmware" in metafunc.fixturenames: + fw_list = [] + ids = [] + # First pass: enable only demanded firmwares + for fw in FIRMWARES: + if metafunc.config.getoption(fw.device): + fw_list.append(fw) + ids.append(fw.device + " " + fw.version) + # Second pass if no specific firmware demanded: add them all + if not fw_list: + for fw in FIRMWARES: + fw_list.append(fw) + ids.append(fw.device + " " + fw.version) + metafunc.parametrize("firmware", fw_list, ids=ids) + + +def prepare_speculos_args(firmware, display: bool): + speculos_args = [] + + if display: + speculos_args += ["--display", "qt"] + + app_path = app_path_from_app_name(APPS_DIRECTORY, APP_NAME, firmware.device) + + return ([app_path], {"args": speculos_args}) + + +def create_backend(backend: str, firmware: Firmware, display: bool): + if backend.lower() == "ledgercomm": + return LedgerCommBackend(firmware, interface="hid") + elif backend.lower() == "ledgerwallet": + return LedgerWalletBackend(firmware) + elif backend.lower() == "speculos": + args, kwargs = prepare_speculos_args(firmware, display) + return SpeculosBackend(*args, firmware, **kwargs) + else: + raise ValueError(f"Backend '{backend}' is unknown. Valid backends are: {BACKENDS}") + +@pytest.fixture +def client(backend, firmware, display: bool): + with create_backend(backend, firmware, display) as b: + yield b + +@pytest.fixture(autouse=True) +def use_only_on_backend(request, backend): + if request.node.get_closest_marker('use_on_backend'): + current_backend = request.node.get_closest_marker('use_on_backend').args[0] + if current_backend != backend: + pytest.skip('skipped on this backend: {}'.format(current_backend)) + +def pytest_configure(config): + config.addinivalue_line( + "markers", "use_only_on_backend(backend): skip test if not on the specified backend", + ) diff --git a/tests/elfs/hedera_nanos.elf b/tests/elfs/hedera_nanos.elf new file mode 100755 index 00000000..6309ae92 Binary files /dev/null and b/tests/elfs/hedera_nanos.elf differ diff --git a/tests/elfs/hedera_nanosplus.elf b/tests/elfs/hedera_nanosplus.elf new file mode 100755 index 00000000..9ba1f2ca Binary files /dev/null and b/tests/elfs/hedera_nanosplus.elf differ diff --git a/tests/elfs/hedera_nanox.elf b/tests/elfs/hedera_nanox.elf new file mode 100755 index 00000000..16bdff6f Binary files /dev/null and b/tests/elfs/hedera_nanox.elf differ diff --git a/tests/nanopb_pb2.py b/tests/nanopb_pb2.py new file mode 120000 index 00000000..4c34e39b --- /dev/null +++ b/tests/nanopb_pb2.py @@ -0,0 +1 @@ +../vendor/nanopb/generator/proto/nanopb_pb2.py \ No newline at end of file diff --git a/tests/pdm.lock b/tests/pdm.lock new file mode 100644 index 00000000..51f7c840 --- /dev/null +++ b/tests/pdm.lock @@ -0,0 +1,1274 @@ +[[package]] +name = "aniso8601" +version = "9.0.1" +summary = "A library for parsing ISO 8601 strings." + +[[package]] +name = "asn1crypto" +version = "1.5.1" +summary = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" + +[[package]] +name = "attrs" +version = "22.1.0" +requires_python = ">=3.5" +summary = "Classes Without Boilerplate" + +[[package]] +name = "base58" +version = "2.1.1" +requires_python = ">=3.5" +summary = "Base58 and Base58Check implementation." + +[[package]] +name = "bip-utils" +version = "2.7.0" +requires_python = ">=3.7" +summary = "Generation of mnemonics, seeds, private/public keys and addresses for different types of cryptocurrencies" +dependencies = [ + "cbor2~=5.1", + "coincurve<18.0.0,>=15.0.1", + "crcmod~=1.7", + "ecdsa~=0.15", + "ed25519-blake2b~=1.4", + "py-sr25519-bindings<2.0.0,>=0.1.3", + "pycryptodome~=3.6", + "pynacl~=1.4", +] + +[[package]] +name = "bip32" +version = "3.3" +summary = "Minimalistic implementation of the BIP32 key derivation scheme" +dependencies = [ + "base58~=2.0", + "coincurve<18,>=15.0", +] + +[[package]] +name = "black" +version = "22.10.0" +requires_python = ">=3.7" +summary = "The uncompromising code formatter." +dependencies = [ + "click>=8.0.0", + "mypy-extensions>=0.4.3", + "pathspec>=0.9.0", + "platformdirs>=2", + "tomli>=1.1.0; python_full_version < \"3.11.0a7\"", +] + +[[package]] +name = "cbor2" +version = "5.4.3" +requires_python = ">=3.7" +summary = "CBOR (de)serializer with extensive tag support" + +[[package]] +name = "certifi" +version = "2022.9.24" +requires_python = ">=3.6" +summary = "Python package for providing Mozilla's CA Bundle." + +[[package]] +name = "cffi" +version = "1.15.1" +summary = "Foreign Function Interface for Python calling C code." +dependencies = [ + "pycparser", +] + +[[package]] +name = "charset-normalizer" +version = "2.1.1" +requires_python = ">=3.6.0" +summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." + +[[package]] +name = "click" +version = "8.1.3" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +dependencies = [ + "colorama; platform_system == \"Windows\"", +] + +[[package]] +name = "coincurve" +version = "17.0.0" +requires_python = ">=3.7" +summary = "Cross-platform Python CFFI bindings for libsecp256k1" +dependencies = [ + "asn1crypto", + "cffi>=1.3.0", +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." + +[[package]] +name = "construct" +version = "2.10.68" +requires_python = ">=3.6" +summary = "A powerful declarative symmetric parser/builder for binary data" + +[[package]] +name = "coverage" +version = "6.5.0" +requires_python = ">=3.7" +summary = "Code coverage measurement for Python" + +[[package]] +name = "coverage" +version = "6.5.0" +extras = ["toml"] +requires_python = ">=3.7" +summary = "Code coverage measurement for Python" +dependencies = [ + "coverage==6.5.0", + "tomli; python_full_version <= \"3.11.0a6\"", +] + +[[package]] +name = "crcmod" +version = "1.7" +summary = "CRC Generator" + +[[package]] +name = "ecdsa" +version = "0.18.0" +requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "ECDSA cryptographic signature library (pure python)" +dependencies = [ + "six>=1.9.0", +] + +[[package]] +name = "ed25519-blake2b" +version = "1.4" +summary = "Ed25519 public-key signatures (BLAKE2b fork)" + +[[package]] +name = "exceptiongroup" +version = "1.0.4" +requires_python = ">=3.7" +summary = "Backport of PEP 654 (exception groups)" + +[[package]] +name = "flask" +version = "2.2.2" +requires_python = ">=3.7" +summary = "A simple framework for building complex web applications." +dependencies = [ + "Jinja2>=3.0", + "Werkzeug>=2.2.2", + "click>=8.0", + "itsdangerous>=2.0", +] + +[[package]] +name = "flask-restful" +version = "0.3.9" +summary = "Simple framework for creating REST APIs" +dependencies = [ + "Flask>=0.8", + "aniso8601>=0.82", + "pytz", + "six>=1.3.0", +] + +[[package]] +name = "idna" +version = "3.4" +requires_python = ">=3.5" +summary = "Internationalized Domain Names in Applications (IDNA)" + +[[package]] +name = "iniconfig" +version = "1.1.1" +summary = "iniconfig: brain-dead simple config-ini parsing" + +[[package]] +name = "itsdangerous" +version = "2.1.2" +requires_python = ">=3.7" +summary = "Safely pass data to untrusted environments and back." + +[[package]] +name = "jinja2" +version = "3.1.2" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +dependencies = [ + "MarkupSafe>=2.0", +] + +[[package]] +name = "jsonschema" +version = "3.2.0" +summary = "An implementation of JSON Schema validation for Python" +dependencies = [ + "attrs>=17.4.0", + "pyrsistent>=0.14.0", + "setuptools", + "six>=1.11.0", +] + +[[package]] +name = "markupsafe" +version = "2.1.1" +requires_python = ">=3.7" +summary = "Safely add untrusted strings to HTML/XML markup." + +[[package]] +name = "mnemonic" +version = "0.20" +requires_python = ">=3.5" +summary = "Implementation of Bitcoin BIP-0039" + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +summary = "Experimental type system extensions for programs checked with the mypy typechecker." + +[[package]] +name = "mypy-protobuf" +version = "3.3.0" +requires_python = ">=3.7" +summary = "Generate mypy stub files from protobuf specs" +dependencies = [ + "protobuf>=3.19.4", + "types-protobuf>=3.19.12", +] + +[[package]] +name = "packaging" +version = "21.3" +requires_python = ">=3.6" +summary = "Core utilities for Python packages" +dependencies = [ + "pyparsing!=3.0.5,>=2.0.2", +] + +[[package]] +name = "pathspec" +version = "0.10.2" +requires_python = ">=3.7" +summary = "Utility library for gitignore style pattern matching of file paths." + +[[package]] +name = "pillow" +version = "9.3.0" +requires_python = ">=3.7" +summary = "Python Imaging Library (Fork)" + +[[package]] +name = "platformdirs" +version = "2.5.4" +requires_python = ">=3.7" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." + +[[package]] +name = "pluggy" +version = "1.0.0" +requires_python = ">=3.6" +summary = "plugin and hook calling mechanisms for python" + +[[package]] +name = "protobuf" +version = "3.20.3" +requires_python = ">=3.7" +summary = "Protocol Buffers" + +[[package]] +name = "py-sr25519-bindings" +version = "0.1.4" +summary = "Python bindings for sr25519 library" + +[[package]] +name = "pycparser" +version = "2.21" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "C parser in Python" + +[[package]] +name = "pycryptodome" +version = "3.15.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +summary = "Cryptographic library for Python" + +[[package]] +name = "pyelftools" +version = "0.29" +summary = "Library for analyzing ELF files and DWARF debugging information" + +[[package]] +name = "pynacl" +version = "1.5.0" +requires_python = ">=3.6" +summary = "Python binding to the Networking and Cryptography (NaCl) library" +dependencies = [ + "cffi>=1.4.1", +] + +[[package]] +name = "pyparsing" +version = "3.0.9" +requires_python = ">=3.6.8" +summary = "pyparsing module - Classes and methods to define and execute parsing grammars" + +[[package]] +name = "pyqt5" +version = "5.15.7" +requires_python = ">=3.7" +summary = "Python bindings for the Qt cross platform application toolkit" +dependencies = [ + "PyQt5-Qt5>=5.15.0", + "PyQt5-sip<13,>=12.11", +] + +[[package]] +name = "pyqt5-qt5" +version = "5.15.2" +summary = "The subset of a Qt installation needed by PyQt5." + +[[package]] +name = "pyqt5-sip" +version = "12.11.0" +requires_python = ">=3.7" +summary = "The sip module support for PyQt5" + +[[package]] +name = "pyrsistent" +version = "0.19.2" +requires_python = ">=3.7" +summary = "Persistent/Functional/Immutable data structures" + +[[package]] +name = "pytest" +version = "7.2.0" +requires_python = ">=3.7" +summary = "pytest: simple powerful testing with Python" +dependencies = [ + "attrs>=19.2.0", + "colorama; sys_platform == \"win32\"", + "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", + "iniconfig", + "packaging", + "pluggy<2.0,>=0.12", + "tomli>=1.0.0; python_version < \"3.11\"", +] + +[[package]] +name = "pytest-cov" +version = "4.0.0" +requires_python = ">=3.6" +summary = "Pytest plugin for measuring coverage." +dependencies = [ + "coverage[toml]>=5.2.1", + "pytest>=4.6", +] + +[[package]] +name = "pytz" +version = "2022.6" +summary = "World timezone definitions, modern and historical" + +[[package]] +name = "ragger" +version = "0.7.0" +requires_python = ">=3.6" +summary = "Testing framework using Speculos and LedgerComm as backends" +dependencies = [ + "bip-utils>=2.4.0", + "bip32>=3.3", + "py-sr25519-bindings==0.1.4", + "semver>=2.13.0", +] + +[[package]] +name = "ragger" +version = "0.7.0" +extras = ["speculos", "tests"] +requires_python = ">=3.6" +summary = "Testing framework using Speculos and LedgerComm as backends" +dependencies = [ + "pytest", + "pytest-cov", + "ragger==0.7.0", + "speculos>=0.1.128", +] + +[[package]] +name = "requests" +version = "2.28.1" +requires_python = ">=3.7, <4" +summary = "Python HTTP for Humans." +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<3,>=2", + "idna<4,>=2.5", + "urllib3<1.27,>=1.21.1", +] + +[[package]] +name = "semver" +version = "2.13.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "Python helper for Semantic Versioning (http://semver.org/)" + +[[package]] +name = "setuptools" +version = "65.5.1" +requires_python = ">=3.7" +summary = "Easily download, build, install, upgrade, and uninstall Python packages" + +[[package]] +name = "six" +version = "1.16.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" + +[[package]] +name = "speculos" +version = "0.1.157" +requires_python = ">=3.6.0" +summary = "Ledger Blue and Nano S/X application emulator" +dependencies = [ + "construct<3.0.0,>=2.10.56", + "flask-restful<1.0,>=0.3.9", + "flask<3.0.0,>=2.0.0", + "jsonschema<4.0.0,>=3.2.0", + "mnemonic<1.0,>=0.19", + "pillow<10.0.0,>=8.0.0", + "pyelftools<1.0,>=0.27", + "pyqt5<6.0.0,>=5.15.2", + "requests<3.0.0,>=2.25.1", +] + +[[package]] +name = "tomli" +version = "2.0.1" +requires_python = ">=3.7" +summary = "A lil' TOML parser" + +[[package]] +name = "types-protobuf" +version = "3.20.4.5" +summary = "Typing stubs for protobuf" + +[[package]] +name = "urllib3" +version = "1.26.12" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +summary = "HTTP library with thread-safe connection pooling, file post, and more." + +[[package]] +name = "werkzeug" +version = "2.2.2" +requires_python = ">=3.7" +summary = "The comprehensive WSGI web application library." +dependencies = [ + "MarkupSafe>=2.1.1", +] + +[metadata] +lock_version = "4.0" +content_hash = "sha256:38717b41c12cd6cc82e65935b165b9fbaccb282850ed20fd7e7ab88120a0f1e0" + +[metadata.files] +"aniso8601 9.0.1" = [ + {url = "https://files.pythonhosted.org/packages/cb/72/be3db445b03944bfbb2b02b82d00cb2a2bcf96275c4543f14bf60fa79e12/aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, + {url = "https://files.pythonhosted.org/packages/e3/04/e97c12dc034791d7b504860acfcdd2963fa21ae61eaca1c9d31245f812c3/aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, +] +"asn1crypto 1.5.1" = [ + {url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {url = "https://files.pythonhosted.org/packages/de/cf/d547feed25b5244fcb9392e288ff9fdc3280b10260362fc45d37a798a6ee/asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] +"attrs 22.1.0" = [ + {url = "https://files.pythonhosted.org/packages/1a/cb/c4ffeb41e7137b23755a45e1bfec9cbb76ecf51874c6f1d113984ecaa32c/attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, + {url = "https://files.pythonhosted.org/packages/f2/bc/d817287d1aa01878af07c19505fafd1165cd6a119e9d0821ca1d1c20312d/attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, +] +"base58 2.1.1" = [ + {url = "https://files.pythonhosted.org/packages/4a/45/ec96b29162a402fc4c1c5512d114d7b3787b9d1c2ec241d9568b4816ee23/base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, + {url = "https://files.pythonhosted.org/packages/7f/45/8ae61209bb9015f516102fa559a2914178da1d5868428bd86a1b4421141d/base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"}, +] +"bip-utils 2.7.0" = [ + {url = "https://files.pythonhosted.org/packages/e7/2f/baba669c680fef1e841d05840f4089d24841cd485fa4b0c7ce4bedd24d25/bip_utils-2.7.0.tar.gz", hash = "sha256:bc6302840a95695609e215ad362ddb42d70d472b3cb1494d1fb2112d08c1c707"}, + {url = "https://files.pythonhosted.org/packages/f7/5f/0327b3d8938453ebfdbf1e9b95f69f5c0efe533536950371517c17cee38a/bip_utils-2.7.0-py3-none-any.whl", hash = "sha256:b04c0f60628f1ab2792e8946f8547515b72269d8f431f722592f6a71fa7ff9fc"}, +] +"bip32 3.3" = [ + {url = "https://files.pythonhosted.org/packages/61/b1/37685f64de6d6f5ede0394fe70a9ab45f4f0691a2fe99a231f1b569e1c6d/bip32-3.3-py3-none-any.whl", hash = "sha256:c1ed0b2896b090e090cbd3c7de94ed10a9e1f9157e264f09f1a7177cb0b31b6d"}, + {url = "https://files.pythonhosted.org/packages/7e/ec/bc151473168920d24ca6af7288dd5cdacb71c69cf6d357b146945777c4b2/bip32-3.3.tar.gz", hash = "sha256:664e0700ee53c79bf2573b277f278b7f4a6ca14ee4ce5e8499721c4c752e32c1"}, +] +"black 22.10.0" = [ + {url = "https://files.pythonhosted.org/packages/2c/11/f2737cd3b458d91401801e83a014e87c63e8904dc063200f77826c352f54/black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, + {url = "https://files.pythonhosted.org/packages/3d/c5/b3ab9b563f35fb284d37ab2b14acaed9a27d8cdea9c31364766eb54946a7/black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, + {url = "https://files.pythonhosted.org/packages/56/df/913d71817c7034edba25d596c54f782c2f809b6af30367d2f00309e8890a/black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, + {url = "https://files.pythonhosted.org/packages/69/21/846c95710cc6561ba980bd6c72479dbcdde742e927ff5ef7340916d003ac/black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, + {url = "https://files.pythonhosted.org/packages/69/84/903cdf41514088d5a716538cb189c471ab34e56ae9a1c2da6b8bfe8e4dbf/black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, + {url = "https://files.pythonhosted.org/packages/71/f8/57e47ea67f59613c4368a952062bc3429131249920cffbb8362fd404b733/black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, + {url = "https://files.pythonhosted.org/packages/86/da/edebcc6c13441d91eff6761e50512bc6d6886a556dc5357b399694122b4f/black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, + {url = "https://files.pythonhosted.org/packages/91/e6/d9b78987d7d903369ba1a0b795bce4de06f0155be6609f15e8950aef8f7e/black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, + {url = "https://files.pythonhosted.org/packages/a3/89/629fca2eea0899c06befaa58dc0f49d56807d454202bb2e54bd0d98c77f3/black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, + {url = "https://files.pythonhosted.org/packages/a5/5f/9cfc6dd95965f8df30194472543e6f0515a10d78ea5378426ef1546735c7/black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, + {url = "https://files.pythonhosted.org/packages/a6/84/5c3f3ffc4143fa7e208d745d2239d915e74d3709fdbc64c3e98d3fd27e56/black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, + {url = "https://files.pythonhosted.org/packages/ab/15/61119d166a44699827c112d7c4726421f14323c2cb7aa9f4c26628f237f9/black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, + {url = "https://files.pythonhosted.org/packages/ae/49/ea03c318a25be359b8e5178a359d47e2da8f7524e1522c74b8f74c66b6f8/black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, + {url = "https://files.pythonhosted.org/packages/b0/9e/fa912c5ae4b8eb6d36982fc8ac2d779cf944dbd7c3c1fe7a28acf462c1ed/black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, + {url = "https://files.pythonhosted.org/packages/b9/51/403b0b0eb9fb412ca02b79dc38472469f2f88c9aacc6bb5262143e4ff0bc/black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, + {url = "https://files.pythonhosted.org/packages/ce/6f/74492b8852ee4f2ad2178178f6b65bc8fc80ad539abe56c1c23eab6732e2/black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, + {url = "https://files.pythonhosted.org/packages/d0/5a/5f31494e3acbb6319ee60c3a3a09d3e536a3fd2353f76af9cbff799c4999/black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, + {url = "https://files.pythonhosted.org/packages/e2/2f/a8406a9e337a213802aa90a3e9fbf90c86f3edce92f527255fd381309b77/black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, + {url = "https://files.pythonhosted.org/packages/e3/b4/9203f1a0c99aa30389b61fa8cb54bc9f4bf16ac3aa74630c6b974ed3f3b0/black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, + {url = "https://files.pythonhosted.org/packages/f2/23/f4278377cabf882298b4766e977fd04377f288d1ccef706953076a1e0598/black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, + {url = "https://files.pythonhosted.org/packages/ff/ce/22281871536b3d79474fd44d48dad48f7cbc5c3982bddf6a7495e7079d00/black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, +] +"cbor2 5.4.3" = [ + {url = "https://files.pythonhosted.org/packages/06/bc/a06502ec2156236bf3de33d88a8afcad31834fb2a315f4762710af5e551e/cbor2-5.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e10f2f4fcf5ab6a8b24d22f7109f48cad8143f669795899370170d7b36ed309f"}, + {url = "https://files.pythonhosted.org/packages/0b/79/30c4336c03e2f22cf4780d680af89b3d1a7b20dcf935a745ec1f6920c992/cbor2-5.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37ae0ce5afe864d1a1c5b05becaf8aaca7b7131cb7b0b935d7e79b29fb1cea28"}, + {url = "https://files.pythonhosted.org/packages/0e/74/0c5b7d875031cd39734a9eb369d32560712512e68fd520408a02e59e366a/cbor2-5.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:4e8590193fcbbb9477010ca0f094f6540a5e723965c90eea7a37edbe75f0ec4d"}, + {url = "https://files.pythonhosted.org/packages/0f/58/d8fa51d2fb85fdcc539a7e434dc91f5a53a40676ab04ccfb3c2105694d8b/cbor2-5.4.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:981b9ffc4f2947a0f030e71ce5eac31334bc81369dd57c6c1273c94c6cdb0b5a"}, + {url = "https://files.pythonhosted.org/packages/17/9f/649aa0e5927ffb0f5920d5b4ab2c9236d3c7243f6aae6a79dc8239dbbce5/cbor2-5.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:0a3a1b2f6b83ab4ce806df48360cc16d34cd315f17549dbda9fdd371bea04497"}, + {url = "https://files.pythonhosted.org/packages/19/58/0caea0a5bc265b8eb09cb633873be06ea8bf4779a5338e42d389c9113c37/cbor2-5.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5c50da4702ac5ca3a8e7cb9f34f62b4ea91bc81b76c2fba03888b366da299cd8"}, + {url = "https://files.pythonhosted.org/packages/3b/e6/6d070295033b286b79a066577c4e2f32a30da87b9080cdcbb4bff0bb02c5/cbor2-5.4.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07975f956baddb8dfeca4966f1871fd2482cb36af24c461f763732a44675225"}, + {url = "https://files.pythonhosted.org/packages/45/aa/078ff9bb5f6075289434db76f8f0ab3f0e0815a7c8d1b42250983df552a7/cbor2-5.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c617c7f94936d65ed9c8e99c6c03e3dc83313d69c6bfea810014ec658e9b1a9d"}, + {url = "https://files.pythonhosted.org/packages/69/3e/23c195efcc1a09c0cb3ad3cf255ac628290e69c325a6094427552faf1bc2/cbor2-5.4.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9538ab1b4207e76ee02a52362d77e312921ec1dc75b6fb42182887d87d0ca53e"}, + {url = "https://files.pythonhosted.org/packages/6b/b3/ba530faf197f4fa76715f65f375af50e36848468e9154ae4847a7a9da0ef/cbor2-5.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0e4ae67a697c664b579b87c4ef9d60e26c146b95bff443a9a38abb16f6981ff0"}, + {url = "https://files.pythonhosted.org/packages/7a/7a/95def2895c54362eeec30b336b6b00b9a7e5d77ddce5f15902f8f15f75c7/cbor2-5.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab6c934806759d453a9bb5318f2703c831e736be005ac35d5bd5cf2093ba57b1"}, + {url = "https://files.pythonhosted.org/packages/7d/80/cd612cc982cb1e3b4e801d241dec977a7e60acdef204862e19ab5607e2f2/cbor2-5.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:70789805b9aebd215626188aa05bb09908ed51e3268d4db5ae6a08276efdbcb1"}, + {url = "https://files.pythonhosted.org/packages/81/2b/c9f71180c14cc8009b8ea3a4a33e5a5e82cd14f78d5bd3e53e465ae54da0/cbor2-5.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b35c5d4d14fe804f718d5a5968a528970d2a7046aa87045538f189a98e5c7055"}, + {url = "https://files.pythonhosted.org/packages/92/15/c8df882191534848c094cdba1ee43c24a1f203158317055b88b5c1ef8b9c/cbor2-5.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:20291dad09cf9c4e5f434d376dd9d60f5ab5e066b308005f50e7c5e22e504214"}, + {url = "https://files.pythonhosted.org/packages/92/b7/d68849bb65dfd6fa359be214451e714f433973f16123b1454bdd24c9dc1c/cbor2-5.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cbe7cdeed26cd8ec2dcfed2b8876bc137ad8b9e0abb07aa5fb05770148a4b5c7"}, + {url = "https://files.pythonhosted.org/packages/96/39/e4c02d596fbfe74cb87435648b278f08298ad12547501c8f4ffddd2d7839/cbor2-5.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8a643b19ace1584043bbf4e2d0b4fae8bebd6b6ffab14ea6478d3ff07f58e854"}, + {url = "https://files.pythonhosted.org/packages/96/d5/1636607ae4080bec89bded6a7191245449555d1f64c053c90578634b3362/cbor2-5.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4b09ff6148a8cd529512479a1d6521fb7687fb03b448973933c3b03711d00bfc"}, + {url = "https://files.pythonhosted.org/packages/9c/08/bd9e416077e1f35de5ab9d5616eba237922f679287a4dcc60f3f78b69c9c/cbor2-5.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5aaf3406c9d661d11f87e792edb9a38561dba1441afba7fb883d6d963e67f32c"}, + {url = "https://files.pythonhosted.org/packages/9d/c9/cfa5c35a62642a19c14bf9a12dfbf0ee134466be1f062df2258a2ec2f2f7/cbor2-5.4.3.tar.gz", hash = "sha256:62b863c5ee6ced4032afe948f3c1484f375550995d3b8498145237fe28e546c2"}, + {url = "https://files.pythonhosted.org/packages/ac/11/b163ea396a02a2e27bdbd63a4e44d317feecd2cb72094a61977dbdf6bb82/cbor2-5.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62fc15bfe187e4994c457e6055687514c417d6099de62dd33ae766561f05847e"}, + {url = "https://files.pythonhosted.org/packages/ae/52/2e690a63afb3822da5825048a47accaccddf713d7148e2696fe0445c1215/cbor2-5.4.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:cbca58220f52fd50d8985e4079e10c71196d538fb6685f157f608a29253409a4"}, + {url = "https://files.pythonhosted.org/packages/af/ec/2f48f091d7be9ea98aed93ee3b74bc544295efef65f0b38fa68fe4a03e72/cbor2-5.4.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d21ccd1ec802e88dba1c373724a09538a0237116ab589c5301ca4c59478f7c10"}, + {url = "https://files.pythonhosted.org/packages/b4/6c/02b19eeafb7161f70516d5caf385f9d27eab2d20abd4d462ccd38fc2eecf/cbor2-5.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fab0e00c28305db59f7005150447d08dd13da6a82695a2132c28beba590fd2c"}, + {url = "https://files.pythonhosted.org/packages/b5/65/5a5e224bc3f0fa78c6a723f64a30c129e7a0c770584a8cd31e7b3dcbe4fa/cbor2-5.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3843a9bb970343e9c896aa71a34fa80983cd0ddec6eacdb2284b5e83f4ee7511"}, + {url = "https://files.pythonhosted.org/packages/c4/1a/5d8f054f79e40bd864cb05a4e0bddd0b99d87400a14e7ba68de27d87206d/cbor2-5.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de925608dc6d73cd1aab08800bff38f71f90459c15db3a71a67023b0fc697da"}, + {url = "https://files.pythonhosted.org/packages/cd/7a/258d271ffbac9a6222f79c91f84c5a0ff2fdc71a158286e22e3477b292cb/cbor2-5.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2f30f7ef329ea6ec630ceabe5a539fed407b9c81e27e2322644e3efbbd1b2a76"}, + {url = "https://files.pythonhosted.org/packages/ee/e5/6d3a4a6ebc47554b9042c4b934f2d3d0111abbf331c545b016df293036c8/cbor2-5.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bc8c5606aa0ae510bdb3c7d987f92df39ef87d09e0f0588a4d1daffd3cb0453"}, + {url = "https://files.pythonhosted.org/packages/fc/90/47c8068827cca8df95b5ce603f44cff8be525bcf7792b465f5a822892c4a/cbor2-5.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d549abea7115c8a0d7c61a31a895c031f902a7b4c875f9efd8ce41e466baf83a"}, +] +"certifi 2022.9.24" = [ + {url = "https://files.pythonhosted.org/packages/1d/38/fa96a426e0c0e68aabc68e896584b83ad1eec779265a028e156ce509630e/certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, + {url = "https://files.pythonhosted.org/packages/cb/a4/7de7cd59e429bd0ee6521ba58a75adaec136d32f91a761b28a11d8088d44/certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, +] +"cffi 1.15.1" = [ + {url = "https://files.pythonhosted.org/packages/00/05/23a265a3db411b0bfb721bf7a116c7cecaf3eb37ebd48a6ea4dfb0a3244d/cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {url = "https://files.pythonhosted.org/packages/03/7b/259d6e01a6083acef9d3c8c88990c97d313632bb28fa84d6ab2bb201140a/cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {url = "https://files.pythonhosted.org/packages/0e/65/0d7b5dad821ced4dcd43f96a362905a68ce71e6b5f5cfd2fada867840582/cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {url = "https://files.pythonhosted.org/packages/0e/e2/a23af3d81838c577571da4ff01b799b0c2bbde24bd924d97e228febae810/cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {url = "https://files.pythonhosted.org/packages/10/72/617ee266192223a38b67149c830bd9376b69cf3551e1477abc72ff23ef8e/cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {url = "https://files.pythonhosted.org/packages/18/8f/5ff70c7458d61fa8a9752e5ee9c9984c601b0060aae0c619316a1e1f1ee5/cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {url = "https://files.pythonhosted.org/packages/1d/76/bcebbbab689f5f6fc8a91e361038a3001ee2e48c5f9dbad0a3b64a64cc9e/cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {url = "https://files.pythonhosted.org/packages/22/c6/df826563f55f7e9dd9a1d3617866282afa969fe0d57decffa1911f416ed8/cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {url = "https://files.pythonhosted.org/packages/23/8b/2e8c2469eaf89f7273ac685164949a7e644cdfe5daf1c036564208c3d26b/cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {url = "https://files.pythonhosted.org/packages/2b/a8/050ab4f0c3d4c1b8aaa805f70e26e84d0e27004907c5b8ecc1d31815f92a/cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, + {url = "https://files.pythonhosted.org/packages/2d/86/3ca57cddfa0419f6a95d1c8478f8f622ba597e3581fd501bbb915b20eb75/cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {url = "https://files.pythonhosted.org/packages/2e/7a/68c35c151e5b7a12650ecc12fdfb85211aa1da43e9924598451c4a0a3839/cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {url = "https://files.pythonhosted.org/packages/32/2a/63cb8c07d151de92ff9d897b2eb27ba6a0e78dda8e4c5f70d7b8c16cd6a2/cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {url = "https://files.pythonhosted.org/packages/32/bd/d0809593f7976828f06a492716fbcbbfb62798bbf60ea1f65200b8d49901/cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {url = "https://files.pythonhosted.org/packages/37/5a/c37631a86be838bdd84cc0259130942bf7e6e32f70f4cab95f479847fb91/cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {url = "https://files.pythonhosted.org/packages/3a/12/d6066828014b9ccb2bbb8e1d9dc28872d20669b65aeb4a86806a0757813f/cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {url = "https://files.pythonhosted.org/packages/3a/75/a162315adeaf47e94a3b7f886a8e31d77b9e525a387eef2d6f0efc96a7c8/cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {url = "https://files.pythonhosted.org/packages/3f/fa/dfc242febbff049509e5a35a065bdc10f90d8c8585361c2c66b9c2f97a01/cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {url = "https://files.pythonhosted.org/packages/43/a0/cc7370ef72b6ee586369bacd3961089ab3d94ae712febf07a244f1448ffd/cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {url = "https://files.pythonhosted.org/packages/47/51/3049834f07cd89aceef27f9c56f5394ca6725ae6a15cff5fbdb2f06a24ad/cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {url = "https://files.pythonhosted.org/packages/47/97/137f0e3d2304df2060abb872a5830af809d7559a5a4b6a295afb02728e65/cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {url = "https://files.pythonhosted.org/packages/50/34/4cc590ad600869502c9838b4824982c122179089ed6791a8b1c95f0ff55e/cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {url = "https://files.pythonhosted.org/packages/5b/1a/e1ee5bed11d8b6540c05a8e3c32448832d775364d4461dd6497374533401/cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {url = "https://files.pythonhosted.org/packages/5d/4e/4e0bb5579b01fdbfd4388bd1eb9394a989e1336203a4b7f700d887b233c1/cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {url = "https://files.pythonhosted.org/packages/5d/6f/3a2e167113eabd46ed300ff3a6a1e9277a3ad8b020c4c682f83e9326fcf7/cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {url = "https://files.pythonhosted.org/packages/69/bf/335f8d95510b1a26d7c5220164dc739293a71d5540ecd54a2f66bac3ecb8/cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {url = "https://files.pythonhosted.org/packages/71/d7/0fe0d91b0bbf610fb7254bb164fa8931596e660d62e90fb6289b7ee27b09/cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {url = "https://files.pythonhosted.org/packages/77/b7/d3618d612be01e184033eab90006f8ca5b5edafd17bf247439ea4e167d8a/cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {url = "https://files.pythonhosted.org/packages/79/4b/33494eb0adbcd884656c48f6db0c98ad8a5c678fb8fb5ed41ab546b04d8c/cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {url = "https://files.pythonhosted.org/packages/7c/3e/5d823e5bbe00285e479034bcad44177b7353ec9fdcd7795baac5ccf82950/cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {url = "https://files.pythonhosted.org/packages/85/1f/a3c533f8d377da5ca7edb4f580cc3edc1edbebc45fac8bb3ae60f1176629/cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {url = "https://files.pythonhosted.org/packages/87/4b/64e8bd9d15d6b22b6cb11997094fbe61edf453ea0a97c8675cb7d1c3f06f/cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {url = "https://files.pythonhosted.org/packages/87/ee/ddc23981fc0f5e7b5356e98884226bcb899f95ebaefc3e8e8b8742dd7e22/cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {url = "https://files.pythonhosted.org/packages/88/89/c34caf63029fb7628ec2ebd5c88ae0c9bd17db98c812e4065a4d020ca41f/cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {url = "https://files.pythonhosted.org/packages/91/bc/b7723c2fe7a22eee71d7edf2102cd43423d5f95ff3932ebaa2f82c7ec8d0/cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {url = "https://files.pythonhosted.org/packages/93/d0/2e2b27ea2f69b0ec9e481647822f8f77f5fc23faca2dd00d1ff009940eb7/cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {url = "https://files.pythonhosted.org/packages/9f/52/1e2b43cfdd7d9a39f48bc89fcaee8d8685b1295e205a4f1044909ac14d89/cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {url = "https://files.pythonhosted.org/packages/a4/42/54bdf22cf6c8f95113af645d0bd7be7f9358ea5c2d57d634bb11c6b4d0b2/cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {url = "https://files.pythonhosted.org/packages/a8/16/06b84a7063a4c0a2b081030fdd976022086da9c14e80a9ed4ba0183a98a9/cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {url = "https://files.pythonhosted.org/packages/a9/ba/e082df21ebaa9cb29f2c4e1d7e49a29b90fcd667d43632c6674a16d65382/cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {url = "https://files.pythonhosted.org/packages/aa/02/ab15b3aa572759df752491d5fa0f74128cd14e002e8e3257c1ab1587810b/cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {url = "https://files.pythonhosted.org/packages/ad/26/7b3a73ab7d82a64664c7c4ea470e4ec4a3c73bb4f02575c543a41e272de5/cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {url = "https://files.pythonhosted.org/packages/af/cb/53b7bba75a18372d57113ba934b27d0734206c283c1dfcc172347fbd9f76/cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {url = "https://files.pythonhosted.org/packages/af/da/9441d56d7dd19d07dcc40a2a5031a1f51c82a27cee3705edf53dadcac398/cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {url = "https://files.pythonhosted.org/packages/b3/b8/89509b6357ded0cbacc4e430b21a4ea2c82c2cdeb4391c148b7c7b213bed/cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {url = "https://files.pythonhosted.org/packages/b5/7d/df6c088ef30e78a78b0c9cca6b904d5abb698afb5bc8f5191d529d83d667/cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {url = "https://files.pythonhosted.org/packages/b5/80/ce5ba093c2475a73df530f643a61e2969a53366e372b24a32f08cd10172b/cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {url = "https://files.pythonhosted.org/packages/b7/8b/06f30caa03b5b3ac006de4f93478dbd0239e2a16566d81a106c322dc4f79/cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {url = "https://files.pythonhosted.org/packages/b9/4a/dde4d093a3084d0b0eadfb2703f71e31a5ced101a42c839ac5bbbd1710f2/cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {url = "https://files.pythonhosted.org/packages/c1/25/16a082701378170559bb1d0e9ef2d293cece8dc62913d79351beb34c5ddf/cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {url = "https://files.pythonhosted.org/packages/c2/0b/3b09a755ddb977c167e6d209a7536f6ade43bb0654bad42e08df1406b8e4/cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {url = "https://files.pythonhosted.org/packages/c5/ff/3f9d73d480567a609e98beb0c64359f8e4f31cb6a407685da73e5347b067/cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {url = "https://files.pythonhosted.org/packages/c6/3d/dd085bb831b22ce4d0b7ba8550e6d78960f02f770bbd1314fea3580727f8/cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {url = "https://files.pythonhosted.org/packages/c9/e3/0a52838832408cfbbf3a59cb19bcd17e64eb33795c9710ca7d29ae10b5b7/cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {url = "https://files.pythonhosted.org/packages/d3/56/3e94aa719ae96eeda8b68b3ec6e347e0a23168c6841dc276ccdcdadc9f32/cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {url = "https://files.pythonhosted.org/packages/d3/e1/e55ca2e0dd446caa2cc8f73c2b98879c04a1f4064ac529e1836683ca58b8/cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {url = "https://files.pythonhosted.org/packages/da/ff/ab939e2c7b3f40d851c0f7192c876f1910f3442080c9c846532993ec3cef/cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {url = "https://files.pythonhosted.org/packages/df/02/aef53d4aa43154b829e9707c8c60bab413cd21819c4a36b0d7aaa83e2a61/cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {url = "https://files.pythonhosted.org/packages/e8/ff/c4b7a358526f231efa46a375c959506c87622fb4a2c5726e827c55e6adf2/cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {url = "https://files.pythonhosted.org/packages/ea/be/c4ad40ad441ac847b67c7a37284ae3c58f39f3e638c6b0f85fb662233825/cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {url = "https://files.pythonhosted.org/packages/ed/a3/c5f01988ddb70a187c3e6112152e01696188c9f8a4fa4c68aa330adbb179/cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {url = "https://files.pythonhosted.org/packages/ef/41/19da352d341963d29a33bdb28433ba94c05672fb16155f794fad3fd907b0/cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {url = "https://files.pythonhosted.org/packages/f9/96/fc9e118c47b7adc45a0676f413b4a47554e5f3b6c99b8607ec9726466ef1/cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {url = "https://files.pythonhosted.org/packages/ff/fe/ac46ca7b00e9e4f9c62e7928a11bc9227c86e2ff43526beee00cdfb4f0e8/cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {url = "https://test-files.pythonhosted.org/packages/de/b0/ee80a110b7efabc2e7a952973c74c1bbffd1e4eda707f9e932fcd53fdca4/cffi-1.15.1-cp311-cp311-win_arm64.whl", hash = "sha256:e2fb0d88adc12260498e8c291b20d4ecf8f74dea78c5cd3d5880c1e68158345f"}, +] +"charset-normalizer 2.1.1" = [ + {url = "https://files.pythonhosted.org/packages/a1/34/44964211e5410b051e4b8d2869c470ae8a68ae274953b1c7de6d98bbcf94/charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {url = "https://files.pythonhosted.org/packages/db/51/a507c856293ab05cdc1db77ff4bc1268ddd39f29e7dc4919aa497f0adbec/charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, +] +"click 8.1.3" = [ + {url = "https://files.pythonhosted.org/packages/59/87/84326af34517fca8c58418d148f2403df25303e02736832403587318e9e8/click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {url = "https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {url = "https://test-files.pythonhosted.org/packages/59/87/84326af34517fca8c58418d148f2403df25303e02736832403587318e9e8/click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {url = "https://test-files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, +] +"coincurve 17.0.0" = [ + {url = "https://files.pythonhosted.org/packages/02/73/3e04569c0cb7478383710d1f7612e8da4d79eac1aa18522ac44f7dae5ec6/coincurve-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6aec70238dbe7a5d66b5f9438ff45b08eb5e0990d49c32ebb65247c5d5b89d7a"}, + {url = "https://files.pythonhosted.org/packages/08/63/f2510bae1af0ce4bf6f6272bbe27fd20876695362b20d5df114d9295f7c5/coincurve-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1bef812da1da202cdd601a256825abcf26d86e8634fac3ec3e615e3bb3ff08c"}, + {url = "https://files.pythonhosted.org/packages/28/cf/eccd05c927792538bfe3e3357c9ad07584d288a3984ff55527e268b50a64/coincurve-17.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f47806527d3184da3e8b146fac92a8ed567bbd225194f4517943d8cdc85f9542"}, + {url = "https://files.pythonhosted.org/packages/29/06/b84a8ea38c24f168b8674fda3f87e99478692df52856dd43b692a57ad740/coincurve-17.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0503326963916c85b61d16f611ea0545f03c9e418fa8007c233c815429e381e8"}, + {url = "https://files.pythonhosted.org/packages/29/94/293294bd71a0370a6dae079773db9cd2aed825ff1df975cb865158a1f0ec/coincurve-17.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1013c1597b65684ae1c3e42497f9ef5a04527fa6136a84a16b34602606428c74"}, + {url = "https://files.pythonhosted.org/packages/34/8e/5b705213841580f5f1378f7e9a8766b2d698037bc84892db8d1a41c87deb/coincurve-17.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74cedb3d3a1dc5abe0c9c2396e1b82cc64496babc5b42e007e72e185cb1edad8"}, + {url = "https://files.pythonhosted.org/packages/3f/01/970679d7c3c152a6710b550298d4d09e415c9f5cfdbcc7fb95b282bed5fa/coincurve-17.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c71caffb97dd3d0c243beb62352669b1e5dafa3a4bccdbb27d36bd82f5e65d20"}, + {url = "https://files.pythonhosted.org/packages/63/e9/31d148c8333d43cd5f0d406ddd5baf43be4dac69fcf96590150e80f649da/coincurve-17.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b88642edf7f281649b0c0b6ffade051945ccceae4b885e40445634877d0b3049"}, + {url = "https://files.pythonhosted.org/packages/65/5f/2c2b9d86f62b3453104e63aa2aaed987a58762b8101e0d878f6bd7880995/coincurve-17.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac8c87d6fd080faa74e7ecf64a6ed20c11a254863238759eb02c3f13ad12b0c4"}, + {url = "https://files.pythonhosted.org/packages/65/e9/e64d29c3f1ec9fe0fe4a114b95556e23d63bde0d48ec80e5ba22a3531a1f/coincurve-17.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:db874c5c1dcb1f3a19379773b5e8cffc777625a7a7a60dd9a67206e31e62e2e9"}, + {url = "https://files.pythonhosted.org/packages/66/32/8c15a00d4452f65e01b18cf543d49897882b4a8a48e7f65470ce152a2696/coincurve-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:896b01941254f0a218cf331a9bddfe2d43892f7f1ba10d6e372e2eb744a744c2"}, + {url = "https://files.pythonhosted.org/packages/66/e3/43c2766e850ad6dad13bf4b2005f22007f33f300064fe444f0f832381d7a/coincurve-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8852dc01af4f0fe941ffd04069f7e4fecdce9b867a016f823a02286a1a1f07b5"}, + {url = "https://files.pythonhosted.org/packages/69/1c/d6f976de439a1e98faea23a6dee96cfaf47ea01c94b87335efbaa2260fb8/coincurve-17.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e2c2e8a1f0b1f8e48049c891af4ae3cad65d115d358bde72f6b8abdbb8a23170"}, + {url = "https://files.pythonhosted.org/packages/83/a3/98a8e1cbad2b12f869966ce14f31c722dbf116b20dea6f3f8139b1b655d1/coincurve-17.0.0-py3-none-win_amd64.whl", hash = "sha256:630388080da3026e0b0176cc6762eaabecba857ee3fc85767577dea063ea7c6e"}, + {url = "https://files.pythonhosted.org/packages/88/4d/16a487d4fc1d854b7266d208af2507e4babd07f52d5ede1a7b4c07a233ae/coincurve-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:25dfa105beba24c8de886f8ed654bb1133866e4e22cfd7ea5ad8438cae6ed924"}, + {url = "https://files.pythonhosted.org/packages/89/16/6bf9aa224615cafedc319cf5ed355d3a984144bbc82a9e7d1d19da97afd8/coincurve-17.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4beef321fd6434448aab03a0c245f31c4e77f43b54b82108c0948d29852ac7e"}, + {url = "https://files.pythonhosted.org/packages/8a/24/4b218c110341cadb728efed4e0ea40450dd7e1cc080b574673ac96f5095e/coincurve-17.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ea057f777842396d387103c606babeb3a1b4c6126769cc0a12044312fc6c465"}, + {url = "https://files.pythonhosted.org/packages/8d/0b/317a2d20a29cd296a5d19abb6f642d067b2a596695781a8051a01c8ed070/coincurve-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:73f39579dd651a9fc29da5a8fc0d8153d872bcbc166f876457baced1a1c01501"}, + {url = "https://files.pythonhosted.org/packages/94/6a/60401d0723aff65138e6eba7be09f63998ac96bfb64509c4aa020541abd6/coincurve-17.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1ef72574aa423bc33665ef4be859164a478bad24d48442da874ef3dc39a474d"}, + {url = "https://files.pythonhosted.org/packages/95/79/646c5fc4fc204c11a2129ab8ef2fdda9e64ce2f4ce04b79a274899ae5d84/coincurve-17.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d154e2eb5711db8c5ef52fcd80935b5a0e751c057bc6ffb215a7bb409aedef03"}, + {url = "https://files.pythonhosted.org/packages/98/9f/da66d4a851925110dc03371db036551074a039700c089ce5f94af3ac7842/coincurve-17.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:747215254e51dd4dfbe6dded9235491263da5d88fe372d66541ca16b51ea078f"}, + {url = "https://files.pythonhosted.org/packages/9e/f2/482210b2330fb453fd84b7309b9b79e49e6f17dd0efa70ff4d427156f144/coincurve-17.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad2f6df39ba1e2b7b14bb984505ffa7d0a0ecdd697e8d7dbd19e04bc245c87ed"}, + {url = "https://files.pythonhosted.org/packages/a8/8d/74eb01cbf256ecb10140bf67e1a2e0f5ac1751fbc1347bc7e52f399f2b89/coincurve-17.0.0.tar.gz", hash = "sha256:68da55aff898702952fda3ee04fd6ed60bb6b91f919c69270786ed766b548b93"}, + {url = "https://files.pythonhosted.org/packages/b8/1c/558879c99e3abdea2a870cac35601fe8957b6006c8ff76851968326c5f61/coincurve-17.0.0-py3-none-win32.whl", hash = "sha256:b956b0b2c85e25a7d00099970ff5d8338254b45e46f0a940f4a2379438ce0dde"}, + {url = "https://files.pythonhosted.org/packages/b9/e5/8f7b2d8e9d777b42e16eca3e8ab308953cf4539eb5e04e7bb1200fe752e4/coincurve-17.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d694ad194bee9e8792e2e75879dc5238d8a184010cde36c5ad518fcfe2cd8f2"}, + {url = "https://files.pythonhosted.org/packages/c9/10/f0d5eaa344fd147bda5e80d254807a5fc500c1758034140199d2858392f6/coincurve-17.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abbefc9ccb170cb255a31df32457c2e43084b9f37589d0694dacc2dea6ddaf7c"}, + {url = "https://files.pythonhosted.org/packages/d5/8b/fad420c85fb6772485dbc63388a6593d56b0a6d4c24448bffc9d92ce4490/coincurve-17.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:abbd9d017a7638dc38a3b9bb4851f8801b7818d4e5ac22e0c75e373b3c1dbff0"}, + {url = "https://files.pythonhosted.org/packages/d7/04/8e69159edcce60a7a60df2afbc85300158a5d756061ebdfede15801efe6b/coincurve-17.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfd4fab857bcd975edc39111cb5f5c104f138dac2e9ace35ea8434d37bcea3be"}, + {url = "https://files.pythonhosted.org/packages/db/83/6c42d6bf051034c5b4a01b8e5d4d8aa905af7340fec864f69e38338554fe/coincurve-17.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8c571445b166c714af4f8155e38a894376c16c0431e88963f2fff474a9985d87"}, + {url = "https://files.pythonhosted.org/packages/e0/f6/7502bdc8b3768441e8769eea5959d3ff586dc5baa7b9840986a6702e5e07/coincurve-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24284d17162569df917a640f19d9654ba3b43cf560ced8864f270da903f73a5"}, + {url = "https://files.pythonhosted.org/packages/e7/5b/d30131984b8b048a2f778384b109471cbe5e3f0ebf2b3bcc289269600c88/coincurve-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:698efdd53e4fe1bbebaee9b75cbc851be617974c1c60098e9145cb7198ae97fb"}, + {url = "https://files.pythonhosted.org/packages/f2/69/b0e6e1cee2f14f7bd1e1af640a3e2a9649aab19de124dfd8422c9218358f/coincurve-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30dd44d1039f1d237aaa2da6d14a455ca88df3bcb00610b41f3253fdca1be97b"}, + {url = "https://files.pythonhosted.org/packages/f2/c1/dda5434308b3940dfdb84bc24351cf34f2aa846b2a7aaaefdaad528283d5/coincurve-17.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51e56373ac79f4ec1cfc5da53d72c55f5e5ac28d848b0849ef5e687ace857888"}, + {url = "https://files.pythonhosted.org/packages/fc/bd/9d0ef98d759005bab56212d9ff21b84dda6f49b9b43a08f0e0416d032410/coincurve-17.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a80a207131813b038351c5bdae8f20f5f774bbf53622081f208d040dd2b7528f"}, +] +"colorama 0.4.6" = [ + {url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, + {url = "https://test-files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {url = "https://test-files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +"construct 2.10.68" = [ + {url = "https://files.pythonhosted.org/packages/e0/b7/a4a032e94bcfdff481f2e6fecd472794d9da09f474a2185ed33b2c7cad64/construct-2.10.68.tar.gz", hash = "sha256:7b2a3fd8e5f597a5aa1d614c3bd516fa065db01704c72a1efaaeec6ef23d8b45"}, +] +"coverage 6.5.0" = [ + {url = "https://files.pythonhosted.org/packages/02/7a/a45f3958442d50b9a930a62f0dba9ee502521213ebd016203c2890ea212f/coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {url = "https://files.pythonhosted.org/packages/05/63/a789b462075395d34f8152229dccf92b25ca73eac05b3f6cd75fa5017095/coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {url = "https://files.pythonhosted.org/packages/06/f1/5177428c35f331f118e964f727f79e3a3073a10271a644c8361d3cea8bfd/coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {url = "https://files.pythonhosted.org/packages/07/82/79fa21ceca9a9b091eb3c67e27eb648dade27b2c9e1eb23af47232a2a365/coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {url = "https://files.pythonhosted.org/packages/0d/ef/8735875a8dc09e1c4e484a5436c8b4148731b70daccc6f203c50b05e7505/coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {url = "https://files.pythonhosted.org/packages/10/9e/68e384940179713640743a010ac7f7c813d1087c8730a9c0bdfa73bdffd7/coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {url = "https://files.pythonhosted.org/packages/11/9e/7afba355bdabc550b3b2669e3432e71aec87d79400372d7686c09aab0acf/coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {url = "https://files.pythonhosted.org/packages/13/f3/c6025ba30f2ce21d20d5332c3819880fe8afdfc008c2e2f9c075c7b67543/coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {url = "https://files.pythonhosted.org/packages/15/b0/3639d84ee8a900da0cf6450ab46e22517e4688b6cec0ba8ab6f8166103a2/coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {url = "https://files.pythonhosted.org/packages/18/95/27f80dcd8273171b781a19d109aeaed7f13d78ef6d1e2f7134a5826fd1b4/coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {url = "https://files.pythonhosted.org/packages/2f/8b/ca3fe3cfbd66d63181f6e6a06b8b494bb327ba8222d2fa628b392b9ad08a/coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {url = "https://files.pythonhosted.org/packages/32/40/e2b1ffa42028365e3465d1340e7d390d096fc992dec2c80e4afed6361e83/coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {url = "https://files.pythonhosted.org/packages/36/f3/5cbd79cf4cd059c80b59104aca33b8d05af4ad5bf5b1547645ecee716378/coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {url = "https://files.pythonhosted.org/packages/3c/7d/d5211ea782b193ab8064b06dc0cc042cf1a4ca9c93a530071459172c550f/coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {url = "https://files.pythonhosted.org/packages/40/3b/cd68cb278c4966df00158811ec1e357b9a7d132790c240fc65da57e10013/coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {url = "https://files.pythonhosted.org/packages/4b/66/6e588f5dfc93ccedd06d6785c8143f17bb92b89247d50128d8789e9588d0/coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {url = "https://files.pythonhosted.org/packages/50/cf/455930004231fa87efe8be06d13512f34e070ddfee8b8bf5a050cdc47ab3/coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {url = "https://files.pythonhosted.org/packages/58/2c/213861cec1d9f6451d29c0b1838769b558f6a8c6844b001f6e98c37c4b1b/coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {url = "https://files.pythonhosted.org/packages/5c/66/38d1870cb7cf62da49add1d6803fdbcdef632b2808b5c80bcac35b7634d8/coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, + {url = "https://files.pythonhosted.org/packages/61/a6/af54588e2091693026df94b09106ee10dcbcdc8c9b2c3989149e6e44a324/coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {url = "https://files.pythonhosted.org/packages/63/e9/f23e8664ec4032d7802a1cf920853196bcbdce7b56408e3efe1b2da08f3c/coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {url = "https://files.pythonhosted.org/packages/64/7f/13f5d58f5ca41182d7020af5257c8fd08ddf33921d2a28cf66753571c278/coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {url = "https://files.pythonhosted.org/packages/6a/63/8e82513b7e4a1b8d887b4e85c1c2b6c9b754a581b187c0b084f3330ac479/coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {url = "https://files.pythonhosted.org/packages/6b/ba/ef67c1e859b8ddd8cafb81199986ff702efcd4ee5d373670a0bc0a293d1f/coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {url = "https://files.pythonhosted.org/packages/6b/f2/919f0fdc93d3991ca074894402074d847be8ac1e1d78e7e9e1c371b69a6f/coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {url = "https://files.pythonhosted.org/packages/6e/e6/b31a4b2aa9489da59b35ee0ea4259d6fe9b321a1eaa6492f19342d03d53b/coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {url = "https://files.pythonhosted.org/packages/76/44/78c1936c2bd9e7705f170d5e413ed34d9d6d7d0324757786627f88df1514/coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {url = "https://files.pythonhosted.org/packages/78/98/253ce0cfcc3b352d3072940940ed44a035614f2abe781477f77038d21d9f/coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {url = "https://files.pythonhosted.org/packages/85/03/9dcc8b7e269cfeaf5519d433d841a7d78f283c5fb016385d4690e1aedfc1/coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {url = "https://files.pythonhosted.org/packages/89/58/5ec19b43a6511288511f64fc4763d95af8403f5926e7e4556e6b29b03a26/coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {url = "https://files.pythonhosted.org/packages/89/a2/cbf599e50bb4be416e0408c4cf523c354c51d7da39935461a9687e039481/coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {url = "https://files.pythonhosted.org/packages/8f/17/e1d54e0e5a1e82dea1b1d9463dfe347ded58037beda00d326f943a9ef2d4/coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {url = "https://files.pythonhosted.org/packages/a1/6b/7efeeffc7559150a705931b2144b936042c561e63ef248f0e0d9f4523d74/coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {url = "https://files.pythonhosted.org/packages/a3/a0/4c59586df0511b18f7b59593672a4baadacef8f393024052d59c6222477c/coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {url = "https://files.pythonhosted.org/packages/a8/d9/b367c52cb1297414ba967e38fe9b5338ee4700a2d1592fc78532dc9f882f/coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {url = "https://files.pythonhosted.org/packages/ac/bc/c9d4fd6b3494d2cc1e26f4b98eb19206b92a59094617ad02d5689ac9d3c4/coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {url = "https://files.pythonhosted.org/packages/ae/a3/f45cb5d32de0751863945d22083c15eb8854bb53681b2e792f2066c629b9/coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {url = "https://files.pythonhosted.org/packages/b6/08/a88a9f3a11bb2d97c7a6719535a984b009728433838fbc65766488867c80/coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {url = "https://files.pythonhosted.org/packages/bd/a0/e263b115808226fdb2658f1887808c06ac3f1b579ef5dda02309e0d54459/coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {url = "https://files.pythonhosted.org/packages/c0/18/2a0a9b3c29376ce04ceb7ca2948559dad76409a2c9b3f664756581101e16/coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {url = "https://files.pythonhosted.org/packages/c4/8d/5ec7d08f4601d2d792563fe31db5e9322c306848fec1e65ec8885927f739/coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {url = "https://files.pythonhosted.org/packages/c8/e8/e712b61abf1282ce3ac9826473ab4b245a4319303cce2e4115a8de1435f2/coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {url = "https://files.pythonhosted.org/packages/cd/48/65d314e702b4a7095ea96da0a319a5a377e594354a4a6badde483832bb5a/coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {url = "https://files.pythonhosted.org/packages/d6/00/3e12af83af2a46c1fd27b78486f404736934d0288bda4975119611a01cb3/coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {url = "https://files.pythonhosted.org/packages/d6/0f/012a7370aaf61123a222b34b657dedc63e03ce2af8d064ac5c5afe14f29c/coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {url = "https://files.pythonhosted.org/packages/e5/fb/11982f5faf2990d4d9159e01a12bbf0a7d7873893d4d2e2acec012ad69ae/coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {url = "https://files.pythonhosted.org/packages/e6/24/7fe8ededb4060dd8c3f1d86cb624fcb3452f66fbef5051ed7fab126c5c0c/coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {url = "https://files.pythonhosted.org/packages/e9/f0/3be949bd129237553714149b1909d34c94137ca4b86e036bc7060431da18/coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {url = "https://files.pythonhosted.org/packages/ea/52/c08080405329326a7ff16c0dfdb4feefaa8edd7446413df67386fe1bbfe0/coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {url = "https://files.pythonhosted.org/packages/ff/27/339089b558672f04e62d0cd2d49b9280270bad3bc95de24e7eb03deb4638/coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {url = "https://test-files.pythonhosted.org/packages/02/7a/a45f3958442d50b9a930a62f0dba9ee502521213ebd016203c2890ea212f/coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {url = "https://test-files.pythonhosted.org/packages/05/63/a789b462075395d34f8152229dccf92b25ca73eac05b3f6cd75fa5017095/coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {url = "https://test-files.pythonhosted.org/packages/06/f1/5177428c35f331f118e964f727f79e3a3073a10271a644c8361d3cea8bfd/coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {url = "https://test-files.pythonhosted.org/packages/07/82/79fa21ceca9a9b091eb3c67e27eb648dade27b2c9e1eb23af47232a2a365/coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {url = "https://test-files.pythonhosted.org/packages/0d/ef/8735875a8dc09e1c4e484a5436c8b4148731b70daccc6f203c50b05e7505/coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {url = "https://test-files.pythonhosted.org/packages/10/9e/68e384940179713640743a010ac7f7c813d1087c8730a9c0bdfa73bdffd7/coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {url = "https://test-files.pythonhosted.org/packages/11/9e/7afba355bdabc550b3b2669e3432e71aec87d79400372d7686c09aab0acf/coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {url = "https://test-files.pythonhosted.org/packages/13/f3/c6025ba30f2ce21d20d5332c3819880fe8afdfc008c2e2f9c075c7b67543/coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {url = "https://test-files.pythonhosted.org/packages/15/b0/3639d84ee8a900da0cf6450ab46e22517e4688b6cec0ba8ab6f8166103a2/coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {url = "https://test-files.pythonhosted.org/packages/18/95/27f80dcd8273171b781a19d109aeaed7f13d78ef6d1e2f7134a5826fd1b4/coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {url = "https://test-files.pythonhosted.org/packages/2f/8b/ca3fe3cfbd66d63181f6e6a06b8b494bb327ba8222d2fa628b392b9ad08a/coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {url = "https://test-files.pythonhosted.org/packages/32/40/e2b1ffa42028365e3465d1340e7d390d096fc992dec2c80e4afed6361e83/coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {url = "https://test-files.pythonhosted.org/packages/36/f3/5cbd79cf4cd059c80b59104aca33b8d05af4ad5bf5b1547645ecee716378/coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {url = "https://test-files.pythonhosted.org/packages/3c/7d/d5211ea782b193ab8064b06dc0cc042cf1a4ca9c93a530071459172c550f/coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {url = "https://test-files.pythonhosted.org/packages/40/3b/cd68cb278c4966df00158811ec1e357b9a7d132790c240fc65da57e10013/coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {url = "https://test-files.pythonhosted.org/packages/4b/66/6e588f5dfc93ccedd06d6785c8143f17bb92b89247d50128d8789e9588d0/coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {url = "https://test-files.pythonhosted.org/packages/50/cf/455930004231fa87efe8be06d13512f34e070ddfee8b8bf5a050cdc47ab3/coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {url = "https://test-files.pythonhosted.org/packages/58/2c/213861cec1d9f6451d29c0b1838769b558f6a8c6844b001f6e98c37c4b1b/coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {url = "https://test-files.pythonhosted.org/packages/5c/66/38d1870cb7cf62da49add1d6803fdbcdef632b2808b5c80bcac35b7634d8/coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, + {url = "https://test-files.pythonhosted.org/packages/61/a6/af54588e2091693026df94b09106ee10dcbcdc8c9b2c3989149e6e44a324/coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {url = "https://test-files.pythonhosted.org/packages/63/e9/f23e8664ec4032d7802a1cf920853196bcbdce7b56408e3efe1b2da08f3c/coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {url = "https://test-files.pythonhosted.org/packages/64/7f/13f5d58f5ca41182d7020af5257c8fd08ddf33921d2a28cf66753571c278/coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {url = "https://test-files.pythonhosted.org/packages/6a/63/8e82513b7e4a1b8d887b4e85c1c2b6c9b754a581b187c0b084f3330ac479/coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {url = "https://test-files.pythonhosted.org/packages/6b/ba/ef67c1e859b8ddd8cafb81199986ff702efcd4ee5d373670a0bc0a293d1f/coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {url = "https://test-files.pythonhosted.org/packages/6b/f2/919f0fdc93d3991ca074894402074d847be8ac1e1d78e7e9e1c371b69a6f/coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {url = "https://test-files.pythonhosted.org/packages/6e/e6/b31a4b2aa9489da59b35ee0ea4259d6fe9b321a1eaa6492f19342d03d53b/coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {url = "https://test-files.pythonhosted.org/packages/76/44/78c1936c2bd9e7705f170d5e413ed34d9d6d7d0324757786627f88df1514/coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {url = "https://test-files.pythonhosted.org/packages/78/98/253ce0cfcc3b352d3072940940ed44a035614f2abe781477f77038d21d9f/coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {url = "https://test-files.pythonhosted.org/packages/85/03/9dcc8b7e269cfeaf5519d433d841a7d78f283c5fb016385d4690e1aedfc1/coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {url = "https://test-files.pythonhosted.org/packages/89/58/5ec19b43a6511288511f64fc4763d95af8403f5926e7e4556e6b29b03a26/coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {url = "https://test-files.pythonhosted.org/packages/89/a2/cbf599e50bb4be416e0408c4cf523c354c51d7da39935461a9687e039481/coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {url = "https://test-files.pythonhosted.org/packages/8f/17/e1d54e0e5a1e82dea1b1d9463dfe347ded58037beda00d326f943a9ef2d4/coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {url = "https://test-files.pythonhosted.org/packages/a1/6b/7efeeffc7559150a705931b2144b936042c561e63ef248f0e0d9f4523d74/coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {url = "https://test-files.pythonhosted.org/packages/a3/a0/4c59586df0511b18f7b59593672a4baadacef8f393024052d59c6222477c/coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {url = "https://test-files.pythonhosted.org/packages/a8/d9/b367c52cb1297414ba967e38fe9b5338ee4700a2d1592fc78532dc9f882f/coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {url = "https://test-files.pythonhosted.org/packages/ac/bc/c9d4fd6b3494d2cc1e26f4b98eb19206b92a59094617ad02d5689ac9d3c4/coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {url = "https://test-files.pythonhosted.org/packages/ae/a3/f45cb5d32de0751863945d22083c15eb8854bb53681b2e792f2066c629b9/coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {url = "https://test-files.pythonhosted.org/packages/b6/08/a88a9f3a11bb2d97c7a6719535a984b009728433838fbc65766488867c80/coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {url = "https://test-files.pythonhosted.org/packages/bd/a0/e263b115808226fdb2658f1887808c06ac3f1b579ef5dda02309e0d54459/coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {url = "https://test-files.pythonhosted.org/packages/c0/18/2a0a9b3c29376ce04ceb7ca2948559dad76409a2c9b3f664756581101e16/coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {url = "https://test-files.pythonhosted.org/packages/c4/8d/5ec7d08f4601d2d792563fe31db5e9322c306848fec1e65ec8885927f739/coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {url = "https://test-files.pythonhosted.org/packages/c8/e8/e712b61abf1282ce3ac9826473ab4b245a4319303cce2e4115a8de1435f2/coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {url = "https://test-files.pythonhosted.org/packages/cd/48/65d314e702b4a7095ea96da0a319a5a377e594354a4a6badde483832bb5a/coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {url = "https://test-files.pythonhosted.org/packages/d6/00/3e12af83af2a46c1fd27b78486f404736934d0288bda4975119611a01cb3/coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {url = "https://test-files.pythonhosted.org/packages/d6/0f/012a7370aaf61123a222b34b657dedc63e03ce2af8d064ac5c5afe14f29c/coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {url = "https://test-files.pythonhosted.org/packages/e5/fb/11982f5faf2990d4d9159e01a12bbf0a7d7873893d4d2e2acec012ad69ae/coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {url = "https://test-files.pythonhosted.org/packages/e6/24/7fe8ededb4060dd8c3f1d86cb624fcb3452f66fbef5051ed7fab126c5c0c/coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {url = "https://test-files.pythonhosted.org/packages/e9/f0/3be949bd129237553714149b1909d34c94137ca4b86e036bc7060431da18/coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {url = "https://test-files.pythonhosted.org/packages/ea/52/c08080405329326a7ff16c0dfdb4feefaa8edd7446413df67386fe1bbfe0/coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {url = "https://test-files.pythonhosted.org/packages/ff/27/339089b558672f04e62d0cd2d49b9280270bad3bc95de24e7eb03deb4638/coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, +] +"crcmod 1.7" = [ + {url = "https://files.pythonhosted.org/packages/6b/b0/e595ce2a2527e169c3bcd6c33d2473c1918e0b7f6826a043ca1245dd4e5b/crcmod-1.7.tar.gz", hash = "sha256:dc7051a0db5f2bd48665a990d3ec1cc305a466a77358ca4492826f41f283601e"}, +] +"ecdsa 0.18.0" = [ + {url = "https://files.pythonhosted.org/packages/09/d4/4f05f5d16a4863b30ba96c23b23e942da8889abfa1cdbabf2a0df12a4532/ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {url = "https://files.pythonhosted.org/packages/ff/7b/ba6547a76c468a0d22de93e89ae60d9561ec911f59532907e72b0d8bc0f1/ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] +"ed25519-blake2b 1.4" = [ + {url = "https://files.pythonhosted.org/packages/1d/f1/035bafb5b2ed5b379abd4bf26ebad6e9fed73e21756971cefe027bd55ec2/ed25519-blake2b-1.4.tar.gz", hash = "sha256:d1a1cb9032ec307ce95b41c619440fd4d3fcecc18f224035cc7d6dc7a7d8ef40"}, + {url = "https://test-files.pythonhosted.org/packages/1e/6a/7c1ef79662d40e2e031e5b1a155c0d6f0dd6029d3747fc9f3d5b27c49740/ed25519-blake2b-1.4.tar.gz", hash = "sha256:0ef98ed9ebec59f312a447a0693c2d8bcc8a4d011ca5bb7d09104d358669b122"}, +] +"exceptiongroup 1.0.4" = [ + {url = "https://files.pythonhosted.org/packages/ce/2e/9a327cc0d2d674ee2d570ee30119755af772094edba86d721dda94404d1a/exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, + {url = "https://files.pythonhosted.org/packages/fa/e1/4f89a2608978a78876a76e69e82fa1fc5ce3fb346297eed2a51dd3df2dcf/exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, +] +"flask 2.2.2" = [ + {url = "https://files.pythonhosted.org/packages/0f/43/15f4f9ab225b0b25352412e8daa3d0e3d135fcf5e127070c74c3632c8b4c/Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"}, + {url = "https://files.pythonhosted.org/packages/69/b6/53cfa30eed5aa7343daff36622843688ba8c6fe9829bb2b92e193ab1163f/Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b"}, + {url = "https://test-files.pythonhosted.org/packages/0f/43/15f4f9ab225b0b25352412e8daa3d0e3d135fcf5e127070c74c3632c8b4c/Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"}, + {url = "https://test-files.pythonhosted.org/packages/69/b6/53cfa30eed5aa7343daff36622843688ba8c6fe9829bb2b92e193ab1163f/Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b"}, +] +"flask-restful 0.3.9" = [ + {url = "https://files.pythonhosted.org/packages/5c/50/4892719b13abd401f40a69359c3d859d0ea76bf78e66db058d6c06a95b01/Flask-RESTful-0.3.9.tar.gz", hash = "sha256:ccec650b835d48192138c85329ae03735e6ced58e9b2d9c2146d6c84c06fa53e"}, + {url = "https://files.pythonhosted.org/packages/a9/02/7e21a73564fe0d9d1a3a4ff478dfc407815c4e2fa4e5121bcfc646ba5d15/Flask_RESTful-0.3.9-py2.py3-none-any.whl", hash = "sha256:4970c49b6488e46c520b325f54833374dc2b98e211f1b272bd4b0c516232afe2"}, +] +"idna 3.4" = [ + {url = "https://files.pythonhosted.org/packages/8b/e1/43beb3d38dba6cb420cefa297822eac205a277ab43e5ba5d5c46faf96438/idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {url = "https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, +] +"iniconfig 1.1.1" = [ + {url = "https://files.pythonhosted.org/packages/23/a2/97899f6bd0e873fed3a7e67ae8d3a08b21799430fb4da15cfedf10d6e2c2/iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, + {url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {url = "https://test-files.pythonhosted.org/packages/75/f0/6126fa58c2647daefb619a9ea449c53b5b61acd44f6d2693beae754977e0/iniconfig-1.1.1.tar.gz", hash = "sha256:714dfcaf550420126ed7e66bb1cbc893752af388742828f294c21f847d78b88a"}, + {url = "https://test-files.pythonhosted.org/packages/76/83/2373f13f03040a54c77272adac18956b8202a50b60e8221449df00d19e87/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:8cd395a2a89caee27b08d7879c1515fa9d93e0c477a5b38c9893787fc5e51896"}, +] +"itsdangerous 2.1.2" = [ + {url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, + {url = "https://test-files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {url = "https://test-files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] +"jinja2 3.1.2" = [ + {url = "https://files.pythonhosted.org/packages/7a/ff/75c28576a1d900e87eb6335b063fab47a8ef3c8b4d88524c4bf78f670cce/Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {url = "https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {url = "https://test-files.pythonhosted.org/packages/7a/ff/75c28576a1d900e87eb6335b063fab47a8ef3c8b4d88524c4bf78f670cce/Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {url = "https://test-files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, +] +"jsonschema 3.2.0" = [ + {url = "https://files.pythonhosted.org/packages/69/11/a69e2a3c01b324a77d3a7c0570faa372e8448b666300c4117a516f8b1212/jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, + {url = "https://files.pythonhosted.org/packages/c5/8f/51e89ce52a085483359217bc72cdbf6e75ee595d5b1d4b5ade40c7e018b8/jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, +] +"markupsafe 2.1.1" = [ + {url = "https://files.pythonhosted.org/packages/06/7f/d5e46d7464360b6ac39c5b0b604770dba937e3d7cab485d2f3298454717b/MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {url = "https://files.pythonhosted.org/packages/0f/53/b14de4ede9c2bd76d28e7911033b065ac42896f1cfb258d3ff65cf0332d2/MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {url = "https://files.pythonhosted.org/packages/18/a6/913b1d80fe93f7c3aa79206544b98841616c3eaa7790f37bdfb9fc13311e/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {url = "https://files.pythonhosted.org/packages/1b/b3/93411f10caaccc6fc9c53bbdae4f6d26997248ae574e2f0c90e912b67f73/MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {url = "https://files.pythonhosted.org/packages/1d/97/2288fe498044284f39ab8950703e88abbac2abbdf65524d576157af70556/MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, + {url = "https://files.pythonhosted.org/packages/25/c4/a75659da6d6b03d2d8ef296b2a8bc73e8c5b1533ee31569a958a292f0929/MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {url = "https://files.pythonhosted.org/packages/26/03/2c11ba1a8b2327adea3f59f1c9c9ee9c59e86023925f929e63c4f028b10a/MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {url = "https://files.pythonhosted.org/packages/2c/81/91062a81ac8a18f557f12e2618475b53878755c016c9914c8aa207155c4e/MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {url = "https://files.pythonhosted.org/packages/3a/fc/dccc18170917f2cc2a5b77aad97f5f27d992ef0f2b9fb9e334ee71bf5301/MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {url = "https://files.pythonhosted.org/packages/3c/d3/c7ab031b14ae4e83214949acee957a8fcf6a992698edff039ee1e77eb4e1/MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {url = "https://files.pythonhosted.org/packages/3d/4b/15e5b9d40c4b58e97ebcb8ed5845a215fa5b7cf49a7f1cc7908f8db9cf46/MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {url = "https://files.pythonhosted.org/packages/3f/38/f422a81b41bdac48f1d19c45f6e203c04bc45d2c35505873fdecdddee1ec/MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {url = "https://files.pythonhosted.org/packages/48/a9/cf226ea201542a724b113bac70dd0dfb72106da3621120c525c8eafadac2/MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {url = "https://files.pythonhosted.org/packages/5c/1a/ac3a2b2a4ef1196c15dd8a143fc28eddeb6e6871d6d1de64dc44ef7f59b6/MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {url = "https://files.pythonhosted.org/packages/5e/3d/0a7df21deca52e20de81f8a895ac29df68944588c0030be9aa1e6c07877c/MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {url = "https://files.pythonhosted.org/packages/68/b5/b3aafabe7e1f71aa64ffe32fd8c767fd7db1bb304d339d8df6f2fdd2543c/MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {url = "https://files.pythonhosted.org/packages/69/60/08791e4a971ea976f0fd58fb916d76de7c962dc8e26430564258820ac21f/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {url = "https://files.pythonhosted.org/packages/6c/44/cd459988fe29cb82f0482fe6b6c47ec17ae700a500634edd876075d5e1ee/MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {url = "https://files.pythonhosted.org/packages/71/dc/41cbfe0d9aefdf14226dbf4eccfd0079a0e297809a17c5b902c9a7a3cc9a/MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {url = "https://files.pythonhosted.org/packages/73/68/628f6dbbf5088723a2b939d97c0a2e059d0cc654ce92a6fac5c7959edaff/MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {url = "https://files.pythonhosted.org/packages/7f/d7/a0ee1e3a608ca2f80c66c43c699ab063b4b8979c51f8299229b1960f6860/MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {url = "https://files.pythonhosted.org/packages/82/3d/523e40c45dc1f53b35e60c6e8563dec523f7b6c113f823d5e123dd431631/MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {url = "https://files.pythonhosted.org/packages/87/31/ab37f60fde001c02ac115da6f66a2d934d37407f257ad8e15ed69093c7fb/MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {url = "https://files.pythonhosted.org/packages/8c/96/7e608e1a942232cb8c81ca24093e71e07e2bacbeb2dad62a0f82da28ed54/MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {url = "https://files.pythonhosted.org/packages/92/7c/3c33294e506eafa7f1c40dd283089a45652ea0f073fc0ce24419d46bfe4b/MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {url = "https://files.pythonhosted.org/packages/9e/82/2e089c6f34e77c073aa5a67040d368aac0dfb9b8ccbb46d381452c26fc33/MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {url = "https://files.pythonhosted.org/packages/9f/83/b221ce5a0224f409b9f02b0dc6cb0b921c46033f4870d64fa3e8a96af701/MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {url = "https://files.pythonhosted.org/packages/a3/47/9dcc08eff8ab94f1e50f59f9cd322b710ef5db7e8590fdd8df924406fc9c/MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {url = "https://files.pythonhosted.org/packages/ad/fa/292a72cddad41e3c06227b446a0af53ff642a40755fc5bd695f439c35ba8/MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {url = "https://files.pythonhosted.org/packages/ba/16/3627f852d8a846c0fc52ad1beac6e27894a8344cc2c26036db51acb82c3e/MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {url = "https://files.pythonhosted.org/packages/ba/a9/6291d3fdaf0ecac5fbafe462258c5174f11fd752076ba05c2c022ee64f77/MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {url = "https://files.pythonhosted.org/packages/be/d8/5ab7f07d8f60155c4f12b4b2dca785355b8ee7e16b2d3f00c3830add5f10/MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {url = "https://files.pythonhosted.org/packages/d0/1f/9677deb5b2768ca503e5ed8464a28f47a854d415b9d1b84445efa8363ca6/MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {url = "https://files.pythonhosted.org/packages/d3/4f/9ea1c0a7796f7f81371b40d32aa31766b76fbdba316abf888897042e6e0f/MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {url = "https://files.pythonhosted.org/packages/d9/60/94e9de017674f88a514804e2924bdede9a642aba179d2045214719d6ec76/MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {url = "https://files.pythonhosted.org/packages/df/06/c515c5bc43b90462e753bc768e6798193c6520c9c7eb2054c7466779a9db/MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {url = "https://files.pythonhosted.org/packages/f9/f8/13ffc95bf8a8c98d611b9f9fa5aa88625b9a82fce528e58f1aafba14946b/MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {url = "https://files.pythonhosted.org/packages/fc/e4/78c7607352dd574d524daad079f855757d406d36b919b1864a5a07978390/MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {url = "https://files.pythonhosted.org/packages/fd/f4/524d2e8f5a3727cf309c2b7df7c732038375322df1376c9e9ef3aa92fcaf/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {url = "https://files.pythonhosted.org/packages/ff/3a/42262a3aa6415befee33b275b31afbcef4f7f8d2f4380061b226c692ee2a/MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {url = "https://test-files.pythonhosted.org/packages/06/7f/d5e46d7464360b6ac39c5b0b604770dba937e3d7cab485d2f3298454717b/MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {url = "https://test-files.pythonhosted.org/packages/0f/53/b14de4ede9c2bd76d28e7911033b065ac42896f1cfb258d3ff65cf0332d2/MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {url = "https://test-files.pythonhosted.org/packages/18/a6/913b1d80fe93f7c3aa79206544b98841616c3eaa7790f37bdfb9fc13311e/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {url = "https://test-files.pythonhosted.org/packages/1b/b3/93411f10caaccc6fc9c53bbdae4f6d26997248ae574e2f0c90e912b67f73/MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {url = "https://test-files.pythonhosted.org/packages/1d/97/2288fe498044284f39ab8950703e88abbac2abbdf65524d576157af70556/MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, + {url = "https://test-files.pythonhosted.org/packages/25/c4/a75659da6d6b03d2d8ef296b2a8bc73e8c5b1533ee31569a958a292f0929/MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {url = "https://test-files.pythonhosted.org/packages/26/03/2c11ba1a8b2327adea3f59f1c9c9ee9c59e86023925f929e63c4f028b10a/MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {url = "https://test-files.pythonhosted.org/packages/2c/81/91062a81ac8a18f557f12e2618475b53878755c016c9914c8aa207155c4e/MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {url = "https://test-files.pythonhosted.org/packages/3a/fc/dccc18170917f2cc2a5b77aad97f5f27d992ef0f2b9fb9e334ee71bf5301/MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {url = "https://test-files.pythonhosted.org/packages/3c/d3/c7ab031b14ae4e83214949acee957a8fcf6a992698edff039ee1e77eb4e1/MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {url = "https://test-files.pythonhosted.org/packages/3d/4b/15e5b9d40c4b58e97ebcb8ed5845a215fa5b7cf49a7f1cc7908f8db9cf46/MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {url = "https://test-files.pythonhosted.org/packages/3f/38/f422a81b41bdac48f1d19c45f6e203c04bc45d2c35505873fdecdddee1ec/MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {url = "https://test-files.pythonhosted.org/packages/48/a9/cf226ea201542a724b113bac70dd0dfb72106da3621120c525c8eafadac2/MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {url = "https://test-files.pythonhosted.org/packages/5c/1a/ac3a2b2a4ef1196c15dd8a143fc28eddeb6e6871d6d1de64dc44ef7f59b6/MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {url = "https://test-files.pythonhosted.org/packages/5e/3d/0a7df21deca52e20de81f8a895ac29df68944588c0030be9aa1e6c07877c/MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {url = "https://test-files.pythonhosted.org/packages/68/b5/b3aafabe7e1f71aa64ffe32fd8c767fd7db1bb304d339d8df6f2fdd2543c/MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {url = "https://test-files.pythonhosted.org/packages/69/60/08791e4a971ea976f0fd58fb916d76de7c962dc8e26430564258820ac21f/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {url = "https://test-files.pythonhosted.org/packages/6c/44/cd459988fe29cb82f0482fe6b6c47ec17ae700a500634edd876075d5e1ee/MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {url = "https://test-files.pythonhosted.org/packages/71/dc/41cbfe0d9aefdf14226dbf4eccfd0079a0e297809a17c5b902c9a7a3cc9a/MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {url = "https://test-files.pythonhosted.org/packages/73/68/628f6dbbf5088723a2b939d97c0a2e059d0cc654ce92a6fac5c7959edaff/MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {url = "https://test-files.pythonhosted.org/packages/7f/d7/a0ee1e3a608ca2f80c66c43c699ab063b4b8979c51f8299229b1960f6860/MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {url = "https://test-files.pythonhosted.org/packages/82/3d/523e40c45dc1f53b35e60c6e8563dec523f7b6c113f823d5e123dd431631/MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {url = "https://test-files.pythonhosted.org/packages/87/31/ab37f60fde001c02ac115da6f66a2d934d37407f257ad8e15ed69093c7fb/MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {url = "https://test-files.pythonhosted.org/packages/8c/96/7e608e1a942232cb8c81ca24093e71e07e2bacbeb2dad62a0f82da28ed54/MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {url = "https://test-files.pythonhosted.org/packages/92/7c/3c33294e506eafa7f1c40dd283089a45652ea0f073fc0ce24419d46bfe4b/MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {url = "https://test-files.pythonhosted.org/packages/9e/82/2e089c6f34e77c073aa5a67040d368aac0dfb9b8ccbb46d381452c26fc33/MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {url = "https://test-files.pythonhosted.org/packages/9f/83/b221ce5a0224f409b9f02b0dc6cb0b921c46033f4870d64fa3e8a96af701/MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {url = "https://test-files.pythonhosted.org/packages/a3/47/9dcc08eff8ab94f1e50f59f9cd322b710ef5db7e8590fdd8df924406fc9c/MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {url = "https://test-files.pythonhosted.org/packages/ad/fa/292a72cddad41e3c06227b446a0af53ff642a40755fc5bd695f439c35ba8/MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {url = "https://test-files.pythonhosted.org/packages/ba/16/3627f852d8a846c0fc52ad1beac6e27894a8344cc2c26036db51acb82c3e/MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {url = "https://test-files.pythonhosted.org/packages/ba/a9/6291d3fdaf0ecac5fbafe462258c5174f11fd752076ba05c2c022ee64f77/MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {url = "https://test-files.pythonhosted.org/packages/be/d8/5ab7f07d8f60155c4f12b4b2dca785355b8ee7e16b2d3f00c3830add5f10/MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {url = "https://test-files.pythonhosted.org/packages/d0/1f/9677deb5b2768ca503e5ed8464a28f47a854d415b9d1b84445efa8363ca6/MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {url = "https://test-files.pythonhosted.org/packages/d3/4f/9ea1c0a7796f7f81371b40d32aa31766b76fbdba316abf888897042e6e0f/MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {url = "https://test-files.pythonhosted.org/packages/d9/60/94e9de017674f88a514804e2924bdede9a642aba179d2045214719d6ec76/MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {url = "https://test-files.pythonhosted.org/packages/df/06/c515c5bc43b90462e753bc768e6798193c6520c9c7eb2054c7466779a9db/MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {url = "https://test-files.pythonhosted.org/packages/f9/f8/13ffc95bf8a8c98d611b9f9fa5aa88625b9a82fce528e58f1aafba14946b/MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {url = "https://test-files.pythonhosted.org/packages/fc/e4/78c7607352dd574d524daad079f855757d406d36b919b1864a5a07978390/MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {url = "https://test-files.pythonhosted.org/packages/fd/f4/524d2e8f5a3727cf309c2b7df7c732038375322df1376c9e9ef3aa92fcaf/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {url = "https://test-files.pythonhosted.org/packages/ff/3a/42262a3aa6415befee33b275b31afbcef4f7f8d2f4380061b226c692ee2a/MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, +] +"mnemonic 0.20" = [ + {url = "https://files.pythonhosted.org/packages/ae/95/3e07c33ffb26f5823b45a1c30db8acea44763198c2bd393e07e884f3295f/mnemonic-0.20-py3-none-any.whl", hash = "sha256:acd2168872d0379e7a10873bb3e12bf6c91b35de758135c4fbd1015ef18fafc5"}, + {url = "https://files.pythonhosted.org/packages/f8/8d/d4dc2b2bddfeb57cab4404a41749b577f578f71140ab754da9afa8f5c599/mnemonic-0.20.tar.gz", hash = "sha256:7c6fb5639d779388027a77944680aee4870f0fcd09b1e42a5525ee2ce4c625f6"}, +] +"mypy-extensions 0.4.3" = [ + {url = "https://files.pythonhosted.org/packages/5c/eb/975c7c080f3223a5cdaff09612f3a5221e4ba534f7039db34c35d95fa6a5/mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {url = "https://files.pythonhosted.org/packages/63/60/0582ce2eaced55f65a4406fc97beba256de4b7a95a0034c6576458c6519f/mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +"mypy-protobuf 3.3.0" = [ + {url = "https://files.pythonhosted.org/packages/9d/59/48f1df10f87cf87d0638dd38f54549e98cb43fcf7ce0ab6c159816e85f23/mypy-protobuf-3.3.0.tar.gz", hash = "sha256:24f3b0aecb06656e983f58e07c732a90577b9d7af3e1066fc2b663bbf0370248"}, + {url = "https://files.pythonhosted.org/packages/d3/7b/b00434b3f508eb4bc637b83c2238cacaf1ce3cc5e540a2b76f5f99302446/mypy_protobuf-3.3.0-py3-none-any.whl", hash = "sha256:15604f6943b16c05db646903261e3b3e775cf7f7990b7c37b03d043a907b650d"}, + {url = "https://test-files.pythonhosted.org/packages/9d/59/48f1df10f87cf87d0638dd38f54549e98cb43fcf7ce0ab6c159816e85f23/mypy-protobuf-3.3.0.tar.gz", hash = "sha256:24f3b0aecb06656e983f58e07c732a90577b9d7af3e1066fc2b663bbf0370248"}, + {url = "https://test-files.pythonhosted.org/packages/d3/7b/b00434b3f508eb4bc637b83c2238cacaf1ce3cc5e540a2b76f5f99302446/mypy_protobuf-3.3.0-py3-none-any.whl", hash = "sha256:15604f6943b16c05db646903261e3b3e775cf7f7990b7c37b03d043a907b650d"}, +] +"packaging 21.3" = [ + {url = "https://files.pythonhosted.org/packages/05/8e/8de486cbd03baba4deef4142bd643a3e7bbe954a784dc1bb17142572d127/packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {url = "https://files.pythonhosted.org/packages/df/9e/d1a7217f69310c1db8fdf8ab396229f55a699ce34a203691794c5d1cad0c/packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +"pathspec 0.10.2" = [ + {url = "https://files.pythonhosted.org/packages/42/79/94b21d5fabb97749ca94590315abe150a750483c87add8543781bcb6cd26/pathspec-0.10.2-py3-none-any.whl", hash = "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5"}, + {url = "https://files.pythonhosted.org/packages/a2/29/959c72e1a6c3c25eaa46b9bfcc7fd401f65af83163d4796af09272c83c8a/pathspec-0.10.2.tar.gz", hash = "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0"}, +] +"pillow 9.3.0" = [ + {url = "https://files.pythonhosted.org/packages/00/73/000575ca7b7635ecd4075e71925b71f2648300d725b6c9a1f969fe2d5a87/Pillow-9.3.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9"}, + {url = "https://files.pythonhosted.org/packages/06/2f/c17a2d559009b875f7da804a3a4e0f47baca6df2dfedb29a1df641c51e1a/Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627"}, + {url = "https://files.pythonhosted.org/packages/0d/87/f696d8464c949c5a7ac133c8b7297a4243e0319fd29ae3a90b4ac90f0a5b/Pillow-9.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4"}, + {url = "https://files.pythonhosted.org/packages/15/13/232be66adb8482f0636b3f3e254d9e18d7093bd1848b3610ffaf72717924/Pillow-9.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c"}, + {url = "https://files.pythonhosted.org/packages/16/11/da8d395299ca166aa56d9436e26fe8440e5443471de16ccd9a1d06f5993a/Pillow-9.3.0.tar.gz", hash = "sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f"}, + {url = "https://files.pythonhosted.org/packages/1d/15/054644ecbed79d3af017a56eaabd13bf712b21c3a667b8a67a335174f4b7/Pillow-9.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11"}, + {url = "https://files.pythonhosted.org/packages/23/b3/20f97e1bacd02b89953b50b2502be1b10e01d36a0618fb197e4a055e50f4/Pillow-9.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe"}, + {url = "https://files.pythonhosted.org/packages/26/d6/9355d59a2ee9406e4d5129ff0ff99835e7f6adb6133815fab2099e1879b5/Pillow-9.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2"}, + {url = "https://files.pythonhosted.org/packages/2c/3d/36cd1f6c04ab30ab7e6bc8d17a4337fc29fa5b35ebd4c520d916ce7e39e6/Pillow-9.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4"}, + {url = "https://files.pythonhosted.org/packages/2c/74/109e3d1fd2847c19c556fe4ce9b3f4aac2147b56c7b5d0924ae9586d2a47/Pillow-9.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193"}, + {url = "https://files.pythonhosted.org/packages/2e/c8/03495501bb9beabf34979781b14cc625f39cbd8ff5310a93a85e45987590/Pillow-9.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f"}, + {url = "https://files.pythonhosted.org/packages/2f/73/ec6b3e3f6b311cf1468eafc92a890f690a2cacac0cfd0f1bcc2b891d1334/Pillow-9.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b"}, + {url = "https://files.pythonhosted.org/packages/39/5e/585dfbe0eb4660ea3ee082289625f94e402239282458c4bf78ecc84fbb93/Pillow-9.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502"}, + {url = "https://files.pythonhosted.org/packages/40/57/c8695a77561a83bd39eba30daf4d894b0b910aad55e2b60f0ef1b1b5205c/Pillow-9.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b"}, + {url = "https://files.pythonhosted.org/packages/41/e9/7bae68c360de8d1e7e32aeab252c6dc9336c0d779c9ad5e24ce475d523fd/Pillow-9.3.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228"}, + {url = "https://files.pythonhosted.org/packages/47/b2/6e5fc952713bfee71c5e25e7917b18207b6f445d81746008b14d9c80a91b/Pillow-9.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07"}, + {url = "https://files.pythonhosted.org/packages/48/92/820b43fcb333fe7f4cf726ed3c0cfcfd062f65b8fd23c0e6972a6107f440/Pillow-9.3.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c"}, + {url = "https://files.pythonhosted.org/packages/4b/f9/2fa7d6cce0b9867ec3917878d03d5298d7a88522ed7dadc6cac4642bce8b/Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02"}, + {url = "https://files.pythonhosted.org/packages/4e/8c/84131e85d4ebd600a823fad1707157d55055a7ea80ce6c8c2f6e2de93f2c/Pillow-9.3.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20"}, + {url = "https://files.pythonhosted.org/packages/56/02/2c8fde18b251c6ce2061b23b4fa04f3e71d386c725a09753bad19649e1b1/Pillow-9.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636"}, + {url = "https://files.pythonhosted.org/packages/5b/48/1e4437f90c0531c0a8f5c5b1e793e0f234fe8203019de04001213b4fb38e/Pillow-9.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c"}, + {url = "https://files.pythonhosted.org/packages/5f/1e/8a26e1ff2064b665f3923a727cb9240919cdcdce303e4800729635aefc40/Pillow-9.3.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c"}, + {url = "https://files.pythonhosted.org/packages/5f/71/ad106c6e3a28f7ff81b2e172a05c158181b61de2a6292896355f0e50f46c/Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e"}, + {url = "https://files.pythonhosted.org/packages/6c/a8/3cb2d902c4094b6ad06f069b937c2696fd47c5fc02da107e33470f468534/Pillow-9.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32"}, + {url = "https://files.pythonhosted.org/packages/6d/d2/610687610050eb3ce0ebdd0a3f87d4bb3f1637a89f9fe00060a080a167da/Pillow-9.3.0-1-cp37-cp37m-win32.whl", hash = "sha256:e6ea6b856a74d560d9326c0f5895ef8050126acfdc7ca08ad703eb0081e82b74"}, + {url = "https://files.pythonhosted.org/packages/6f/64/3acfbffd532ec2e82f69d4f22de9c29197540ae61926f5b7072654281e91/Pillow-9.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2"}, + {url = "https://files.pythonhosted.org/packages/77/56/bb8dc927b3c44df0ca938b4a17b08c59ef5e1e7cc06300bddf20d4e2935f/Pillow-9.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc"}, + {url = "https://files.pythonhosted.org/packages/78/8c/ae0c7cfb6cae4eb0946b7369258f78e3104c22eeffda45ad6228bc0838f5/Pillow-9.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1"}, + {url = "https://files.pythonhosted.org/packages/88/9f/012551c5a9d8b4d0304b66d16119b9c79bac052e0b0bd37de29b64c1bf0c/Pillow-9.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8"}, + {url = "https://files.pythonhosted.org/packages/8a/e8/124718316b3f4f9fa24e4b7e915d8e02d58d43a5e3c44fcbceef17d60647/Pillow-9.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3"}, + {url = "https://files.pythonhosted.org/packages/a0/6c/6e11abff7f944b51dbeea1bdfe14ad21976858e822ef667fa4eb82ec6f7c/Pillow-9.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4"}, + {url = "https://files.pythonhosted.org/packages/a1/81/10f4f9665ce366c7501141c973c755cd5c9ebe577886ebfa7ca3e9a115f0/Pillow-9.3.0-cp38-cp38-win32.whl", hash = "sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0"}, + {url = "https://files.pythonhosted.org/packages/a7/9e/8acd4d170596fa876b0baaae3542de3b6d0a709b4652ce78285aff59e849/Pillow-9.3.0-cp37-cp37m-win32.whl", hash = "sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29"}, + {url = "https://files.pythonhosted.org/packages/ac/0b/cb7ecd88cea2422818057db720966bbb56bbcec3b359f4a5cafdf737d9e5/Pillow-9.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448"}, + {url = "https://files.pythonhosted.org/packages/af/7f/c5f7a35fd7053a4aee79c41fc4b125281745577c6ce3445e57ed8b4eff56/Pillow-9.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3"}, + {url = "https://files.pythonhosted.org/packages/b7/34/7a88f5ec5f26ac68d3d7a158c67c0a610a4e6b6d22c35266d0e715485b09/Pillow-9.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b"}, + {url = "https://files.pythonhosted.org/packages/b9/b8/3e2c23766019a9a03ee26ac6b7af9392aba8574806ce10e5074733987240/Pillow-9.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535"}, + {url = "https://files.pythonhosted.org/packages/bb/0e/83dd785dff8d62b4d663391ac62390ffadfcdfa4bd11959742f90d02127c/Pillow-9.3.0-cp39-cp39-win32.whl", hash = "sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48"}, + {url = "https://files.pythonhosted.org/packages/bc/28/0bcf18f0c22cd89626ee2e071edbe5736d54f9031fe986ae94389776864b/Pillow-9.3.0-cp310-cp310-win32.whl", hash = "sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91"}, + {url = "https://files.pythonhosted.org/packages/be/3e/a757fd2fdd5814aa0e9e1e838f79d33e0098e10a2e3afb7acdcb72278290/Pillow-9.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040"}, + {url = "https://files.pythonhosted.org/packages/c0/47/4023dab2d77ea3f687939770b06e0c191b4a5a20590f158a6e8dbb03e357/Pillow-9.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72"}, + {url = "https://files.pythonhosted.org/packages/c0/8f/dfa473f3a6241bff91ae8bb905bd0afceb827f37de2917a94b5c4b1112bf/Pillow-9.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2"}, + {url = "https://files.pythonhosted.org/packages/c1/c3/be8222fce0553e05264bfe66f2cc73483567b961f144acef88753fba9c6c/Pillow-9.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c"}, + {url = "https://files.pythonhosted.org/packages/c2/1d/5d0fd887bb790a42a9efb7fe742358f1eae332ee3120a7d4f79ee37358c9/Pillow-9.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20"}, + {url = "https://files.pythonhosted.org/packages/c8/7c/f5fe5b37b6a8f2218db98f33d2278f2900e0fc4ef019cd4d9e522d6e3206/Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699"}, + {url = "https://files.pythonhosted.org/packages/c9/b8/27c526c45f482450a53c0faab6c0c4baf9cddee0a8f879a8526f7dd8adf0/Pillow-9.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee"}, + {url = "https://files.pythonhosted.org/packages/ca/97/9677bf1d97c408779e5940b89675e38a8ebe8bcc9dbae8adfc9e7080a314/Pillow-9.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad"}, + {url = "https://files.pythonhosted.org/packages/cf/22/fed65d07e5e7b19cc608754e940a9b101bebe78cb6cd9d764639717848a4/Pillow-9.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb"}, + {url = "https://files.pythonhosted.org/packages/d0/ae/b5e8750a7e745926e2227a43766d905c8c6a0f03739c7aec5aea9e975355/Pillow-9.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7"}, + {url = "https://files.pythonhosted.org/packages/d1/6f/b5e87428a5566563b6d661824c694bbc4b0386aed1c939b9aec47d9ee573/Pillow-9.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c"}, + {url = "https://files.pythonhosted.org/packages/d4/0d/e8f6ed8e2328007020bb242a0443d925f23ee06d059486c5f1308f2a5e26/Pillow-9.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef"}, + {url = "https://files.pythonhosted.org/packages/d5/31/b026f9f7c87adf8027f51f98f392f6558982485b7202af5f9276492b2141/Pillow-9.3.0-1-cp37-cp37m-win_amd64.whl", hash = "sha256:32a44128c4bdca7f31de5be641187367fe2a450ad83b833ef78910397db491aa"}, + {url = "https://files.pythonhosted.org/packages/d5/79/191ea841f818aba4eb9783b9f91a3c16c51deff7326f5a2f47de54959a28/Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b"}, + {url = "https://files.pythonhosted.org/packages/d6/be/996d629efd03aa305472a17183fcdfe2dd8529acea767d0ba2242d90cbfa/Pillow-9.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de"}, + {url = "https://files.pythonhosted.org/packages/db/b0/6438c96e80d4cad5b6381ba774018de9d8eae591d65bdfd80e3ad630928a/Pillow-9.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c"}, + {url = "https://files.pythonhosted.org/packages/dc/20/30e5ea4ecb35b36d9bc4ff4e8edc048e017a8e3d2e087a512b0622bfdde3/Pillow-9.3.0-cp311-cp311-win32.whl", hash = "sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c"}, + {url = "https://files.pythonhosted.org/packages/e0/59/74d88751801101259abd695f77c44b97a828b6a4e7981a79a75d2c3379ae/Pillow-9.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812"}, + {url = "https://files.pythonhosted.org/packages/ec/4f/bc65f543b4d774d6c888cb0936a8b91cf7c5fbb25e16f923e332822b279b/Pillow-9.3.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f"}, + {url = "https://files.pythonhosted.org/packages/f1/e4/2f7e4efeb79ab0a2c5ecba320b2e5a3aad7a49cc543407f4c1268ed804bb/Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65"}, + {url = "https://files.pythonhosted.org/packages/f7/11/763c3992c3e6d00e9f6de81b0d721306757bb7201155d135a16032460943/Pillow-9.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd"}, + {url = "https://files.pythonhosted.org/packages/fc/12/32101461796addca02fd14dc4d2b76068a2cbf895002ed668550caf3fcc1/Pillow-9.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8"}, +] +"platformdirs 2.5.4" = [ + {url = "https://files.pythonhosted.org/packages/61/e0/15ba41c6716acb033c3793be3a02f26c53914ecd9bdd6b315001f8f5f581/platformdirs-2.5.4-py3-none-any.whl", hash = "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"}, + {url = "https://files.pythonhosted.org/packages/cb/5f/dda8451435f17ed8043eab5ffe04e47d703debe8fe845eb074f42260e50a/platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"}, +] +"pluggy 1.0.0" = [ + {url = "https://files.pythonhosted.org/packages/9e/01/f38e2ff29715251cf25532b9082a1589ab7e4f571ced434f98d0139336dc/pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {url = "https://files.pythonhosted.org/packages/a1/16/db2d7de3474b6e37cbb9c008965ee63835bba517e22cdb8c35b5116b5ce1/pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +"protobuf 3.20.3" = [ + {url = "https://files.pythonhosted.org/packages/00/e7/d23c439c55c90ae2e52184363162f7079ca3e7d86205b411d4e9dc266f81/protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, + {url = "https://files.pythonhosted.org/packages/11/a5/e52b731415ad6ef3d841e9e6e337a690249e800cc7c06f0749afab26348c/protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, + {url = "https://files.pythonhosted.org/packages/28/55/b80e8567ec327c060fa39b242392e25690c8899c489ecd7bb65b46b7bb55/protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, + {url = "https://files.pythonhosted.org/packages/2a/7c/e7091f0eea6eec70547d28c6c0d8c7335ee58f6b13456608beec8c94a62a/protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, + {url = "https://files.pythonhosted.org/packages/31/be/80a9c6f16dfa4d41be3edbe655349778ae30882407fa8275eb46b4d34854/protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, + {url = "https://files.pythonhosted.org/packages/32/f8/52f598bceb16fe365f4ef8e957ac8890aeb56abf97d365ff5abd8c1250cf/protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, + {url = "https://files.pythonhosted.org/packages/36/8b/433071fed0058322090a55021bdc8da76d16c7bc9823f5795797803dd6d0/protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, + {url = "https://files.pythonhosted.org/packages/3c/14/16ef7da61d30a519d84e6841d096fe446c4d8a2c39b083b0376b4785f1f3/protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, + {url = "https://files.pythonhosted.org/packages/4c/12/62e1d5505c172e1a7f803d83b0b1693f7952c3c271eb2f155703012ae67a/protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, + {url = "https://files.pythonhosted.org/packages/55/5b/e3d951e34f8356e5feecacd12a8e3b258a1da6d9a03ad1770f28925f29bc/protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, + {url = "https://files.pythonhosted.org/packages/6f/5e/fc6feb366b0a9f28e0a2de3b062667c521cd9517d4ff55077b8f351ba2f3/protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, + {url = "https://files.pythonhosted.org/packages/8d/14/619e24a4c70df2901e1f4dbc50a6291eb63a759172558df326347dce1f0d/protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, + {url = "https://files.pythonhosted.org/packages/98/07/4c75a689fa173c12b92c9a64a82efad44797b9b2b784c8562f36ab28b551/protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, + {url = "https://files.pythonhosted.org/packages/99/25/5825472ecd911f4ac2ac4e9ab039a48b6d03874e2add92fb633e080bf3eb/protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, + {url = "https://files.pythonhosted.org/packages/9c/d8/dc6a9ee6ec43a1001e3d71cccda70cf50ac0098000fc455023dba3b46ebf/protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, + {url = "https://files.pythonhosted.org/packages/9f/1a/6848ed1669a6c70bf947d25d64ce6dcc65ccec06e917072df516944fa17e/protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, + {url = "https://files.pythonhosted.org/packages/b5/b6/ec87636b9381137f17292851461902ceac7632a00476c2afbcd864ed5447/protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, + {url = "https://files.pythonhosted.org/packages/c7/df/ec3ecb8c940b36121c7b77c10acebf3d1c736498aa2f1fe3b6231ee44e76/protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, + {url = "https://files.pythonhosted.org/packages/da/e4/4d62585593e9f962cb02614534f62f930de6a80a0a3784282094a01919b2/protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, + {url = "https://files.pythonhosted.org/packages/db/96/948d3fcc1fa816e7ae1d27af59b9d8c5c5e582f3994fd14394f31da95b99/protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, + {url = "https://files.pythonhosted.org/packages/ef/d4/765a106ca96d487f94f3c99e46b399218f53735628c3b2d759a832e2adab/protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, + {url = "https://files.pythonhosted.org/packages/fe/8f/d9db035740002d61b4140aaef53a8bac7e316b18ec8744eb6c1fcf83c310/protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, + {url = "https://test-files.pythonhosted.org/packages/8d/14/619e24a4c70df2901e1f4dbc50a6291eb63a759172558df326347dce1f0d/protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, + {url = "https://test-files.pythonhosted.org/packages/f4/de/904b3f09af0e6a2f0072a25878d784d151283546a5bb3e2ba12def8997c6/protobuf-3.20.3.tar.gz", hash = "sha256:e8a258d4d36b076db944e6ed7f023b47443e197d134858cb0e87b7f9b973e9bd"}, +] +"py-sr25519-bindings 0.1.4" = [ + {url = "https://files.pythonhosted.org/packages/00/17/33406429b979af786a6cf2066b6463e224a2ac763105e82483f513bfaadb/py_sr25519_bindings-0.1.4-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:721cae28e86038682dd522191f9cb58a71b3455ef461433ba27a76d2d321197c"}, + {url = "https://files.pythonhosted.org/packages/06/41/3d76392a2e0dd37e3035528291c84488e9d074f16e121565dde1928f1bd4/py_sr25519_bindings-0.1.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8e62369aca9455131330cf63b0fcec3be198cabad936317321aa0db9d143435b"}, + {url = "https://files.pythonhosted.org/packages/08/18/c25beaad05e559c7105e40b97aa2929cc8c0174b9b69bd8c4a7cf0705ec6/py_sr25519_bindings-0.1.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:aed508e15e695c5e3147be936db7a40f7bf5cb0a4078a9eb0e25fbdeb7c08982"}, + {url = "https://files.pythonhosted.org/packages/0c/81/cbe028f361e6d395635ab761e7324adaa3db5dd9c8e8a44df74202d698e3/py_sr25519_bindings-0.1.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb1edb9db13548252fe3410868111dcda48a5b6ecda11cebb156930f0f472368"}, + {url = "https://files.pythonhosted.org/packages/21/36/8a3eb5cf00999d6f137bfe2a1e2a6e374f35e06d6cf31aa082142c1a1ef1/py_sr25519_bindings-0.1.4-cp37-cp37m-manylinux_2_28_armv7l.whl", hash = "sha256:6be4f52320d021fe8a86d72d71fae821b6c251f12b36e7e3deee5a1140da55b6"}, + {url = "https://files.pythonhosted.org/packages/25/c0/3041d947eaeabf8f84032934bc7a1f39a308befd59f27896f891f2fe9aa7/py_sr25519_bindings-0.1.4-cp38-none-win_amd64.whl", hash = "sha256:127057216b8cd32ea322d64aa52e70ba7b42a55cd4eb31e8df7c0120934f2445"}, + {url = "https://files.pythonhosted.org/packages/29/c6/e18d4c1a2882d1f1389948c859d8bfe0a0cebe4e7cb118b9516c09488c8e/py_sr25519_bindings-0.1.4-cp37-none-win_amd64.whl", hash = "sha256:d13e3a6b262494f66f36de3ba45d1040fee7a0c4104d56086b15315332026db9"}, + {url = "https://files.pythonhosted.org/packages/38/21/dd5ca55d51bf77ad63f641e080c4cc6a0b09bccd07bf44ec9886e2e29107/py_sr25519_bindings-0.1.4-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:5a0f591f9b474ca31f0ca199ebc5d2c70a8869ff6e211188a1562310614558dd"}, + {url = "https://files.pythonhosted.org/packages/3f/02/0d807a6e7d970580485c65b55e8f3a6d737da6700de4ef23dde8cc6fd1be/py_sr25519_bindings-0.1.4-cp310-none-win_amd64.whl", hash = "sha256:1ca0d8e1ca66a17c41e3f6da0c3837e7dbf5b391eb4ea41cfe6026bad0adc8ea"}, + {url = "https://files.pythonhosted.org/packages/44/02/d8a4d2e5b314e8b4d9d0e18f3ce8b89b3fe5e59fbb0413e787c8b1f6e671/py_sr25519_bindings-0.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6715852eb2ea733c4978b77f9a049cc9eb763d54270d5b1263b198256656c38e"}, + {url = "https://files.pythonhosted.org/packages/4c/2c/d263ede4827a7a5e02290b7e8d34515b244897d80005ea345d69311cb99d/py_sr25519_bindings-0.1.4-cp39-none-win32.whl", hash = "sha256:0a09f5886a706fcee6786f94dd59a04bdc95f2d6c4b7e1b3118ca805d322a055"}, + {url = "https://files.pythonhosted.org/packages/64/82/f55600547af686b1ef3db123471493800ce6f9a61ac7a9e355b3bd6360aa/py_sr25519_bindings-0.1.4.tar.gz", hash = "sha256:5e12ca977014f148f4bfb6ba662f1529b15cc8ce030719d726c4e16a379e976e"}, + {url = "https://files.pythonhosted.org/packages/6a/7c/b4c3ea21d696507b7bf129e6ee1eda0a7bb505604ff5aebe8666a021f7d9/py_sr25519_bindings-0.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b901675f8ea8ca4a6e55887ea48448a3f6bf55f22baa4acfa77d7c844e82e9db"}, + {url = "https://files.pythonhosted.org/packages/74/a7/377ba7820127f72a97cf6355cfa59ee92a767e64c0730d3c2de5b99f3a36/py_sr25519_bindings-0.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3f52666b02075483dfdd294b9d0fd903eaf96ac7140e5be45390b25ded52ca6a"}, + {url = "https://files.pythonhosted.org/packages/76/cb/0e5541edee87356945beac027e9180cdb1a7850c92890c76e1b45d67d24f/py_sr25519_bindings-0.1.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4940ebfcb482b33468c41dda9e8c19137c14cc3b993e1084e55387c6acfa11ab"}, + {url = "https://files.pythonhosted.org/packages/7b/48/7ff1093cf5237c79c7d5c80693b19f0bb2c9fc46f828b596ca65add25cc0/py_sr25519_bindings-0.1.4-cp38-cp38-manylinux_2_28_armv7l.whl", hash = "sha256:9b80e4aa4037b1e8fd3d5ff0fb4e2c3d92e04b2936f29c79d5411c5a22562f25"}, + {url = "https://files.pythonhosted.org/packages/7e/7c/b6f9996af8ccd71ee9436e69a0e35229805e44fa12913a276230d3e43493/py_sr25519_bindings-0.1.4-cp38-none-win32.whl", hash = "sha256:ce3639509e87ab04652c62aab626f13a80e308347047173bf74749397d0be539"}, + {url = "https://files.pythonhosted.org/packages/81/07/fa3613aa467368f6a8bbbba1342e2e4ad5f01c4c69bdbae320910c748d7d/py_sr25519_bindings-0.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:67dd46da0a571859418fc938af15ab9a0586e47244d21f0f26c5cbf4daf10a3b"}, + {url = "https://files.pythonhosted.org/packages/82/32/8ceb7a90a7ef390c06483ddf801da300f0f53b9d534d54d20b191b6ae70a/py_sr25519_bindings-0.1.4-cp310-cp310-manylinux_2_28_armv7l.whl", hash = "sha256:34635da67e6798bb4535543e63c3e1d3b19f17a4df1f9b613c69b66fc8296fa3"}, + {url = "https://files.pythonhosted.org/packages/93/65/c2571cd841cacbc686b992d3dc2c86943300e5b018277865b02e627623f3/py_sr25519_bindings-0.1.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1121e5da273c81b2e7ba0ca4bef8a89c527a6e25ed3110a95e93fd2caa04c50b"}, + {url = "https://files.pythonhosted.org/packages/9a/b2/de252739677fbbae043cdec66c85c41947fe45e05a262f36afb417f3b287/py_sr25519_bindings-0.1.4-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:197892cd89077baf2be51e3307a40de2c32006a2b89fec7d767a1d0b2749712d"}, + {url = "https://files.pythonhosted.org/packages/a3/6a/da96376c02c6b11bb1ec84dd225016bcaebd39e73ed0635b39017e0c919a/py_sr25519_bindings-0.1.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d1c1247b4bf4c5670767e8a35bc259b425b275c8d72c6e3f1cfac47cf9b80e89"}, + {url = "https://files.pythonhosted.org/packages/a5/ae/16878ea5c1be67708bfd39b7e3f98fa29f0a22a8952499daa18568abc528/py_sr25519_bindings-0.1.4-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:d1b1b51d997363a264fb3f91978f21ea784f5ee50ed26e11ed0a0f26c497a4eb"}, + {url = "https://files.pythonhosted.org/packages/ae/58/3af038d8a625f4fe6b2a8208a3aabdd7d7fa7a56adc8506f9a2ec7d02e2c/py_sr25519_bindings-0.1.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3845f55a88dcba825c16e65fc9e26742cbb9103fba5229fd0c3e9e8deffd323b"}, + {url = "https://files.pythonhosted.org/packages/af/5f/a876044eba658ed2272cc640d56cc13c1378f801b0d921858fbaaa677a64/py_sr25519_bindings-0.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72b49cba419f5c76436bf06de4575ee27713afd8b867c4c14cb7a29e1046c30a"}, + {url = "https://files.pythonhosted.org/packages/b0/94/439f7574765167520ef20f89a2d11e77fafa097ba7e0b212942f3d29384a/py_sr25519_bindings-0.1.4-cp39-none-win_amd64.whl", hash = "sha256:855af2efee23ebbb4b5544027b2b9a4818db34717b88741bcfd74f5739e9152f"}, + {url = "https://files.pythonhosted.org/packages/b3/ef/342e55303451366e28381fd48117b248168fdb251995ded395364389b185/py_sr25519_bindings-0.1.4-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:14a06ca42d5fd07673138bf9970db9091059a1e4d704545bdf156dd05de3e3c6"}, + {url = "https://files.pythonhosted.org/packages/b8/98/b40bc2afa4e1a47fa18b083cf467a437512377366746c1100e53b517a554/py_sr25519_bindings-0.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42097fab2702186a6591471bc366d7c804c7c30744acd59d6c6a38fdcad4bedb"}, + {url = "https://files.pythonhosted.org/packages/c5/a2/0d05e8b565cddfb32fa21ea78fcea9e400360217e1955aa7047eafe2a977/py_sr25519_bindings-0.1.4-cp39-cp39-manylinux_2_28_armv7l.whl", hash = "sha256:465c2ae7e3191f24ac10519aece185772a6b9d6179b8948fad78c3b1dbe77d8c"}, + {url = "https://files.pythonhosted.org/packages/ca/80/d476d745110b1a727e976242a1f7fbaee47115b7c093fcc0f4dd99576887/py_sr25519_bindings-0.1.4-cp37-none-win32.whl", hash = "sha256:1fb9ca7b65e60b64bd6e81ee2d5e6556d93b317af3c95a2e24bf45a3c44041a0"}, + {url = "https://files.pythonhosted.org/packages/cd/dd/904edf234ede3847ff879fe2ca65cf3085255442935e006ea02306afd1bb/py_sr25519_bindings-0.1.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:34786ba33f602d9d4f2495bd29d9f0cb357813a6ee8ae5cd9d37dbda86c141a4"}, + {url = "https://files.pythonhosted.org/packages/e3/d8/7fb1e71f673c4d50fc54051ed7a3c08612b8e9b86d6d086ce31c3220cdf3/py_sr25519_bindings-0.1.4-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:9cbee7341ed35d08533c7dac4d227e2fe21b815072ef66178a7ddb77f4e7be36"}, + {url = "https://files.pythonhosted.org/packages/e8/6d/d10583af0eb1f8d01f4ec641ee72d45893327e037333586d3b9e2ede4857/py_sr25519_bindings-0.1.4-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e07cc588754da27c03185e2d3823537790f616b5aab3df1703ca0f6f578114c"}, + {url = "https://files.pythonhosted.org/packages/e8/99/a9d5da4aea85990fe4d088bebfff28d27067dc8d5249763b748821691953/py_sr25519_bindings-0.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e5118cc7e1a77083307d260ccda21a0ec14efb0b98535f8fc9e2c54c7594836"}, + {url = "https://files.pythonhosted.org/packages/f6/39/08b76be2855ebf643e3a5de5de7fc383b961221e3911df547806be9561c8/py_sr25519_bindings-0.1.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec17c935f8ae9a00bb5d6adac6cc455fd6052042959ace5563448c355e2960a"}, + {url = "https://files.pythonhosted.org/packages/f8/dd/38bd462934574c44f8e7ca635d5fcc843fbaa3e1252724183c67651dd695/py_sr25519_bindings-0.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0046dda17c554376f5ba11ea91163b1b883ac61fcc1b1ba588e31b1cb58add28"}, + {url = "https://files.pythonhosted.org/packages/ff/35/b25799b4ce26db05c08027dfbafa6e7f60812576d22b02e5e52db4531c90/py_sr25519_bindings-0.1.4-cp310-none-win32.whl", hash = "sha256:f7ca8f4e62aee8bc33916f6422e8ac5ffeeb96bc11f7ca52792354b771216bd8"}, + {url = "https://test-files.pythonhosted.org/packages/1d/f9/7680e56c118871726cf7d75cf5b6fce8eb2444748d85db675b82925a410a/py_sr25519_bindings-0.1.4-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:938190d97f27566953468caf19baba3c7ee776d60eef816c7d233224624b00a6"}, + {url = "https://test-files.pythonhosted.org/packages/5e/3d/752274043b7af794cfb7e541127d35df56bfb0b42120813c54ab52c9628b/py_sr25519_bindings-0.1.4.tar.gz", hash = "sha256:d9c7fd291c3233de404161c739b0626ff10ace60aabfd8334e4a8432a0a9e702"}, +] +"pycparser 2.21" = [ + {url = "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, + {url = "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, +] +"pycryptodome 3.15.0" = [ + {url = "https://files.pythonhosted.org/packages/00/07/5a262e3213a9358e2f7caf9080aa8a984f44bf4aee84592dfb965dd34355/pycryptodome-3.15.0-cp35-abi3-win_amd64.whl", hash = "sha256:c77126899c4b9c9827ddf50565e93955cb3996813c18900c16b2ea0474e130e9"}, + {url = "https://files.pythonhosted.org/packages/01/bc/7c67348624581fc57e5cb34e650ba09ba668e08e41937d1d1bbdc8cd9e9b/pycryptodome-3.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:7c9ed8aa31c146bef65d89a1b655f5f4eab5e1120f55fc297713c89c9e56ff0b"}, + {url = "https://files.pythonhosted.org/packages/11/e4/a8e8056a59c39f8c9ddd11d3bc3e1a67493abe746df727e531f66ecede9e/pycryptodome-3.15.0.tar.gz", hash = "sha256:9135dddad504592bcc18b0d2d95ce86c3a5ea87ec6447ef25cfedea12d6018b8"}, + {url = "https://files.pythonhosted.org/packages/1e/ed/e908d15473f14975f1b29d52de57fee7b035f87ff9560f9dae2e37bf9bc2/pycryptodome-3.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2ea63d46157386c5053cfebcdd9bd8e0c8b7b0ac4a0507a027f5174929403884"}, + {url = "https://files.pythonhosted.org/packages/2e/6f/27fbd8f3fd8b48feba2b4226f7f8d23af7755c54957fccc3fe6f44b764cf/pycryptodome-3.15.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:4c3ccad74eeb7b001f3538643c4225eac398c77d617ebb3e57571a897943c667"}, + {url = "https://files.pythonhosted.org/packages/2f/dc/e5bb825eb7348773b77ace0d977f549af851c1d8300f1ba60119e88ba715/pycryptodome-3.15.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9ee40e2168f1348ae476676a2e938ca80a2f57b14a249d8fe0d3cdf803e5a676"}, + {url = "https://files.pythonhosted.org/packages/34/09/ab89d75316862ae9fced5516ba533dc17da89938e7de4d5ddfd8483fd9e8/pycryptodome-3.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0926f7cc3735033061ef3cf27ed16faad6544b14666410727b31fea85a5b16eb"}, + {url = "https://files.pythonhosted.org/packages/35/5b/ba592bfd0d3bad9450645b751c132cf1028dc111ae699fd8e70808414941/pycryptodome-3.15.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:092a26e78b73f2530b8bd6b3898e7453ab2f36e42fd85097d705d6aba2ec3e5e"}, + {url = "https://files.pythonhosted.org/packages/38/a7/ff3d1e9ef28726433b5d6edb5ded96a0b9d85722dad9c3faf27a1372b0a3/pycryptodome-3.15.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:9c772c485b27967514d0df1458b56875f4b6d025566bf27399d0c239ff1b369f"}, + {url = "https://files.pythonhosted.org/packages/55/60/28d873c1efe46cf62494a0393fe34e4757821123fb1af9c45be3b2eeba8a/pycryptodome-3.15.0-cp35-abi3-win32.whl", hash = "sha256:e244ab85c422260de91cda6379e8e986405b4f13dc97d2876497178707f87fc1"}, + {url = "https://files.pythonhosted.org/packages/5a/3d/56084bd2b973c262a59d6b7c5618d93f8ee6045c484e7675e97104e48ac3/pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c3640deff4197fa064295aaac10ab49a0d55ef3d6a54ae1499c40d646655c89f"}, + {url = "https://files.pythonhosted.org/packages/5c/9c/2cfbb08a3f573e35818fe49d4f6efdc6c157553b71bc7d65592de49f623f/pycryptodome-3.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:7e3a8f6ee405b3bd1c4da371b93c31f7027944b2bcce0697022801db93120d83"}, + {url = "https://files.pythonhosted.org/packages/6a/09/84ac32b49e991308749d615e7a5b9ae13b94adb01279224fbca584636977/pycryptodome-3.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2ffd8b31561455453ca9f62cb4c24e6b8d119d6d531087af5f14b64bee2c23e6"}, + {url = "https://files.pythonhosted.org/packages/6c/73/7b25e21cbb4aa8817f85fa86537798d681f3dd479fd5d4737e9f3efbaf9e/pycryptodome-3.15.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2ec709b0a58b539a4f9d33fb8508264c3678d7edb33a68b8906ba914f71e8c13"}, + {url = "https://files.pythonhosted.org/packages/74/4d/1340e63264e07ff5f1e1daec9d66015ade99d64f3b966a52ff7ff3f4a362/pycryptodome-3.15.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:ecaaef2d21b365d9c5ca8427ffc10cebed9d9102749fd502218c23cb9a05feb5"}, + {url = "https://files.pythonhosted.org/packages/74/f8/e6e9e2426f332b2216950df88bdf160ed90b2dbe42dfd5fc5e8ac33bd583/pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2aa55aae81f935a08d5a3c2042eb81741a43e044bd8a81ea7239448ad751f763"}, + {url = "https://files.pythonhosted.org/packages/7d/ac/843a78bc3c5c680f4c22f530bdb6e2927b770f77630948b0e0247cc23c04/pycryptodome-3.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff7ae90e36c1715a54446e7872b76102baa5c63aa980917f4aa45e8c78d1a3ec"}, + {url = "https://files.pythonhosted.org/packages/7d/be/e3e56f7f92bebf506aec486eb71d91952d2e9faf5e10872a89931db4120f/pycryptodome-3.15.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4b52cb18b0ad46087caeb37a15e08040f3b4c2d444d58371b6f5d786d95534c2"}, + {url = "https://files.pythonhosted.org/packages/86/93/93d5752292d6cf2709d9c3343c26e5a6f308976a566b6e4a389094b83fbe/pycryptodome-3.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:5099c9ca345b2f252f0c28e96904643153bae9258647585e5e6f649bb7a1844a"}, + {url = "https://files.pythonhosted.org/packages/9b/c0/6aac989804de5526099062b263be17c7216901fc2fbbc4e08b6e44d9c236/pycryptodome-3.15.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:045d75527241d17e6ef13636d845a12e54660aa82e823b3b3341bcf5af03fa79"}, + {url = "https://files.pythonhosted.org/packages/9b/e8/628f92b38ee4475d7b316d04c2913d397cdcc3f3a873bdbea10a438ba9fe/pycryptodome-3.15.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:ff287bcba9fbeb4f1cccc1f2e90a08d691480735a611ee83c80a7d74ad72b9d9"}, + {url = "https://files.pythonhosted.org/packages/b1/54/ad3e2e07a5a7ceb926971398f7e3a0eeee85b6e1d3e658df8f71a4497daa/pycryptodome-3.15.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:1b22bcd9ec55e9c74927f6b1f69843cb256fb5a465088ce62837f793d9ffea88"}, + {url = "https://files.pythonhosted.org/packages/b1/6c/4ee93e2e863e95caffc5a356b867e86f5596f4e38cddb43848412dd69176/pycryptodome-3.15.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:60b4faae330c3624cc5a546ba9cfd7b8273995a15de94ee4538130d74953ec2e"}, + {url = "https://files.pythonhosted.org/packages/b1/d5/4a140b9d316681e9d2e55ac8a29f7f70b446c795e0af5f3de2500d7654b0/pycryptodome-3.15.0-cp27-cp27m-win32.whl", hash = "sha256:fd2184aae6ee2a944aaa49113e6f5787cdc5e4db1eb8edb1aea914bd75f33a0c"}, + {url = "https://files.pythonhosted.org/packages/b1/e4/079a70b03928a01d5517cc69a5cf4bca79df7c3d85e27e4e66e9b4e211e7/pycryptodome-3.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b9c5b1a1977491533dfd31e01550ee36ae0249d78aae7f632590db833a5012b8"}, + {url = "https://files.pythonhosted.org/packages/b5/de/a1d1407e0bfd396e62c9efe3261be0c76888a8f3722b9b7f61f460e0e328/pycryptodome-3.15.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:d2a39a66057ab191e5c27211a7daf8f0737f23acbf6b3562b25a62df65ffcb7b"}, + {url = "https://files.pythonhosted.org/packages/bb/7a/0e51d1dc253d0571106009b94762b8ab5a7c905079c354247b721ae1f198/pycryptodome-3.15.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:9eaadc058106344a566dc51d3d3a758ab07f8edde013712bc8d22032a86b264f"}, + {url = "https://files.pythonhosted.org/packages/c5/b4/526dd68f6c8ff6b785d7a08da7a6bba5646cced508454f1d1ab948e6b737/pycryptodome-3.15.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:57f565acd2f0cf6fb3e1ba553d0cb1f33405ec1f9c5ded9b9a0a5320f2c0bd3d"}, + {url = "https://files.pythonhosted.org/packages/c7/d0/319a673a6514beb9a203fdb60f28b87135bbbdda0b3ea782c022414a9034/pycryptodome-3.15.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b9cc96e274b253e47ad33ae1fccc36ea386f5251a823ccb50593a935db47fdd2"}, + {url = "https://files.pythonhosted.org/packages/c8/03/1f745c158299000664a6fb5c0b28d5359837b05b4ba1f5a37645da5cad4d/pycryptodome-3.15.0-pp27-pypy_73-win32.whl", hash = "sha256:a8f06611e691c2ce45ca09bbf983e2ff2f8f4f87313609d80c125aff9fad6e7f"}, +] +"pyelftools 0.29" = [ + {url = "https://files.pythonhosted.org/packages/04/7c/867630e6e6293793f838b31034aa1875e1c3bd8c1ec34a0929a2506f350c/pyelftools-0.29-py2.py3-none-any.whl", hash = "sha256:519f38cf412f073b2d7393aa4682b0190fa901f7c3fa0bff2b82d537690c7fc1"}, + {url = "https://files.pythonhosted.org/packages/0e/35/e76da824595452a5ad07f289ea1737ca0971fc6cc7b6ee9464279be06b5e/pyelftools-0.29.tar.gz", hash = "sha256:ec761596aafa16e282a31de188737e5485552469ac63b60cfcccf22263fd24ff"}, +] +"pynacl 1.5.0" = [ + {url = "https://files.pythonhosted.org/packages/25/2d/b7df6ddb0c2a33afdb358f8af6ea3b8c4d1196ca45497dd37a56f0c122be/PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {url = "https://files.pythonhosted.org/packages/3d/85/c262db650e86812585e2bc59e497a8f59948a005325a11bbbc9ecd3fe26b/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {url = "https://files.pythonhosted.org/packages/59/bb/fddf10acd09637327a97ef89d2a9d621328850a72f1fdc8c08bdf72e385f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {url = "https://files.pythonhosted.org/packages/5d/70/87a065c37cca41a75f2ce113a5a2c2aa7533be648b184ade58971b5f7ccc/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {url = "https://files.pythonhosted.org/packages/5e/22/d3db169895faaf3e2eda892f005f433a62db2decbcfbc2f61e6517adfa87/PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {url = "https://files.pythonhosted.org/packages/66/28/ca86676b69bf9f90e710571b67450508484388bfce09acf8a46f0b8c785f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {url = "https://files.pythonhosted.org/packages/a7/22/27582568be639dfe22ddb3902225f91f2f17ceff88ce80e4db396c8986da/PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, + {url = "https://files.pythonhosted.org/packages/ce/75/0b8ede18506041c0bf23ac4d8e2971b4161cd6ce630b177d0a08eb0d8857/PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {url = "https://files.pythonhosted.org/packages/ee/87/f1bb6a595f14a327e8285b9eb54d41fef76c585a0edef0a45f6fc95de125/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {url = "https://files.pythonhosted.org/packages/fd/1a/cc308a884bd299b651f1633acb978e8596c71c33ca85e9dc9fa33a5399b9/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, +] +"pyparsing 3.0.9" = [ + {url = "https://files.pythonhosted.org/packages/6c/10/a7d0fa5baea8fe7b50f448ab742f26f52b80bfca85ac2be9d35cdd9a3246/pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {url = "https://files.pythonhosted.org/packages/71/22/207523d16464c40a0310d2d4d8926daffa00ac1f5b1576170a32db749636/pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +"pyqt5 5.15.7" = [ + {url = "https://files.pythonhosted.org/packages/14/75/596d5e9ed7a135918bb157ed315004ac008e09b9b4c9328f94568c88f003/PyQt5-5.15.7-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:1a793748c60d5aff3850b7abf84d47c1d41edb11231b7d7c16bef602c36be643"}, + {url = "https://files.pythonhosted.org/packages/22/0d/4ea05714826a5f6478dc2845cd94ad76dce3c4c0ead0842b06cacd4e3492/PyQt5-5.15.7-cp37-abi3-manylinux1_x86_64.whl", hash = "sha256:e319c9d8639e0729235c1b09c99afdadad96fa3dbd8392ab561b5ab5946ee6ef"}, + {url = "https://files.pythonhosted.org/packages/bd/85/31a12415765acb48fddac3e207cfffcbbae826fe194cf1d92179d8872f59/PyQt5-5.15.7-cp37-abi3-win_amd64.whl", hash = "sha256:232fe5b135a095cbd024cf341d928fc672c963f88e6a52b0c605be8177c2fdb5"}, + {url = "https://files.pythonhosted.org/packages/c6/ea/5fa344ed69086218b30542c3b2e381c1f52ec4b141f878e9601e3b81dc71/PyQt5-5.15.7-cp37-abi3-win32.whl", hash = "sha256:08694f0a4c7d4f3d36b2311b1920e6283240ad3b7c09b515e08262e195dcdf37"}, + {url = "https://files.pythonhosted.org/packages/e1/57/2023316578646e1adab903caab714708422f83a57f97eb34a5d13510f4e1/PyQt5-5.15.7.tar.gz", hash = "sha256:755121a52b3a08cb07275c10ebb96576d36e320e572591db16cfdbc558101594"}, +] +"pyqt5-qt5 5.15.2" = [ + {url = "https://files.pythonhosted.org/packages/1c/7e/ce7c66a541a105fa98b41d6405fe84940564695e29fc7dccf6d9e8c5f898/PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"}, + {url = "https://files.pythonhosted.org/packages/37/97/5d3b222b924fa2ed4c2488925155cd0b03fd5d09ee1cfcf7c553c11c9f66/PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"}, + {url = "https://files.pythonhosted.org/packages/62/09/99a222b0360616250fb2e6003a54e43a2a06b0774f0f8d5daafb86a2c375/PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"}, + {url = "https://files.pythonhosted.org/packages/83/d4/241a6a518d0bcf0a9fcdcbad5edfed18d43e884317eab8d5230a2b27e206/PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"}, +] +"pyqt5-sip 12.11.0" = [ + {url = "https://files.pythonhosted.org/packages/0a/0a/5386772f8e4fe865d306e9e6d91f3605c09209a3e226919b898270127340/PyQt5_sip-12.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b69a1911f768b489846335e31e49eb34795c6b5a038ca24d894d751e3b0b44da"}, + {url = "https://files.pythonhosted.org/packages/0d/1c/ccc6fd48150c07cbae52f6d2c9c40500e3252aa23cd98d5813973b7a150a/PyQt5_sip-12.11.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:205f3e1b3eea3597d8e878936c1a06e04bd23a59e8b179ee806465d72eea3071"}, + {url = "https://files.pythonhosted.org/packages/0e/bf/af987c45abd8604efcc99551550546f8cdef5e986b258eb09e0e88ede239/PyQt5_sip-12.11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9356260d4feb60dbac0ab66f8a791a0d2cda1bf98c9dec8e575904a045fbf7c5"}, + {url = "https://files.pythonhosted.org/packages/12/0f/54228b287741959a486d0a38a6626e0ba0ffc7c1c8d4fab0951c77c11e3a/PyQt5_sip-12.11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bd733667098cac70e89279d9c239106d543fb480def62a44e6366ccb8f68510b"}, + {url = "https://files.pythonhosted.org/packages/22/9b/deddfddae70651d058df6ca674790c4214d4021b59f22c3ae3a51ceb2551/PyQt5_sip-12.11.0-cp39-cp39-win32.whl", hash = "sha256:686071be054e5be6ca5aaaef7960931d4ba917277e839e2e978c7cbe3f43bb6e"}, + {url = "https://files.pythonhosted.org/packages/25/e3/e869173f015fbff0a6764f002f5217ac2b8da2ae137b2792547bc81c65c0/PyQt5_sip-12.11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f9e312ff8284d6dfebc5366f6f7d103f84eec23a4da0be0482403933e68660"}, + {url = "https://files.pythonhosted.org/packages/29/f9/aed0c4194c1efe8840f658719efad248d10ee797278fecb23dcf236c52f3/PyQt5_sip-12.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:f6b72035da4e8fecbb0bc4a972e30a5674a9ad5608dbddaa517e983782dbf3bf"}, + {url = "https://files.pythonhosted.org/packages/2e/1a/cf06313fb7d19d075d0185fcab5fc64ade7b8429788092fd1e3fd3c4e5bd/PyQt5_sip-12.11.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ec5e9ef78852e1f96f86d7e15c9215878422b83dde36d44f1539a3062942f19c"}, + {url = "https://files.pythonhosted.org/packages/39/5f/fd9384fdcb9cd0388088899c110838007f49f5da1dd1ef6749bfb728a5da/PyQt5_sip-12.11.0.tar.gz", hash = "sha256:b4710fd85b57edef716cc55fae45bfd5bfac6fc7ba91036f1dcc3f331ca0eb39"}, + {url = "https://files.pythonhosted.org/packages/41/b5/473fbe697cfe01b5389c968ba16bbcb6fa4080f594661ed44e8cc3ea5f7a/PyQt5_sip-12.11.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4e5c1559311515291ea0ab0635529f14536954e3b973a7c7890ab7e4de1c2c23"}, + {url = "https://files.pythonhosted.org/packages/53/32/af0a7d44f0abfceae185134efa4ef65770eeac20f21239bdce4aee0460bd/PyQt5_sip-12.11.0-cp311-cp311-win32.whl", hash = "sha256:43dfe6dd409e713edeb67019b85419a7a0dc9923bfc451d6cf3778471c122532"}, + {url = "https://files.pythonhosted.org/packages/6e/8c/00fee4ad4602c3cbf4b73e1b42a0bba1018005633ec7ef831db33dbbd80a/PyQt5_sip-12.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:afa4ffffc54e306669bf2b66ea37abbc56c5cdda4f3f474d20324e3634302b12"}, + {url = "https://files.pythonhosted.org/packages/71/47/a25ac7a064ab715a785bacd6d516e5787e56eaff19285fd0650d5d910a10/PyQt5_sip-12.11.0-cp37-cp37m-win32.whl", hash = "sha256:d12b81c3a08abf7657a2ebc7d3649852a1f327eb2146ebadf45930486d32e920"}, + {url = "https://files.pythonhosted.org/packages/84/43/f64bb816feb9bb9e02d472092199635c243ae86ba12e77528dac05eab8db/PyQt5_sip-12.11.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec1d8ce50be76c5c1d1c86c6dc0ccacc2446172dde98b663a17871f532f9bd44"}, + {url = "https://files.pythonhosted.org/packages/c3/b6/da9a15efacb7b4a73c976e3fe13f9b6ff9694299093bc32d288ff89ddcc4/PyQt5_sip-12.11.0-cp310-cp310-win32.whl", hash = "sha256:ad21ca0ee8cae2a41b61fc04949dccfab6fe008749627d94e8c7078cb7a73af1"}, + {url = "https://files.pythonhosted.org/packages/c7/8b/0f35a6e016c9f49da0cd8834ee2918e4d45dea449fba9ed1321d87826afd/PyQt5_sip-12.11.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:4031547dfb679be309094bfa79254f5badc5ddbe66b9ad38e319d84a7d612443"}, + {url = "https://files.pythonhosted.org/packages/c8/62/cd0c7bab5ee60f8a4416857aa703bb7e285bf09d3b2564e136e55a5c3a79/PyQt5_sip-12.11.0-cp38-cp38-win32.whl", hash = "sha256:9bca450c5306890cb002fe36bbca18f979dd9e5b810b766dce8e3ce5e66ba795"}, + {url = "https://files.pythonhosted.org/packages/d1/85/e30b11daf7b8d4e00ed51c17204ec3df446041206d22087d2c5dc17d8543/PyQt5_sip-12.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:42320e7a94b1085ed85d49794ed4ccfe86f1cae80b44a894db908a8aba2bc60e"}, + {url = "https://files.pythonhosted.org/packages/d2/f9/7f62da7cc4a15d6a9c83a7d18b29ec9a6a04235fb9989973facbfbf51080/PyQt5_sip-12.11.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0f77655c62ec91d47c2c99143f248624d44dd2d8a12d016e7c020508ad418aca"}, + {url = "https://files.pythonhosted.org/packages/ea/58/13becbb979991ea31ac08cdc7a1d3581b0ec9c4c85e76cb39c0ae645c720/PyQt5_sip-12.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:51e377789d59196213eddf458e6927f33ba9d217b614d17d20df16c9a8b2c41c"}, + {url = "https://files.pythonhosted.org/packages/fa/88/d8846f019aa9b2f0e7f0a49e1ee9ba84ac200175d8856178b2167c8e770d/PyQt5_sip-12.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:3126c84568ab341c12e46ded2230f62a9a78752a70fdab13713f89a71cd44f73"}, +] +"pyrsistent 0.19.2" = [ + {url = "https://files.pythonhosted.org/packages/16/dd/17f6f147234f7c99b5a23fdcc318adac1c84ef5d179ce1bfe6afe19213a9/pyrsistent-0.19.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879b4c2f4d41585c42df4d7654ddffff1239dc4065bc88b745f0341828b83e78"}, + {url = "https://files.pythonhosted.org/packages/23/d9/3ffebba2a20084b56f8132ab1fa959161dcf6da4f27c85a4a4c9cc438db1/pyrsistent-0.19.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f1258f4e6c42ad0b20f9cfcc3ada5bd6b83374516cd01c0960e3cb75fdca6770"}, + {url = "https://files.pythonhosted.org/packages/3c/55/666888db7c7070e4d64944ea2a786073e5a53aaaa67efdfe99a6d6c605b0/pyrsistent-0.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:3ba4134a3ff0fc7ad225b6b457d1309f4698108fb6b35532d015dca8f5abed73"}, + {url = "https://files.pythonhosted.org/packages/42/c2/019693915e5d6fc88a145f4a4846fb4dc2a83cfa0a04256c76771eafb015/pyrsistent-0.19.2-cp310-cp310-win32.whl", hash = "sha256:456cb30ca8bff00596519f2c53e42c245c09e1a4543945703acd4312949bfd41"}, + {url = "https://files.pythonhosted.org/packages/44/91/42f8dab8dbe3bb1f07409566085feeb237c28e9cf6e8957aa2377cad78c1/pyrsistent-0.19.2-cp39-cp39-win_amd64.whl", hash = "sha256:dec3eac7549869365fe263831f576c8457f6c833937c68542d08fde73457d291"}, + {url = "https://files.pythonhosted.org/packages/54/d3/986fcfeaf62047840c571a857fb8f3ad1e9622081d5e7d0ee5e3451ca2e7/pyrsistent-0.19.2-py3-none-any.whl", hash = "sha256:ea6b79a02a28550c98b6ca9c35b9f492beaa54d7c5c9e9949555893c8a9234d0"}, + {url = "https://files.pythonhosted.org/packages/54/d8/96308460ee237abf28b7798c491f8545a51e1c3e2f01895da4530c7bff86/pyrsistent-0.19.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:187d5730b0507d9285a96fca9716310d572e5464cadd19f22b63a6976254d77a"}, + {url = "https://files.pythonhosted.org/packages/57/79/562894286bce4d1391abf4efbafa8a4a63ae482e1408ff08bf8a855c8358/pyrsistent-0.19.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e371b844cec09d8dc424d940e54bba8f67a03ebea20ff7b7b0d56f526c71d584"}, + {url = "https://files.pythonhosted.org/packages/5e/f7/31748ccbbe68eb990347f252c4a39fcb1766e22907314922262a1dc5e1f6/pyrsistent-0.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:b39725209e06759217d1ac5fcdb510e98670af9e37223985f330b611f62e7425"}, + {url = "https://files.pythonhosted.org/packages/7d/f2/ad0363cb87673d3b076f6116c5d4385dc01fde8cce8292c7c7e133ba2fff/pyrsistent-0.19.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111156137b2e71f3a9936baf27cb322e8024dac3dc54ec7fb9f0bcf3249e68bb"}, + {url = "https://files.pythonhosted.org/packages/8c/a8/bd70a21552061cc43e4ea9aa329990ca16af0ab747d3f8524d3778aba2f0/pyrsistent-0.19.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd880614c6237243ff53a0539f1cb26987a6dc8ac6e66e0c5a40617296a045e"}, + {url = "https://files.pythonhosted.org/packages/8f/2b/737a53eeb4b2036c58d8ac2e033590f1e4a2ffe7b174850aca4787a899a4/pyrsistent-0.19.2-cp37-cp37m-win32.whl", hash = "sha256:d690b18ac4b3e3cab73b0b7aa7dbe65978a172ff94970ff98d82f2031f8971c2"}, + {url = "https://files.pythonhosted.org/packages/96/ad/a95f96af0f645ff74dc2145ab3d920c77a0d9d547dbaddb721c31bbeec53/pyrsistent-0.19.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d6982b5a0237e1b7d876b60265564648a69b14017f3b5f908c5be2de3f9abb7a"}, + {url = "https://files.pythonhosted.org/packages/99/79/4ba4ef77bce2fb0c1140929e456a5101e5668af3aa46723e0a1daf2f437f/pyrsistent-0.19.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c43bec251bbd10e3cb58ced80609c5c1eb238da9ca78b964aea410fb820d00d6"}, + {url = "https://files.pythonhosted.org/packages/a1/96/15848046e77dfe560f11099face554dc2e4b50f8b28d152be0ed72cd5a08/pyrsistent-0.19.2-cp39-cp39-win32.whl", hash = "sha256:71d332b0320642b3261e9fee47ab9e65872c2bd90260e5d225dabeed93cbd42b"}, + {url = "https://files.pythonhosted.org/packages/a4/8d/7d7e504e14d1030e31e8ea656482e5fe5b556e11ce43ac899cf5ed1467f5/pyrsistent-0.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:9cd3e9978d12b5d99cbdc727a3022da0430ad007dacf33d0bf554b96427f33ab"}, + {url = "https://files.pythonhosted.org/packages/a5/55/165b1e579eeba5711f796362e151a4eba5a7bab6c307af68231b5d49c83f/pyrsistent-0.19.2-cp38-cp38-win32.whl", hash = "sha256:e5d8f84d81e3729c3b506657dddfe46e8ba9c330bf1858ee33108f8bb2adb38a"}, + {url = "https://files.pythonhosted.org/packages/b8/ef/325da441a385a8a931b3eeb70db23cb52da42799691988d8d943c5237f10/pyrsistent-0.19.2.tar.gz", hash = "sha256:bfa0351be89c9fcbcb8c9879b826f4353be10f58f8a677efab0c017bf7137ec2"}, + {url = "https://files.pythonhosted.org/packages/cd/2d/cf7bcdfcf5aba1a68ced5b32620e7c165d0491bd962d6cd1e78b1c1b2d7a/pyrsistent-0.19.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21455e2b16000440e896ab99e8304617151981ed40c29e9507ef1c2e4314ee95"}, + {url = "https://files.pythonhosted.org/packages/d9/85/f175c246505fecf2ba073384e98188216b5f1547a82f8a69c58d78c698d3/pyrsistent-0.19.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a178209e2df710e3f142cbd05313ba0c5ebed0a55d78d9945ac7a4e09d923308"}, + {url = "https://files.pythonhosted.org/packages/d9/ca/47cbb30a777aa74e4c40221385fb0c5eee5adfdb5406d29b4d10170dd243/pyrsistent-0.19.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:055ab45d5911d7cae397dc418808d8802fb95262751872c841c170b0dbf51eed"}, + {url = "https://files.pythonhosted.org/packages/f6/3a/3cfb47a3cfd3993d0f57ba305ef71697d490b8e2efe8c3752bc5b0f321f4/pyrsistent-0.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aede922a488861de0ad00c7630a6e2d57e8023e4be72d9d7147a9fcd2d30712"}, + {url = "https://test-files.pythonhosted.org/packages/0a/59/b0a557f8868d0b0fd0838e4e8736bd3d855814ffc13edd1711c1bb53b00b/pyrsistent-0.19.2-cp310-cp310-win32.whl", hash = "sha256:366d5880bfea17db0701d30ed31d213f3bfb701041f1d3dca8f28c2f5b2e1b57"}, + {url = "https://test-files.pythonhosted.org/packages/11/33/79dd36095faf26cc5283b6845476bf403cf3f9bc4632c8e1d67b21bca507/pyrsistent-0.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:355eb7ba5207b0083d6eac5277d1eb15d7c8ecff501789f90fa2cfefede36aa8"}, + {url = "https://test-files.pythonhosted.org/packages/1e/d9/9fcb5f5c4a97c921e5564a971e34e3f64a78cbe01cc7851e309588371419/pyrsistent-0.19.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bd37861e2ba095ff93a0ae5e03c6ae69493ec6139f9d5612bc4bcc16050e636"}, + {url = "https://test-files.pythonhosted.org/packages/23/a6/a6652b0d30e56c68a8ff083de3c9f3d4e1672de14d06356bf6bf9be72120/pyrsistent-0.19.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e673ad715330f2b77f43be88bd40682bb74f986dcd8ea517acc865b6968282dc"}, + {url = "https://test-files.pythonhosted.org/packages/3e/73/3659176beb7e8e3b344659e239150a64ab6263637ed39cbf78da947dcff7/pyrsistent-0.19.2-cp37-cp37m-win32.whl", hash = "sha256:38e82add4c51dee3af852d687b17d606c8c9620288fbe5c2619ba1becd63bc89"}, + {url = "https://test-files.pythonhosted.org/packages/40/71/ae801fa4edce35d6631678e683de898ebfaf218332068ecc6b880a03a4a9/pyrsistent-0.19.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:656a4ccad1c9bca66cde99fb1a3644cc7d404bb0dbcc46c7b069a9d2afa76764"}, + {url = "https://test-files.pythonhosted.org/packages/54/41/2d741180d5848977e4594301db4dc37a6c357baf91399036b7b7ed85e87d/pyrsistent-0.19.2.tar.gz", hash = "sha256:3798f2578f7df1f073ad77fa0c75f28ad8ae389a3d4d491268e63618530900cc"}, + {url = "https://test-files.pythonhosted.org/packages/57/42/c9a50e25af9dc14c8fb257786247728c1432e8ef2552dcb608b6b918a10a/pyrsistent-0.19.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9ff5be704ea20f4e50f82c3f34c19bb7cf62cd722c7a270211edbb299ab06864"}, + {url = "https://test-files.pythonhosted.org/packages/57/e6/3fd3c54b417b9898ecbd33d4c487e167976204276efae873df2ed4d50b11/pyrsistent-0.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:46123c3478c02a2369c20465a40e012e17a493629ab2dcba4f243adfffb4a5f5"}, + {url = "https://test-files.pythonhosted.org/packages/58/07/5ad851368c22c10c198003b9f6346206a543cab8c45cfc4bfef6f80f4c31/pyrsistent-0.19.2-py3-none-any.whl", hash = "sha256:ce0eec0b2bff4374f45f065f51ba12362328dc6a94204b604974092506513477"}, + {url = "https://test-files.pythonhosted.org/packages/64/c3/b68f450df9084480ef0220c719ddc0ff87477b5315c68020be23066b4445/pyrsistent-0.19.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46b9e70c4816fff946fbb845fc960a3c905a967d258b97d120f3e08eeba312f9"}, + {url = "https://test-files.pythonhosted.org/packages/67/61/99dd9971507f3076486064fd8c9290c187a9278517f266aabccdef32775b/pyrsistent-0.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:2cbb12fbbdad4bc06e4691b7e1b39149eb74e3b96cc64e9077e1c2563763756f"}, + {url = "https://test-files.pythonhosted.org/packages/6e/43/a4cdf6d7febc0b1610a25b02c1477a934efa3a254c1e52879b1b9edcf266/pyrsistent-0.19.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5387688a245d7636e7c608376268fdd6713e686e1b38ce492dcbf015d18191ca"}, + {url = "https://test-files.pythonhosted.org/packages/80/67/6dfc93f8335be5fafac8611b4b5417aa44f1092e3e20ad7d9238266381be/pyrsistent-0.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0add490da5d10f906cea2c3e131f8c86c842b021fb77037c451744afa67f00a4"}, + {url = "https://test-files.pythonhosted.org/packages/90/f2/6f3c8b0ef92520bc929ec0c867003ef2c107d1b9dd706e1e8843b867e5f1/pyrsistent-0.19.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:982753d01827a54e982dee341c68469d1e357224fde5c60c8a32882ebec371ff"}, + {url = "https://test-files.pythonhosted.org/packages/a2/0f/390999a7c47d46f14fbfea7a13d2d2460fa5caeec9f8da564638e5f1c533/pyrsistent-0.19.2-cp39-cp39-win32.whl", hash = "sha256:064da395ebb976c6489a785b3f2b6c7672e38294c450eb7618beb19d5396ca4c"}, + {url = "https://test-files.pythonhosted.org/packages/a6/51/40812842800ac3efb5a662b81322634f6b09e6cb3fbdd14f25df1d1a004a/pyrsistent-0.19.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:445de06b3445903459621ca8da19aa560d8093bc0450bbb91d9e50f9e24ea66f"}, + {url = "https://test-files.pythonhosted.org/packages/c5/7f/ebc7ff7c63331ce0f1973e6ead6ee866b40c83ec71caf0a98dcfb13fc697/pyrsistent-0.19.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b25f04d43e829c61bd67b7e424ac340112dfab1c710e45a5fec7ea044917523"}, + {url = "https://test-files.pythonhosted.org/packages/da/af/b0e16e72699f06584a9187dfdb759a2372f2e5d7bc155db81562c033de87/pyrsistent-0.19.2-cp38-cp38-win32.whl", hash = "sha256:1a342d37e7bc9339ee23eba1fc5a3e0d18cdb8abc3302052fd18ecf21e4ebba3"}, + {url = "https://test-files.pythonhosted.org/packages/ed/f2/3fcb6bc123bb0dbdd46c54a2c2bc2c8654e5f212e84a19b97750601d85e2/pyrsistent-0.19.2-cp39-cp39-win_amd64.whl", hash = "sha256:b51d9226e1d83f6aa5475405fe0ff244b7920dd2346766c5cc278bb0d5991863"}, + {url = "https://test-files.pythonhosted.org/packages/ef/19/99ba519948e1c0a4625258770fc8ee025b4385a364f8856dc80075c50692/pyrsistent-0.19.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:475bcdd31a8751eddad1187da473f367254a9e5294a6c4746ea691511421086a"}, + {url = "https://test-files.pythonhosted.org/packages/f5/b4/6c3c619372ab53a614f6ed3374e7fe52492f4deaf7580f316bdbc5fa1eea/pyrsistent-0.19.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e212ba610996f7f9c5b7925d7c6f4bba984adc6a3e9759fc600dd371ab30338f"}, +] +"pytest 7.2.0" = [ + {url = "https://files.pythonhosted.org/packages/0b/21/055f39bf8861580b43f845f9e8270c7786fe629b2f8562ff09007132e2e7/pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, + {url = "https://files.pythonhosted.org/packages/67/68/a5eb36c3a8540594b6035e6cdae40c1ef1b6a2bfacbecc3d1a544583c078/pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, +] +"pytest-cov 4.0.0" = [ + {url = "https://files.pythonhosted.org/packages/ea/70/da97fd5f6270c7d2ce07559a19e5bf36a76f0af21500256f005a69d9beba/pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, + {url = "https://files.pythonhosted.org/packages/fe/1f/9ec0ddd33bd2b37d6ec50bb39155bca4fe7085fa78b3b434c05459a860e3/pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, +] +"pytz 2022.6" = [ + {url = "https://files.pythonhosted.org/packages/76/63/1be349ff0a44e4795d9712cc0b2d806f5e063d4d34631b71b832fac715a8/pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, + {url = "https://files.pythonhosted.org/packages/85/ac/92f998fc52a70afd7f6b788142632afb27cd60c8c782d1452b7466603332/pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, +] +"ragger 0.7.0" = [ + {url = "https://test-files.pythonhosted.org/packages/09/e3/e96cbe8924818c956a3a250fa7f147989dac83575e4a4ffc7e60519da20d/ragger-0.7.0-py3-none-any.whl", hash = "sha256:7cd2ca1b53edd4c62ca2bb38861f6fa7f8faf4d67c1ec6b97de00dc8b0f97900"}, + {url = "https://test-files.pythonhosted.org/packages/7a/90/539a89548e8fc77041c566ab59a971a3a7cebfe0f2e8b9289a34e09dd379/ragger-0.7.0.tar.gz", hash = "sha256:4a81a226a0f47017835b8c4074a4cae85f61dcad3c510364b12640c2897a94b0"}, +] +"requests 2.28.1" = [ + {url = "https://files.pythonhosted.org/packages/a5/61/a867851fd5ab77277495a8709ddda0861b28163c4613b011bc00228cc724/requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, + {url = "https://files.pythonhosted.org/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c0441b5cda699c90f618b6f8a1b42/requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, +] +"semver 2.13.0" = [ + {url = "https://files.pythonhosted.org/packages/0b/70/b84f9944a03964a88031ef6ac219b6c91e8ba2f373362329d8770ef36f02/semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"}, + {url = "https://files.pythonhosted.org/packages/31/a9/b61190916030ee9af83de342e101f192bbb436c59be20a4cb0cdb7256ece/semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, + {url = "https://test-files.pythonhosted.org/packages/0b/70/b84f9944a03964a88031ef6ac219b6c91e8ba2f373362329d8770ef36f02/semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"}, + {url = "https://test-files.pythonhosted.org/packages/31/a9/b61190916030ee9af83de342e101f192bbb436c59be20a4cb0cdb7256ece/semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, +] +"setuptools 65.5.1" = [ + {url = "https://files.pythonhosted.org/packages/26/f4/ca5cb6df512f453ad50f78900bf7ec6a5491ee44bb49d0f6f76802dbdd43/setuptools-65.5.1.tar.gz", hash = "sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f"}, + {url = "https://files.pythonhosted.org/packages/b6/40/353c051f77ee5618adaf1fd96f4f6bae9714ed0a22c7142c01c24eb77fe4/setuptools-65.5.1-py3-none-any.whl", hash = "sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31"}, +] +"six 1.16.0" = [ + {url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, +] +"speculos 0.1.157" = [ + {url = "https://test-files.pythonhosted.org/packages/2b/0e/64fbe15430b4d0b978c71496f1eaccb00e50d88eb5fd8dd14c776111a21e/speculos-0.1.157-py3-none-any.whl", hash = "sha256:5e6284649675229e7a836d2fa8466054f378e636bdbbe1ddc25160b353bc1ebb"}, + {url = "https://test-files.pythonhosted.org/packages/2d/63/5d6442d5e4249e71e792b23a76f83639051c26023881c589cc9acec4eda2/speculos-0.1.157.tar.gz", hash = "sha256:e432468cf8cc0c19a9b425d18384f56ef6b20e52a7478a6d0cc3122864b01a44"}, +] +"tomli 2.0.1" = [ + {url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +"types-protobuf 3.20.4.5" = [ + {url = "https://files.pythonhosted.org/packages/0a/e3/d80ef95536b29285096f2de61b83e4ef733eb2ef34cbcc4f9e8bd4b40ee1/types-protobuf-3.20.4.5.tar.gz", hash = "sha256:e9b45008d106e1d10cc77a29d2d344b85c0f01e2e643aaccf32f69e9e81b0cdd"}, + {url = "https://files.pythonhosted.org/packages/92/02/cebbb618bf72fb671e9e0a710da26c959579a8964d8df79e1c60b3d38187/types_protobuf-3.20.4.5-py3-none-any.whl", hash = "sha256:97af5ce70d890fdb94cb0c906f5a6624ca2fef58bc04e27990a25509e992a950"}, +] +"urllib3 1.26.12" = [ + {url = "https://files.pythonhosted.org/packages/6f/de/5be2e3eed8426f871b170663333a0f627fc2924cc386cd41be065e7ea870/urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, + {url = "https://files.pythonhosted.org/packages/b2/56/d87d6d3c4121c0bcec116919350ca05dc3afd2eeb7dc88d07e8083f8ea94/urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, +] +"werkzeug 2.2.2" = [ + {url = "https://files.pythonhosted.org/packages/c8/27/be6ddbcf60115305205de79c29004a0c6bc53cec814f733467b1bb89386d/Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, + {url = "https://files.pythonhosted.org/packages/f8/c1/1c8e539f040acd80f844c69a5ef8e2fccdf8b442dabb969e497b55d544e1/Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, + {url = "https://test-files.pythonhosted.org/packages/c8/27/be6ddbcf60115305205de79c29004a0c6bc53cec814f733467b1bb89386d/Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, + {url = "https://test-files.pythonhosted.org/packages/f8/c1/1c8e539f040acd80f844c69a5ef8e2fccdf8b442dabb969e497b55d544e1/Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, +] diff --git a/tests/proto b/tests/proto new file mode 120000 index 00000000..5c8d3525 --- /dev/null +++ b/tests/proto @@ -0,0 +1 @@ +../proto \ No newline at end of file diff --git a/tests/pyproject.toml b/tests/pyproject.toml new file mode 100644 index 00000000..faafe071 --- /dev/null +++ b/tests/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "" +version = "" +description = "" +authors = [ + {name = "", email = "chris@launchbadge.com"}, +] +dependencies = [ + "base58", + "bip32", + "ragger[speculos,tests]", + "protobuf==3.20.3", + "black>=22.10.0", + "mypy-protobuf>=3.3.0", +] +requires-python = ">=3.10" +license = {text = "MIT"} + +[build-system] +requires = ["pdm-pep517>=1.0.0"] +build-backend = "pdm.pep517.api" + +[[tool.pdm.source]] +url = "https://test.pypi.org/simple/" +verify_ssl = true +name = "PyPI Tests" diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 00000000..1151f759 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,4 @@ +base58 +bip32 +ragger[tests,speculos] +protobuf==3.20.3 diff --git a/tests/test_hedera.py b/tests/test_hedera.py new file mode 100644 index 00000000..bbf26221 --- /dev/null +++ b/tests/test_hedera.py @@ -0,0 +1,476 @@ +from ragger.backend.interface import RAPDU, RaisePolicy + +from .apps.hedera import HederaClient, ErrorType +from .apps.hedera_builder import crypto_create_account_conf +from .apps.hedera_builder import crypto_create_account_stake_account_conf +from .apps.hedera_builder import crypto_create_account_stake_node_conf +from .apps.hedera_builder import crypto_create_account_stake_toggle_rewards_conf +from .apps.hedera_builder import account_update_conf +from .apps.hedera_builder import crypto_transfer_token_conf +from .apps.hedera_builder import crypto_transfer_hbar_conf +from .apps.hedera_builder import crypto_transfer_verify +from .apps.hedera_builder import token_associate_conf +from .apps.hedera_builder import token_dissociate_conf +from .apps.hedera_builder import token_burn_conf +from .apps.hedera_builder import token_mint_conf + + +def test_hedera_get_public_key_ok(client, firmware): + hedera = HederaClient(client) + values = [ + (0, "78be747e6894ee5f965e3fb0e4c1628af2f9ae0d94dc01d9b9aab75484c3184b"), + (11095, "644ef690d394e8140fa278273913425bc83c59067a392a9e7f703ead4973caf8"), + (294967295, "02357008e57f96bb250f789c63eb3a241c1eae034d461468b76b8174a59bdc9b"), + ( + 2294967295, + "2cbd40ac0a3e25a315aed7e211fd0056127075dfa4ba1717a7a047a2030b5efb", + ), + ] + for (index, key) in values: + from_public_key = hedera.get_public_key_non_confirm(index).data + assert from_public_key.hex() == key + with hedera.get_public_key_confirm(index): + if firmware.device == "nanos": + hedera.validate() + else: + hedera.validate_screen(1) + + from_public_key = hedera.get_async_response().data + assert from_public_key.hex() == key + + +def test_hedera_get_public_key_refused(client, firmware): + hedera = HederaClient(client) + with hedera.get_public_key_confirm(0): + client.raise_policy = RaisePolicy.RAISE_NOTHING + if firmware.device == "nanos": + hedera.refuse() + else: + hedera.validate_screen(1 + 1) + + rapdu = hedera.get_async_response() + assert rapdu.status == ErrorType.EXCEPTION_USER_REJECTED + + +def test_hedera_crypto_create_account_ok(client, firmware): + hedera = HederaClient(client) + conf = crypto_create_account_conf(initialBalance=5) + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(7) + + +def test_hedera_crypto_create_account_refused(client, firmware): + hedera = HederaClient(client) + conf = crypto_create_account_conf(initialBalance=5) + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + client.raise_policy = RaisePolicy.RAISE_NOTHING + hedera.validate_screen(8) + + rapdu = hedera.get_async_response() + assert rapdu.status == ErrorType.EXCEPTION_USER_REJECTED + + +def test_hedera_crypto_create_account_stake_account(client, firmware): + hedera = HederaClient(client) + conf = crypto_create_account_stake_account_conf() + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(7) + + +def test_hedera_crypto_create_account_stake_node(client, firmware): + hedera = HederaClient(client) + conf = crypto_create_account_stake_node_conf() + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(7) + + +def test_hedera_crypto_create_account_stake_toggle_rewards(client, firmware): + hedera = HederaClient(client) + conf = crypto_create_account_stake_toggle_rewards_conf() + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(7) + + +def test_hedera_account_update(client, firmware): + hedera = HederaClient(client) + conf = account_update_conf() + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(7) + + +def test_hedera_transfer_token_ok(client, firmware): + hedera = HederaClient(client) + conf = crypto_transfer_token_conf( + token_shardNum=15, + token_realmNum=16, + token_tokenNum=17, + sender_shardNum=57, + sender_realmNum=58, + sender_accountNum=59, + recipient_shardNum=100, + recipient_realmNum=101, + recipient_accountNum=102, + amount=1234567890, + decimals=9, + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(7) + + +def test_hedera_transfer_token_refused(client, firmware): + hedera = HederaClient(client) + conf = crypto_transfer_token_conf( + token_shardNum=15, + token_realmNum=16, + token_tokenNum=17, + sender_shardNum=57, + sender_realmNum=58, + sender_accountNum=59, + recipient_shardNum=100, + recipient_realmNum=101, + recipient_accountNum=102, + amount=1234567890, + decimals=9, + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + client.raise_policy = RaisePolicy.RAISE_NOTHING + hedera.validate_screen(7 + 1) + + rapdu = hedera.get_async_response() + assert rapdu.status == ErrorType.EXCEPTION_USER_REJECTED + + +def test_hedera_transfer_hbar_ok(client, firmware): + hedera = HederaClient(client) + conf = crypto_transfer_hbar_conf( + sender_shardNum=57, + sender_realmNum=58, + sender_accountNum=59, + recipient_shardNum=100, + recipient_realmNum=101, + recipient_accountNum=102, + amount=1234567890, + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(7) + + +def test_hedera_transfer_hbar_refused(client, firmware): + hedera = HederaClient(client) + conf = crypto_transfer_hbar_conf( + sender_shardNum=57, + sender_realmNum=58, + sender_accountNum=59, + recipient_shardNum=100, + recipient_realmNum=101, + recipient_accountNum=102, + amount=1234567890, + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + client.raise_policy = RaisePolicy.RAISE_NOTHING + hedera.validate_screen(7 + 1) + + rapdu = hedera.get_async_response() + assert rapdu.status == ErrorType.EXCEPTION_USER_REJECTED + + +def test_hedera_token_associate_ok(client, firmware): + hedera = HederaClient(client) + conf = token_associate_conf( + token_shardNum=57, + token_realmNum=58, + token_tokenNum=59, + sender_shardNum=100, + sender_realmNum=101, + sender_accountNum=102, + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(6) + + +def test_hedera_token_associate_refused(client, firmware): + hedera = HederaClient(client) + conf = token_associate_conf( + token_shardNum=57, + token_realmNum=58, + token_tokenNum=59, + sender_shardNum=100, + sender_realmNum=101, + sender_accountNum=102, + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + client.raise_policy = RaisePolicy.RAISE_NOTHING + hedera.validate_screen(7) + + rapdu = hedera.get_async_response() + assert rapdu.status == ErrorType.EXCEPTION_USER_REJECTED + + +def test_hedera_token_dissociate_ok(client, firmware): + hedera = HederaClient(client) + conf = token_dissociate_conf( + token_shardNum=57, + token_realmNum=58, + token_tokenNum=59, + sender_shardNum=666, + sender_realmNum=666, + sender_accountNum=666, + ) + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(6) + + +def test_hedera_token_dissoicate_refused(client, firmware): + hedera = HederaClient(client) + conf = token_dissociate_conf( + token_shardNum=57, + token_realmNum=58, + token_tokenNum=59, + sender_shardNum=666, + sender_realmNum=666, + sender_accountNum=666, + ) + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + client.raise_policy = RaisePolicy.RAISE_NOTHING + hedera.validate_screen(7) + + rapdu = hedera.get_async_response() + assert rapdu.status == ErrorType.EXCEPTION_USER_REJECTED + + +def test_hedera_token_burn_ok(client, firmware): + hedera = HederaClient(client) + conf = token_burn_conf( + token_shardNum=57, token_realmNum=58, token_tokenNum=59, amount=7745309389 + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(6) + + +def test_hedera_token_burn_refused(client, firmware): + hedera = HederaClient(client) + conf = token_burn_conf( + token_shardNum=57, token_realmNum=58, token_tokenNum=59, amount=7745309389 + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + client.raise_policy = RaisePolicy.RAISE_NOTHING + hedera.validate_screen(7) + + rapdu = hedera.get_async_response() + assert rapdu.status == ErrorType.EXCEPTION_USER_REJECTED + + +def test_hedera_token_mint_ok(client, firmware): + hedera = HederaClient(client) + conf = token_mint_conf( + token_shardNum=57, token_realmNum=58, token_tokenNum=59, amount=7745309389 + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(6) + + +def test_hedera_token_mint_refused(client, firmware): + hedera = HederaClient(client) + conf = token_mint_conf( + token_shardNum=57, token_realmNum=58, token_tokenNum=59, amount=7745309389 + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=5, + memo="this_is_the_memo", + conf=conf, + ): + client.raise_policy = RaisePolicy.RAISE_NOTHING + hedera.validate_screen(7) + + rapdu = hedera.get_async_response() + assert rapdu.status == ErrorType.EXCEPTION_USER_REJECTED + + +def test_hedera_transfer_verify_ok(client, firmware): + hedera = HederaClient(client) + conf = crypto_transfer_verify( + sender_shardNum=57, sender_realmNum=58, sender_accountNum=59 + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=1, + memo="this_is_the_memo", + conf=conf, + ): + hedera.validate_screen(2) + + +def test_hedera_transfer_verify_refused(client, firmware): + hedera = HederaClient(client) + conf = crypto_transfer_verify( + sender_shardNum=57, sender_realmNum=58, sender_accountNum=59 + ) + + with hedera.send_sign_transaction( + index=0, + operator_shard_num=1, + operator_realm_num=2, + operator_account_num=3, + transaction_fee=1, + memo="this_is_the_memo", + conf=conf, + ): + client.raise_policy = RaisePolicy.RAISE_NOTHING + hedera.validate_screen(3) + + rapdu = hedera.get_async_response() + assert rapdu.status == ErrorType.EXCEPTION_USER_REJECTED diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 00000000..88cc0973 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,57 @@ +from typing import Optional, Tuple +from pathlib import Path +from bip32 import HARDENED_INDEX +from enum import IntEnum + +from ragger.backend import SpeculosBackend + + +def app_path_from_app_name(app_dir, app_name: str, device: str) -> Path: + assert app_dir.is_dir(), f"{app_dir} is not a directory" + app_path = app_dir / (app_name + "_" + device + ".elf") + assert app_path.is_file(), f"{app_path} must exist" + return app_path + +def prefix_with_len(to_prefix: bytes) -> bytes: + return len(to_prefix).to_bytes(1, byteorder="big") + to_prefix + +def validate_displayed_message(client: SpeculosBackend, num_screen_skip: int): + for _ in range(num_screen_skip): + client.right_click() + client.both_click() + +# DERIVATION PATHS CALCULATIONS + +class BtcDerivationPathFormat(IntEnum): + LEGACY = 0x00 + P2SH = 0x01 + BECH32 = 0x02 + CASHADDR = 0x03 # Deprecated + BECH32M = 0x04 + +def pack_derivation_path(derivation_path: str) -> bytes: + split = derivation_path.split("/") + assert split.pop(0) == "m", "master expected" + derivation_path: bytes = (len(split)).to_bytes(1, byteorder='big') + for i in split: + if (i[-1] == "'"): + derivation_path += (int(i[:-1]) | HARDENED_INDEX).to_bytes(4, byteorder='big') + else: + derivation_path += int(i).to_bytes(4, byteorder='big') + return derivation_path + +def bitcoin_pack_derivation_path(format: BtcDerivationPathFormat, derivation_path: str) -> bytes: + assert isinstance(format, BtcDerivationPathFormat) + return format.to_bytes(1, "big") + pack_derivation_path(derivation_path) + + +# CURRENCY CONFIG CALCULATIONS + +def create_currency_config(main_ticker: str, application_name: str, sub_coin_config: Optional[Tuple[str, int]] = None) -> bytes: + sub_config: bytes = b"" + if sub_coin_config is not None: + sub_config = prefix_with_len(sub_coin_config[0].encode()) + sub_coin_config[1].to_bytes(1, byteorder="big") + coin_config: bytes = b"" + for element in [main_ticker.encode(), application_name.encode(), sub_config]: + coin_config += prefix_with_len(element) + return coin_config