diff --git a/Cargo.lock b/Cargo.lock index 7e49d98..459f186 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1923,6 +1923,7 @@ dependencies = [ name = "secret-sdk-proto" version = "0.1.0" dependencies = [ + "bumpalo", "cosmos-sdk-proto", "getrandom", "prost", @@ -1932,7 +1933,7 @@ dependencies = [ [[package]] name = "secret-utils" -version = "0.1.0" +version = "0.0.0" dependencies = [ "aes-siv", "async-trait", @@ -1956,20 +1957,29 @@ dependencies = [ [[package]] name = "secretrs" -version = "0.0.1" +version = "0.0.0" dependencies = [ + "aes-siv", + "async-trait", "base64 0.22.0", + "bip32", + "bip39", "color-eyre", "cosmrs", "hex", + "hkdf", + "nanorand", "regex", + "secret-cosmwasm-std", "secret-sdk-proto", - "secret-utils", "serde", "serde_json", + "sha2 0.10.8", + "thiserror", "tokio", "tonic", "tonic-web-wasm-client", + "x25519-dalek", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3da6692..d5ddc0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" -members = ["secret-sdk-proto", "secret-grpc", "secretrs", "secret-utils"] -exclude = ["proto-build"] +members = ["secret-sdk-proto", "secret-grpc", "secretrs"] +exclude = ["proto-build", "secret-utils"] # This patch adds target_arch = "wasm32" configurations to the rpc clients [patch.crates-io] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- 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 - boilerplate 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 [yyyy] [name of copyright owner] - - 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/README.md b/README.md index 99562df..2d0f41f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ +
+ +
+ # Secret Rust -_Insert cool description here._ +An extension of [Cosmos Rust](https://github.com/cosmos/cosmos-rust) for [Secret](https://github.com/scrtlabs/SecretNetwork). ## Crates @@ -20,8 +24,7 @@ _Insert cool description here._ - [x] `proto-build` crate like cosmos-rust and tendermint-rs each have - [x] `secret-sdk-proto` crate that re-exports `cosmos-sdk-proto` and adds secret protobuf structs -- [x] `secretrs` crate that re-exports `cosmrs` and adds secret type conversions - - [x] `secret-utils` provide encryption and decryption tools for use with any client +- [x] `secretrs` crate that re-exports `cosmrs` and adds secret types, encryption tools - [ ] `secret-grpc` crate with full gRPC client implementation - [ ] `secret-grpc-gateway` crate with full gRPC-gateway (REST) client implementation diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..b3dbff0 --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,22 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/banner.webp b/banner.webp new file mode 100644 index 0000000..6ba5a11 Binary files /dev/null and b/banner.webp differ diff --git a/secret-grpc/Cargo.toml b/secret-grpc/Cargo.toml index 91c1a54..f033200 100644 --- a/secret-grpc/Cargo.toml +++ b/secret-grpc/Cargo.toml @@ -2,6 +2,7 @@ name = "secret-grpc" version = "0.1.0" authors = ["Kent"] +license = "Unlicense" edition = "2021" publish = false diff --git a/secret-grpc/LICENSE b/secret-grpc/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/secret-grpc/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 - boilerplate 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 [yyyy] [name of copyright owner] - - 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/secret-sdk-proto/Cargo.toml b/secret-sdk-proto/Cargo.toml index 67fd4ce..4cf5e47 100644 --- a/secret-sdk-proto/Cargo.toml +++ b/secret-sdk-proto/Cargo.toml @@ -2,7 +2,7 @@ name = "secret-sdk-proto" version = "0.1.0" authors = ["Kent"] -license = "Apache-2.0" +license = "Unlicense" repository = "https://github.com/kent-3/secret-rust/tree/main/secret-sdk-proto" description = "Protobuf struct definitions for interacting with Secret" readme = "README.md" diff --git a/secret-sdk-proto/README.md b/secret-sdk-proto/README.md index e6f502a..68c9980 100644 --- a/secret-sdk-proto/README.md +++ b/secret-sdk-proto/README.md @@ -3,7 +3,7 @@ [![Crate][crate-image]][crate-link] [![Docs][docs-image]][docs-link] [![Build Status][build-image]][build-link] -[![Apache 2.0 Licensed][license-image]][license-link] +[![License][license-image]][license-link] ![MSRV][rustc-image] Rust crate for interacting with [Protobufs] defined by [Secret Network]. @@ -23,8 +23,8 @@ This crate is supported on Rust **1.72** or newer. [docs-link]: https://docs.rs/secret-sdk-proto/ [build-image]: https://github.com/kent-3/secret-rust/workflows/secret-sdk-proto/badge.svg [build-link]: https://github.com/kent-3/secret-rust/actions/workflows/secret-sdk-proto.yml -[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg -[license-link]: https://github.com/kent-3/secret-rust/blob/master/LICENSE +[license-image]: https://img.shields.io/badge/license-Unlicense-blue.svg +[license-link]: https://github.com/kent-3/secret-rust/blob/master/UNLICENSE [rustc-image]: https://img.shields.io/badge/rustc-1.72+-blue.svg [//]: # "links" [Protobufs]: https://github.com/scrtlabs/SecretNetwork/tree/master/proto/secret diff --git a/secret-utils/Cargo.toml b/secret-utils/Cargo.toml index 3e5904b..b3f2bff 100644 --- a/secret-utils/Cargo.toml +++ b/secret-utils/Cargo.toml @@ -1,6 +1,11 @@ [package] name = "secret-utils" -version = "0.1.0" +version = "0.0.0" +authors = ["Kent"] +license = "Unlicense" +repository = "https://github.com/kent-3/secret-rust/tree/main/secret-utils" +# description = "" +readme = "README.md" edition = "2021" [dependencies] diff --git a/secretrs/Cargo.toml b/secretrs/Cargo.toml index 34d1f59..b48f54c 100644 --- a/secretrs/Cargo.toml +++ b/secretrs/Cargo.toml @@ -1,16 +1,20 @@ [package] name = "secretrs" -version = "0.0.1" +version = "0.0.0" authors = ["Kent"] -license = "Apache-2.0" +license = "Unlicense" repository = "https://github.com/kent-3/secret-rust/tree/main/secretrs" -description = "" +description = "An extension of `cosmrs` for Secret." readme = "README.md" categories = ["cryptography", "cryptography::cryptocurrencies"] keywords = ["blockchain", "cosmos"] edition = "2021" rust-version = "1.72" +[[example]] +name = "encrypt" +path = "examples/encrypt.rs" + [[example]] name = "query" path = "examples/query.rs" @@ -22,9 +26,30 @@ path = "examples/contract_query.rs" required-features = ["grpc"] [dependencies] + +# blockchain cosmrs = { version = "0.16.0" } secret-sdk-proto = { version = "0.1.0", default-features = false, path = "../secret-sdk-proto" } -secret-utils = { path = "../secret-utils" } +cosmwasm-std = { version = "=1.1.11", package = "secret-cosmwasm-std" } +bip32 = "0.5.1" +bip39 = "2.0.0" + +# general +async-trait = "0.1.80" +serde = "1.0.198" +serde_json = "1.0.116" +nanorand = { version = "0.7.0", features = ["getrandom", "zeroize"] } + +# crypto +base64 = "0.22.0" +hex = "0.4.3" +hkdf = "0.12.4" +sha2 = "0.10.8" +aes-siv = "0.7.0" +x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } + +# errors +thiserror = "1.0.51" [features] default = ["bip32"] @@ -52,3 +77,7 @@ tonic-web-wasm-client = "0.5.1" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio = { version = "1.37", features = ["rt", "sync", "time", "macros"] } tonic = { version = "0.11.0", features = ["tls", "tls-webpki-roots", "transport"] } + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/secretrs/README.md b/secretrs/README.md index b084160..e4dc6ed 100644 --- a/secretrs/README.md +++ b/secretrs/README.md @@ -3,7 +3,7 @@ [![Crate][crate-image]][crate-link] [![Docs][docs-image]][docs-link] [![Build Status][build-image]][build-link] -[![Apache 2.0 Licensed][license-image]][license-link] +[![License][license-image]][license-link] ![MSRV][rustc-image] ## Examples @@ -24,6 +24,6 @@ This crate is supported on Rust **1.72** or newer. [docs-link]: https://docs.rs/secretrs/ [build-image]: https://github.com/kent-3/secret-rust/workflows/secretrs/badge.svg [build-link]: https://github.com/kent-3/secret-rust/actions/workflows/secretrs.yml -[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg -[license-link]: https://github.com/kent-3/secret-rust/blob/master/LICENSE +[license-image]: https://img.shields.io/badge/license-Unlicense-blue.svg +[license-link]: https://github.com/kent-3/secret-rust/blob/master/UNLICENSE [rustc-image]: https://img.shields.io/badge/rustc-1.72+-blue.svg diff --git a/secretrs/examples/contract_query.rs b/secretrs/examples/contract_query.rs index fbe06e4..2f62c76 100644 --- a/secretrs/examples/contract_query.rs +++ b/secretrs/examples/contract_query.rs @@ -46,7 +46,7 @@ async fn main() -> Result<()> { // Encryption Utils section - let account = secret_utils::Account::random(); + let account = secretrs::utils::Account::random(); let (nonce, encrypted) = encrypt_msg(&query, &code_hash, &account, &enclave_key).await?; let decrypter = decrypter(&nonce, &account, &enclave_key).await?; @@ -84,9 +84,9 @@ async fn main() -> Result<()> { let encrypted_bytes = BASE64_STANDARD.decode(&caps[1])?; let decrypted_bytes = decrypter.decrypt(&encrypted_bytes)?; let decrypted_string = String::from_utf8(decrypted_bytes)?; - Err(secret_utils::Error::Generic(decrypted_string)) + Err(secretrs::utils::Error::Generic(decrypted_string)) } else { - Err(secret_utils::Error::Generic(error_message.to_string())) + Err(secretrs::utils::Error::Generic(error_message.to_string())) } } }?; diff --git a/secretrs/examples/encrypt.rs b/secretrs/examples/encrypt.rs new file mode 100644 index 0000000..140ab6e --- /dev/null +++ b/secretrs/examples/encrypt.rs @@ -0,0 +1,30 @@ +use color_eyre::Result; +use serde::{Deserialize, Serialize}; + +const CODE_HASH: &str = "9a00ca4ad505e9be7e6e6dddf8d939b7ec7e9ac8e109c8681f10db9cacb36d42"; +const TESTNET_ENCLAVE_KEY: &str = + "e24a22b31e3d34e0e00bcd32189548f1ccbdc9cda8f5a266219b908582b6f03f"; + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + TokenInfo {}, +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + let query = QueryMsg::TokenInfo {}; + let account = secretrs::utils::Account::random(); + + let (_nonce, encrypted) = + secretrs::utils::encrypt_msg(&query, CODE_HASH, &account, TESTNET_ENCLAVE_KEY).await?; + + println!("{}", hex::encode(&encrypted)); + + // Use this to decrypt responses from the enclave: + // + // let decrypter = secret_utils::decrypter(&nonce, &account, TESTNET_ENCLAVE_KEY).await?; + // let decrypted_bytes = decrypter.decrypt(&response.data)?; + + Ok(()) +} diff --git a/secretrs/src/lib.rs b/secretrs/src/lib.rs index 6a96a20..58b8158 100644 --- a/secretrs/src/lib.rs +++ b/secretrs/src/lib.rs @@ -2,8 +2,7 @@ pub use cosmrs::*; pub use secret_sdk_proto::{self as proto, SECRET_VERSION}; pub mod compute; - -pub use secret_utils as utils; +pub mod utils; #[cfg(feature = "grpc-core")] pub use crate::tonic::{query_clients::*, service_clients::*}; diff --git a/secretrs/src/utils/account.rs b/secretrs/src/utils/account.rs new file mode 100644 index 0000000..4dcbe37 --- /dev/null +++ b/secretrs/src/utils/account.rs @@ -0,0 +1,114 @@ +use cosmrs::{ + crypto::{secp256k1::SigningKey, PublicKey}, + AccountId, +}; +use cosmwasm_std::Addr; + +use crate::utils::{ + consts, + crypto::{self, Key}, +}; + +#[derive(Clone)] +pub struct Account { + prvk: bip32::XPrv, + pubk: PublicKey, +} + +impl Account { + pub fn from_mnemonic(s: &str) -> Option { + let mnemonic = bip39::Mnemonic::parse(s).ok()?; + // empty passphrase + Some(Account::from_seed(mnemonic.to_seed(""))) + } + + pub fn from_seed(seed: [u8; 64]) -> Account { + let path = consts::SCRT_DERIVATION_PATH + .parse() + .expect("invalid scrt derivation path"); + let prvk = + bip32::XPrv::derive_from_path(seed, &path).expect("private key derivation failed"); + let pubk = SigningKey::from(&prvk).public_key(); + Account { prvk, pubk } + } + + pub fn random() -> Account { + use nanorand::rand::Rng; + let mut seed = [0; 64]; + let mut rng = nanorand::rand::ChaCha8::new(); + rng.fill_bytes(&mut seed); + + let path = consts::SCRT_DERIVATION_PATH + .parse() + .expect("invalid scrt derivation path"); + let prvk = + bip32::XPrv::derive_from_path(seed, &path).expect("private key derivation failed"); + let pubk = SigningKey::from(&prvk).public_key(); + Account { prvk, pubk } + } + + pub fn addr(&self) -> Addr { + Addr::unchecked(self.id().as_ref()) + } + + #[allow(unused)] + pub(crate) fn signing_key(&self) -> SigningKey { + SigningKey::from(&self.prvk) + } + + pub(crate) fn id(&self) -> AccountId { + self.pubk + .account_id(consts::CHAIN_PREFIX) + .expect("invalid public key type") + } + + pub(crate) fn prv_pub_bytes(&self) -> (Key, Key) { + crypto::edd25519_keys(&self.prvk) + } +} + +pub fn a() -> Account { + Account::from_mnemonic(A_MNEMONIC).unwrap() +} + +pub fn b() -> Account { + Account::from_mnemonic(B_MNEMONIC).unwrap() +} + +pub fn c() -> Account { + Account::from_mnemonic(C_MNEMONIC).unwrap() +} + +pub fn d() -> Account { + Account::from_mnemonic(D_MNEMONIC).unwrap() +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn accounts_from_mnemonic() { + assert_eq!( + a().addr(), + Addr::unchecked("secret1ap26qrlp8mcq2pg6r47w43l0y8zkqm8a450s03") + ); + assert_eq!( + b().addr(), + Addr::unchecked("secret1fc3fzy78ttp0lwuujw7e52rhspxn8uj52zfyne") + ); + assert_eq!( + c().addr(), + Addr::unchecked("secret1ajz54hz8azwuy34qwy9fkjnfcrvf0dzswy0lqq") + ); + assert_eq!( + d().addr(), + Addr::unchecked("secret1ldjxljw7v4vk6zhyduywh04hpj0jdwxsmrlatf") + ); + } +} + +static A_MNEMONIC: &str = "grant rice replace explain federal release fix clever romance raise often wild taxi quarter soccer fiber love must tape steak together observe swap guitar"; +static B_MNEMONIC: &str = "jelly shadow frog dirt dragon use armed praise universe win jungle close inmate rain oil canvas beauty pioneer chef soccer icon dizzy thunder meadow"; +static C_MNEMONIC: &str = "chair love bleak wonder skirt permit say assist aunt credit roast size obtain minute throw sand usual age smart exact enough room shadow charge"; +static D_MNEMONIC: &str = "word twist toast cloth movie predict advance crumble escape whale sail such angry muffin balcony keen move employ cook valve hurt glimpse breeze brick"; diff --git a/secretrs/src/utils/consts.rs b/secretrs/src/utils/consts.rs new file mode 100644 index 0000000..fe21295 --- /dev/null +++ b/secretrs/src/utils/consts.rs @@ -0,0 +1,11 @@ +#![allow(unused)] + +// Chain info +pub static CHAIN_PREFIX: &str = "secret"; +pub static SCRT_DERIVATION_PATH: &str = "m/44'/529'/0'/0/0"; +pub static COIN_DENOM: &str = "uscrt"; + +// Host info +pub static TESTNET_HOST: &str = "http://rpc.testnet.secretsaturn.net"; +pub static TESTNET_ENCLAVE_KEY: &str = + "e24a22b31e3d34e0e00bcd32189548f1ccbdc9cda8f5a266219b908582b6f03f"; diff --git a/secretrs/src/utils/crypto/cert.rs b/secretrs/src/utils/crypto/cert.rs new file mode 100644 index 0000000..9685820 --- /dev/null +++ b/secretrs/src/utils/crypto/cert.rs @@ -0,0 +1,68 @@ +#![allow(unused)] + +use super::Key; +use base64::prelude::{Engine, BASE64_STANDARD}; + +#[derive(Debug, thiserror::Error)] +pub enum MalformedError { + #[error("The DER byte buffer is too short")] + IncorrectLength, + #[error("The Netscape Comment is invalid utf8: {0}")] + Utf8(#[from] std::str::Utf8Error), + #[error("The Netscape Comment has invalid base64 encoding: {0}")] + Base64(#[from] base64::DecodeError), +} + +// https://github.com/scrtlabs/SecretNetwork/blob/19bbd80307b4d6b49f04ad5c62008a3f25ba3f1e/cosmwasm/enclaves/execute/src/registration/cert.rs#L213 +fn extract_asn1_value<'a>(cert: &'a [u8], oid: &[u8]) -> Result<&'a [u8], MalformedError> { + let mut offset = cert + .windows(oid.len()) + .position(|window| window == oid) + .ok_or(MalformedError::IncorrectLength)?; + + offset += 12; // 11 + TAG (0x04) + + if offset + 2 >= cert.len() { + return Err(MalformedError::IncorrectLength); + } + + // Obtain Netscape Comment length + let mut len = cert[offset] as usize; + if len > 0x80 { + len = (cert[offset + 1] as usize) * 0x100 + (cert[offset + 2] as usize); + offset += 2; + } + + // Obtain Netscape Comment + offset += 1; + + if offset + len >= cert.len() { + return Err(MalformedError::IncorrectLength); + } + + Ok(&cert[offset..offset + len]) +} + +fn extract_netscape_comment(cert_der: &[u8]) -> Result<&[u8], MalformedError> { + // Search for Netscape Comment OID + let ns_cmt_oid = &[ + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x42, 0x01, 0x0D, + ]; + extract_asn1_value(cert_der, ns_cmt_oid) +} + +pub(crate) fn consensus_io_pubk(cert_der: &[u8]) -> Result { + // localsecret used software SGX so we can just deserialise the payload: + // https://github.com/scrtlabs/SecretNetwork/blob/19bbd80307b4d6b49f04ad5c62008a3f25ba3f1e/x/registration/remote_attestation/remote_attestation.go#L25 + let buf = extract_netscape_comment(cert_der)?; + let b64 = std::str::from_utf8(buf)?; + let pubk = BASE64_STANDARD.decode(b64)?; + + if pubk.len() < super::KEY_LEN { + return Err(MalformedError::IncorrectLength); + } + + let pubk = super::clone_into_key(&pubk); + + Ok(pubk) +} diff --git a/secretrs/src/utils/crypto/mod.rs b/secretrs/src/utils/crypto/mod.rs new file mode 100644 index 0000000..7a74c6b --- /dev/null +++ b/secretrs/src/utils/crypto/mod.rs @@ -0,0 +1,173 @@ +use aes_siv::{siv::Aes128Siv as Siv, Key as SivKey, KeyInit}; + +pub mod cert; + +const KEY_LEN: usize = 256 / 8; // 32 bytes +const NONCE_LEN: usize = 32; + +pub type Nonce = [u8; NONCE_LEN]; +pub type Key = [u8; KEY_LEN]; + +#[derive(Debug, thiserror::Error)] +pub enum CryptoError { + #[error("Incorrect key length, expected 32 byte key")] + IncorrectKeyLength, + #[error("Encryption failed")] + Encrypt, + #[error("Decryption failed")] + Decrypt, +} + +pub fn edd25519_keys(secret: &bip32::XPrv) -> (Key, Key) { + let secret = clone_into_key(&secret.private_key().to_bytes()); + let secret = x25519_dalek::StaticSecret::from(secret); + let public = x25519_dalek::PublicKey::from(&secret); + (secret.to_bytes(), public.to_bytes()) +} + +pub fn encrypt( + secret: &Key, + public: &Key, + peer: &Key, + plaintext: &[u8], +) -> Result<(Nonce, Vec), CryptoError> { + let nonce = generate_nonce(); + + let shared_secret = encryption_key(secret, peer, &nonce)?; + + let mut cipher = Siv::new(&SivKey::::from(shared_secret)); + + let ciphertext = cipher + .encrypt([[]], plaintext) + .map_err(|_| CryptoError::Encrypt)?; + + let ciphertext = [nonce.as_slice(), public.as_slice(), &ciphertext].concat(); + + Ok((nonce, ciphertext)) +} + +pub fn decrypt( + secret: &Key, + peer: &Key, + nonce: &Nonce, + ciphertext: &[u8], +) -> Result, CryptoError> { + let shared_secret = encryption_key(secret, peer, nonce)?; + + let mut cipher = Siv::new(&SivKey::::from(shared_secret)); + + let plaintext = cipher + .decrypt([[]], ciphertext) + .map_err(|_| CryptoError::Decrypt)?; + + Ok(plaintext) +} + +#[derive(Debug, Clone, Copy)] +pub struct Decrypter { + secret: Key, + peer: Key, + nonce: Nonce, +} + +impl Decrypter { + pub fn new(secret: Key, peer: Key, nonce: Nonce) -> Self { + Decrypter { + secret, + peer, + nonce, + } + } + + pub fn decrypt(&self, ciphertext: &[u8]) -> Result, CryptoError> { + decrypt(&self.secret, &self.peer, &self.nonce, ciphertext) + } +} + +fn generate_nonce() -> Nonce { + use nanorand::rand::Rng; + let mut nonce = [0; NONCE_LEN]; + let mut rng = nanorand::rand::ChaCha8::new(); + rng.fill_bytes(&mut nonce); + nonce +} + +fn encryption_key(secret: &Key, public: &Key, nonce: &Nonce) -> Result { + let secret = x25519_dalek::StaticSecret::from(*secret); + let public = x25519_dalek::PublicKey::from(*public); + let shared = secret.diffie_hellman(&public); + + let ikm = &[shared.as_bytes(), nonce.as_slice()].concat(); + + let mut key = [0u8; KEY_LEN]; + hkdf::Hkdf::::new(Some(&HKDF_SALT), ikm) + .expand(&[], &mut key) + .map_err(|_| CryptoError::IncorrectKeyLength)?; + + Ok(key) +} + +// TODO - this could panic. impl TryFrom or something +pub fn clone_into_key(slice: &[u8]) -> Key { + let mut key = Default::default(); + Key::as_mut(&mut key).clone_from_slice(slice); + key +} + +static HKDF_SALT: [u8; 32] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x4b, 0xea, 0xd8, 0xdf, 0x69, 0x99, + 0x08, 0x52, 0xc2, 0x02, 0xdb, 0x0e, 0x00, 0x97, 0xc1, 0xa1, 0x2e, 0xa6, 0x37, 0xd7, 0xe9, 0x6d, +]; + +#[cfg(test)] +mod test { + use crate::utils::account::Account; + + use super::*; + + fn gen_seed() -> [u8; 64] { + use nanorand::rand::Rng; + let mut seed = [0; 64]; + let mut rng = nanorand::rand::ChaCha8::new(); + rng.fill_bytes(&mut seed); + seed + } + + fn gen_keypair() -> (Key, Key) { + let acc = Account::from_seed(gen_seed()); + acc.prv_pub_bytes() + } + + fn gen_bip32_key() -> bip32::XPrv { + bip32::XPrv::new(gen_seed()).unwrap() + } + + #[test] + fn x25519_works() { + let my_key = gen_bip32_key(); + let peer_key = gen_bip32_key(); + + let my_priv = clone_into_key(&my_key.private_key().to_bytes()); + let my_priv = x25519_dalek::StaticSecret::from(my_priv); + let my_pub = x25519_dalek::PublicKey::from(&my_priv); + + let peer_priv = clone_into_key(&peer_key.private_key().to_bytes()); + let peer_priv = x25519_dalek::StaticSecret::from(peer_priv); + let peer_pub = x25519_dalek::PublicKey::from(&peer_priv); + + let my_shared = my_priv.diffie_hellman(&peer_pub); + let peer_shared = peer_priv.diffie_hellman(&my_pub); + + assert_eq!(my_shared.to_bytes(), peer_shared.to_bytes()); + } + + #[test] + fn shared_encryption_key() { + let (my_priv, my_pub) = gen_keypair(); + let (peer_priv, peer_pub) = gen_keypair(); + let nonce = generate_nonce(); + let my_shared_key = encryption_key(&my_priv, &peer_pub, &nonce).unwrap(); + let peer_shared_key = encryption_key(&peer_priv, &my_pub, &nonce).unwrap(); + assert_eq!(my_shared_key, peer_shared_key) + } +} diff --git a/secretrs/src/utils/error.rs b/secretrs/src/utils/error.rs new file mode 100644 index 0000000..a40fb45 --- /dev/null +++ b/secretrs/src/utils/error.rs @@ -0,0 +1,45 @@ +pub type Result = std::result::Result; + +pub use crate::utils::crypto::cert::MalformedError; +pub use crate::utils::crypto::CryptoError; +pub use crate::utils::types::ParseError; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + Generic(String), + #[error("Failed to read contract file: {0} - {1}")] + ContractFile(String, std::io::Error), + #[error("Contract with label {0} already deployed")] + ContractLabelExists(String), + #[error("Contract Info not found for code id: {0}")] + ContractInfoNotFound(crate::utils::CodeId), + #[error("Timed out waiting for first block after {0} seconds")] + FirstBlockTimeout(u128), + #[error("ABCI Query failed: {0}")] + AbciQuery(String), + #[error("CosmWasm Error: {0}")] + CosmWasm(#[from] cosmwasm_std::StdError), + #[error("Account {0} not found")] + AccountNotFound(cosmwasm_std::Addr), + #[error("CosmRs error: {0}")] + CosmRs(#[from] cosmrs::ErrorReport), + #[error("Broadcast error - check tx failed: {0}")] + BroadcastTxCheck(String), + #[error("Broadcast error - deliver tx failed: {0}")] + BroadcastTxDeliver(String), + #[error("Failed to parse message response: {0}")] + ParseMsgResponse(#[from] ParseError), + #[error("Parsing TEE cert failed: {0}")] + ParseTEECert(#[from] MalformedError), + #[error("Cryptographic error: {0}")] + Crypto(#[from] CryptoError), + #[error("Failed to deserialise JSON response: {0}")] + Json(#[from] serde_json::Error), + #[error("Failed to decode Base64 response: {0}")] + Base64(#[from] base64::DecodeError), + #[error(transparent)] + Utf8(#[from] std::string::FromUtf8Error), + #[error(transparent)] + ParseHex(#[from] hex::FromHexError), +} diff --git a/secretrs/src/utils/mod.rs b/secretrs/src/utils/mod.rs new file mode 100644 index 0000000..94a8f7f --- /dev/null +++ b/secretrs/src/utils/mod.rs @@ -0,0 +1,64 @@ +pub mod account; +pub(crate) mod consts; +pub(crate) mod crypto; +pub mod error; +pub mod types; + +pub use crate::utils::error::Error; +pub use account::Account; +pub use types::{CodeHash, CodeId, Contract, TxResponse}; + +use crate::utils::error::Result; +use crypto::{Decrypter, Nonce}; +use std::str::FromStr; + +// IDEA - for a nice API, take as input only a code_hash, enclave_public_key, and some entropy. +// do a sha256 on the entropy to get a [u8;32] to use as the prv_key. generate a pub_key. +// return a struct that has methods for encrypt and decrypt +// +// let enigma = secretrs::Enigma::new(code_hash, enclave_public_key, entropy); +// let encrypted = enigma.encrypt(msg); +// +// OR +// +// let (encrypter, decrypter) = secretrs::Enigma::new(code_hash, enclave_public_key, entropy); +// let encrypted = encrypter.encrypt(msg); + +// TODO - instead of `Account`, accept a key / seed [u8; 32] +// For queries, the specific keys don't matter, so it doesn't make sense to require `Account` +// as input. You only need to provide a private key / seed, and it returns a prv_pub pair. +pub async fn encrypt_msg( + msg: &M, + code_hash: &str, + account: &Account, + enclave_public_key: &str, +) -> Result<(Nonce, Vec)> { + let code_hash = CodeHash::from_str(code_hash)?; + + let enclave_key = crypto::clone_into_key(&hex::decode(enclave_public_key)?); + let msg = serde_json::to_vec(msg).expect("msg cannot be serialized as JSON"); + let plaintext = [code_hash.to_hex_string().as_bytes(), msg.as_slice()].concat(); + encrypt_msg_raw(&plaintext, account, enclave_key).await +} + +async fn encrypt_msg_raw( + msg: &[u8], + account: &Account, + enclave_public_key: crypto::Key, +) -> Result<(Nonce, Vec)> { + let (prvk, pubk) = account.prv_pub_bytes(); + let io_key = enclave_public_key; + let nonce_ciphertext = crypto::encrypt(&prvk, &pubk, &io_key, msg)?; + Ok(nonce_ciphertext) +} + +pub async fn decrypter( + nonce: &Nonce, + account: &Account, + enclave_public_key: &str, +) -> Result { + let (secret, _) = account.prv_pub_bytes(); + let enclave_key = crypto::clone_into_key(&hex::decode(enclave_public_key)?); + let io_key = enclave_key; + Ok(Decrypter::new(secret, io_key, *nonce)) +} diff --git a/secretrs/src/utils/types.rs b/secretrs/src/utils/types.rs new file mode 100644 index 0000000..736b79c --- /dev/null +++ b/secretrs/src/utils/types.rs @@ -0,0 +1,219 @@ +#![allow(unused)] + +use std::{collections::HashMap, str::FromStr}; + +use cosmrs::AccountId; + +use crate::utils::consts::CHAIN_PREFIX; + +#[derive(Debug, thiserror::Error)] +pub enum ParseError { + #[error("Failed to parse code id: {0}")] + CodeId(#[from] ParseCodeIdError), + #[error("Failed to parse code hash: {0}")] + CodeHash(#[from] hex::FromHexError), + #[error("Failed to parse contract address: {0}")] + ContractAddress(#[from] cosmrs::ErrorReport), + #[error("Failed to parse contract initialisation: {0}")] + ContractInit(#[from] ParseContractInitError), +} + +#[derive(Debug, Clone, Copy)] +pub struct CodeId(u64); + +impl std::fmt::Display for CodeId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for u64 { + fn from(ci: CodeId) -> Self { + ci.0 + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ParseCodeIdError { + #[error(transparent)] + Utf8(#[from] std::str::Utf8Error), + #[error(transparent)] + ParseInt(#[from] std::num::ParseIntError), +} + +impl TryFrom> for CodeId { + type Error = ParseError; + + fn try_from(value: Vec) -> Result { + fn parse(value: &[u8]) -> Result { + let s = std::str::from_utf8(value)?; + let code_id = s.parse()?; + Ok(CodeId(code_id)) + } + parse(&value).map_err(Self::Error::from) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ContractInit(AccountId); + +impl ContractInit { + pub fn into_contract(self, code_hash: CodeHash) -> Contract { + Contract { + id: self.0, + code_hash, + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ParseContractInitError { + #[error("Failed to parse address bytes: {0}")] + Address(#[from] cosmrs::ErrorReport), +} + +impl TryFrom> for ContractInit { + type Error = ParseError; + + fn try_from(value: Vec) -> Result { + fn parse(value: &[u8]) -> Result { + let id = AccountId::new(CHAIN_PREFIX, value)?; + Ok(ContractInit(id)) + } + parse(&value).map_err(Self::Error::from) + } +} + +#[derive(Debug, Clone)] +pub struct Contract { + id: AccountId, + code_hash: CodeHash, +} + +impl Contract { + pub fn try_from_address_with_code_hash( + address: &str, + code_hash: &str, + ) -> Result { + let id = AccountId::from_str(address)?; + let code_hash = CodeHash::from_str(code_hash)?; + Ok(Contract { id, code_hash }) + } + pub fn addr(&self) -> cosmwasm_std::Addr { + cosmwasm_std::Addr::unchecked(self.id.as_ref()) + } + + pub fn code_hash_string(&self) -> String { + self.code_hash.to_hex_string() + } + + pub(crate) fn id(&self) -> AccountId { + self.id.clone() + } + + pub(crate) fn code_hash(&self) -> &CodeHash { + &self.code_hash + } +} + +#[derive(Debug, Clone)] +pub struct CodeHash(Vec); + +impl From> for CodeHash { + fn from(b: Vec) -> Self { + CodeHash(b) + } +} + +impl FromStr for CodeHash { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + hex::decode(s).map(CodeHash).map_err(ParseError::from) + } +} + +impl CodeHash { + pub fn as_bytes(&self) -> &[u8] { + self.0.as_slice() + } + + pub fn to_hex_string(&self) -> String { + use core::fmt::Write; + let mut s = String::with_capacity(2 * self.0.len()); + for byte in self.as_bytes() { + write!(s, "{:02X}", byte).expect("could not write byte as hex to string"); + } + s + } +} + +impl std::fmt::Display for CodeHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_hex_string()) + } +} + +#[derive(Debug)] +pub struct Event { + pub(crate) _type: String, + pub(crate) attrs: HashMap, +} + +#[derive(Debug)] +pub struct TxResponse { + pub response: Option, + pub gas_used: u64, + pub(crate) events: Vec, +} + +impl TxResponse { + pub fn event_attr(&self, event_type: &str, attr: &str) -> Option<&str> { + self.events + .iter() + .find(|e| e._type == event_type) + .and_then(|e| e.attrs.get(attr)) + .map(String::as_str) + } + + /// panics if the response is `None` + pub fn into_inner(self) -> T { + self.response.unwrap() + } + + pub(crate) fn map U>(self, f: F) -> TxResponse { + TxResponse { + response: self.response.map(f), + gas_used: self.gas_used, + events: self.events, + } + } + + pub(crate) fn try_map(self, f: F) -> crate::utils::error::Result> + where + crate::utils::error::Error: From, + F: FnOnce(T) -> Result, + { + let response = self.response.map(f).transpose()?; + Ok(TxResponse { + response, + gas_used: self.gas_used, + events: self.events, + }) + } +} + +#[derive(Debug)] +pub(crate) struct AccountInfo { + pub account_number: u64, + pub sequence_number: cosmrs::tx::SequenceNumber, +} + +impl From for AccountInfo { + fn from(ba: cosmrs::proto::cosmos::auth::v1beta1::BaseAccount) -> Self { + AccountInfo { + account_number: ba.account_number, + sequence_number: ba.sequence, + } + } +}