diff --git a/.github/workflows/publish-android.yaml b/.github/workflows/publish-android.yaml new file mode 100644 index 0000000..bca9a77 --- /dev/null +++ b/.github/workflows/publish-android.yaml @@ -0,0 +1,26 @@ +name: Publish Android +on: + release: + types: [ created ] +jobs: + publish-package: + runs-on: [ "macos-14" ] + env: + TERM: xterm + GITHUB_TOKEN: ${{ secrets.git_pass }} + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + - name: Build and Test Android + run: | + brew install protobuf + cargo clean + make clean-mobile-build + make test-android + - name: Publish Package + run: | + export LIB_VERSION=${GITHUB_REF#refs/tags/} + echo "Publishing version $LIB_VERSION" + make publish-android \ No newline at end of file diff --git a/.github/workflows/publish-ios.yaml b/.github/workflows/publish-ios.yaml new file mode 100644 index 0000000..1d7f824 --- /dev/null +++ b/.github/workflows/publish-ios.yaml @@ -0,0 +1,91 @@ +name: Publish iOS +on: + release: + types: [ created ] +permissions: + contents: write +jobs: + upload-xcframework: + runs-on: [ "macos-14" ] + env: + TERM: xterm + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + - name: Build and Test Framework + run: | + brew install protobuf + cargo clean + make clean-mobile-build + make test-ios + - name: Generate checksum + run: | + swift package compute-checksum packages/kos-mobile/ios/XCFrameworks/KOSMobile.xcframework.zip > checksum.txt + - name: Upload checksum + uses: actions/upload-artifact@v4 + with: + name: checksum + path: checksum.txt + - name: Upload Release Asset + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.git_pass }} + with: + name: ${{ github.event.release.name }} + files: ./packages/kos-mobile/ios/XCFrameworks/KOSMobile.xcframework.zip + + create-ios-release: + needs: upload-xcframework + runs-on: [ "macos-14" ] + env: + TERM: xterm + GITHUB_TOKEN: ${{ secrets.git_pass }} + steps: + - name: Checkout kos-rs-xcframework + uses: actions/checkout@v4 + with: + repository: klever-io/kos-rs-xcframework + token: ${{ secrets.git_pass }} + - name: Download checksum + uses: actions/download-artifact@v4 + with: + name: checksum + path: . + - name: Update Swift Package Url and Checksum + run: | + TAG_NAME=${GITHUB_REF#refs/tags/} + export NEW_URL="https://github.com/klever-io/kos-rs/releases/download/$TAG_NAME/KOSMobile.xcframework.zip" + export NEW_CHECKSUM=$(cat checksum.txt) + echo "URL: $NEW_URL" + echo "Checksum: $NEW_CHECKSUM" + chmod +x ./update_package.sh + ./update_package.sh + - name: Commit Changes + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + git add -f Package.swift + git commit -m "Update package.swift" + git push origin main + - name: Create Release + run: | + brew install jq + TAG_NAME=${GITHUB_REF#refs/tags/} + RELEASE_NAME="${{ github.event.release.name }}" + RELEASE_BODY="${{ github.event.release.body }}" + PRERELEASE=${{ github.event.release.prerelease }} + ESCAPED_BODY=$(echo "${RELEASE_BODY}" | jq -Rsa .) + curl -f -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "tag_name": "'"${TAG_NAME}"'", + "target_commitish": "main", + "name": "'"${RELEASE_NAME}"'", + "body": '"${ESCAPED_BODY}"', + "draft": false, + "prerelease": '"${PRERELEASE}"' + }' \ + https://api.github.com/repos/klever-io/kos-rs-xcframework/releases \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ea4f4b7..4e59e0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,6 +61,55 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -73,6 +122,47 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "askama" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" +dependencies = [ + "askama_derive", + "askama_escape", +] + +[[package]] +name = "askama_derive" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83" +dependencies = [ + "askama_parser", + "basic-toml", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "serde", + "syn 2.0.66", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "askama_parser" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" +dependencies = [ + "nom", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -161,12 +251,30 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + [[package]] name = "bech32" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitcoin" version = "0.30.2" @@ -279,6 +387,38 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cbc" version = "0.1.2" @@ -329,6 +469,46 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "4.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "cobs" version = "0.2.3" @@ -393,6 +573,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -841,6 +1027,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-err" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] + [[package]] name = "funty" version = "2.0.0" @@ -1004,6 +1199,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "goblin" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47" +dependencies = [ + "log", + "plain", + "scroll", +] + [[package]] name = "group" version = "0.13.0" @@ -1408,6 +1614,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -1526,6 +1738,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "kos-mobile" +version = "0.1.0" +dependencies = [ + "hex", + "kos-crypto", + "kos-proto", + "kos-sdk", + "kos-types", + "kos-utils", + "lazy_static", + "thiserror", + "uniffi", +] + [[package]] name = "kos-proto" version = "0.1.0" @@ -1567,6 +1794,7 @@ dependencies = [ "kos-proto", "kos-types", "kos-utils", + "lazy_static", "log", "pbjson", "pbjson-types", @@ -1663,6 +1891,22 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.3" @@ -1713,6 +1957,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint" version = "0.4.5" @@ -1876,6 +2130,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pbjson" version = "0.5.1" @@ -1997,6 +2257,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "png" version = "0.17.13" @@ -2587,6 +2853,26 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "sec1" version = "0.7.3" @@ -2650,6 +2936,9 @@ name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] [[package]] name = "send_wrapper" @@ -2759,6 +3048,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -2774,6 +3069,12 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "socket2" version = "0.5.7" @@ -2815,6 +3116,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.25.0" @@ -2910,6 +3217,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "smawk", +] + [[package]] name = "thiserror" version = "1.0.61" @@ -3028,6 +3344,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.6.6" @@ -3145,6 +3470,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -3166,6 +3500,137 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "uniffi" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2db87def739fe4183947f8419d572d1849a4a09355eba4e988a2105cfd0ac6a7" +dependencies = [ + "anyhow", + "camino", + "cargo_metadata", + "clap", + "uniffi_bindgen", + "uniffi_build", + "uniffi_core", + "uniffi_macros", +] + +[[package]] +name = "uniffi_bindgen" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a112599c9556d1581e4a3d72019a74c2c3e122cc27f4af12577a429c4d5e614" +dependencies = [ + "anyhow", + "askama", + "camino", + "cargo_metadata", + "fs-err", + "glob", + "goblin", + "heck 0.5.0", + "once_cell", + "paste", + "serde", + "textwrap", + "toml", + "uniffi_meta", + "uniffi_udl", +] + +[[package]] +name = "uniffi_build" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b12684401d2a8508ca9c72a95bbc45906417e42fc80942abaf033bbf01aa33" +dependencies = [ + "anyhow", + "camino", + "uniffi_bindgen", +] + +[[package]] +name = "uniffi_checksum_derive" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22dbe67c1c957ac6e7611bdf605a6218aa86b0eebeb8be58b70ae85ad7d73dc" +dependencies = [ + "quote", + "syn 2.0.66", +] + +[[package]] +name = "uniffi_core" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c35aaad30e3a9e6d4fe34e358d64dbc92ee09045b48591b05fc9f12e0905b" +dependencies = [ + "anyhow", + "bytes", + "camino", + "log", + "once_cell", + "paste", + "static_assertions", +] + +[[package]] +name = "uniffi_macros" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db66474c5c61b0f7afc3b4995fecf9b72b340daa5ca0ef3da7778d75eb5482ea" +dependencies = [ + "bincode", + "camino", + "fs-err", + "once_cell", + "proc-macro2", + "quote", + "serde", + "syn 2.0.66", + "toml", + "uniffi_meta", +] + +[[package]] +name = "uniffi_meta" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d898893f102e0e39b8bcb7e3d2188f4156ba280db32db9e8af1f122d057e9526" +dependencies = [ + "anyhow", + "bytes", + "siphasher", + "uniffi_checksum_derive", +] + +[[package]] +name = "uniffi_testing" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6aa4f0cf9d12172d84fc00a35a6c1f3522b526daad05ae739f709f6941b9b6" +dependencies = [ + "anyhow", + "camino", + "cargo_metadata", + "fs-err", + "once_cell", +] + +[[package]] +name = "uniffi_udl" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b044e9c519e0bb51e516ab6f6d8f4f4dcf900ce30d5ad07c03f924e2824f28e" +dependencies = [ + "anyhow", + "textwrap", + "uniffi_meta", + "uniffi_testing", + "weedle2", +] + [[package]] name = "universal-hash" version = "0.5.1" @@ -3199,6 +3664,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "vcpkg" version = "0.2.15" @@ -3371,6 +3842,15 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "weedle2" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "998d2c24ec099a87daf9467808859f9d82b61f1d9c9701251aea037f514eae0e" +dependencies = [ + "nom", +] + [[package]] name = "which" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml index 5fdd5c9..d3c3b5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "packages/kos", "packages/kos-crypto", "packages/kos-sdk", + "packages/kos-mobile", ] # This makes the compiled code faster and smaller, but it makes compiling slower, # so it's only enabled in release mode. @@ -41,6 +42,8 @@ pbjson = { version = "0.5", git = "https://github.com/klever-io/pbjson" } pbjson-types = { version = "0.5", git = "https://github.com/klever-io/pbjson" } pbjson-build = { version = "0.5", git = "https://github.com/klever-io/pbjson" } +uniffi = { version = "0.28.1"} + reqwest = { version = "0.12", default-features = false, feature = ["rustls-tls", "blocking", "json"] } wasm-bindgen = "0.2" enum_delegate = "0.2" @@ -48,9 +51,11 @@ serde = { version = "1.0", default-features = false } serde_json = "1.0" log = "0.4" +lazy_static = "1.4.0" +thiserror = "1.0" kos-types = { version = "0.1.0", path = "./packages/kos-types", default-features = false } kos-crypto = { version = "0.1.0", path = "./packages/kos-crypto", default-features = false } kos-sdk = { version = "0.1.0", path = "./packages/kos-sdk", default-features = false } kos-utils = { version = "0.1.0", path = "./packages/kos-utils", default-features = false } -kos-proto = { version = "0.1.0", path = "./packages/kos-proto", default-features = false } +kos-proto = { version = "0.1.0", path = "./packages/kos-proto", default-features = false } \ No newline at end of file diff --git a/Makefile b/Makefile index 060dd1e..feadc9b 100644 --- a/Makefile +++ b/Makefile @@ -35,3 +35,27 @@ webpack: webpack-npm: wasm-pack build --scope klever --target bundler --release --out-name index --out-dir ../../demo/kos ./packages/kos + +clean-mobile-build: + cd packages/kos-mobile && ./build_clean.sh + +build-android: + cd packages/kos-mobile && ./build_android.sh + +publish-android: + cd packages/kos-mobile/android && ./gradlew lib:publishKOSPublicationToGithubPackagesRepository + +build-ios: + cd packages/kos-mobile && ./build_ios.sh + +test-ios: build-ios + cd packages/kos-mobile/ios/framework/KOSMobile && xcodebuild \ + -project KOSMobile.xcodeproj \ + -scheme KOSMobile \ + -sdk iphonesimulator \ + -destination 'platform=iOS Simulator,OS=17.2,name=iPhone 15 Pro' \ + CODE_SIGNING_ALLOWED=NO \ + test + +test-android: build-android + cd packages/kos-mobile/android && ./gradlew lib:testDebugUnitTest \ No newline at end of file diff --git a/packages/kos-mobile/Cargo.toml b/packages/kos-mobile/Cargo.toml new file mode 100644 index 0000000..6331fd1 --- /dev/null +++ b/packages/kos-mobile/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "kos-mobile" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lib] +crate-type = ["cdylib", "staticlib"] + +[[bin]] +name = "uniffi-bindgen" +path = "src/bin/uniffi-bindgen.rs" + +[dependencies] +kos-types = { workspace = true, features = ["serde"] } +kos-crypto = { workspace = true } +kos-proto = { workspace = true } +kos-utils = { workspace = true } +kos-sdk = { workspace = true, features = ["serde"] } + +hex = { workspace = true } +lazy_static = { workspace = true } +thiserror = { workspace = true } + +uniffi = { workspace = true, features = ["cli"] } + +[build-dependencies] +uniffi = { workspace = true, features = ["build"] } \ No newline at end of file diff --git a/packages/kos-mobile/android/.gitignore b/packages/kos-mobile/android/.gitignore new file mode 100644 index 0000000..7ac67d5 --- /dev/null +++ b/packages/kos-mobile/android/.gitignore @@ -0,0 +1,58 @@ +# Build and compilation directories +build/ +.build +out/ +.gradle/ +.idea/ +.kotlin +docs + +# Local IntelliJ IDEA configuration files +*.iml +*.ipr +*.iws +.idea/ + +# Kotlin Native user-specific local file +.konan/ + +# Gradle cache folder +.gradle/ +!gradle-wrapper.jar + +# Log files +*.log + +# Temporary files +*.tmp +*.tmp.* + +# Operating system specific files +.DS_Store +Thumbs.db + +# JAR output folder +libs/ + +# Android-specific ignores +*.apk +*.ap_ +*.aab +bin/ +gen/ +local.properties + +# Keystore files for Android signing +*.jks +*.keystore + +# Google Services (exclude your google-services.json) +/google-services.json + +# specific + +ndk +openssl +openssl-libs +lib/src/main/kotlin/uniffi +lib/src/main/jniLibs \ No newline at end of file diff --git a/packages/kos-mobile/android/build.gradle.kts b/packages/kos-mobile/android/build.gradle.kts new file mode 100644 index 0000000..e037951 --- /dev/null +++ b/packages/kos-mobile/android/build.gradle.kts @@ -0,0 +1,4 @@ +plugins { + alias(libs.plugins.android.library) apply false + alias(libs.plugins.jetbrains.kotlin.android) apply false +} \ No newline at end of file diff --git a/packages/kos-mobile/android/gradle.properties b/packages/kos-mobile/android/gradle.properties new file mode 100644 index 0000000..20e2a01 --- /dev/null +++ b/packages/kos-mobile/android/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/packages/kos-mobile/android/gradle/libs.versions.toml b/packages/kos-mobile/android/gradle/libs.versions.toml new file mode 100644 index 0000000..e4f5e34 --- /dev/null +++ b/packages/kos-mobile/android/gradle/libs.versions.toml @@ -0,0 +1,13 @@ +[versions] +agp = "8.3.1" +kotlin = "1.9.23" +jna = "5.13.0" +junit = "4.13.2" + +[libraries] +java-jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } +junit = { group = "junit", name = "junit", version.ref = "junit" } + +[plugins] +android-library = { id = "com.android.library", version.ref = "agp" } +jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } \ No newline at end of file diff --git a/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.jar b/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.properties b/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2f465c7 --- /dev/null +++ b/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Aug 19 09:00:38 BRT 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/kos-mobile/android/gradlew b/packages/kos-mobile/android/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/packages/kos-mobile/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/packages/kos-mobile/android/gradlew.bat b/packages/kos-mobile/android/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/packages/kos-mobile/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/packages/kos-mobile/android/lib/build.gradle.kts b/packages/kos-mobile/android/lib/build.gradle.kts new file mode 100644 index 0000000..9fd6331 --- /dev/null +++ b/packages/kos-mobile/android/lib/build.gradle.kts @@ -0,0 +1,84 @@ +import java.net.URI +import java.util.Properties + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.jetbrains.kotlin.android) + id("maven-publish") +} + +group = "io.klever" +version = System.getenv("LIB_VERSION")?.removePrefix("v") ?: "LOCAL" + +android { + namespace = "uniffi.kos_mobile" + compileSdk = 34 + defaultConfig { + minSdk = 27 + } + buildTypes { + release { isMinifyEnabled = false } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +dependencies { + implementation(libs.java.jna) + testImplementation(libs.junit) +} + +publishing { + repositories { + maven { + name = "GithubPackages" + url = URI.create("https://maven.pkg.github.com/klever-io/kos-rs") + credentials { + username = "" + password = (System.getenv("GITHUB_TOKEN") + ?: (getLocalProperties()["git.token"] as? String?) + ?: "") + } + } + } + + publications { + create("KOS", MavenPublication::class.java) { + artifactId = "kos-mobile" + afterEvaluate { + artifact(tasks.getByName("bundleReleaseAar")) + } + } + } +} + +tasks.register("copyKOSMobileDarwinAarch64LibForTestDebugUnitTest", Copy::class) { + dependsOn("processDebugUnitTestJavaRes") + from("$rootDir/lib/src/main/jniLibs/darwin-aarch64") { + include("libkos_mobile.dylib") + } + into("$rootDir/lib/build/tmp/kotlin-classes/debugUnitTest/darwin-aarch64") +} + +afterEvaluate { + tasks.named("testDebugUnitTest") { + dependsOn("copyKOSMobileDarwinAarch64LibForTestDebugUnitTest") + } +} + +fun getLocalProperties(): Properties = System.getProperties().apply { + try { + load(File("${rootDir}${File.separator}local.properties").inputStream()) + } catch (_: Exception) { + } +} \ No newline at end of file diff --git a/packages/kos-mobile/android/lib/src/main/AndroidManifest.xml b/packages/kos-mobile/android/lib/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1d26c87 --- /dev/null +++ b/packages/kos-mobile/android/lib/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/kos-mobile/android/lib/src/test/kotlin/uniffi/kos_mobile/KOSTest.kt b/packages/kos-mobile/android/lib/src/test/kotlin/uniffi/kos_mobile/KOSTest.kt new file mode 100644 index 0000000..0f102b0 --- /dev/null +++ b/packages/kos-mobile/android/lib/src/test/kotlin/uniffi/kos_mobile/KOSTest.kt @@ -0,0 +1,68 @@ +package uniffi.kos_mobile + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class KOSTest { + + @Test + fun testKOS() { + + val dataToEncrypt = "Hello" + val password = "password" + val klvChainId = 38 + val mnemonic = + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + val klvPk0 = "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d" + val klvAddr0 = "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy" + val klvPath0 = "m/44'/690'/0'/0'/0'" + val klvKey0 = "e41b323a571fd955e09cd41660ff4465c3f44693c87f2faea4a0fc408727c8ea" + + val isValidMnemonicValid = validateMnemonic(mnemonic) + assertTrue(isValidMnemonicValid) + + val isInvalidMnemonicValid = + validateMnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon klv") + assertFalse(isInvalidMnemonicValid) + + val mnemonic12 = generateMnemonic(12) + assertTrue(mnemonic12.split(" ").size == 12) + + val mnemonic24 = generateMnemonic(24) + assertTrue(mnemonic24.split(" ").size == 24) + + val gmcEncryptedData = encryptWithGmc(dataToEncrypt, password) + assertTrue(gmcEncryptedData.isNotEmpty()) + + val cbcEncryptedData = encryptWithCbc(dataToEncrypt, password) + assertTrue(cbcEncryptedData.isNotEmpty()) + + val cfbEncryptedData = encryptWithCfb(dataToEncrypt, password) + assertTrue(cfbEncryptedData.isNotEmpty()) + + val gmcDecryptedData = decrypt(gmcEncryptedData, password) + assertEquals(dataToEncrypt, gmcDecryptedData) + + val cbcDecryptedData = decrypt(cbcEncryptedData, password) + assertEquals(dataToEncrypt, cbcDecryptedData) + + val cfbDecryptedData = decrypt(cfbEncryptedData, password) + assertEquals(dataToEncrypt, cfbDecryptedData) + + val walletFromMnemonic = generateWalletFromMnemonic(mnemonic, klvChainId, 0, false) + assertEquals(klvChainId, walletFromMnemonic.chainId) + assertEquals(klvPk0, walletFromMnemonic.privateKey) + assertEquals(klvAddr0, walletFromMnemonic.address) + assertEquals(klvPath0, walletFromMnemonic.path) + assertEquals(klvKey0, walletFromMnemonic.publicKey) + + val walletFromPk = generateWalletFromPrivateKey(klvChainId, klvPk0) + assertEquals(klvChainId, walletFromPk.chainId) + assertEquals(klvPk0, walletFromPk.privateKey) + assertEquals(klvAddr0, walletFromPk.address) + assertEquals("", walletFromPk.path) + assertEquals(klvKey0, walletFromPk.publicKey) + } +} \ No newline at end of file diff --git a/packages/kos-mobile/android/settings.gradle.kts b/packages/kos-mobile/android/settings.gradle.kts new file mode 100644 index 0000000..9a6ec3d --- /dev/null +++ b/packages/kos-mobile/android/settings.gradle.kts @@ -0,0 +1,26 @@ +@file:Suppress("UnstableApiUsage") + +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "kos" +include(":lib") + \ No newline at end of file diff --git a/packages/kos-mobile/build_android.sh b/packages/kos-mobile/build_android.sh new file mode 100755 index 0000000..d4dbcfa --- /dev/null +++ b/packages/kos-mobile/build_android.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +source build_source.sh + +set -o pipefail +set -e + +configure_cargo() { + cd "$BUILD_HOME" + for i in $(seq 0 $((${#ANDROID_TOOLCHAINS[@]} - 1))); do + toolchain="${ANDROID_TOOLCHAINS[i]}" + if [ "$toolchain" = "armv7a-linux-androideabi" ]; then + toolchain="armv7-linux-androideabi" + fi + rustup target add $toolchain + done + export AR="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/llvm-ar" + export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/aarch64-linux-android$ANDROID_MIN_API-clang" + export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/armv7a-linux-androideabi$ANDROID_MIN_API-clang" + export CARGO_TARGET_I686_LINUX_ANDROID_LINKER="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/i686-linux-android$ANDROID_MIN_API-clang" + export CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/x86_64-linux-android$ANDROID_MIN_API-clang" +} + +assemble_android_lib() { + toolchain=$1 + jni=$2 + rust_toolchain=$toolchain + cd "$BUILD_HOME" + if [ "$toolchain" = "armv7a-linux-androideabi" ]; then + rust_toolchain="armv7-linux-androideabi" + fi + export OPENSSL_LIB_DIR="$OPENSSL_GENERATED_LIBS_PATH/$toolchain" + export CC="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/$toolchain$ANDROID_MIN_API-clang" + log_status "assembling android lib to $toolchain..." + cargo build --target $rust_toolchain --release -q + export CC="" + mkdir -p "$ANDROID_JNI_LIBS_PATH" + mkdir -p "$ANDROID_JNI_LIBS_PATH/$jni" + cp -f ../../target/$rust_toolchain/release/lib"$PACKAGE_NAME".so "$ANDROID_JNI_LIBS_PATH"/"$jni" +} + +assemble_android_lib_unit_test() { + cd "$BUILD_HOME" + jni=darwin-aarch64 + log_status "assembling android test lib..." + export OPENSSL_LIB_DIR="$OPENSSL_GENERATED_LIBS_PATH/$OS_TOOLCHAIN" + cargo build --release -q + mkdir -p "$ANDROID_JNI_LIBS_PATH/$jni" + cp -f ../../target/release/lib"$PACKAGE_NAME".dylib "$ANDROID_JNI_LIBS_PATH"/$jni +} + +generate_binds() { + cd "$BUILD_HOME" + log_status "generating android binds..." + export OPENSSL_LIB_DIR="$OPENSSL_GENERATED_LIBS_PATH/$OS_TOOLCHAIN" + cargo run -q --bin uniffi-bindgen generate --library ../../target/"${ANDROID_TOOLCHAINS[0]}"/release/lib"${PACKAGE_NAME}".so --language kotlin --out-dir android_binds + mkdir -p "$ANDROID_GENERATED_BINDS_PATH" + cp -f -r android_binds/* "$ANDROID_GENERATED_BINDS_PATH" + rm -rf android_binds +} + +clear +echo -e "${ANDROID}########################################################${NC}" +echo -e "${ANDROID}######### INITIALIZING ANDROID RUST BUILD ############${NC}" +echo -e "${ANDROID}########################################################${NC}\n" + +configure_android_ndk +configure_openssl +configure_cargo + +for i in $(seq 0 $((${#ANDROID_ARCHS[@]} - 1))); do + assemble_openssl_lib "${ANDROID_ARCHS[i]}" "${ANDROID_TOOLCHAINS[i]}" + assemble_android_lib "${ANDROID_TOOLCHAINS[i]}" "${ANDROID_JNI[i]}" +done + +assemble_openssl_lib "$OS_ARCH" "$OS_TOOLCHAIN" +assemble_android_lib_unit_test +generate_binds + +echo -e "${ANDROID}ANDROID RUST BUILD FINISHED ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰${NC}" diff --git a/packages/kos-mobile/build_clean.sh b/packages/kos-mobile/build_clean.sh new file mode 100755 index 0000000..ae4de35 --- /dev/null +++ b/packages/kos-mobile/build_clean.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +source build_source.sh + +set -o pipefail +set -e + +clear_android() { + rm -rf "$ANDROID_JNI_LIBS_PATH" + rm -rf "$ANDROID_GENERATED_BINDS_PATH"/uniffi +} + +clear_ios() { + cd "$BUILD_HOME" + rm -rf ios/XCFrameworks + rm -rf ios/binds + rm -rf ios/framework/KOSMobile/aarch64-apple-ios + rm -rf ios/framework/KOSMobile/aarch64-apple-ios-sim +} + +log_status "cleaning android files..." +clear_android +log_status "cleaning iOS files..." +clear_ios + +log_status "clear finished!" diff --git a/packages/kos-mobile/build_ios.sh b/packages/kos-mobile/build_ios.sh new file mode 100755 index 0000000..7569ede --- /dev/null +++ b/packages/kos-mobile/build_ios.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +source build_source.sh + +set -o pipefail +set -e + +configure_cargo() { + cd "$BUILD_HOME" + for i in $(seq 0 $((${#IOS_ARCHS[@]} - 1))); do + rustup target add "${IOS_ARCHS[i]}" + done +} + +assemble_ios_lib() { + arch=$1 + cd "$BUILD_HOME" + log_status "assembling iOS lib to $arch..." + cargo build --target "$arch" --release -q + cd ../../target/"$arch"/release + mv lib"$PACKAGE_NAME".a "$arch"-lib"$PACKAGE_NAME".a +} + +generate_binds() { + cd "$BUILD_HOME/../.." + log_status "generating iOS binds..." + rm -rf ios/binds + cargo run -q --bin uniffi-bindgen generate --library target/"${IOS_ARCHS[0]}"/release/lib"$PACKAGE_NAME".dylib --language swift --out-dir packages/kos-mobile/ios/binds +} + +generate_xcframework() { + log_status "generating XCFramework..." + cd "$BUILD_HOME"/ios/framework/KOSMobile + for i in $(seq 0 $((${#IOS_ARCHS[@]} - 1))); do + rm -rf "${IOS_ARCHS[i]}" + mkdir -p "${IOS_ARCHS[i]}"/ + if [ "${IOS_ARCHS[i]}" = "aarch64-apple-ios" ]; then + xcodebuild -project KOSMobile.xcodeproj \ + -scheme KOSMobile \ + -configuration Release \ + -sdk iphoneos \ + -arch arm64 \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + SKIP_INSTALL=NO \ + clean build + BUILT_PRODUCTS_DIR=$(xcodebuild -project KOSMobile.xcodeproj \ + -scheme KOSMobile \ + -configuration Release \ + -sdk iphoneos \ + -arch arm64 \ + -showBuildSettings | + grep "BUILT_PRODUCTS_DIR" | + grep -oEi "\/.*") + cp -f -r "$BUILT_PRODUCTS_DIR"/* "${IOS_ARCHS[i]}"/ + elif [ "${IOS_ARCHS[i]}" = "aarch64-apple-ios-sim" ]; then + xcodebuild -project KOSMobile.xcodeproj \ + -scheme KOSMobile \ + -configuration Release \ + -sdk iphonesimulator \ + -arch arm64 \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + SKIP_INSTALL=NO \ + clean build + BUILT_PRODUCTS_DIR=$(xcodebuild -project KOSMobile.xcodeproj \ + -scheme KOSMobile \ + -configuration Release \ + -sdk iphonesimulator \ + -arch arm64 \ + -showBuildSettings | + grep "BUILT_PRODUCTS_DIR" | + grep -oEi "\/.*") + cp -f -r "$BUILT_PRODUCTS_DIR"/* "${IOS_ARCHS[i]}"/ + fi + done + rm -rf ../../XCFrameworks + mkdir -p ../../XCFrameworks + xcodebuild -create-xcframework \ + -framework "${IOS_ARCHS[0]}"/KOSMobile.framework \ + -framework "${IOS_ARCHS[1]}"/KOSMobile.framework \ + -output ../../XCFrameworks/KOSMobile.xcframework + cd ../../XCFrameworks + zip -r -y KOSMobile.xcframework.zip KOSMobile.xcframework +} + +clear +echo -e "${IOS}########################################################${NC}" +echo -e "${IOS}########### INITIALIZING iOS RUST BUILD ##############${NC}" +echo -e "${IOS}########################################################${NC}\n" + +configure_cargo +for i in $(seq 0 $((${#IOS_ARCHS[@]} - 1))); do + assemble_ios_lib "${IOS_ARCHS[i]}" +done +generate_binds +generate_xcframework + +echo -e "${IOS}iOS RUST BUILD FINISHED ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰${NC}" diff --git a/packages/kos-mobile/build_source.sh b/packages/kos-mobile/build_source.sh new file mode 100755 index 0000000..e37d721 --- /dev/null +++ b/packages/kos-mobile/build_source.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +# build env +BUILD_HOME=$(pwd) +OS="darwin-x86_64" +OS_ARCH="darwin64-arm64-cc" +OS_TOOLCHAIN="darwin-x86_64" +ANDROID_PROJECT_PATH="android" +ANDROID_NDK_PATH="$BUILD_HOME/android/ndk" +ANDROID_JNI_LIBS_PATH="$BUILD_HOME/$ANDROID_PROJECT_PATH/lib/src/main/jniLibs" +ANDROID_GENERATED_BINDS_PATH="$BUILD_HOME/$ANDROID_PROJECT_PATH/lib/src/main/kotlin" +ANDROID_MIN_API="27" +ANDROID_ARCHS=("android-arm64" "android-arm" "android-x86" "android-x86_64") +ANDROID_TOOLCHAINS=("aarch64-linux-android" "armv7a-linux-androideabi" "i686-linux-android" "x86_64-linux-android") +ANDROID_JNI=("arm64-v8a" "armeabi-v7a" "x86" "x86_64") +OPENSSL_VERSION="openssl-3.2.1" +OPENSSL_PATH="$BUILD_HOME/android/openssl" +OPENSSL_GENERATED_LIBS_PATH="$OPENSSL_PATH-libs" +IOS_ARCHS=("aarch64-apple-ios" "aarch64-apple-ios-sim") +PACKAGE_NAME="kos_mobile" + +# colors +ANDROID='\033[0;92m' +IOS='\033[0;97m' +RED='\033[0;31m' +GRAY='\033[37m' +NC='\033[0m' + +dir_exists() { + if [ ! -d "$1" ]; then + return 1 + else + return 0 + fi +} + +file_exists() { + if [ -f "$1" ]; then + return 0 + else + return 1 + fi +} + +log_status() { + echo -e "${GRAY}$1${NC}" +} + +log_error() { + echo -e "${RED}$1${NC}" +} + +configure_android_ndk() { + if ! dir_exists "$ANDROID_NDK_PATH"; then + log_status "configuring ndk..." + rm -f ndk.dmg + log_status "starting ndk download..." + curl -0 https://dl.google.com/android/repository/android-ndk-r26b-darwin.dmg --output "$BUILD_HOME"/ndk.dmg + hdiutil attach -quiet -nobrowse -noverify -noautoopen "$BUILD_HOME"/ndk.dmg + mkdir "$ANDROID_NDK_PATH" + log_status "copying ndk files..." + cp -r /Volumes/Android\ NDK\ r26b/AndroidNDK10909125.app/Contents/NDK/* "$ANDROID_NDK_PATH" + hdiutil detach -quiet /Volumes/Android\ NDK\ r26b/ + rm ndk.dmg + fi + export ANDROID_NDK_ROOT="$ANDROID_NDK_PATH" +} + +configure_openssl() { + if ! dir_exists "$OPENSSL_PATH"; then + log_status "configuring open-ssl..." + rm -f "$OPENSSL_VERSION".tar.gz + log_status "starting $OPENSSL_VERSION download..." + curl -L -o "$OPENSSL_VERSION".tar.gz https://github.com/openssl/openssl/releases/download/"$OPENSSL_VERSION"/"$OPENSSL_VERSION".tar.gz + tar xfz "${OPENSSL_VERSION}.tar.gz" + mkdir "$OPENSSL_PATH" + cp -r "$BUILD_HOME"/"$OPENSSL_VERSION"/* "$OPENSSL_PATH" + rm -f "$OPENSSL_VERSION".tar.gz + rm -rf "$OPENSSL_VERSION" + mkdir -p "$OPENSSL_GENERATED_LIBS_PATH" + fi + export OPENSSL_DIR=$OPENSSL_PATH + export TOOLCHAIN_ROOT="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS" + export SYSROOT="$TOOLCHAIN_ROOT/sysroot" + export PATH="$TOOLCHAIN_ROOT/bin:$SYSROOT/usr/local/bin:$PATH" +} + +assemble_openssl_lib() { + arch=$1 + toolchain=$2 + if ! dir_exists "$OPENSSL_GENERATED_LIBS_PATH/$toolchain"; then + cd "$OPENSSL_PATH" + log_status "configuring openssl to $toolchain..." + if [ "$arch" = "$OS_ARCH" ]; then + ./Configure "$arch" no-asm no-shared + else + export CC="${TOOLCHAIN_ROOT}/bin/$toolchain${ANDROID_MIN_API}-clang" + export CXX="${TOOLCHAIN_ROOT}/bin/$toolchain${ANDROID_MIN_API}-clang++" + export CXXFLAGS="-fPIC" + export CPPFLAGS="-DANDROID -fPIC" + ./Configure "$arch" no-asm no-shared -D__ANDROID_API__="$ANDROID_MIN_API" + fi + log_status "assembling openssl to $toolchain..." + make clean -s + make -s + mkdir -p "$OPENSSL_GENERATED_LIBS_PATH"/"$toolchain" + cp -f libcrypto.a "$OPENSSL_GENERATED_LIBS_PATH"/"$toolchain" + cp -f libssl.a "$OPENSSL_GENERATED_LIBS_PATH"/"$toolchain" + else + log_status "skipping assemble openssl to $toolchain..." + fi + export CC="" + export CXX="" + export CXXFLAGS="" + export CPPFLAGS="" +} diff --git a/packages/kos-mobile/ios/.gitignore b/packages/kos-mobile/ios/.gitignore new file mode 100644 index 0000000..47b6501 --- /dev/null +++ b/packages/kos-mobile/ios/.gitignore @@ -0,0 +1,2 @@ +XCFrameworks +binds \ No newline at end of file diff --git a/packages/kos-mobile/ios/framework/.gitignore b/packages/kos-mobile/ios/framework/.gitignore new file mode 100644 index 0000000..c1eb49a --- /dev/null +++ b/packages/kos-mobile/ios/framework/.gitignore @@ -0,0 +1,41 @@ +.DS_Store +/build/ +DerivedData/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ +*.xccheckout +*.moved-aside +*.xcworkspace +!default.xcworkspace +*.plist +!default.plist +*.swp +*.swo +*~ +*.ipa +*.dSYM.zip +*.dSYM +*.hmap +*.ipa +*.xcuserstate +/project.xcworkspace +/xcshareddata/ +/swiftlint_report/ +/fastlane/report.xml +/fastlane/Preview.html +/fastlane/screenshots +/fastlane/test_output +/slather-report/ +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/project.xcworkspace/ +!*.xcodeproj/xcshareddata/ +KOSMobile/aarch64-apple-ios +KOSMobile/aarch64-apple-ios-sim \ No newline at end of file diff --git a/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/project.pbxproj b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e8c57d9 --- /dev/null +++ b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/project.pbxproj @@ -0,0 +1,512 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 6561E0B92C76772200907790 /* KOSMobile.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6561E0B02C76772200907790 /* KOSMobile.framework */; }; + 6561E0BE2C76772200907790 /* KOSMobileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6561E0BD2C76772200907790 /* KOSMobileTests.swift */; }; + 6561E0BF2C76772200907790 /* KOSMobile.h in Headers */ = {isa = PBXBuildFile; fileRef = 6561E0B32C76772200907790 /* KOSMobile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6561E0CB2C76782000907790 /* kos_mobile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6561E0C82C76782000907790 /* kos_mobile.swift */; }; + 6561E0CC2C76782000907790 /* kos_mobileFFI.h in Headers */ = {isa = PBXBuildFile; fileRef = 6561E0CA2C76782000907790 /* kos_mobileFFI.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6561E0CF2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6561E0CE2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a */; }; + 6561E0D12C76785700907790 /* aarch64-apple-ios-libkos_mobile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6561E0D02C76785700907790 /* aarch64-apple-ios-libkos_mobile.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 6561E0BA2C76772200907790 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6561E0A72C76772200907790 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6561E0AF2C76772200907790; + remoteInfo = KOSMobile; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 6561E0B02C76772200907790 /* KOSMobile.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KOSMobile.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6561E0B32C76772200907790 /* KOSMobile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KOSMobile.h; sourceTree = ""; }; + 6561E0B82C76772200907790 /* KOSMobileTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KOSMobileTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6561E0BD2C76772200907790 /* KOSMobileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KOSMobileTests.swift; sourceTree = ""; }; + 6561E0C82C76782000907790 /* kos_mobile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = kos_mobile.swift; path = ../../../binds/kos_mobile.swift; sourceTree = ""; }; + 6561E0C92C76782000907790 /* kos_mobileFFI.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; name = kos_mobileFFI.modulemap; path = ../../../binds/kos_mobileFFI.modulemap; sourceTree = ""; }; + 6561E0CA2C76782000907790 /* kos_mobileFFI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = kos_mobileFFI.h; path = ../../../binds/kos_mobileFFI.h; sourceTree = ""; }; + 6561E0CE2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "aarch64-apple-ios-sim-libkos_mobile.a"; path = "../../../../../target/aarch64-apple-ios-sim/release/aarch64-apple-ios-sim-libkos_mobile.a"; sourceTree = ""; }; + 6561E0D02C76785700907790 /* aarch64-apple-ios-libkos_mobile.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "aarch64-apple-ios-libkos_mobile.a"; path = "../../../../../target/aarch64-apple-ios/release/aarch64-apple-ios-libkos_mobile.a"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6561E0AD2C76772200907790 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6561E0D12C76785700907790 /* aarch64-apple-ios-libkos_mobile.a in Frameworks */, + 6561E0CF2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6561E0B52C76772200907790 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6561E0B92C76772200907790 /* KOSMobile.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6561E0A62C76772200907790 = { + isa = PBXGroup; + children = ( + 6561E0B22C76772200907790 /* KOSMobile */, + 6561E0BC2C76772200907790 /* KOSMobileTests */, + 6561E0B12C76772200907790 /* Products */, + 6561E0CD2C76784A00907790 /* Frameworks */, + ); + sourceTree = ""; + }; + 6561E0B12C76772200907790 /* Products */ = { + isa = PBXGroup; + children = ( + 6561E0B02C76772200907790 /* KOSMobile.framework */, + 6561E0B82C76772200907790 /* KOSMobileTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 6561E0B22C76772200907790 /* KOSMobile */ = { + isa = PBXGroup; + children = ( + 6561E0C82C76782000907790 /* kos_mobile.swift */, + 6561E0CA2C76782000907790 /* kos_mobileFFI.h */, + 6561E0C92C76782000907790 /* kos_mobileFFI.modulemap */, + 6561E0B32C76772200907790 /* KOSMobile.h */, + ); + path = KOSMobile; + sourceTree = ""; + }; + 6561E0BC2C76772200907790 /* KOSMobileTests */ = { + isa = PBXGroup; + children = ( + 6561E0BD2C76772200907790 /* KOSMobileTests.swift */, + ); + path = KOSMobileTests; + sourceTree = ""; + }; + 6561E0CD2C76784A00907790 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6561E0D02C76785700907790 /* aarch64-apple-ios-libkos_mobile.a */, + 6561E0CE2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 6561E0AB2C76772200907790 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6561E0CC2C76782000907790 /* kos_mobileFFI.h in Headers */, + 6561E0BF2C76772200907790 /* KOSMobile.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 6561E0AF2C76772200907790 /* KOSMobile */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6561E0C22C76772200907790 /* Build configuration list for PBXNativeTarget "KOSMobile" */; + buildPhases = ( + 6561E0AB2C76772200907790 /* Headers */, + 6561E0AC2C76772200907790 /* Sources */, + 6561E0AD2C76772200907790 /* Frameworks */, + 6561E0AE2C76772200907790 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KOSMobile; + productName = KOSMobile; + productReference = 6561E0B02C76772200907790 /* KOSMobile.framework */; + productType = "com.apple.product-type.framework"; + }; + 6561E0B72C76772200907790 /* KOSMobileTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6561E0C52C76772200907790 /* Build configuration list for PBXNativeTarget "KOSMobileTests" */; + buildPhases = ( + 6561E0B52C76772200907790 /* Frameworks */, + 6561E0B42C76772200907790 /* Sources */, + 6561E0B62C76772200907790 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 6561E0BB2C76772200907790 /* PBXTargetDependency */, + ); + name = KOSMobileTests; + productName = KOSMobileTests; + productReference = 6561E0B82C76772200907790 /* KOSMobileTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6561E0A72C76772200907790 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1520; + LastUpgradeCheck = 1520; + TargetAttributes = { + 6561E0AF2C76772200907790 = { + CreatedOnToolsVersion = 15.2; + LastSwiftMigration = 1520; + }; + 6561E0B72C76772200907790 = { + CreatedOnToolsVersion = 15.2; + }; + }; + }; + buildConfigurationList = 6561E0AA2C76772200907790 /* Build configuration list for PBXProject "KOSMobile" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6561E0A62C76772200907790; + productRefGroup = 6561E0B12C76772200907790 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6561E0AF2C76772200907790 /* KOSMobile */, + 6561E0B72C76772200907790 /* KOSMobileTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6561E0AE2C76772200907790 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6561E0B62C76772200907790 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6561E0AC2C76772200907790 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6561E0CB2C76782000907790 /* kos_mobile.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6561E0B42C76772200907790 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6561E0BE2C76772200907790 /* KOSMobileTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 6561E0BB2C76772200907790 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6561E0AF2C76772200907790 /* KOSMobile */; + targetProxy = 6561E0BA2C76772200907790 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 6561E0C02C76772200907790 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 6561E0C12C76772200907790 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 6561E0C32C76772200907790 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + "OTHER_LDFLAGS[sdk=iphoneos*]" = "\"$(SRCROOT)/../../../../../target/aarch64-apple-ios/release/aarch64-apple-ios-libkos_mobile.a\""; + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = "\"$(SRCROOT)/../../../../../target/aarch64-apple-ios-sim/release/aarch64-apple-ios-sim-libkos_mobile.a\""; + PRODUCT_BUNDLE_IDENTIFIER = io.klever.KOSMobile; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6561E0C42C76772200907790 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + "OTHER_LDFLAGS[sdk=iphoneos*]" = "\"$(SRCROOT)/../../../../../target/aarch64-apple-ios/release/aarch64-apple-ios-libkos_mobile.a\""; + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = "\"$(SRCROOT)/../../../../../target/aarch64-apple-ios-sim/release/aarch64-apple-ios-sim-libkos_mobile.a\""; + PRODUCT_BUNDLE_IDENTIFIER = io.klever.KOSMobile; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 6561E0C62C76772200907790 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.klever.KOSMobileTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6561E0C72C76772200907790 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.klever.KOSMobileTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6561E0AA2C76772200907790 /* Build configuration list for PBXProject "KOSMobile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6561E0C02C76772200907790 /* Debug */, + 6561E0C12C76772200907790 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6561E0C22C76772200907790 /* Build configuration list for PBXNativeTarget "KOSMobile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6561E0C32C76772200907790 /* Debug */, + 6561E0C42C76772200907790 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6561E0C52C76772200907790 /* Build configuration list for PBXNativeTarget "KOSMobileTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6561E0C62C76772200907790 /* Debug */, + 6561E0C72C76772200907790 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6561E0A72C76772200907790 /* Project object */; +} diff --git a/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/xcshareddata/xcschemes/KOSMobile.xcscheme b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/xcshareddata/xcschemes/KOSMobile.xcscheme new file mode 100644 index 0000000..acac82b --- /dev/null +++ b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/xcshareddata/xcschemes/KOSMobile.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile/KOSMobile.h b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile/KOSMobile.h new file mode 100644 index 0000000..1fd9dd2 --- /dev/null +++ b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile/KOSMobile.h @@ -0,0 +1,15 @@ +// +// KOSMobile.h +// KOSMobile +// +// Created by Daniel Falcรฃo on 21/08/24. +// + +#import +#import + +//! Project version number for KOSMobile. +FOUNDATION_EXPORT double KOSMobileVersionNumber; + +//! Project version string for KOSMobile. +FOUNDATION_EXPORT const unsigned char KOSMobileVersionString[]; \ No newline at end of file diff --git a/packages/kos-mobile/ios/framework/KOSMobile/KOSMobileTests/KOSMobileTests.swift b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobileTests/KOSMobileTests.swift new file mode 100644 index 0000000..fa884a6 --- /dev/null +++ b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobileTests/KOSMobileTests.swift @@ -0,0 +1,69 @@ +// +// KOSMobileTests.swift +// KOSMobileTests +// +// Created by Daniel Falcรฃo on 21/08/24. +// + +import XCTest +@testable import KOSMobile + +final class KOSMobileTests: XCTestCase { + + func testKOS() { + + let dataToEncrypt = "Hello" + let password = "password" + let klvChainId = Int32(38) + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + let klvPk0 = "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d" + let klvAddr0 = "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy" + let klvPath0 = "m/44'/690'/0'/0'/0'" + let klvKey0 = "e41b323a571fd955e09cd41660ff4465c3f44693c87f2faea4a0fc408727c8ea" + + let isValidMnemonicValid = validateMnemonic(mnemonic: mnemonic) + XCTAssertTrue(isValidMnemonicValid) + + let isInvalidMnemonicValid = validateMnemonic(mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon klv") + XCTAssertFalse(isInvalidMnemonicValid) + + let mnemonic12 = try! generateMnemonic(size: 12) + XCTAssertTrue(mnemonic12.split(separator: " ").count == 12) + + let mnemonic24 = try! generateMnemonic(size: 24) + XCTAssertTrue(mnemonic24.split(separator: " ").count == 24) + + let gmcEncryptedData = try! encryptWithGmc(data: dataToEncrypt, password: password) + XCTAssertTrue(!gmcEncryptedData.isEmpty) + + let cbcEncryptedData = try! encryptWithCbc(data: dataToEncrypt, password: password) + XCTAssertTrue(!cbcEncryptedData.isEmpty) + + let cfbEncryptedData = try! encryptWithCfb(data: dataToEncrypt, password: password) + XCTAssertTrue(!cfbEncryptedData.isEmpty) + + let gmcDecryptedData = try! decrypt(data: gmcEncryptedData, password: password) + XCTAssertEqual(dataToEncrypt, gmcDecryptedData) + + let cbcDecryptedData = try! decrypt(data: cbcEncryptedData, password: password) + XCTAssertEqual(dataToEncrypt, cbcDecryptedData) + + let cfbDecryptedData = try! decrypt(data: cfbEncryptedData, password: password) + XCTAssertEqual(dataToEncrypt, cfbDecryptedData) + + let walletFromMnemonic = try! generateWalletFromMnemonic(mnemonic: mnemonic, chainId: klvChainId, index: 0, useLegacyPath: false) + XCTAssertEqual(klvChainId, walletFromMnemonic.chainId) + XCTAssertEqual(klvPk0, walletFromMnemonic.privateKey) + XCTAssertEqual(klvAddr0, walletFromMnemonic.address) + XCTAssertEqual(klvPath0, walletFromMnemonic.path) + XCTAssertEqual(klvKey0, walletFromMnemonic.publicKey) + + let walletFromPk = try! generateWalletFromPrivateKey(chainId: klvChainId, privateKey: klvPk0) + XCTAssertEqual(klvChainId, walletFromPk.chainId) + XCTAssertEqual(klvPk0, walletFromPk.privateKey) + XCTAssertEqual(klvAddr0, walletFromPk.address) + XCTAssertEqual("", walletFromPk.path) + XCTAssertEqual(klvKey0, walletFromPk.publicKey) + + } +} \ No newline at end of file diff --git a/packages/kos-mobile/src/bin/uniffi-bindgen.rs b/packages/kos-mobile/src/bin/uniffi-bindgen.rs new file mode 100644 index 0000000..f6cff6c --- /dev/null +++ b/packages/kos-mobile/src/bin/uniffi-bindgen.rs @@ -0,0 +1,3 @@ +fn main() { + uniffi::uniffi_bindgen_main() +} diff --git a/packages/kos-mobile/src/lib.rs b/packages/kos-mobile/src/lib.rs new file mode 100644 index 0000000..3ff6631 --- /dev/null +++ b/packages/kos-mobile/src/lib.rs @@ -0,0 +1,308 @@ +use hex::FromHexError; +use hex::ToHex; + +use kos_crypto::cipher; +use kos_crypto::cipher::CipherAlgo; +use kos_sdk::chain::Chain; +use kos_sdk::models::PathOptions; +use kos_sdk::wallet::Wallet; +use kos_types::error::Error as KosError; + +uniffi::setup_scaffolding!(); + +#[derive(Debug, thiserror::Error, uniffi::Error)] +enum KOSError { + #[error("UnsupportedChainError: Unsupported chain {id}")] + UnsupportedChain { id: String }, + #[error("KOSDelegateError: {0}")] + KOSDelegate(String), + #[error("HexDecodeError: {0}")] + HexDecode(String), +} + +impl From for KOSError { + fn from(err: KosError) -> Self { + KOSError::KOSDelegate(err.to_string()) + } +} + +impl From for KOSError { + fn from(err: FromHexError) -> Self { + KOSError::HexDecode(err.to_string()) + } +} + +#[derive(uniffi::Record)] +struct KOSAccount { + pub chain_id: i32, + pub private_key: String, + pub public_key: String, + pub address: String, + pub path: String, +} + +#[uniffi::export] +fn generate_mnemonic(size: i32) -> Result { + Ok(kos_crypto::mnemonic::generate_mnemonic(size as usize)?.to_phrase()) +} + +#[uniffi::export] +fn validate_mnemonic(mnemonic: String) -> bool { + kos_crypto::mnemonic::validate_mnemonic(mnemonic.as_str()).is_ok() +} + +#[uniffi::export] +fn generate_wallet_from_mnemonic( + mnemonic: String, + chain_id: i32, + index: i32, + use_legacy_path: bool, +) -> Result { + let chain = get_chain_by(chain_id)?; + let mut path_options = PathOptions::new(index as u32); + path_options.set_legacy(use_legacy_path); + let path = chain.get_path(&path_options)?; + let wallet = Wallet::from_mnemonic(chain, mnemonic, path, None)?; + Ok(KOSAccount { + chain_id, + private_key: wallet.get_private_key(), + public_key: wallet.get_public_key(), + address: wallet.get_address(), + path: wallet.get_path(), + }) +} + +#[uniffi::export] +fn generate_wallet_from_private_key( + chain_id: i32, + private_key: String, +) -> Result { + let chain = get_chain_by(chain_id)?; + let wallet = Wallet::from_private_key(chain, private_key)?; + Ok(KOSAccount { + chain_id, + private_key: wallet.get_private_key(), + public_key: wallet.get_public_key(), + address: wallet.get_address(), + path: wallet.get_path(), + }) +} + +#[uniffi::export] +fn encrypt_with_gmc(data: String, password: String) -> Result { + let encrypted_data = CipherAlgo::GMC.encrypt(data.as_bytes(), password.as_str())?; + Ok(encrypted_data.encode_hex()) +} + +#[uniffi::export] +fn encrypt_with_cbc(data: String, password: String) -> Result { + let encrypted_data = CipherAlgo::CBC.encrypt(data.as_bytes(), password.as_str())?; + Ok(encrypted_data.encode_hex()) +} + +#[uniffi::export] +fn encrypt_with_cfb(data: String, password: String) -> Result { + let encrypted_data = CipherAlgo::CFB.encrypt(data.as_bytes(), password.as_str())?; + Ok(encrypted_data.encode_hex()) +} + +#[uniffi::export] +fn decrypt(data: String, password: String) -> Result { + let data_in_byte = hex::decode(data)?; + let decrypted_data = cipher::decrypt(&data_in_byte, password.as_str())?; + Ok(String::from_utf8_lossy(&decrypted_data).to_string()) +} + +fn get_chain_by(id: i32) -> Result { + let id_u8 = u8::try_from(id).map_err(|_| KOSError::UnsupportedChain { id: id.to_string() })?; + let chain = Chain::get_by_code(id_u8) + .ok_or_else(|| KOSError::UnsupportedChain { id: id.to_string() })?; + Ok(chain) +} + +#[cfg(test)] +mod tests { + use crate::*; + + #[test] + fn should_generate_mnemonic() { + let size = 12; + match generate_mnemonic(size) { + Ok(mnemonic) => assert!(!mnemonic.is_empty(), "The mnemonic should not be empty"), + Err(_) => panic!("unexpected error!"), + } + } + + #[test] + fn should_fail_to_generate_mnemonic() { + let size = -1; + match generate_mnemonic(size) { + Ok(_) => panic!("A error was expected but found a mnemonic"), + Err(e) => assert!(matches!(e, KOSError::KOSDelegate(..)), "Invalid error"), + } + } + + #[test] + fn should_validate_mnemonic_with_success() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let result = validate_mnemonic(mnemonic); + assert!(result, "The mnemonic should be valid") + } + + #[test] + fn should_validate_mnemonic_with_failure() { + let mnemonic = "abandon xxx abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let result = validate_mnemonic(mnemonic); + assert!(!result, "The mnemonic should be not valid") + } + + #[test] + fn should_fail_to_get_account_from_mnemonic_with_invalid_chain() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let index = 0; + let chain_id = 999; + match generate_wallet_from_mnemonic(mnemonic, chain_id, index, false) { + Ok(_) => panic!("A error was expected but found a mnemonic"), + Err(e) => { + if let KOSError::UnsupportedChain { id } = e { + assert_eq!(id, chain_id.to_string(), "Invalid error"); + } else { + panic!("Expected UnsupportedChainError but found different error"); + } + } + } + } + + #[test] + fn should_get_account_from_mnemonic() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let index = 0; + let chain_id = 38; + match generate_wallet_from_mnemonic(mnemonic, chain_id, index, false) { + Ok(account) => { + assert_eq!( + account.address, + "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy".to_string(), + "The address doesn't match" + ); + assert_eq!( + account.private_key, + "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d".to_string(), + "The private_key doesn't match" + ); + assert_eq!(account.chain_id, chain_id, "The chain_id doesn't match"); + } + Err(_) => panic!("unexpected error!"), + } + } + + #[test] + fn should_get_all_supported_chains_account_from_mnemonic() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let index = 0; + for (&chain_code, _) in Chain::get_chains().iter() { + println!("code = {}", chain_code) + } + for (&chain_code, _) in Chain::get_chains().iter() { + match generate_wallet_from_mnemonic( + mnemonic.clone(), + i32::from(chain_code), + index, + false, + ) { + Ok(account) => { + assert!( + !account.address.is_empty(), + "The address for chain {} is empty", + chain_code + ); + assert!( + !account.private_key.is_empty(), + "The private_key for chain {} is empty", + chain_code + ); + assert_eq!( + account.chain_id, + i32::from(chain_code), + "The chain_id doesn't match" + ); + } + Err(e) => panic!("unexpected error! {}", e.to_string()), + } + } + } + + #[test] + fn should_get_account_from_private_key() { + let private_key = + "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d".to_string(); + let chain_id = 38; + match generate_wallet_from_private_key(chain_id, private_key) { + Ok(account) => { + assert_eq!( + account.address, + "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy".to_string(), + "The address doesn't match" + ); + assert_eq!( + account.private_key, + "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d".to_string(), + "The private_key doesn't match" + ); + assert_eq!(account.chain_id, chain_id, "The chain_id doesn't match"); + } + Err(_) => panic!("unexpected error!"), + } + } + + #[test] + fn should_fail_to_get_account_from_private_key() { + let private_key = "".to_string(); + let chain_id = 38; + match generate_wallet_from_private_key(chain_id, private_key) { + Ok(account) => panic!( + "A error was expected but found a pk {}.", + account.private_key + ), + Err(e) => assert!(matches!(e, KOSError::KOSDelegate(..)), " Invalid error"), + } + } + + #[test] + fn should_encrypt_with_gmc_and_decrypt_data() { + let original_data = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let password = "myPass".to_string(); + let encrypted_data = encrypt_with_gmc(original_data.clone(), password.clone()).unwrap(); + let decrypted_data = decrypt(encrypted_data, password.clone()).unwrap(); + assert_eq!(original_data, decrypted_data, "The data is not the same"); + } + + #[test] + fn should_encrypt_with_cbc_and_decrypt_data() { + let original_data = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let password = "myPass".to_string(); + let encrypted_data = encrypt_with_cbc(original_data.clone(), password.clone()).unwrap(); + let decrypted_data = decrypt(encrypted_data, password.clone()).unwrap(); + assert_eq!(original_data, decrypted_data, "The data is not the same"); + } + + #[test] + fn should_encrypt_with_cbf_and_decrypt_data() { + let original_data = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let password = "myPass".to_string(); + let encrypted_data = encrypt_with_cfb(original_data.clone(), password.clone()).unwrap(); + let decrypted_data = decrypt(encrypted_data, password.clone()).unwrap(); + assert_eq!(original_data, decrypted_data, "The data is not the same"); + } + + #[test] + fn should_fail_to_decrypt_with_wrong_password() { + let original_data = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let password = "myPass".to_string(); + let encrypted_data = encrypt_with_gmc(original_data.clone(), password.clone()).unwrap(); + match decrypt(encrypted_data, "wrong".to_string()) { + Ok(_) => panic!("A error was expected but found a decrypted data"), + Err(e) => assert!(matches!(e, KOSError::KOSDelegate(..)), " Invalid error"), + } + } +} diff --git a/packages/kos-sdk/Cargo.toml b/packages/kos-sdk/Cargo.toml index 30f96f6..32004f1 100644 --- a/packages/kos-sdk/Cargo.toml +++ b/packages/kos-sdk/Cargo.toml @@ -40,6 +40,8 @@ rlp = "0.5" reqwest = { workspace = true, default-features = false, features = ["rustls-tls", "blocking", "json"] } wasm-bindgen-futures = "0.4" +lazy_static = { workspace = true } + kos-types = { workspace = true, features = ["serde"] } kos-crypto = { workspace = true } kos-proto = { workspace = true } diff --git a/packages/kos-sdk/src/chain.rs b/packages/kos-sdk/src/chain.rs index bb281a1..1a7087b 100644 --- a/packages/kos-sdk/src/chain.rs +++ b/packages/kos-sdk/src/chain.rs @@ -4,7 +4,9 @@ use kos_crypto::keypair::KeyPair; use kos_types::error::Error; use kos_types::number::BigNumber; +use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use strum::{EnumCount, IntoStaticStr}; use wasm_bindgen::prelude::*; @@ -17,6 +19,16 @@ macro_rules! createChains { $($name,)* } + lazy_static! { + static ref CHAIN_MAP: HashMap = { + let mut map = HashMap::new(); + $(map.insert($name::base_chain().chain_code, Chain::$name);)* + // remove the NONE chain + map.remove(&0); + map + }; + } + impl Chain { pub fn base_chain(&self) -> BaseChain { match self { @@ -54,6 +66,14 @@ macro_rules! createChains { } } + pub fn get_by_code(code: u8) -> Option { + CHAIN_MAP.get(&code).cloned() + } + + pub fn get_chains() -> HashMap { + CHAIN_MAP.clone() + } + /// Sign digest data with the private key. pub fn sign_digest(&self, digest: &[u8], keypair: &KeyPair) -> Result, Error> { match self {