diff --git a/.github/workflows/pull-develop.yaml b/.github/workflows/pull-develop.yaml index 992e8f6..c0a0dbe 100644 --- a/.github/workflows/pull-develop.yaml +++ b/.github/workflows/pull-develop.yaml @@ -17,7 +17,7 @@ on: - '**/*.md' - '.gitignore' - '.github/**' - + jobs: format: runs-on: ubuntu-latest @@ -46,16 +46,12 @@ jobs: cargo deny check cargo pants - - name: Restore .env file - run: echo "$NODES_ENV" | base64 -d > packages/kos-sdk/.env.nodes - env: - NODES_ENV: ${{ secrets.NODES_ENV }} - name: Run Tests run: cargo test build: - needs: [format] + needs: [ format ] runs-on: ubuntu-latest steps: - name: Checkout repository @@ -68,10 +64,5 @@ jobs: with: with_cache: true - - name: Restore .env file - run: echo $NODES_ENV | base64 -d > packages/kos-sdk/.env.nodes - env: - NODES_ENV: ${{ secrets.NODES_ENV }} - - name: Build run: make webpack-npm diff --git a/.github/workflows/pull-master-release.yaml b/.github/workflows/pull-master-release.yaml index 01b5f5b..c269242 100644 --- a/.github/workflows/pull-master-release.yaml +++ b/.github/workflows/pull-master-release.yaml @@ -1,4 +1,4 @@ -name: KOS-RS Bump version && Release +name: KOS-RS Bump version && Release on: pull_request: @@ -24,17 +24,12 @@ jobs: with: submodules: recursive fetch-depth: 0 - + - name: RustUp uses: klever-io/kos-rs/.github/actions/rustup@develop with: with_cache: true - - name: Restore .env file - run: echo "$NODES_ENV" | base64 -d > packages/kos-sdk/.env.nodes - env: - NODES_ENV: ${{ secrets.NODES_ENV }} - - name: Run Tests run: cargo test diff --git a/.gitignore b/.gitignore index a8c2b32..eb75cc1 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,8 @@ dist .DS_Store demo/.DS_Store packages/.DS_Store -.idea/* \ No newline at end of file +.idea/* + +packages/kos/target +packages/kos/Cargo.lock +packages/kos/.idea \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 2a7d142..44977c9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "packages/kos-proto/proto/tron"] - path = packages/kos-proto/proto/tron - url = https://github.com/tronprotocol/protocol +[submodule "packages/kos/src/protos/tron"] + path = packages/kos/src/protos/tron + url = https://github.com/tronprotocol/protocol.git diff --git a/Cargo.lock b/Cargo.lock index 74406d6..c35b239 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,19 +3,10 @@ version = 3 [[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -61,11 +52,149 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloy-dyn-abi" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2364c782a245cf8725ea6dbfca5f530162702b5d685992ea03ce64529136cc" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "derive_more", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-json-abi" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84c506bf264110fa7e90d9924f742f40ef53c6572ea56a0b0bd714a567ed389" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fce5dbd6a4f118eecc4719eaa9c7ffc31c315e6c5ccde3642db927802312425" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash", + "hashbrown", + "hex-literal", + "indexmap", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0822426598f95e45dd1ea32a738dac057529a709ee645fcc516ffa4cbde08f" +dependencies = [ + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9343289b4a7461ed8bab8618504c995c049c082b70c7332efd7b32125633dc05" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4222d70bec485ceccc5d8fd4f2909edd65b5d5e43d4aca0b5dcee65d519ae98f" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck 0.5.0", + "indexmap", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.87", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e17f2677369571b976e51ea1430eb41c3690d344fef567b840bfc0b01b6f83a" +dependencies = [ + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.87", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa64d80ae58ffaafdff9d5d84f58d03775f66c84433916dc9a64ed16af5755da" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6520d427d4a8eb7aa803d852d7a52ceb0c519e784c292f64bb339e636918cf27" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -78,49 +207,179 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "askama" @@ -145,7 +404,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -164,56 +423,30 @@ dependencies = [ ] [[package]] -name = "async-stream" -version = "0.3.5" +name = "atomic-polyfill" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", + "critical-section", ] [[package]] -name = "async-stream-impl" -version = "0.3.5" +name = "auto_impl" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", -] - -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", + "syn 2.0.87", ] [[package]] name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "backtrace" -version = "0.3.72" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base16ct" @@ -222,10 +455,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] -name = "base58" -version = "0.2.0" +name = "base58ck" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals", + "bitcoin_hashes", +] [[package]] name = "base64" @@ -266,6 +503,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "bincode" version = "1.3.3" @@ -275,34 +518,76 @@ dependencies = [ "serde", ] +[[package]] +name = "bip39-dict" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f1d227703899f704884cb6dfe1dc6a1bd447ea9f91418539989618ebf01685" +dependencies = [ + "cryptoxide", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitcoin" -version = "0.30.2" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ - "bech32", - "bitcoin-private", + "base58ck", + "bech32 0.11.0", + "bitcoin-internals", + "bitcoin-io", + "bitcoin-units", "bitcoin_hashes", + "hex-conservative", "hex_lit", "secp256k1", - "serde", ] [[package]] -name = "bitcoin-private" -version = "0.1.0" +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" + +[[package]] +name = "bitcoin-io" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals", +] [[package]] name = "bitcoin_hashes" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ - "bitcoin-private", - "serde", + "bitcoin-io", + "hex-conservative", ] [[package]] @@ -313,9 +598,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -329,6 +614,21 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2b-ref" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "294d17c72e0ba59fad763caa112368d0672083779cdebbb97164f4bb4c1e339a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -353,7 +653,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2", + "sha2 0.10.8", "tinyvec", ] @@ -371,9 +671,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" [[package]] name = "byteorder" @@ -383,9 +683,12 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +dependencies = [ + "serde", +] [[package]] name = "camino" @@ -413,7 +716,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -430,9 +733,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" +dependencies = [ + "shlex", +] [[package]] name = "cfb-mode" @@ -471,9 +777,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -481,9 +787,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -493,21 +799,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "cobs" @@ -523,11 +829,11 @@ checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" dependencies = [ "bs58", "coins-core", - "digest", - "hmac", + "digest 0.10.7", + "hmac 0.12.1", "k256", "serde", - "sha2", + "sha2 0.10.8", "thiserror", ] @@ -539,11 +845,11 @@ checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ "bitvec", "coins-bip32", - "hmac", + "hmac 0.12.1", "once_cell", "pbkdf2", "rand", - "sha2", + "sha2 0.10.8", "thiserror", ] @@ -554,15 +860,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ "base64 0.21.7", - "bech32", + "bech32 0.9.1", "bs58", - "digest", + "digest 0.10.7", "generic-array", "hex", "ripemd", "serde", "serde_derive", - "sha2", + "sha2 0.10.8", "sha3", "thiserror", ] @@ -575,18 +881,21 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] -name = "console_error_panic_hook" -version = "0.1.7" +name = "const-hex" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" dependencies = [ "cfg-if", - "wasm-bindgen", + "cpufeatures", + "hex", + "proptest", + "serde", ] [[package]] @@ -595,33 +904,11 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -637,9 +924,9 @@ dependencies = [ [[package]] name = "critical-section" -version = "1.1.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crunchy" @@ -670,6 +957,22 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "cryptoxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382ce8820a5bb815055d3553a610e8cb542b2d767bbacea99038afda96cd760d" + [[package]] name = "ctr" version = "0.9.2" @@ -688,9 +991,9 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", - "digest", + "digest 0.10.7", "fiat-crypto", - "rustc_version", + "rustc_version 0.4.1", "subtle", "zeroize", ] @@ -703,7 +1006,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -712,21 +1015,49 @@ version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ - "const-oid", - "zeroize", + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", ] [[package]] -name = "derive_more" -version = "0.99.17" +name = "derive_more-impl" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version", - "syn 1.0.109", + "syn 2.0.87", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", ] [[package]] @@ -735,23 +1066,17 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", ] [[package]] -name = "dotenv-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4547f16c17f6051a12cdb8c62b803f94bee6807c74aa7c530b30b737df981fc" - -[[package]] -name = "dotenvy" -version = "0.15.7" +name = "dunce" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "ecdsa" @@ -760,7 +1085,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", @@ -773,8 +1098,6 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8", - "serde", "signature", ] @@ -786,17 +1109,15 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", - "serde", - "sha2", + "sha2 0.10.8", "subtle", - "zeroize", ] [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" @@ -806,7 +1127,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -824,13 +1145,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" [[package]] -name = "encoding_rs" -version = "0.8.34" +name = "embedded-io" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" [[package]] name = "enum_delegate" @@ -865,7 +1183,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -895,60 +1213,27 @@ dependencies = [ ] [[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3", - "thiserror", - "uint", -] - -[[package]] -name = "ethbloom" -version = "0.13.0" +name = "fastrand" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-rlp", - "impl-serde", - "tiny-keccak", -] +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] -name = "ethereum-types" -version = "0.14.1" +name = "fastrlp" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" dependencies = [ - "ethbloom", - "fixed-hash", - "impl-rlp", - "impl-serde", - "primitive-types", - "uint", + "arrayvec", + "auto_impl", + "bytes", ] -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" dependencies = [ "simd-adler32", ] @@ -989,9 +1274,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1004,28 +1289,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "foldhash" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" [[package]] name = "fs-err" @@ -1042,105 +1309,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" -dependencies = [ - "gloo-timers", - "send_wrapper", -] - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1165,6 +1333,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom_or_panic" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" +dependencies = [ + "rand_core", +] + [[package]] name = "ghash" version = "0.5.1" @@ -1175,30 +1352,12 @@ dependencies = [ "polyval", ] -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" - [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "goblin" version = "0.8.2" @@ -1221,25 +1380,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hash32" version = "0.2.1" @@ -1251,32 +1391,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - -[[package]] -name = "headers" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" -dependencies = [ - "base64 0.21.7", - "bytes", - "headers-core", - "http 0.2.12", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" dependencies = [ - "http 0.2.12", + "foldhash", + "serde", ] [[package]] @@ -1287,7 +1407,7 @@ checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", "hash32", - "rustc_version", + "rustc_version 0.4.1", "serde", "spin", "stable_deref_trait", @@ -1305,230 +1425,73 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex_lit" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "html-escape" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" -dependencies = [ - "utf8-width", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ - "bytes", - "fnv", - "itoa", + "serde", ] [[package]] -name = "http-body" -version = "0.4.6" +name = "hex-conservative" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", + "arrayvec", ] [[package]] -name = "http-body" -version = "1.0.0" +name = "hex-literal" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http 1.1.0", -] +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] -name = "http-body-util" +name = "hex_lit" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" -dependencies = [ - "bytes", - "futures-core", - "http 1.1.0", - "http-body 1.0.0", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" -dependencies = [ - "futures-util", - "http 1.1.0", - "hyper 1.3.1", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" [[package]] -name = "hyper-tls" -version = "0.5.0" +name = "hmac" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" dependencies = [ - "bytes", - "hyper 0.14.29", - "native-tls", - "tokio", - "tokio-native-tls", + "crypto-mac", + "digest 0.9.0", ] [[package]] -name = "hyper-util" -version = "0.1.5" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", - "pin-project-lite", - "socket2", - "tokio", - "tower", - "tower-service", - "tracing", + "digest 0.10.7", ] [[package]] -name = "idna" -version = "0.4.0" +name = "hmac-drbg" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", ] [[package]] -name = "idna" -version = "0.5.0" +name = "html-escape" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "utf8-width", ] [[package]] @@ -1553,24 +1516,6 @@ dependencies = [ "parity-scale-codec", ] -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde", -] - [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -1584,12 +1529,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", + "serde", ] [[package]] @@ -1608,12 +1554,6 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" -[[package]] -name = "ipnet" -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" @@ -1646,39 +1586,24 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonrpc-core" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" -dependencies = [ - "futures", - "futures-executor", - "futures-util", - "log", - "serde", - "serde_derive", - "serde_json", -] - [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", "once_cell", - "sha2", + "sha2 0.10.8", "signature", ] @@ -1692,50 +1617,55 @@ dependencies = [ ] [[package]] -name = "kos" -version = "0.1.2" +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" dependencies = [ - "console_error_panic_hook", - "futures", - "hex", - "js-sys", - "kos-crypto", - "kos-sdk", - "kos-types", - "kos-utils", - "log", - "qrcode-generator", - "wasm-bindgen", - "wasm-bindgen-test", - "web-sys", + "digest 0.10.7", + "sha3-asm", ] [[package]] -name = "kos-crypto" +name = "kos" version = "0.1.2" dependencies = [ "aes", "aes-gcm", - "base64 0.21.7", + "alloy-dyn-abi", + "bech32 0.9.1", + "bip39-dict", + "bitcoin", + "blake2b-ref", "cbc", "cfb-mode", "coins-bip32", "coins-bip39", "ed25519-dalek", + "getrandom", + "heck 0.4.1", "hex", - "hmac", - "kos-types", - "log", + "hmac 0.12.1", + "libsecp256k1", + "parity-scale-codec", "pbkdf2", "pem", + "prost", + "prost-build", + "prost-types", + "prost-wkt", + "prost-wkt-build", + "prost-wkt-types", + "quote", "rand", - "secp256k1", - "serde", - "sha1", - "sha2", + "rand_core", + "ripemd", + "rlp", + "schnorrkel", + "serde_json", + "sha2 0.10.8", "sha3", - "wasm-bindgen", - "zeroize", + "tiny-json-rs", ] [[package]] @@ -1743,119 +1673,101 @@ name = "kos-mobile" version = "0.1.2" dependencies = [ "hex", - "kos-crypto", - "kos-proto", - "kos-sdk", - "kos-types", - "kos-utils", + "kos", "lazy_static", + "num-bigint", + "num-traits", + "serde", "thiserror", "uniffi", ] [[package]] -name = "kos-proto" +name = "kos-web" version = "0.1.2" dependencies = [ - "bytes", - "glob", - "heck 0.4.1", - "kos-types", - "pbjson", - "pbjson-build", - "pbjson-types", - "prost 0.11.9", - "prost-build 0.12.6", - "prost-types 0.11.9", - "prost-wkt", - "prost-wkt-build", - "prost-wkt-types", - "quote", - "serde", - "serde_json", - "wasm-bindgen", -] - -[[package]] -name = "kos-sdk" -version = "0.1.2" -dependencies = [ - "base58", - "bech32", - "bitcoin", - "coins-bip39", - "dotenv-build", - "dotenvy", "enum_delegate", "enum_dispatch", "hex", - "hmac", - "kos-crypto", - "kos-proto", - "kos-types", - "kos-utils", + "kos", "lazy_static", - "log", - "pbjson", - "pbjson-types", + "num-bigint", + "num-traits", "pem", - "prost 0.11.9", - "rand", - "reqwest 0.12.4", - "rlp", - "secp256k1", + "postcard", + "qrcode-generator", "serde", "serde-wasm-bindgen", "serde_json", "strum", - "tokio-test", "wasm-bindgen", - "wasm-bindgen-futures", - "web3", ] [[package]] -name = "kos-types" -version = "0.1.2" +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ - "coins-bip32", - "coins-bip39", - "ed25519-dalek", - "hex", - "log", - "num-bigint", - "num-traits", + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", "rand", - "reqwest 0.12.4", - "secp256k1", "serde", - "serde_json", - "wasm-bindgen", + "sha2 0.9.9", + "typenum", ] [[package]] -name = "kos-utils" -version = "0.1.2" +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ - "kos-types", - "log", - "postcard", - "serde", - "wasm-bindgen", - "web-sys", + "crunchy", + "digest 0.9.0", + "subtle", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] [[package]] -name = "libc" -version = "0.2.155" +name = "libsecp256k1-gen-genmult" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] [[package]] name = "linux-raw-sys" @@ -1875,15 +1787,27 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "merlin" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core", + "zeroize", +] [[package]] name = "mime" @@ -1909,54 +1833,20 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", "simd-adler32", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - [[package]] name = "multimap" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" -[[package]] -name = "native-tls" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nom" version = "7.1.3" @@ -1969,9 +1859,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -1993,32 +1883,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" -dependencies = [ - "memchr", + "libm", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -2026,50 +1898,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "openssl" -version = "0.10.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "parity-scale-codec" version = "3.6.12" @@ -2096,29 +1924,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.5", -] - [[package]] name = "password-hash" version = "0.5.0" @@ -2136,51 +1941,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pbjson" -version = "0.5.1" -source = "git+https://github.com/klever-io/pbjson#e0e9f4e558c53ef6611ddcabce28d49c1b47629d" -dependencies = [ - "base64 0.13.1", - "hex", - "serde", -] - -[[package]] -name = "pbjson-build" -version = "0.5.1" -source = "git+https://github.com/klever-io/pbjson#e0e9f4e558c53ef6611ddcabce28d49c1b47629d" -dependencies = [ - "heck 0.4.1", - "itertools 0.10.5", - "prost 0.11.9", - "prost-types 0.11.9", -] - -[[package]] -name = "pbjson-types" -version = "0.5.1" -source = "git+https://github.com/klever-io/pbjson#e0e9f4e558c53ef6611ddcabce28d49c1b47629d" -dependencies = [ - "bytes", - "chrono", - "pbjson", - "pbjson-build", - "prost 0.11.9", - "prost-build 0.11.9", - "serde", -] - [[package]] name = "pbkdf2" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest", - "hmac", + "digest 0.10.7", + "hmac 0.12.1", "password-hash", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -2190,57 +1960,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ "base64 0.22.1", - "serde", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap", + "serde", ] [[package]] -name = "pin-project" -version = "1.1.5" +name = "pest" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ - "pin-project-internal", + "memchr", + "thiserror", + "ucd-trie", ] [[package]] -name = "pin-project-internal" -version = "1.1.5" +name = "petgraph" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", + "fixedbitset", + "indexmap", ] -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkcs8" version = "0.10.2" @@ -2251,12 +1994,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - [[package]] name = "plain" version = "0.2.3" @@ -2265,9 +2002,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "png" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2290,40 +2027,34 @@ dependencies = [ [[package]] name = "postcard" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" dependencies = [ "cobs", - "embedded-io", + "embedded-io 0.4.0", + "embedded-io 0.6.1", "heapless", "serde", ] [[package]] name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "prettyplease" -version = "0.1.25" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "proc-macro2", - "syn 1.0.109", + "zerocopy", ] [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -2334,69 +2065,77 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", - "impl-rlp", - "impl-serde", "uint", ] [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] [[package]] -name = "proc-macro2" -version = "1.0.85" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "unicode-ident", + "proc-macro2", + "quote", ] [[package]] -name = "prost" -version = "0.11.9" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ - "bytes", - "prost-derive 0.11.9", + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] -name = "prost" -version = "0.12.6" +name = "proc-macro2" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ - "bytes", - "prost-derive 0.12.6", + "unicode-ident", ] [[package]] -name = "prost-build" -version = "0.11.9" +name = "proptest" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ - "bytes", - "heck 0.4.1", - "itertools 0.10.5", + "bit-set", + "bit-vec", + "bitflags 2.6.0", "lazy_static", - "log", - "multimap 0.8.3", - "petgraph", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-types 0.11.9", - "regex", - "syn 1.0.109", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", "tempfile", - "which", + "unarray", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", ] [[package]] @@ -2409,30 +2148,17 @@ dependencies = [ "heck 0.5.0", "itertools 0.12.1", "log", - "multimap 0.10.0", + "multimap", "once_cell", "petgraph", - "prettyplease 0.2.20", - "prost 0.12.6", - "prost-types 0.12.6", + "prettyplease", + "prost", + "prost-types", "regex", - "syn 2.0.66", + "syn 2.0.87", "tempfile", ] -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "prost-derive" version = "0.12.6" @@ -2443,16 +2169,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.66", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost 0.11.9", + "syn 2.0.87", ] [[package]] @@ -2461,18 +2178,18 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ - "prost 0.12.6", + "prost", ] [[package]] name = "prost-wkt" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562788060bcf2bfabe055194bd991ed2442457661744c88e0a0828ff9a08c08b" +checksum = "5fb7ec2850c138ebaa7ab682503b5d08c3cb330343e9c94776612928b6ddb53f" dependencies = [ "chrono", "inventory", - "prost 0.11.9", + "prost", "serde", "serde_derive", "serde_json", @@ -2481,27 +2198,27 @@ dependencies = [ [[package]] name = "prost-wkt-build" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4dca8bcead3b728a6a7da017cc95e7f4cb2320ec4f6896bc593a1c4700f7328" +checksum = "598b7365952c2ed4e32902de0533653aafbe5ae3da436e8e2335c7d375a1cef3" dependencies = [ - "heck 0.4.1", - "prost 0.11.9", - "prost-build 0.11.9", - "prost-types 0.11.9", + "heck 0.5.0", + "prost", + "prost-build", + "prost-types", "quote", ] [[package]] name = "prost-wkt-types" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2377c5680f2342871823045052e791b4487f7c90aae17e0feaee24cf59578a34" +checksum = "1a8eadc2381640a49c1fbfb9f4a857794b4e5bf5a2cbc2d858cfdb74f64dcd22" dependencies = [ "chrono", - "prost 0.11.9", - "prost-build 0.11.9", - "prost-types 0.11.9", + "prost", + "prost-build", + "prost-types", "prost-wkt", "prost-wkt-build", "regex", @@ -2527,11 +2244,17 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142" +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -2551,6 +2274,7 @@ dependencies = [ "libc", "rand_chacha", "rand_core", + "serde", ] [[package]] @@ -2573,19 +2297,19 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.5.1" +name = "rand_xorshift" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "bitflags 2.5.0", + "rand_core", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2595,9 +2319,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2606,91 +2330,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" - -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.29", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg 0.50.0", -] - -[[package]] -name = "reqwest" -version = "0.12.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "hyper 1.3.1", - "hyper-rustls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile 2.1.2", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "winreg 0.52.0", -] +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rfc6979" @@ -2698,32 +2340,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac", + "hmac 0.12.1", "subtle", ] -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - [[package]] name = "ripemd" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -2737,95 +2364,96 @@ dependencies = [ ] [[package]] -name = "rustc-demangle" -version = "0.1.24" +name = "ruint" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] [[package]] -name = "rustc-hex" -version = "2.1.0" +name = "ruint-macro" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] -name = "rustc_version" -version = "0.4.0" +name = "rustc-hash" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] -name = "rustix" -version = "0.38.34" +name = "rustc-hex" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] -name = "rustls" -version = "0.22.4" +name = "rustc_version" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", + "semver 0.11.0", ] [[package]] -name = "rustls-pemfile" -version = "1.0.4" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "base64 0.21.7", + "semver 1.0.23", ] [[package]] -name = "rustls-pemfile" -version = "2.1.2" +name = "rustix" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ - "base64 0.22.1", - "rustls-pki-types", + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] -name = "rustls-pki-types" -version = "1.7.0" +name = "rustversion" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] -name = "rustls-webpki" -version = "0.102.4" +name = "rusty-fork" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", + "fnv", + "quick-error", + "tempfile", + "wait-timeout", ] -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - [[package]] name = "ryu" version = "1.0.18" @@ -2833,20 +2461,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] -name = "schannel" -version = "0.1.23" +name = "schnorrkel" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0" dependencies = [ - "windows-sys 0.52.0", + "arrayref", + "arrayvec", + "curve25519-dalek", + "getrandom_or_panic", + "merlin", + "rand_core", + "sha2 0.10.8", + "subtle", + "zeroize", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -2870,7 +2500,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] @@ -2889,46 +2519,30 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.27.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", - "rand", "secp256k1-sys", - "serde", ] [[package]] name = "secp256k1-sys" -version = "0.8.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] [[package]] -name = "security-framework" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" -dependencies = [ - "bitflags 2.5.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.11.0" +name = "semver" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "core-foundation-sys", - "libc", + "semver-parser", ] [[package]] @@ -2941,16 +2555,19 @@ dependencies = [ ] [[package]] -name = "send_wrapper" -version = "0.4.0" +name = "semver-parser" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] [[package]] name = "serde" -version = "1.0.203" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -2968,47 +2585,38 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ - "form_urlencoded", "itoa", + "memchr", "ryu", "serde", ] [[package]] -name = "sha1" -version = "0.10.6" +name = "sha2" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -3019,7 +2627,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -3028,46 +2636,47 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.7", "keccak", ] [[package]] -name = "signature" -version = "2.2.0" +name = "sha3-asm" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ - "digest", - "rand_core", + "cc", + "cfg-if", ] [[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "siphasher" -version = "0.3.11" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "slab" -version = "0.4.9" +name = "signature" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "autocfg", + "digest 0.10.7", + "rand_core", ] [[package]] -name = "smallvec" -version = "1.13.2" +name = "simd-adler32" +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 = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "smawk" @@ -3075,16 +2684,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "spin" version = "0.9.8" @@ -3141,14 +2740,14 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -3163,9 +2762,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -3173,30 +2772,15 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" +name = "syn-solidity" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "f76fe0a3e1476bdaa0775b9aec5b869ed9520c2b2fedfe9c6df3618f8ea6290b" dependencies = [ - "core-foundation-sys", - "libc", + "paste", + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -3207,14 +2791,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3228,121 +2813,67 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", + "syn 2.0.87", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "socket2", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" +name = "tiny-json-rs" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "922df6879c950dd96850b09e28fa4e7006c28770844eec77a8ee56a759b41f31" dependencies = [ - "native-tls", - "tokio", + "tiny_json_derive", ] [[package]] -name = "tokio-rustls" -version = "0.25.0" +name = "tiny-keccak" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "rustls", - "rustls-pki-types", - "tokio", + "crunchy", ] [[package]] -name = "tokio-stream" -version = "0.1.15" +name = "tiny_json_derive" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "2356490d7043f9ddf974a16550e75868d900f2382d83088db0ec9b917dbaca9e" dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "tokio-test" -version = "0.4.4" +name = "tinyvec" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ - "async-stream", - "bytes", - "futures-core", - "tokio", - "tokio-stream", + "tinyvec_macros", ] [[package]] -name = "tokio-util" -version = "0.7.11" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" @@ -3355,78 +2886,26 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", "winnow", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "typeid" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" [[package]] name = "typenum" @@ -3436,9 +2915,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typetag" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "661d18414ec032a49ece2d56eee03636e43c4e8d577047ab334c0ba892e29aaf" +checksum = "52ba3b6e86ffe0054b2c44f2d86407388b933b16cb0a70eea3929420db1d9bbe" dependencies = [ "erased-serde", "inventory", @@ -3449,15 +2928,21 @@ dependencies = [ [[package]] name = "typetag-impl" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac73887f47b9312552aa90ef477927ff014d63d1920ca8037c6c1951eab64bb1" +checksum = "70b20a22c42c8f1cd23ce5e34f165d4d37038f5b663ad20fb6adbdf029172483" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "uint" version = "0.9.5" @@ -3471,40 +2956,34 @@ dependencies = [ ] [[package]] -name = "unicase" -version = "2.7.0" +name = "unarray" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] -name = "unicode-bidi" -version = "0.3.15" +name = "unicase" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] -name = "unicode-normalization" -version = "0.1.23" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "uniffi" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db87def739fe4183947f8419d572d1849a4a09355eba4e988a2105cfd0ac6a7" +checksum = "51ce6280c581045879e11b400bae14686a819df22b97171215d15549efa04ddb" dependencies = [ "anyhow", "camino", @@ -3518,9 +2997,9 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a112599c9556d1581e4a3d72019a74c2c3e122cc27f4af12577a429c4d5e614" +checksum = "5e9f25730c9db2e878521d606f54e921edb719cdd94d735e7f97705d6796d024" dependencies = [ "anyhow", "askama", @@ -3541,9 +3020,9 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b12684401d2a8508ca9c72a95bbc45906417e42fc80942abaf033bbf01aa33" +checksum = "88dba57ac699bd8ec53d6a352c8dd0e479b33f698c5659831bb1e4ce468c07bd" dependencies = [ "anyhow", "camino", @@ -3552,23 +3031,22 @@ dependencies = [ [[package]] name = "uniffi_checksum_derive" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22dbe67c1c957ac6e7611bdf605a6218aa86b0eebeb8be58b70ae85ad7d73dc" +checksum = "d2c801f0f05b06df456a2da4c41b9c2c4fdccc6b9916643c6c67275c4c9e4d07" dependencies = [ "quote", - "syn 2.0.66", + "syn 2.0.87", ] [[package]] name = "uniffi_core" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0c35aaad30e3a9e6d4fe34e358d64dbc92ee09045b48591b05fc9f12e0905b" +checksum = "61049e4db6212d0ede80982adf0e1d6fa224e6118387324c5cfbe3083dfb2252" dependencies = [ "anyhow", "bytes", - "camino", "log", "once_cell", "paste", @@ -3577,9 +3055,9 @@ dependencies = [ [[package]] name = "uniffi_macros" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db66474c5c61b0f7afc3b4995fecf9b72b340daa5ca0ef3da7778d75eb5482ea" +checksum = "b40fd2249e0c5dcbd2bfa3c263db1ec981f7273dca7f4132bf06a272359a586c" dependencies = [ "bincode", "camino", @@ -3588,16 +3066,16 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.66", + "syn 2.0.87", "toml", "uniffi_meta", ] [[package]] name = "uniffi_meta" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d898893f102e0e39b8bcb7e3d2188f4156ba280db32db9e8af1f122d057e9526" +checksum = "c9ad57039b4fafdbf77428d74fff40e0908e5a1731e023c19cfe538f6d4a8ed6" dependencies = [ "anyhow", "bytes", @@ -3607,9 +3085,9 @@ dependencies = [ [[package]] name = "uniffi_testing" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6aa4f0cf9d12172d84fc00a35a6c1f3522b526daad05ae739f709f6941b9b6" +checksum = "21fa171d4d258dc51bbd01893cc9608c1b62273d2f9ea55fb64f639e77824567" dependencies = [ "anyhow", "camino", @@ -3620,9 +3098,9 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b044e9c519e0bb51e516ab6f6d8f4f4dcf900ce30d5ad07c03f924e2824f28e" +checksum = "f52299e247419e7e2934bef2f94d7cccb0e6566f3248b1d48b160d8f369a2668" dependencies = [ "anyhow", "textwrap", @@ -3641,23 +3119,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -dependencies = [ - "form_urlencoded", - "idna 0.5.0", - "percent-encoding", -] - [[package]] name = "utf8-width" version = "0.1.7" @@ -3671,24 +3132,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "vcpkg" -version = "0.2.15" +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "want" -version = "0.3.1" +name = "wait-timeout" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ - "try-lock", + "libc", ] [[package]] @@ -3699,46 +3160,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3746,101 +3196,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "wasm-bindgen-test" -version = "0.3.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" -dependencies = [ - "console_error_panic_hook", - "js-sys", - "scoped-tls", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web3" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5388522c899d1e1c96a4c307e3797e0f697ba7c77dd8e0e625ecba9dd0342937" -dependencies = [ - "arrayvec", - "base64 0.21.7", - "bytes", - "derive_more", - "ethabi", - "ethereum-types", - "futures", - "futures-timer", - "getrandom", - "headers", - "hex", - "idna 0.4.0", - "js-sys", - "jsonrpc-core", - "log", - "parking_lot", - "pin-project", - "rand", - "reqwest 0.11.27", - "rlp", - "serde", - "serde-wasm-bindgen", - "serde_json", - "tiny-keccak", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", -] - -[[package]] -name = "webpki-roots" -version = "0.26.1" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" -dependencies = [ - "rustls-pki-types", -] +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "weedle2" @@ -3851,193 +3222,125 @@ dependencies = [ "nom", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] [[package]] -name = "winreg" -version = "0.50.0" +name = "wyz" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "tap", ] [[package]] -name = "winreg" -version = "0.52.0" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "byteorder", + "zerocopy-derive", ] [[package]] -name = "wyz" -version = "0.5.1" +name = "zerocopy-derive" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "tap", + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -4057,5 +3360,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.87", ] diff --git a/Cargo.toml b/Cargo.toml index 7f44c83..80f8ccb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,7 @@ # https://doc.rust-lang.org/edition-guide/rust-2021/default-cargo-resolver.html#details resolver = "2" members = [ - "packages/kos-types", - "packages/kos", - "packages/kos-crypto", - "packages/kos-sdk", - "packages/kos-mobile", + "packages/kos", "packages/kos-mobile", "packages/kos-web", ] # This makes the compiled code faster and smaller, but it makes compiling slower, # so it's only enabled in release mode. @@ -42,7 +38,7 @@ 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"} +uniffi = { version = "0.28.1" } reqwest = { version = "0.12", default-features = false, feature = ["rustls-tls", "blocking", "json"] } wasm-bindgen = "0.2" @@ -54,8 +50,5 @@ 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 } \ No newline at end of file +kos = { version = "0.1.0", path = "./packages/kos", default-features = false } +kos-mobile = { version = "0.1.0", path = "./packages/kos-mobile", default-features = false } diff --git a/Makefile b/Makefile index feadc9b..5633901 100644 --- a/Makefile +++ b/Makefile @@ -11,14 +11,6 @@ fmt: clippy: cargo clippy --all -- -D warnings -encode-env: -ifeq ($(UNAME), Linux) - cat packages/kos-sdk/.env.nodes | base64 -w 0 > .env.nodes.base64 -endif -ifeq ($(UNAME), Darwin) - cat packages/kos-sdk/.env.nodes | base64 > .env.nodes.base64 -endif - check: fmt clippy cargo deny check # cargo outdated --exit-code 1 @@ -31,10 +23,10 @@ grcov: # grcov ./target/debug/ -s . -t lcov --llvm --branch --ignore-not-existing --ignore "/*" -o lcov.info webpack: - wasm-pack build --scope klever --target web --out-name index --out-dir ../../demo/kos ./packages/kos + wasm-pack build --scope klever --target web --out-name index --out-dir ../../demo/kos ./packages/kos-web webpack-npm: - wasm-pack build --scope klever --target bundler --release --out-name index --out-dir ../../demo/kos ./packages/kos + wasm-pack build --scope klever --target bundler --release --out-name index --out-dir ../../demo/kos ./packages/kos-web clean-mobile-build: cd packages/kos-mobile && ./build_clean.sh diff --git a/deny.toml b/deny.toml index 646ad73..4e88220 100644 --- a/deny.toml +++ b/deny.toml @@ -6,25 +6,20 @@ deny = [ skip = [ { name = "bitflags", version = "=1.3.2" }, # openssl { name = "base64", version = "<=0.22" }, # openssl - { name = "idna", version = "=0.4.0" }, # - { name = "winreg", version = "<=0.52" }, - { name = "http", version = "<=1.1" }, - { name = "http-body", version = "<=1.0.0" }, - { name = "hyper", version = "<=1.3.1" }, - { name = "reqwest", version = "<=0.12.4" }, - { name = "rustls-pemfile", version = "1.0.4" } -] -skip-tree = [ - { name = "prost-wkt-types", version = "=0.4.2", depth = 20 }, # prost-wkt-build - { name = "prost-wkt-build", version = "=0.4.2", depth = 20 }, # prost-wkt-build + { name = "block-buffer", version = "0.9.0" }, + { name = "digest", version = "0.9.0" }, + { name = "heck", version = "0.4.1" }, + { name = "hmac", version = "0.8.1" }, + { name = "sha2", version = "0.9.9" }, + { name = "syn", version = "1.0.109" }, + { name = "windows-sys", version = "0.52.0" }, + { name = "bech32", version = "0.9.1" } ] [sources] unknown-registry = "deny" unknown-git = "deny" -[sources.allow-org] -github = ["klever-io"] [licenses] confidence-threshold = 0.8 @@ -34,10 +29,9 @@ allow = [ "MIT", "MITNFA", "BSD-3-Clause", + "BSD-2-Clause", "CC0-1.0", - "ISC", "MPL-2.0", - "OpenSSL", ] exceptions = [ { name = "unicode-ident", allow = ["Unicode-DFS-2016"] }, diff --git a/packages/kos-crypto/Cargo.toml b/packages/kos-crypto/Cargo.toml deleted file mode 100644 index 5ab1a3a..0000000 --- a/packages/kos-crypto/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "kos-crypto" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -kos-types = { workspace = true, features = ["serde"] } -serde = { workspace = true, features = ["derive"] } -log = { workspace = true } -wasm-bindgen = { workspace = true } -rand = { workspace = true } -zeroize = { workspace = true } -coins-bip32 = { workspace = true } -coins-bip39 = { workspace = true } -hex = { workspace = true } -sha1 = "0.10" -sha2 = { workspace = true } -sha3 = { workspace = true } -hmac = { workspace = true } -secp256k1 = { workspace = true, features = ["rand", "serde", "bitcoin_hashes"] } -ed25519-dalek = { workspace = true, features = ["serde"] } -aes-gcm = "0.10" -aes = { version = "0.8" } -cfb-mode = "0.8" -cbc = { version = "0.1", features = ["block-padding", "std"] } -pem = "3" -pbkdf2 = { version = "0.12", features = ["simple"] } -base64 = "0.21" diff --git a/packages/kos-crypto/src/blake2b.rs b/packages/kos-crypto/src/blake2b.rs deleted file mode 100644 index 2bdbbe8..0000000 --- a/packages/kos-crypto/src/blake2b.rs +++ /dev/null @@ -1,218 +0,0 @@ -pub const BLOCK_BYTES: usize = 128; -pub const KEY_BYTES: usize = 64; -pub const OUT_BYTES: usize = 64; - -#[rustfmt::skip] -static IV : [u64; 8] = [ - 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, - 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, -]; - -#[rustfmt::skip] -static SIGMA : [[u8; 16]; 12] = [ - [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ], - [ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 ], - [ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 ], - [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 ], - [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 ], - [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 ], - [ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 ], - [ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 ], - [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 ], - [ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 ], - [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ], - [ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 ], -]; - -pub struct Blake2b { - h: [u64; 8], - t: [u64; 2], - f: [u64; 2], - buf: [u8; 2 * BLOCK_BYTES], - buf_len: usize, - output_len: usize, -} - -impl Copy for Blake2b {} -impl Clone for Blake2b { - fn clone(&self) -> Blake2b { - *self - } -} - -impl Blake2b { - pub fn new(size: usize) -> Blake2b { - assert!(size > 0 && size <= OUT_BYTES); - - let param = encode_params(size as u8, 0); - let mut state = IV; - - for i in 0..state.len() { - state[i] ^= load64(¶m[i * 8..]); - } - - Blake2b { - h: state, - t: [0, 0], - f: [0, 0], - buf: [0u8; 2 * BLOCK_BYTES], - buf_len: 0, - output_len: size, - } - } - - pub fn new_with_key(size: usize, key: &[u8]) -> Blake2b { - assert!(size > 0 && size <= OUT_BYTES); - assert!(!key.is_empty() && key.len() <= KEY_BYTES); - - let param = encode_params(size as u8, key.len() as u8); - let mut state = IV; - - for i in 0..state.len() { - println!("i2: {} ", i); - state[i] ^= load64(¶m[i * 8..]); - } - - let mut b = Blake2b { - h: state, - t: [0, 0], - f: [0, 0], - buf: [0u8; 2 * BLOCK_BYTES], - buf_len: 0, - output_len: size, - }; - - let mut block = [0u8; BLOCK_BYTES]; - block[..key.len()].copy_from_slice(key); - b.update(block.as_ref()); - b - } - - pub fn update(&mut self, m: &[u8]) { - let mut m = m; - - while !m.is_empty() { - let left = self.buf_len; - let fill = 2 * BLOCK_BYTES - left; - - if m.len() > fill { - self.buf[left..(fill + left)].copy_from_slice(&m[..fill]); - self.buf_len += fill; - m = &m[fill..]; - self.increment_counter(BLOCK_BYTES as u64); - self.compress(); - for i in 0..BLOCK_BYTES { - self.buf[i] = self.buf[i + BLOCK_BYTES]; - } - self.buf_len -= BLOCK_BYTES; - } else { - self.buf[left..(m.len() + left)].copy_from_slice(m); - self.buf_len += m.len(); - m = &m[m.len()..]; - } - } - } - - pub fn finalize(&mut self) -> Vec { - let mut buf = [0u8; OUT_BYTES]; - if self.buf_len > BLOCK_BYTES { - self.increment_counter(BLOCK_BYTES as u64); - self.compress(); - for i in 0..BLOCK_BYTES { - self.buf[i] = self.buf[i + BLOCK_BYTES]; - } - self.buf_len -= BLOCK_BYTES; - } - let n = self.buf_len as u64; - self.increment_counter(n); - self.f[0] = !0; - for i in self.buf_len..self.buf.len() { - self.buf[i] = 0; - } - self.compress(); - for i in 0..self.h.len() { - store64(&mut buf[i * 8..], self.h[i]); - } - - buf[..self.output_len].to_vec() - } - - fn increment_counter(&mut self, inc: u64) { - self.t[0] += inc; - self.t[1] += if self.t[0] < inc { 1 } else { 0 }; - } - - fn compress(&mut self) { - let mut m = [0u64; 16]; - let mut v = [0u64; 16]; - let block = self.buf.as_ref(); - - assert!(block.len() >= BLOCK_BYTES); - - for i in 0..m.len() { - m[i] = load64(&block[i * 8..]); - } - - v[..8].copy_from_slice(&self.h[..8]); - - v[8] = IV[0]; - v[9] = IV[1]; - v[10] = IV[2]; - v[11] = IV[3]; - v[12] = self.t[0] ^ IV[4]; - v[13] = self.t[1] ^ IV[5]; - v[14] = self.f[0] ^ IV[6]; - v[15] = self.f[1] ^ IV[7]; - - macro_rules! g( - ($r: expr, $i: expr, $a: expr, $b: expr, $c: expr, $d: expr) => ({ - $a = $a.wrapping_add($b).wrapping_add(m[SIGMA[$r][2*$i+0] as usize]); - $d = ($d ^ $a).rotate_right(32); - $c = $c.wrapping_add($d); - $b = ($b ^ $c).rotate_right(24); - $a = $a.wrapping_add($b).wrapping_add(m[SIGMA[$r][2*$i+1] as usize]); - $d = ($d ^ $a).rotate_right(16); - $c = $c.wrapping_add($d); - $b = ($b ^ $c).rotate_right(63); - }); - ); - - macro_rules! round( - ($r: expr) => ({ - g!($r, 0, v[ 0], v[ 4], v[ 8], v[12]); - g!($r, 1, v[ 1], v[ 5], v[ 9], v[13]); - g!($r, 2, v[ 2], v[ 6], v[10], v[14]); - g!($r, 3, v[ 3], v[ 7], v[11], v[15]); - g!($r, 4, v[ 0], v[ 5], v[10], v[15]); - g!($r, 5, v[ 1], v[ 6], v[11], v[12]); - g!($r, 6, v[ 2], v[ 7], v[ 8], v[13]); - g!($r, 7, v[ 3], v[ 4], v[ 9], v[14]); - }); - ); - - for i in 0..12 { - round!(i); - } - - for i in 0..8 { - self.h[i] = self.h[i] ^ v[i] ^ v[i + 8] - } - } -} - -fn encode_params(size: u8, key_len: u8) -> [u8; 64] { - let mut param = [0u8; 64]; - param[0] = size; - param[1] = key_len; - param[2] = 1; // fanout - param[3] = 1; // depth - param -} - -fn load64(b: &[u8]) -> u64 { - u64::from_le_bytes(b[..8].try_into().expect("slice with incorrect length")) -} - -fn store64(b: &mut [u8], v: u64) { - b[..8].copy_from_slice(&v.to_le_bytes()); -} diff --git a/packages/kos-crypto/src/ed25519.rs b/packages/kos-crypto/src/ed25519.rs deleted file mode 100644 index 980440e..0000000 --- a/packages/kos-crypto/src/ed25519.rs +++ /dev/null @@ -1,154 +0,0 @@ -use kos_types::{error::Error, Bytes32}; - -use coins_bip32::path::DerivationPath; -use coins_bip39::{English, Mnemonic}; -use ed25519_dalek::{Signer, SigningKey, VerifyingKey, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH}; -use hmac::{Hmac, Mac}; -use sha2::Sha512; -use std::{fmt, str::FromStr}; - -use wasm_bindgen::prelude::wasm_bindgen; - -type HmacSha521 = Hmac; - -#[derive(serde::Serialize, serde::Deserialize)] -#[wasm_bindgen] -pub struct Ed25519KeyPair { - secret_key: SigningKey, - public_key: VerifyingKey, -} - -impl Default for Ed25519KeyPair { - fn default() -> Self { - Self { - secret_key: SigningKey::from_bytes(&[0u8; SECRET_KEY_LENGTH]), - public_key: VerifyingKey::from_bytes(&[0u8; PUBLIC_KEY_LENGTH]).unwrap(), - } - } -} - -impl Clone for Ed25519KeyPair { - fn clone(&self) -> Ed25519KeyPair { - Ed25519KeyPair { - secret_key: self.secret_key.clone(), - public_key: self.public_key, - } - } -} - -impl Ed25519KeyPair { - pub fn random(rng: &mut R) -> Self - where - R: rand::Rng + ?Sized, - { - let mut secret = Bytes32::zeroed(); - rng.fill(secret.as_mut()); - - Ed25519KeyPair::new(secret.into()) - } - - pub fn new(secret: [u8; 32]) -> Self { - let secret_key: SigningKey = SigningKey::from_bytes(&secret); - let public_key: VerifyingKey = (&secret_key).into(); - - Self { - secret_key, - public_key, - } - } - - pub fn new_from_mnemonic_phrase_with_path( - phrase: &str, - path: &str, - password: Option<&str>, - ) -> Result { - let mnemonic = Mnemonic::::new_from_phrase(phrase)?; - let path = DerivationPath::from_str(path)?; - Self::new_from_mnemonic(path, mnemonic, password) - } - - /// Generate a new secret key from a `DerivationPath` and `Mnemonic`. - pub fn new_from_mnemonic( - d: DerivationPath, - m: Mnemonic, - password: Option<&str>, - ) -> Result { - let seed = m.to_seed(password)?; - - let hardened_child_padding: u8 = 0; - let mut digest = - HmacSha521::new_from_slice(b"ed25519 seed").expect("HMAC can take key of any size"); - digest.update(&seed); - let intermediary: Vec = digest.finalize().into_bytes().into_iter().collect(); - let mut key = intermediary[..SECRET_KEY_LENGTH].to_vec(); - let mut chain_code = intermediary[SECRET_KEY_LENGTH..].to_vec(); - - for child_idx in d.iter() { - let mut buff = [vec![hardened_child_padding], key.clone()].concat(); - buff.push((child_idx >> 24) as u8); - buff.push((child_idx >> 16) as u8); - buff.push((child_idx >> 8) as u8); - buff.push((child_idx & 0xff) as u8); - - digest = - HmacSha521::new_from_slice(&chain_code).expect("HMAC can take key of any size"); - digest.update(&buff); - let intermediary: Vec = digest.finalize().into_bytes().into_iter().collect(); - key = intermediary[..SECRET_KEY_LENGTH].to_vec(); - chain_code = intermediary[SECRET_KEY_LENGTH..].to_vec(); - } - - Ok(Self::new(key.try_into().map_err(|_e| { - Error::InvalidMnemonic("Error convert vec into slice") - })?)) - } -} - -impl Ed25519KeyPair { - pub fn public_key(&self) -> Vec { - self.public_key.to_bytes().to_vec() - } - - pub fn secret_key(&self) -> Vec { - self.secret_key.to_bytes().to_vec() - } -} - -impl Ed25519KeyPair { - pub fn sign_digest(&self, message: &[u8]) -> Vec { - let sig = self.secret_key.sign(message); - sig.to_bytes().to_vec() - } - - pub fn verify_digest(&self, message: &[u8], signature: &[u8], public_key: &[u8]) -> bool { - if signature.len() != 64 { - return false; - } - let mut sig_bytes = [0u8; 64]; - sig_bytes.copy_from_slice(signature); - - let mut pub_bytes = [0u8; 32]; - if public_key.len() != 32 { - pub_bytes = self.public_key.to_bytes(); - } else { - pub_bytes.copy_from_slice(public_key); - } - - VerifyingKey::from_bytes(&pub_bytes) - .map(|public_key| { - public_key - .verify_strict(message, &ed25519_dalek::Signature::from_bytes(&sig_bytes)) - .is_ok() - }) - .unwrap_or(false) - } -} - -impl fmt::Debug for Ed25519KeyPair { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("") - .field(&self.secret_key) - .field(&self.public_key) - .finish() - } -} diff --git a/packages/kos-crypto/src/hash.rs b/packages/kos-crypto/src/hash.rs deleted file mode 100644 index 4f513fa..0000000 --- a/packages/kos-crypto/src/hash.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::blake2b::Blake2b; -use sha2::{Digest, Sha256, Sha512}; -use sha3::Keccak256; - -#[inline] -pub fn sha256(input: &[u8]) -> [u8; 32] { - let mut hasher = Sha256::new(); - hasher.update(input); - hasher.finalize().into() -} - -#[inline] -pub fn keccak256(input: &[u8]) -> [u8; 32] { - let mut hasher = Keccak256::new(); - hasher.update(input); - hasher.finalize().into() -} - -#[inline] -pub fn blake2b256(input: &[u8]) -> [u8; 32] { - let mut hasher = Blake2b::new(32); - hasher.update(input); - hasher.finalize().try_into().unwrap() -} - -#[inline] -pub fn sha512(input: &[u8]) -> [u8; 64] { - let mut hasher = Sha512::new(); - hasher.update(input); - hasher.finalize().into() -} diff --git a/packages/kos-crypto/src/keypair.rs b/packages/kos-crypto/src/keypair.rs deleted file mode 100644 index 4323134..0000000 --- a/packages/kos-crypto/src/keypair.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::ed25519; -use crate::secp256k1; - -use std::fmt; - -use wasm_bindgen::prelude::wasm_bindgen; - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -enum KeyType { - Default, - Ed25519, - Secp256k1, - Secp256k1Compressed, -} - -#[derive(Clone, serde::Serialize, serde::Deserialize)] -#[wasm_bindgen] -pub struct KeyPair { - key_type: KeyType, - ed25519: Option, - secp256k1: Option, -} - -#[wasm_bindgen] -impl KeyPair { - pub fn new_default() -> Self { - Self { - key_type: KeyType::Default, - ed25519: None, - secp256k1: None, - } - } - - pub fn new_ed25519(kp: ed25519::Ed25519KeyPair) -> Self { - Self { - key_type: KeyType::Ed25519, - ed25519: Some(kp), - secp256k1: None, - } - } - - pub fn new_secp256k1(kp: secp256k1::Secp256k1KeyPair) -> Self { - Self { - key_type: if kp.is_compressed() { - KeyType::Secp256k1Compressed - } else { - KeyType::Secp256k1 - }, - ed25519: None, - secp256k1: Some(kp), - } - } -} - -impl KeyPair { - pub fn sign_digest(&self, digest: &[u8]) -> Vec { - match self.key_type { - KeyType::Default => Vec::new(), - KeyType::Ed25519 => self.ed25519.as_ref().unwrap().sign_digest(digest), - KeyType::Secp256k1 => self.secp256k1.as_ref().unwrap().sign_digest(digest), - KeyType::Secp256k1Compressed => self.secp256k1.as_ref().unwrap().sign_digest(digest), - } - } - - pub fn verify_digest(&self, digest: &[u8], signature: &[u8], public_key: &[u8]) -> bool { - match self.key_type { - KeyType::Default => false, - KeyType::Ed25519 => self - .ed25519 - .as_ref() - .unwrap() - .verify_digest(digest, signature, public_key), - KeyType::Secp256k1 => self - .secp256k1 - .as_ref() - .unwrap() - .verify_digest(digest, signature, public_key), - KeyType::Secp256k1Compressed => self - .secp256k1 - .as_ref() - .unwrap() - .verify_digest(digest, signature, public_key), - } - } -} - -impl From for KeyPair { - fn from(kp: ed25519::Ed25519KeyPair) -> Self { - Self::new_ed25519(kp) - } -} - -impl From for KeyPair { - fn from(kp: secp256k1::Secp256k1KeyPair) -> Self { - Self::new_secp256k1(kp) - } -} - -impl KeyPair { - pub fn public_key(&self) -> Vec { - match self.key_type { - KeyType::Default => Vec::new(), - KeyType::Ed25519 => self.ed25519.as_ref().unwrap().public_key(), - KeyType::Secp256k1 | KeyType::Secp256k1Compressed => { - self.secp256k1.as_ref().unwrap().public_key() - } - } - } - - pub fn public_key_hex(&self) -> String { - let bytes = self.public_key(); - hex::encode(bytes) - } - - pub fn secret_key(&self) -> Vec { - match self.key_type { - KeyType::Default => Vec::new(), - KeyType::Ed25519 => self.ed25519.as_ref().unwrap().secret_key(), - KeyType::Secp256k1 | KeyType::Secp256k1Compressed => { - self.secp256k1.as_ref().unwrap().secret_key() - } - } - } - - pub fn secret_key_hex(&self) -> String { - let bytes = self.secret_key(); - hex::encode(bytes) - } -} - -impl fmt::Debug for KeyPair { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("") - .field(&self.key_type) - .field(&self.ed25519) - .field(&self.secp256k1) - .finish() - } -} diff --git a/packages/kos-crypto/src/lib.rs b/packages/kos-crypto/src/lib.rs deleted file mode 100644 index d2d5a19..0000000 --- a/packages/kos-crypto/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod blake2b; -pub mod cipher; -pub mod ed25519; -pub mod hash; -pub mod keypair; -pub mod mnemonic; -pub mod secp256k1; diff --git a/packages/kos-crypto/src/secp256k1.rs b/packages/kos-crypto/src/secp256k1.rs deleted file mode 100644 index 20491d7..0000000 --- a/packages/kos-crypto/src/secp256k1.rs +++ /dev/null @@ -1,141 +0,0 @@ -use kos_types::error::Error; - -use coins_bip32::path::DerivationPath; -use coins_bip39::{English, Mnemonic}; -use secp256k1::{ - ecdsa::{RecoverableSignature, RecoveryId}, - Error as Secp256k1Error, Message, PublicKey, Secp256k1, SecretKey, -}; -use std::{fmt, str::FromStr}; - -use wasm_bindgen::prelude::wasm_bindgen; - -#[derive(Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[wasm_bindgen] -pub struct Secp256k1KeyPair { - secret_key: SecretKey, - public_key: PublicKey, - compressed: bool, -} - -impl Secp256k1KeyPair { - pub fn random(rng: &mut R) -> Self - where - R: rand::Rng + ?Sized, - { - let secp = Secp256k1::new(); - let (secret_key, public_key) = secp.generate_keypair(rng); - Self { - secret_key, - public_key, - compressed: false, - } - } - - pub fn new(secret: [u8; 32]) -> Self { - let secp = Secp256k1::new(); - - let secret_key: SecretKey = SecretKey::from_slice(secret.as_ref()).unwrap(); - let public_key = PublicKey::from_secret_key(&secp, &secret_key); - - Self { - secret_key, - public_key, - compressed: false, - } - } - - pub fn new_from_mnemonic_phrase_with_path( - phrase: &str, - path: &str, - password: Option<&str>, - ) -> Result { - let mnemonic = Mnemonic::::new_from_phrase(phrase)?; - let path = DerivationPath::from_str(path)?; - Self::new_from_mnemonic(path, mnemonic, password) - } - - /// Generate a new secret key from a `DerivationPath` and `Mnemonic`. - pub fn new_from_mnemonic( - d: DerivationPath, - m: Mnemonic, - password: Option<&str>, - ) -> Result { - let derived_priv_key = m.derive_key(d, password)?; - let key: &coins_bip32::prelude::SigningKey = derived_priv_key.as_ref(); - - let secp = Secp256k1::new(); - let secret_key = SecretKey::from_slice(key.to_bytes().as_mut())?; - let public_key = PublicKey::from_secret_key(&secp, &secret_key); - - Ok(Self { - secret_key, - public_key, - compressed: false, - }) - } -} - -impl Secp256k1KeyPair { - pub fn public_key(&self) -> Vec { - if self.compressed { - self.public_key.serialize().to_vec() - } else { - self.public_key.serialize_uncompressed()[1..].to_vec() - } - } - - pub fn secret_key(&self) -> Vec { - self.secret_key[..].to_vec() - } - - pub fn is_compressed(&self) -> bool { - self.compressed - } - - pub fn set_compressed(mut self, compressed: bool) -> Self { - self.compressed = compressed; - self - } -} - -impl Secp256k1KeyPair { - pub fn sign_digest(&self, digest: &[u8]) -> Vec { - let secp = secp256k1::Secp256k1::new(); - let message = Message::from_slice(digest).unwrap(); - let sig = secp.sign_ecdsa_recoverable(&message, &self.secret_key); - - let (rec_id, compact) = sig.serialize_compact(); - let mut raw = vec![0; 65]; - raw[0..64].copy_from_slice(&compact); - raw[64] = (rec_id.to_i32() & 0xff) as u8; - raw - } - - pub fn verify_digest(&self, digest: &[u8], signature: &[u8], public_key: &[u8]) -> bool { - Self::recover(digest, signature).map_or(false, |recovered| recovered == public_key) - } - - pub fn recover(digest: &[u8], sig: &[u8]) -> Result, Error> { - // check signature length - if sig.len() != 65 { - return Err(Secp256k1Error::InvalidSignature.into()); - } - - let secp = secp256k1::Secp256k1::new(); - let recid = RecoveryId::from_i32(sig[64] as i32)?; - let rec_sig = RecoverableSignature::from_compact(&sig[0..64], recid)?; - let message = Message::from_slice(digest)?; - let public_key = secp.recover_ecdsa(&message, &rec_sig)?; - Ok(public_key.serialize_uncompressed()[1..].to_vec()) - } -} - -impl fmt::Debug for Secp256k1KeyPair { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("") - .field(&self.secret_key) - .field(&self.public_key) - .finish() - } -} diff --git a/packages/kos-crypto/src/serde_util.rs b/packages/kos-crypto/src/serde_util.rs deleted file mode 100644 index 3b851b2..0000000 --- a/packages/kos-crypto/src/serde_util.rs +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -use core::fmt; -use core::marker::PhantomData; -use core::str::{self, FromStr}; - -use serde::de; - -/// A serde visitor that works for `T`s implementing `FromStr`. -pub struct FromStrVisitor { - expectation: &'static str, - _pd: PhantomData, -} - -impl FromStrVisitor { - pub fn new(expectation: &'static str) -> Self { - FromStrVisitor { expectation, _pd: PhantomData } - } -} - -impl<'de, T> de::Visitor<'de> for FromStrVisitor -where - T: FromStr, - ::Err: fmt::Display, -{ - type Value = T; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str(self.expectation) - } - - fn visit_str(self, v: &str) -> Result { - FromStr::from_str(v).map_err(E::custom) - } -} - -pub struct BytesVisitor { - expectation: &'static str, - parse_fn: F, -} - -impl BytesVisitor -where - F: FnOnce(&[u8]) -> Result, - Err: fmt::Display, -{ - pub fn new(expectation: &'static str, parse_fn: F) -> Self { - BytesVisitor { expectation, parse_fn } - } -} - -impl<'de, F, T, Err> de::Visitor<'de> for BytesVisitor -where - F: FnOnce(&[u8]) -> Result, - Err: fmt::Display, -{ - type Value = T; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str(self.expectation) - } - - fn visit_bytes(self, v: &[u8]) -> Result { - (self.parse_fn)(v).map_err(E::custom) - } -} - -macro_rules! impl_tuple_visitor { - ($thing:ident, $len:expr) => { - pub(crate) struct $thing { - expectation: &'static str, - parse_fn: F, - } - - impl $thing - where - F: FnOnce(&[u8]) -> Result, - E: fmt::Display, - { - pub fn new(expectation: &'static str, parse_fn: F) -> Self { - $thing { expectation, parse_fn } - } - } - - impl<'de, F, T, E> de::Visitor<'de> for $thing - where - F: FnOnce(&[u8]) -> Result, - E: fmt::Display, - { - type Value = T; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str(self.expectation) - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: de::SeqAccess<'de>, - { - let mut bytes = [0u8; $len]; - - for (i, byte) in bytes.iter_mut().enumerate() { - if let Some(value) = seq.next_element()? { - *byte = value; - } else { - return Err(de::Error::invalid_length(i, &self)); - } - } - (self.parse_fn)(&bytes).map_err(de::Error::custom) - } - } - }; -} - -impl_tuple_visitor!(Tuple32Visitor, 32); -impl_tuple_visitor!(Tuple33Visitor, 33); diff --git a/packages/kos-mobile/Cargo.toml b/packages/kos-mobile/Cargo.toml index 6331fd1..be95ac9 100644 --- a/packages/kos-mobile/Cargo.toml +++ b/packages/kos-mobile/Cargo.toml @@ -9,24 +9,26 @@ rust-version.workspace = true version.workspace = true [lib] -crate-type = ["cdylib", "staticlib"] +crate-type = ["cdylib", "staticlib", "rlib"] + +[features] +default = ["serde"] [[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"] } +kos = { workspace = true } hex = { workspace = true } lazy_static = { workspace = true } thiserror = { workspace = true } uniffi = { workspace = true, features = ["cli"] } +serde = { workspace = true, features = ["derive"], optional = true } +num-bigint = "0.4" +num-traits = "0.2" [build-dependencies] uniffi = { workspace = true, features = ["build"] } \ No newline at end of file diff --git a/packages/kos-mobile/android/lib/build.gradle.kts b/packages/kos-mobile/android/lib/build.gradle.kts index 9fd6331..f04208d 100644 --- a/packages/kos-mobile/android/lib/build.gradle.kts +++ b/packages/kos-mobile/android/lib/build.gradle.kts @@ -34,7 +34,12 @@ android { } dependencies { - implementation(libs.java.jna) + implementation(libs.java.jna) { + artifact { + extension ="aar" + type = "aar" + } + } testImplementation(libs.junit) } diff --git a/packages/kos-mobile/src/lib.rs b/packages/kos-mobile/src/lib.rs index 84c4547..16600be 100644 --- a/packages/kos-mobile/src/lib.rs +++ b/packages/kos-mobile/src/lib.rs @@ -1,26 +1,27 @@ +pub mod number; + 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, Transaction}; -use kos_sdk::wallet::Wallet; -use kos_types::error::Error as KosError; +use kos::chains::{get_chain_by_base_id, Chain, ChainError, Transaction}; +use kos::crypto::cipher; +use kos::crypto::cipher::CipherAlgo; uniffi::setup_scaffolding!(); #[derive(Debug, thiserror::Error, uniffi::Error)] -enum KOSError { +pub enum KOSError { #[error("UnsupportedChainError: Unsupported chain {id}")] UnsupportedChain { id: String }, #[error("KOSDelegateError: {0}")] KOSDelegate(String), #[error("HexDecodeError: {0}")] HexDecode(String), + #[error("KOSNumberError: {0}")] + KOSNumber(String), } -impl From for KOSError { - fn from(err: KosError) -> Self { +impl From for KOSError { + fn from(err: ChainError) -> Self { KOSError::KOSDelegate(err.to_string()) } } @@ -33,7 +34,7 @@ impl From for KOSError { #[derive(uniffi::Record)] struct KOSAccount { - pub chain_id: i32, + pub chain_id: u32, pub private_key: String, pub public_key: String, pub address: String, @@ -42,77 +43,63 @@ struct KOSAccount { #[derive(uniffi::Record)] struct KOSTransaction { - pub chain_id: i32, + pub chain_id: u32, pub raw: String, pub sender: String, pub signature: String, } -#[uniffi::export] -fn sign_transaction(account: KOSAccount, raw: String) -> Result { - let chain = get_chain_by(account.chain_id)?; - let wallet = Wallet::from_private_key(chain, account.private_key.to_string())?; - let transaction = Transaction::from_raw(chain, &raw)?; - let signed_transaction = wallet.sign(transaction)?; - let signature = signed_transaction - .get_signature() - .ok_or(KOSError::KOSDelegate("Signature not found".to_string()))?; - - Ok(KOSTransaction { - chain_id: account.chain_id, - raw: signed_transaction.get_raw()?, - sender: signed_transaction.sender, - signature, - }) -} - #[uniffi::export] fn generate_mnemonic(size: i32) -> Result { - Ok(kos_crypto::mnemonic::generate_mnemonic(size as usize)?.to_phrase()) + 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() + kos::crypto::mnemonic::validate_mnemonic(mnemonic.as_str()).is_ok() } #[uniffi::export] fn generate_wallet_from_mnemonic( mnemonic: String, - chain_id: i32, - index: i32, + chain_id: u32, + index: u32, use_legacy_path: bool, ) -> Result { if !validate_mnemonic(mnemonic.clone()) { return Err(KOSError::KOSDelegate("Invalid mnemonic".to_string())); } 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)?; + let seed = chain.mnemonic_to_seed(mnemonic, String::from(""))?; + let path = chain.get_path(index, use_legacy_path); + let private_key = chain.derive(seed, path.clone())?; + + let public_key = chain.get_pbk(private_key.clone())?; + Ok(KOSAccount { chain_id, - private_key: wallet.get_private_key(), - public_key: wallet.get_public_key(), - address: wallet.get_address(), - path: wallet.get_path(), + private_key: hex::encode(private_key), + public_key: hex::encode(public_key.clone()), + address: chain.get_address(public_key)?, + path, }) } #[uniffi::export] fn generate_wallet_from_private_key( - chain_id: i32, + chain_id: u32, private_key: String, ) -> Result { let chain = get_chain_by(chain_id)?; - let wallet = Wallet::from_private_key(chain, private_key)?; + + let public_key = chain.get_pbk(hex::decode(private_key.clone())?)?; + let address = chain.get_address(public_key.clone())?; Ok(KOSAccount { chain_id, - private_key: wallet.get_private_key(), - public_key: wallet.get_public_key(), - address: wallet.get_address(), - path: wallet.get_path(), + private_key: private_key.clone(), + public_key: hex::encode(public_key.clone()), + address, + path: String::new(), }) } @@ -141,16 +128,58 @@ fn decrypt(data: String, password: String) -> Result { 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) +fn get_chain_by(id: u32) -> Result, KOSError> { + let chain = get_chain_by_base_id(id) .ok_or_else(|| KOSError::UnsupportedChain { id: id.to_string() })?; + Ok(chain) } +#[uniffi::export] +fn sign_transaction(account: KOSAccount, raw: String) -> Result { + let chain = get_chain_by(account.chain_id)?; + let raw_tx_bytes = hex::decode(raw.clone())?; + let transaction = Transaction { + raw_data: raw_tx_bytes, + signature: Vec::new(), + tx_hash: Vec::new(), + options: None, + }; + let pk = hex::decode(account.private_key.clone())?; + + let signed_transaction = chain.sign_tx(pk, transaction)?; + let signature = signed_transaction.signature; + + Ok(KOSTransaction { + chain_id: account.chain_id, + raw: hex::encode(signed_transaction.raw_data), + sender: account.address, + signature: hex::encode(signature), + }) +} + +#[uniffi::export] +fn sign_message(account: KOSAccount, hex: String) -> Result, KOSError> { + let chain = get_chain_by(account.chain_id)?; + let message = hex::decode(hex)?; + let signature = chain.sign_message(hex::decode(account.private_key).unwrap(), message)?; + Ok(signature) +} + +#[uniffi::export] +fn is_chain_supported(chain_id: u32) -> bool { + kos::chains::is_chain_supported(chain_id) +} + +#[uniffi::export] +fn get_supported_chains() -> Vec { + kos::chains::get_supported_chains() +} + #[cfg(test)] mod tests { use crate::*; + use kos::chains::get_chains; #[test] fn should_generate_mnemonic() { @@ -239,16 +268,9 @@ mod tests { 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, - ) { + + for chain_code in get_chains() { + match generate_wallet_from_mnemonic(mnemonic.clone(), chain_code, index, false) { Ok(account) => { assert!( !account.address.is_empty(), @@ -260,17 +282,12 @@ mod tests { "The private_key for chain {} is empty", chain_code ); - assert_eq!( - account.chain_id, - i32::from(chain_code), - "The chain_id doesn't match" - ); + assert_eq!(account.chain_id, 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 = @@ -349,14 +366,15 @@ mod tests { fn should_sign_raw_transaction() { let chain_id = 38; - let raw = "{\"RawData\":{\"BandwidthFee\":1000000,\"ChainID\":\"MTAwNDIw\",\"Contract\":[{\"Parameter\":{\"type_url\":\"type.googleapis.com/proto.TransferContract\",\"value\":\"CiAysyg0Aj8xj/rr5XGU6iJ+ATI29mnRHS0W0BrC1vz0CBgK\"}}],\"KAppFee\":500000,\"Nonce\":39,\"Sender\":\"5BsyOlcf2VXgnNQWYP9EZcP0RpPIfy+upKD8QIcnyOo=\",\"Version\":1}}"; + let raw = hex::encode("{\"RawData\":{\"BandwidthFee\":1000000,\"ChainID\":\"MTAwNDIw\",\"Contract\":[{\"Parameter\":{\"type_url\":\"type.googleapis.com/proto.TransferContract\",\"value\":\"CiAysyg0Aj8xj/rr5XGU6iJ+ATI29mnRHS0W0BrC1vz0CBgK\"}}],\"KAppFee\":500000,\"Nonce\":39,\"Sender\":\"5BsyOlcf2VXgnNQWYP9EZcP0RpPIfy+upKD8QIcnyOo=\",\"Version\":1}}".as_bytes()); let account = generate_wallet_from_mnemonic( "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(), chain_id, 0, - false - ).unwrap(); + false, + ) + .unwrap(); let transaction = sign_transaction(account, raw.to_string()).unwrap(); @@ -366,12 +384,51 @@ mod tests { "The sender doesn't match" ); assert_eq!( - transaction.raw, "{\"RawData\":{\"Nonce\":39,\"Sender\":\"5BsyOlcf2VXgnNQWYP9EZcP0RpPIfy+upKD8QIcnyOo=\",\"Contract\":[{\"Parameter\":{\"typeUrl\":\"type.googleapis.com/proto.TransferContract\",\"value\":\"CiAysyg0Aj8xj/rr5XGU6iJ+ATI29mnRHS0W0BrC1vz0CBgK\"}}],\"KAppFee\":500000,\"BandwidthFee\":1000000,\"Version\":1,\"ChainID\":\"MTAwNDIw\"},\"Signature\":[\"gUZDIPSxSq40QjTBM38/DAAuWTm7D1THo2KWVqhiTYCum5O+OSWwTYlgIU0RgJ6ungg1cuCJPcmYWNgjDKA/DA==\"]}", + transaction.raw, "7b22426c6f636b223a6e756c6c2c2252617744617461223a7b2242616e647769647468466565223a313030303030302c22436861696e4944223a224d5441774e444977222c22436f6e7472616374223a5b7b22506172616d65746572223a7b22747970655f75726c223a22747970652e676f6f676c65617069732e636f6d2f70726f746f2e5472616e73666572436f6e7472616374222c2276616c7565223a224369417973796730416a38786a2f72723558475536694a2b41544932396d6e52485330573042724331767a304342674b227d7d5d2c2244617461223a6e756c6c2c224b417070466565223a3530303030302c224b6461466565223a6e756c6c2c224e6f6e6365223a33392c225065726d697373696f6e4944223a6e756c6c2c2253656e646572223a22354273794f6c6366325658676e4e5157595039455a6350305270504966792b75704b44385149636e794f6f3d222c2256657273696f6e223a317d2c225265636569707473223a6e756c6c2c22526573756c74223a6e756c6c2c22526573756c74436f6465223a6e756c6c2c225369676e6174757265223a5b2267555a444950537853713430516a54424d33382f4441417557546d37443154486f324b5756716869545943756d354f2b4f53577754596c6749553052674a36756e6767316375434a50636d59574e676a444b412f44413d3d225d7d", "The raw doesn't match" ); assert_eq!( - transaction.signature, "gUZDIPSxSq40QjTBM38/DAAuWTm7D1THo2KWVqhiTYCum5O+OSWwTYlgIU0RgJ6ungg1cuCJPcmYWNgjDKA/DA==", + transaction.signature, "81464320f4b14aae344234c1337f3f0c002e5939bb0f54c7a3629656a8624d80ae9b93be3925b04d8960214d11809eae9e083572e0893dc99858d8230ca03f0c", "The signature doesn't match" ); } + + #[test] + fn should_sign_message() { + let chain_id = 38; + let message = hex::encode("Hello World".as_bytes()); + + let account = generate_wallet_from_mnemonic( + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(), + chain_id, + 0, + false + ).unwrap(); + + let signature = sign_message(account, message).unwrap(); + assert_eq!(signature.len(), 64, "The signature length doesn't match"); + } + + #[test] + fn should_return_true_for_supported_chain() { + let chain_id = 38; + let result = is_chain_supported(chain_id); + assert!(result, "The chain should be supported"); + } + + #[test] + fn should_return_false_for_unsupported_chain() { + let chain_id = 999; + let result = is_chain_supported(chain_id); + assert!(!result, "The chain should not be supported"); + } + + #[test] + fn should_get_supported_chains() { + let supported_chains = get_supported_chains(); + assert!( + !supported_chains.is_empty(), + "The supported chains should not be empty" + ); + } } diff --git a/packages/kos-mobile/src/number.rs b/packages/kos-mobile/src/number.rs new file mode 100644 index 0000000..df62e1e --- /dev/null +++ b/packages/kos-mobile/src/number.rs @@ -0,0 +1,99 @@ +use crate::KOSError; +use num_bigint::BigInt; +use num_traits::{One, Signed, Zero}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +#[derive(Debug, Clone, Default, Serialize, Deserialize, uniffi::Record)] +pub struct BigNumber { + pub value: String, +} + +impl BigNumber { + pub fn new(value: &str) -> Result { + BigInt::from_str(value) + .map(|_| BigNumber { + value: value.to_string(), + }) + .map_err(|_| KOSError::KOSNumber("Invalid number".to_string())) + } + + pub fn to_bigint(&self) -> Result { + BigInt::from_str(&self.value).map_err(|_| KOSError::KOSNumber("Invalid number".to_string())) + } +} + +#[uniffi::export] +fn big_number_new(value: String) -> Result { + BigNumber::new(&value) +} + +#[uniffi::export] +fn big_number_add(lhs: BigNumber, rhs: BigNumber) -> Result { + let left = lhs.to_bigint()?; + let right = rhs.to_bigint()?; + Ok(BigNumber { + value: (left + right).to_string(), + }) +} + +#[uniffi::export] +fn big_number_subtract(lhs: BigNumber, rhs: BigNumber) -> Result { + let left = lhs.to_bigint()?; + let right = rhs.to_bigint()?; + Ok(BigNumber { + value: (left - right).to_string(), + }) +} + +#[uniffi::export] +fn big_number_multiply(lhs: BigNumber, rhs: BigNumber) -> Result { + let left = lhs.to_bigint()?; + let right = rhs.to_bigint()?; + Ok(BigNumber { + value: (left * right).to_string(), + }) +} + +#[uniffi::export] +fn big_number_divide(lhs: BigNumber, rhs: BigNumber) -> Result { + let left = lhs.to_bigint()?; + let right = rhs.to_bigint()?; + if right.is_zero() { + return Err(KOSError::KOSNumber("Division by zero".to_string())); + } + Ok(BigNumber { + value: (left / right).to_string(), + }) +} + +#[uniffi::export] +fn big_number_is_zero(value: BigNumber) -> Result { + Ok(value.to_bigint()?.is_zero()) +} + +#[uniffi::export] +fn big_number_increment(value: BigNumber) -> Result { + let current = value.to_bigint()?; + Ok(BigNumber { + value: (current + BigInt::one()).to_string(), + }) +} + +#[uniffi::export] +fn big_number_decrement(value: BigNumber) -> Result { + let current = value.to_bigint()?; + Ok(BigNumber { + value: (current - BigInt::one()).to_string(), + }) +} + +#[uniffi::export] +fn big_number_is_positive(value: BigNumber) -> Result { + Ok(value.to_bigint()?.is_positive()) +} + +#[uniffi::export] +fn big_number_is_negative(value: BigNumber) -> Result { + Ok(value.to_bigint()?.is_negative()) +} diff --git a/packages/kos-proto/Cargo.toml b/packages/kos-proto/Cargo.toml deleted file mode 100644 index 8e95e47..0000000 --- a/packages/kos-proto/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "kos-proto" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[features] - -[dependencies] -kos-types = { workspace = true, features = ["serde"]} - -bytes = { workspace = true } -prost = { workspace = true } -prost-types = { workspace = true } -prost-wkt = "0.4" -prost-wkt-types = "0.4" -pbjson = { workspace = true } -pbjson-types = { workspace = true } - -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -wasm-bindgen = { workspace = true } - -[lib] -test = false -doctest = false - -[build-dependencies] -glob = "0.3" -prost-build = "0.12" -prost-wkt-build = "0.4" -quote = "1.0" -heck = " 0.4" -pbjson-build = { workspace = true } diff --git a/packages/kos-proto/build.rs b/packages/kos-proto/build.rs deleted file mode 100644 index a11091f..0000000 --- a/packages/kos-proto/build.rs +++ /dev/null @@ -1,182 +0,0 @@ -use glob::glob; -use heck::ToUpperCamelCase; -use prost_wkt_build::*; -use quote::{format_ident, quote}; -use std::fs::{self, File, OpenOptions}; -use std::io::Error; -use std::io::Write; -use std::path::Path; -use std::{env, path::PathBuf}; - -fn build_pbjson( - package_name: &str, - proto_serde: &[impl AsRef], - protos: &[impl AsRef], - includes: &[impl AsRef], - use_number_for_ui64: bool, - use_hex_for_bytes: bool, - btree_map: bool, -) -> Result<(), Error> { - let full_path = format!("{}/{}", env::var("OUT_DIR").unwrap().as_str(), package_name); - fs::create_dir_all(&full_path).unwrap(); - - let out_dir = PathBuf::from(&full_path); - let descriptor_file = out_dir.join(format!("{}.descriptors.bin", package_name)); - let mut prost_build = prost_build::Config::new(); - prost_build - .extern_path(".google.protobuf", "::pbjson_types") - .file_descriptor_set_path(&descriptor_file) - .protoc_arg("--experimental_allow_proto3_optional") - // Override prost-types with pbjson-types - .compile_well_known_types(); - - if btree_map { - prost_build.btree_map(["."]); - } - - prost_build - .out_dir(&full_path) - .compile_protos(protos, includes) - .unwrap(); - - let descriptor_bytes = std::fs::read(descriptor_file).unwrap(); - - let descriptor = FileDescriptorSet::decode(&descriptor_bytes[..]).unwrap(); - - let mut binding = pbjson_build::Builder::new(); - binding - .preserve_proto_field_names() - .register_descriptors(&descriptor_bytes)? - .use_number_for_ui64(use_number_for_ui64) - .use_hex_for_bytes(use_hex_for_bytes); - - if btree_map { - binding.btree_map(["."]); - } - - binding.out_dir(&full_path).build(proto_serde)?; - - generate_extras(&out_dir, &descriptor); - - println!("cargo:warning={}", full_path); - - Ok(()) -} - -fn get_files(dir: &str) -> Vec { - let mut list: Vec = vec![]; - for entry in glob(dir).expect("Failed to read glob pattern") { - let Ok(st) = entry else { - continue; - }; - list.push(format!("./{:}", st.display())); - } - list -} - -#[allow(dead_code)] -fn build_prost_serde( - package_name: &str, - protos: &[impl AsRef], - includes: &[impl AsRef], -) -> Result<(), Error> { - let full_path = format!("{}/{}", env::var("OUT_DIR").unwrap().as_str(), package_name); - fs::create_dir_all(&full_path).unwrap(); - - let out_dir = PathBuf::from(&full_path); - let descriptor_file = out_dir.join(format!("{}.descriptors.bin", package_name)); - let mut prost_build = prost_build::Config::new(); - prost_build - .type_attribute(".", "#[derive(serde::Serialize,serde::Deserialize)]") - .compile_well_known_types() - .extern_path(".google.protobuf.Any", "::prost_wkt_types::Any") - .extern_path(".google.protobuf.Timestamp", "::prost_wkt_types::Timestamp") - .extern_path(".google.protobuf.Value", "::prost_wkt_types::Value") - .out_dir(&out_dir) - .file_descriptor_set_path(&descriptor_file) - .compile_protos(protos, includes)?; - - let descriptor_bytes = std::fs::read(descriptor_file)?; - - let descriptor = FileDescriptorSet::decode(&descriptor_bytes[..])?; - - prost_wkt_build::add_serde(out_dir, descriptor); - - println!("cargo:warning={}", full_path); - - Ok(()) -} - -fn main() -> Result<(), Error> { - // kleverchain - build_pbjson( - "klever", - &[".proto"], - get_files("proto/klever/*.proto").as_slice(), - &["proto/klever"], - true, - false, - false, - )?; - - // Tron protocol - let mut list = get_files("proto/tron/core/contract/*.proto"); - list.push("proto/tron/core/Tron.proto".to_string()); - list.push("proto/tron/core/Discover.proto".to_string()); - list.push("proto/tron/api/api.proto".to_string()); - - build_pbjson( - "tron", - &[".protocol"], - list.as_slice(), - &["proto/include", "proto/tron"], - true, - true, - false, - )?; - - Ok(()) -} - -fn generate_extras(out_dir: &Path, file_descriptor_set: &FileDescriptorSet) { - for fd in &file_descriptor_set.file { - let package = match fd.package { - Some(ref pkg) => pkg, - None => continue, - }; - - if package.starts_with("google.") { - continue; - } - - let gen_path = out_dir.join(format!("{}.rs", package)); - let mut gen_file = OpenOptions::new().append(true).open(gen_path).unwrap(); - - for msg in &fd.message_type { - let name = match msg.name { - Some(ref name) => name, - None => continue, - }; - - let type_url = format!("type.googleapis.com/{}.{}", package, name); - let type_name = name.to_upper_camel_case(); - - gen_type_url(&mut gen_file, &type_url, &type_name); - } - } -} - -fn gen_type_url(gen_file: &mut File, type_url: &str, type_name: &str) { - let type_name = format_ident!("{}", type_name); - - let tokens = quote! { - impl crate::TypeUrl for #type_name { - fn type_url() -> &'static str { - #type_url - } - } - }; - - writeln!(gen_file).unwrap(); - writeln!(gen_file, "{}", &tokens).unwrap(); -} diff --git a/packages/kos-proto/proto/include/google/api/annotations.proto b/packages/kos-proto/proto/include/google/api/annotations.proto deleted file mode 100644 index 00741c8..0000000 --- a/packages/kos-proto/proto/include/google/api/annotations.proto +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2015, Google Inc. -// -// 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. - -syntax = "proto3"; - -package google.api; - -import "google/api/http.proto"; -import "google/protobuf/descriptor.proto"; - -option java_multiple_files = true; -option java_outer_classname = "AnnotationsProto"; -option java_package = "com.google.api"; - -extend google.protobuf.MethodOptions { - // See `HttpRule`. - HttpRule http = 72295728; -} \ No newline at end of file diff --git a/packages/kos-proto/proto/include/google/api/http.proto b/packages/kos-proto/proto/include/google/api/http.proto deleted file mode 100644 index e04173e..0000000 --- a/packages/kos-proto/proto/include/google/api/http.proto +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2016 Google Inc. -// -// 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. - -syntax = "proto3"; - -package google.api; - -option cc_enable_arenas = true; -option java_multiple_files = true; -option java_outer_classname = "HttpProto"; -option java_package = "com.google.api"; - - -// Defines the HTTP configuration for a service. It contains a list of -// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method -// to one or more HTTP REST API methods. -message Http { - // A list of HTTP rules for configuring the HTTP REST API methods. - repeated HttpRule rules = 1; -} - -// `HttpRule` defines the mapping of an RPC method to one or more HTTP -// REST APIs. The mapping determines what portions of the request -// message are populated from the path, query parameters, or body of -// the HTTP request. The mapping is typically specified as an -// `google.api.http` annotation, see "google/api/annotations.proto" -// for details. -// -// The mapping consists of a field specifying the path template and -// method kind. The path template can refer to fields in the request -// message, as in the example below which describes a REST GET -// operation on a resource collection of messages: -// -// ```proto -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}"; -// } -// } -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // mapped to the URL -// SubMessage sub = 2; // `sub.subfield` is url-mapped -// } -// message Message { -// string text = 1; // content of the resource -// } -// ``` -// -// This definition enables an automatic, bidrectional mapping of HTTP -// JSON to RPC. Example: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))` -// -// In general, not only fields but also field paths can be referenced -// from a path pattern. Fields mapped to the path pattern cannot be -// repeated and must have a primitive (non-message) type. -// -// Any fields in the request message which are not bound by the path -// pattern automatically become (optional) HTTP query -// parameters. Assume the following definition of the request message: -// -// ```proto -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // mapped to the URL -// int64 revision = 2; // becomes a parameter -// SubMessage sub = 3; // `sub.subfield` becomes a parameter -// } -// ``` -// -// This enables a HTTP JSON to RPC mapping as below: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))` -// -// Note that fields which are mapped to HTTP parameters must have a -// primitive type or a repeated primitive type. Message types are not -// allowed. In the case of a repeated type, the parameter can be -// repeated in the URL, as in `...?param=A¶m=B`. -// -// For HTTP method kinds which allow a request body, the `body` field -// specifies the mapping. Consider a REST update method on the -// message resource collection: -// -// ```proto -// service Messaging { -// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { -// option (google.api.http) = { -// put: "/v1/messages/{message_id}" -// body: "message" -// }; -// } -// } -// message UpdateMessageRequest { -// string message_id = 1; // mapped to the URL -// Message message = 2; // mapped to the body -// } -// ``` -// -// The following HTTP JSON to RPC mapping is enabled, where the -// representation of the JSON in the request body is determined by -// protos JSON encoding: -// -// HTTP | RPC -// -----|----- -// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })` -// -// The special name `*` can be used in the body mapping to define that -// every field not bound by the path template should be mapped to the -// request body. This enables the following alternative definition of -// the update method: -// -// ```proto -// service Messaging { -// rpc UpdateMessage(Message) returns (Message) { -// option (google.api.http) = { -// put: "/v1/messages/{message_id}" -// body: "*" -// }; -// } -// } -// message Message { -// string message_id = 1; -// string text = 2; -// } -// ``` -// -// The following HTTP JSON to RPC mapping is enabled: -// -// HTTP | RPC -// -----|----- -// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")` -// -// Note that when using `*` in the body mapping, it is not possible to -// have HTTP parameters, as all fields not bound by the path end in -// the body. This makes this option more rarely used in practice of -// defining REST APIs. The common usage of `*` is in custom methods -// which don't use the URL at all for transferring data. -// -// It is possible to define multiple HTTP methods for one RPC by using -// the `additional_bindings` option. Example: -// -// ```proto -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get: "/v1/messages/{message_id}" -// additional_bindings { -// get: "/v1/users/{user_id}/messages/{message_id}" -// } -// }; -// } -// } -// message GetMessageRequest { -// string message_id = 1; -// string user_id = 2; -// } -// ``` -// -// This enables the following two alternative HTTP JSON to RPC -// mappings: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` -// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")` -// -// # Rules for HTTP mapping -// -// The rules for mapping HTTP path, query parameters, and body fields -// to the request message are as follows: -// -// 1. The `body` field specifies either `*` or a field path, or is -// omitted. If omitted, it assumes there is no HTTP body. -// 2. Leaf fields (recursive expansion of nested messages in the -// request) can be classified into three types: -// (a) Matched in the URL template. -// (b) Covered by body (if body is `*`, everything except (a) fields; -// else everything under the body field) -// (c) All other fields. -// 3. URL query parameters found in the HTTP request are mapped to (c) fields. -// 4. Any body sent with an HTTP request can contain only (b) fields. -// -// The syntax of the path template is as follows: -// -// Template = "/" Segments [ Verb ] ; -// Segments = Segment { "/" Segment } ; -// Segment = "*" | "**" | LITERAL | Variable ; -// Variable = "{" FieldPath [ "=" Segments ] "}" ; -// FieldPath = IDENT { "." IDENT } ; -// Verb = ":" LITERAL ; -// -// The syntax `*` matches a single path segment. It follows the semantics of -// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String -// Expansion. -// -// The syntax `**` matches zero or more path segments. It follows the semantics -// of [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.3 Reserved -// Expansion. -// -// The syntax `LITERAL` matches literal text in the URL path. -// -// The syntax `Variable` matches the entire path as specified by its template; -// this nested template must not contain further variables. If a variable -// matches a single path segment, its template may be omitted, e.g. `{var}` -// is equivalent to `{var=*}`. -// -// NOTE: the field paths in variables and in the `body` must not refer to -// repeated fields or map fields. -// -// Use CustomHttpPattern to specify any HTTP method that is not included in the -// `pattern` field, such as HEAD, or "*" to leave the HTTP method unspecified for -// a given URL path rule. The wild-card rule is useful for services that provide -// content to Web (HTML) clients. -message HttpRule { - // Selects methods to which this rule applies. - // - // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. - string selector = 1; - - // Determines the URL pattern is matched by this rules. This pattern can be - // used with any of the {get|put|post|delete|patch} methods. A custom method - // can be defined using the 'custom' field. - oneof pattern { - // Used for listing and getting information about resources. - string get = 2; - - // Used for updating a resource. - string put = 3; - - // Used for creating a resource. - string post = 4; - - // Used for deleting a resource. - string delete = 5; - - // Used for updating a resource. - string patch = 6; - - // Custom pattern is used for defining custom verbs. - CustomHttpPattern custom = 8; - } - - // The name of the request field whose value is mapped to the HTTP body, or - // `*` for mapping all fields not captured by the path pattern to the HTTP - // body. NOTE: the referred field must not be a repeated field. - string body = 7; - - // Additional HTTP bindings for the selector. Nested bindings must - // not contain an `additional_bindings` field themselves (that is, - // the nesting may only be one level deep). - repeated HttpRule additional_bindings = 11; -} - -// A custom pattern is used for defining custom HTTP verb. -message CustomHttpPattern { - // The name of this custom HTTP verb. - string kind = 1; - - // The path matched by this custom verb. - string path = 2; -} \ No newline at end of file diff --git a/packages/kos-proto/proto/klever/transaction.proto b/packages/kos-proto/proto/klever/transaction.proto deleted file mode 100644 index 989ab2d..0000000 --- a/packages/kos-proto/proto/klever/transaction.proto +++ /dev/null @@ -1,161 +0,0 @@ -syntax = "proto3"; - -package proto; - -option go_package = "./;transaction"; - -import "google/protobuf/any.proto"; - -// TXContract available -message TXContract { - enum ContractType { - TransferContractType = 0; - CreateAssetContractType = 1; - CreateValidatorContractType = 2; - ValidatorConfigContractType = 3; - FreezeContractType = 4; - UnfreezeContractType = 5; - DelegateContractType = 6; - UndelegateContractType = 7; - WithdrawContractType = 8; - ClaimContractType = 9; - UnjailContractType = 10; - AssetTriggerContractType = 11; - SetAccountNameContractType = 12; - ProposalContractType = 13; - VoteContractType = 14; - ConfigITOContractType = 15; - SetITOPricesContractType = 16; - BuyContractType = 17; - SellContractType = 18; - CancelMarketOrderContractType = 19; - CreateMarketplaceContractType = 20; - ConfigMarketplaceContractType = 21; - UpdateAccountPermissionContractType = 22; - DepositContractType = 23; - ITOTriggerContractType = 24; - SmartContractType = 99; - } - ContractType Type = 1 [json_name = "Type"]; - google.protobuf.Any Parameter = 2 [json_name = "Parameter"]; -} - -// Transaction holds all the data needed for a value transfer -message Transaction { - enum TXResult { - SUCCESS = 0; - FAILED = 1; - } - - enum TXResultCode { - Ok = 0; - OutOfFunds = 1; - AccountError = 2; - AssetError = 3; - ContractInvalid = 4; - ContractNotFound = 5; - FeeInvalid = 6; - ParameterInvalid = 7; - APRInvalid = 8; - AssetIDInvalid = 9; - AssetTypeInvalid = 10; - AssetCantBeMinted = 11; - AssetCantBeBurned = 12; - AssetCantBePaused = 13; - AssetCantBeDelegated = 14; - AssetOwnerCantBeChanged = 15; - AccountNotOwner = 16; - CommissionTooHigh = 17; - DelegationAmountInvalid = 18; - ProposalNotActive = 19; - ValueInvalid = 20; - AmountInvalid = 21; - BucketIDInvalid = 22; - KeyConflict = 23; - MaxDelegationAmount = 24; - InvalidPeerKey = 25; - MinKFIStakedUnreached = 26; - MaxSupplyExeeced = 27; - SaveAccountError = 28; - LoadAccountError = 29; - SameAccountError = 30; - AssetPaused = 31; - DeletegateError = 32; - WithdrawNotAvailable = 33; - ErrOverflow = 34; - SetStakingErr = 35; - SetMarketOrderErr = 36; - BalanceError = 37; - KAPPError = 38; - UnfreezeError = 39; - UndeletegateError = 40; - WithdrawError = 41; - ClaimError = 42; - BucketsExceded = 43; - AssetCantBeWiped = 44; - AssetCantAddRoles = 45; - FreezeError = 46; - ITONotActive = 47; - NFTMintStopped = 48; - RoyaltiesChangeStopped = 49; - ITOKAPPError = 50; - ITOWhiteListError = 51; - NFTMetadataChangeStopped = 52; - AlreadyExists = 53; - IteratorLimitReached = 54; - // FunctionNotFound is returned when the input specifies a function name that does not exist or is not public. - VMFunctionNotFound = 55; - // FunctionWrongSignature is returned when the wrong number of arguments is provided. - VMFunctionWrongSignature = 56; - // UserError is returned for various execution errors. - VMUserError = 57; - // OutOfGas is returned when VM execution runs out of gas. - VMOutOfGas = 58; - // AccountCollision is returned when created account already exists. - VMAccountCollision = 59; - // CallStackOverFlow is returned when stack overflow occurs. - VMCallStackOverFlow = 60; - // Execution Panicked - VMExecutionPanicked = 61; - // ExecutionFailed is returned when the execution of the specified function has failed. - VMExecutionFailed = 62; - // UpgradeFailed is returned when the upgrade of the contract has failed - VMUpgradeFailed = 63; - // SimulateFailed is returned when tx simulation fails execution - VMSimulateFailed = 64; - - Fail = 99; - } - - message KDAFee { - bytes KDA = 1 [json_name = "KDA"]; - int64 Amount = 2 [json_name = "Amount"]; - // TODO: allow spread - } - - message Raw { - uint64 Nonce = 1 [json_name = "Nonce"]; - bytes Sender = 2 [json_name = "Sender"]; - repeated TXContract Contract = 6 [json_name = "Contract"]; - int32 PermissionID = 7 [json_name = "PermissionID"]; - repeated bytes Data = 10 [json_name = "Data"]; - int64 KAppFee = 13 [json_name = "KAppFee"]; - int64 BandwidthFee = 14 [json_name = "BandwidthFee"]; - uint32 Version = 15 [json_name = "Version"]; - bytes ChainID = 16 [json_name = "ChainID"]; - KDAFee KDAFee = 17 [json_name = "KDAFee"]; - } - - message Receipt { - repeated bytes Data = 1 [json_name = "data"]; - } - - Raw RawData = 1 [json_name = "RawData"]; - repeated bytes Signature = 2 [json_name = "Signature"]; - TXResult Result = 3 [json_name = "Result"]; - TXResultCode ResultCode = 4 [json_name = "ResultCode"]; - repeated Receipt Receipts = 5 [json_name = "Receipts"]; - uint64 Block = 6 [json_name = "Block"]; - uint64 GasLimit = 7 [json_name = "GasLimit"]; - uint64 GasMultiplier = 8 [json_name = "GasMultiplier"]; -} diff --git a/packages/kos-proto/proto/klever/userAccountData.proto b/packages/kos-proto/proto/klever/userAccountData.proto deleted file mode 100644 index 1a94d4e..0000000 --- a/packages/kos-proto/proto/klever/userAccountData.proto +++ /dev/null @@ -1,43 +0,0 @@ -syntax = "proto3"; - -package proto; - -option go_package = "./;state"; - -message Key { - bytes address = 1; - int64 weight = 2; -} - -message Permission { - enum PermissionType { - Owner = 0; - User = 1; - } - int32 ID = 1; - PermissionType Type = 2; - string PermissionName = 3; - int64 Threshold = 4; - bytes Operations = 5; - repeated Key Signers = 6; -} - -message UserAccountData { - bytes Address = 1; - bytes Name = 2; - bytes RootHash = 3; - int64 Balance = 5; - int64 Allowance = 6; - uint64 Nonce = 7; - - repeated Permission Permissions = 8; - - bytes OwnerAddress = 9; - bytes CodeHash = 10; - bytes CodeMetadata = 11; -} - -message CodeEntry { - bytes Code = 1; - uint32 NumReferences = 2; -} diff --git a/packages/kos-proto/proto/tron b/packages/kos-proto/proto/tron deleted file mode 160000 index 244c835..0000000 --- a/packages/kos-proto/proto/tron +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 244c8353b9c1368049f83110c33325ace5c55bdf diff --git a/packages/kos-proto/src/lib.rs b/packages/kos-proto/src/lib.rs deleted file mode 100644 index 922f027..0000000 --- a/packages/kos-proto/src/lib.rs +++ /dev/null @@ -1,71 +0,0 @@ -pub mod options; -pub mod klever { - include!(concat!(env!("OUT_DIR"), "/klever/proto.rs")); - include!(concat!(env!("OUT_DIR"), "/klever/proto.serde.rs")); -} - -pub mod tron { - include!(concat!(env!("OUT_DIR"), "/tron/protocol.rs")); - include!(concat!(env!("OUT_DIR"), "/tron/protocol.serde.rs")); -} - -pub fn write_message(message: &M) -> Vec { - let mut buf: Vec = Vec::with_capacity(message.encoded_len()); - message.encode(&mut buf).unwrap(); - buf -} - -pub fn from_bytes(buffer: Vec) -> Result { - let mut r = M::default(); - r.merge(buffer.as_slice())?; - Ok(r) -} - -pub fn clone(msg: &M) -> Result -where - M: prost::Message + Default, -{ - let mut buf = Vec::with_capacity(msg.encoded_len()); - msg.encode(&mut buf) - .map_err(|_| prost::DecodeError::new("encode error"))?; - - let mut new_msg = M::default(); - new_msg.merge(buf.as_slice())?; - - Ok(new_msg) -} - -pub trait TypeUrl { - fn type_url() -> &'static str; -} - -pub fn pack_to_any(msg: M) -> pbjson_types::Any -where - M: prost::Message + TypeUrl, -{ - pbjson_types::Any { - type_url: M::type_url().to_owned(), - value: msg.encode_to_vec().into(), - } -} - -pub fn unpack_from_option_any(msg: &Option) -> Option -where - M: prost::Message + TypeUrl + Default, -{ - match msg { - Some(any) => unpack_from_any(any), - _ => None, - } -} - -pub fn unpack_from_any(msg: &pbjson_types::Any) -> Option -where - M: prost::Message + TypeUrl + Default, -{ - if msg.type_url == M::type_url() { - Some(M::decode(&msg.value[..]).ok()?) - } else { - None - } -} diff --git a/packages/kos-proto/src/options.rs b/packages/kos-proto/src/options.rs deleted file mode 100644 index 1b33be6..0000000 --- a/packages/kos-proto/src/options.rs +++ /dev/null @@ -1,436 +0,0 @@ -use kos_types::{error::Error, number::BigNumber}; -use serde::{Deserialize, Serialize}; - -use wasm_bindgen::prelude::*; - -#[derive(Default, Deserialize, Serialize, Clone, Debug)] -#[wasm_bindgen] -pub struct KLVOptions { - #[wasm_bindgen(skip)] - pub nonce: Option, - #[wasm_bindgen(skip)] - pub kda: Option, - #[wasm_bindgen(skip)] - pub kda_royalties: Option, - #[wasm_bindgen(skip)] - pub kda_fee: Option, - #[wasm_bindgen(skip)] - pub memo: Option>, -} - -#[wasm_bindgen] -impl KLVOptions { - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[wasm_bindgen(js_name = setNonce)] - pub fn set_nonce(&mut self, nonce: i64) { - self.nonce = Some(nonce as u64); - } - - #[wasm_bindgen(js_name = getNonce)] - pub fn get_nonce(&self) -> u64 { - self.nonce.unwrap_or_default() - } - - #[wasm_bindgen(js_name = addMemo)] - pub fn add_memo(&mut self, data: &str) { - let mut memo = self.memo.clone().unwrap_or_default(); - memo.push(data.to_owned()); - - self.memo = Some(memo); - } - - #[wasm_bindgen(js_name = getMemo)] - pub fn get_memo(&self) -> Vec { - self.memo - .clone() - .unwrap_or_default() - .into_iter() - .map(|m| JsValue::from_str(&m)) - .collect() - } - - #[wasm_bindgen(js_name = setKDA)] - pub fn set_kda(&mut self, kda: &str) { - self.kda = Some(kda.to_owned()); - } - - #[wasm_bindgen(js_name = getKDA)] - pub fn get_kda(&self) -> String { - self.kda.clone().unwrap_or_default() - } - - #[wasm_bindgen(js_name = setKDARoyalties)] - pub fn set_kda_royalties(&mut self, royalties: i64) { - self.kda_royalties = Some(royalties); - } - - #[wasm_bindgen(js_name = getKDARoyalties)] - pub fn get_kda_royalties(&self) -> i64 { - self.kda_royalties.unwrap_or_default() - } - - #[wasm_bindgen(js_name = setKDAFee)] - pub fn set_kda_fee(&mut self, kda: &str) { - self.kda_fee = Some(kda.to_owned()); - } - - #[wasm_bindgen(js_name = getKDAFee)] - pub fn get_kda_fee(&self) -> String { - self.kda_fee.clone().unwrap_or_default() - } -} - -#[derive(Deserialize, Serialize, Clone, Debug)] -#[wasm_bindgen] -pub struct TRXOptions { - #[wasm_bindgen(skip)] - pub token: Option, - #[wasm_bindgen(js_name = feeLimit)] - pub fee_limit: Option, - #[wasm_bindgen(skip)] - pub memo: Option, -} - -#[wasm_bindgen] -impl TRXOptions { - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[wasm_bindgen(js_name = setToken)] - pub fn set_token(&mut self, token: &str) { - self.token = Some(token.to_owned()); - } - - #[wasm_bindgen(js_name = getToken)] - pub fn get_token(&self) -> String { - self.token.clone().unwrap_or_default() - } - - #[wasm_bindgen(js_name = setFeeLimit)] - pub fn set_fee_limit(&mut self, fee_limit: u64) { - self.fee_limit = Some(fee_limit as i64); - } - - #[wasm_bindgen(js_name = getFeeLimit)] - pub fn get_fee_limit(&self) -> u64 { - self.fee_limit.unwrap_or_default() as u64 - } - - #[wasm_bindgen(js_name = setMemo)] - pub fn set_memo(&mut self, memo: &str) { - self.memo = Some(memo.to_owned()); - } - - #[wasm_bindgen(js_name = getMemo)] - pub fn get_memo(&self) -> String { - self.memo.clone().unwrap_or_default() - } -} - -impl Default for TRXOptions { - fn default() -> Self { - Self { - token: None, - fee_limit: Some(10_000_000), - memo: None, - } - } -} - -#[derive(Deserialize, Serialize, Default, Clone, Debug)] -#[wasm_bindgen] -pub struct ETHOptions { - #[wasm_bindgen(skip)] - pub legacy_type: Option, - #[wasm_bindgen(skip)] - pub nonce: Option, - #[wasm_bindgen(skip)] - pub chain_id: Option, - #[wasm_bindgen(skip)] - pub token: Option, - #[wasm_bindgen(skip)] - pub gas_limit: Option, - #[wasm_bindgen(skip)] - pub gas_price: Option, - #[wasm_bindgen(skip)] - pub contract_data: Option>, - #[wasm_bindgen(skip)] - pub max_fee_per_gas: Option, - #[wasm_bindgen(skip)] - pub max_priority_fee_per_gas: Option, -} - -#[wasm_bindgen] -impl ETHOptions { - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[wasm_bindgen(js_name = setLegacyType)] - pub fn set_legacy_type(&mut self, legacy_type: bool) { - self.legacy_type = Some(legacy_type); - } - - #[wasm_bindgen(js_name = getLegacyType)] - pub fn get_legacy_type(&self) -> bool { - self.legacy_type.unwrap_or_default() - } - - #[wasm_bindgen(js_name = setNonce)] - pub fn set_nonce(&mut self, nonce: u64) { - self.nonce = Some(nonce); - } - - #[wasm_bindgen(js_name = getNonce)] - pub fn get_nonce(&self) -> u64 { - self.nonce.unwrap_or_default() - } - - #[wasm_bindgen(js_name = setChainId)] - pub fn set_chain_id(&mut self, chain_id: u64) { - self.chain_id = Some(chain_id); - } - - #[wasm_bindgen(js_name = getChainId)] - pub fn get_chain_id(&self) -> u64 { - self.chain_id.unwrap_or_default() - } - - #[wasm_bindgen(js_name = setToken)] - pub fn set_token(&mut self, token: &str) { - self.token = Some(token.to_owned()); - } - - #[wasm_bindgen(js_name = getToken)] - pub fn get_token(&self) -> String { - self.token.clone().unwrap_or_default() - } - - #[wasm_bindgen(js_name = setGasLimit)] - pub fn set_gas_limit(&mut self, gas_limit: &str) -> Result<(), Error> { - self.gas_limit = Some(BigNumber::from_string(gas_limit)?); - Ok(()) - } - - #[wasm_bindgen(js_name = getGasLimit)] - pub fn get_gas_limit(&self) -> String { - self.gas_limit.clone().unwrap_or_default().to_string() - } - - #[wasm_bindgen(js_name = setGasPrice)] - pub fn set_gas_price(&mut self, gas_price: &str) -> Result<(), Error> { - self.gas_price = Some(BigNumber::from_string(gas_price)?); - Ok(()) - } - - #[wasm_bindgen(js_name = getGasPrice)] - pub fn get_gas_price(&self) -> String { - self.gas_price.clone().unwrap_or_default().to_string() - } - - #[wasm_bindgen(js_name = setContractData)] - pub fn set_contract_data(&mut self, contract_data: &[u8]) { - self.contract_data = Some(contract_data.to_owned()); - } - - #[wasm_bindgen(js_name = getContractData)] - pub fn get_contract_data(&self) -> Vec { - self.contract_data.clone().unwrap_or_default() - } - - #[wasm_bindgen(js_name = setMaxFeePerGas)] - pub fn set_max_fee_per_gas(&mut self, max_fee_per_gas: &str) -> Result<(), Error> { - self.max_fee_per_gas = Some(BigNumber::from_string(max_fee_per_gas)?); - Ok(()) - } - - #[wasm_bindgen(js_name = getMaxFeePerGas)] - pub fn get_max_fee_per_gas(&self) -> String { - self.max_fee_per_gas.clone().unwrap_or_default().to_string() - } - - #[wasm_bindgen(js_name = setMaxPriorityFeePerGas)] - pub fn set_max_priority_fee_per_gas( - &mut self, - max_priority_fee_per_gas: &str, - ) -> Result<(), Error> { - self.max_priority_fee_per_gas = Some(BigNumber::from_string(max_priority_fee_per_gas)?); - Ok(()) - } - - #[wasm_bindgen(js_name = getMaxPriorityFeePerGas)] - pub fn get_max_priority_fee_per_gas(&self) -> String { - self.max_priority_fee_per_gas - .clone() - .unwrap_or_default() - .to_string() - } -} - -#[derive(Deserialize, Serialize, Default, Clone, Debug)] -#[wasm_bindgen] -pub struct MATICOptions { - #[wasm_bindgen(skip)] - pub eth: ETHOptions, -} - -#[wasm_bindgen] -impl MATICOptions { - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[wasm_bindgen(js_name = setETHOptions)] - pub fn set_eth_options(&mut self, options: ÐOptions) { - self.eth = options.clone(); - } - - #[wasm_bindgen(js_name = getETHOptions)] - pub fn get_eth_options(&self) -> ETHOptions { - self.eth.clone() - } -} - -#[derive(Deserialize, Serialize, Default, Clone, Debug)] -#[wasm_bindgen] -pub struct BTCOptions { - /// hex magic from network (default is bitcoin mainnet) - #[wasm_bindgen(skip)] - pub network: Option, - #[wasm_bindgen(skip)] - pub sats_per_bytes: Option, - #[wasm_bindgen(skip)] - pub dust_value: Option, - #[wasm_bindgen(skip)] - pub send_all: Option, - #[wasm_bindgen(skip)] - pub change_address: Option, - #[wasm_bindgen(skip)] - pub receivers: Option>, - #[wasm_bindgen(skip)] - pub rbf: Option, -} - -#[wasm_bindgen] -impl BTCOptions { - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[wasm_bindgen(js_name = setNetwork)] - pub fn set_network(&mut self, network: &str) { - self.network = Some(network.to_owned()); - } - - #[wasm_bindgen(js_name = getNetwork)] - pub fn get_network(&self) -> String { - self.network.clone().unwrap_or_default() - } - - #[wasm_bindgen(js_name = setSatsPerBytes)] - pub fn set_sats_per_bytes(&mut self, sats_per_bytes: u64) { - self.sats_per_bytes = Some(sats_per_bytes); - } - - #[wasm_bindgen(js_name = getSatsPerBytes)] - pub fn get_sats_per_bytes(&self) -> u64 { - self.sats_per_bytes.unwrap_or_default() - } - - #[wasm_bindgen(js_name = setDustValue)] - pub fn set_dust_value(&mut self, dust_value: &str) -> Result<(), Error> { - self.dust_value = Some(BigNumber::from_string(dust_value)?); - Ok(()) - } - - #[wasm_bindgen(js_name = getDustValue)] - pub fn get_dust_value(&self) -> String { - self.dust_value.clone().unwrap_or_default().to_string() - } - - #[wasm_bindgen(js_name = setSendAll)] - pub fn set_send_all(&mut self, send_all: bool) { - self.send_all = Some(send_all); - } - - #[wasm_bindgen(js_name = getSendAll)] - pub fn get_send_all(&self) -> bool { - self.send_all.unwrap_or_default() - } - - #[wasm_bindgen(js_name = setChangeAddress)] - pub fn set_change_address(&mut self, change_address: &str) { - self.change_address = Some(change_address.to_owned()); - } - - #[wasm_bindgen(js_name = getChangeAddress)] - pub fn get_change_address(&self) -> String { - self.change_address.clone().unwrap_or_default() - } - - #[wasm_bindgen(js_name = addReceiver)] - pub fn addr_receiver(&mut self, addr: &str, amount: &str) -> Result<(), Error> { - let amount = BigNumber::from_string(amount)?; - let receiver = (addr.to_owned(), amount); - - let mut receivers = self.receivers.clone().unwrap_or_default(); - receivers.push(receiver); - - self.receivers = Some(receivers); - - Ok(()) - } - - #[wasm_bindgen(js_name = getReceivers)] - pub fn get_receivers(&self) -> Vec { - self.receivers - .clone() - .unwrap_or_default() - .into_iter() - .map(|(addr, amount)| { - let obj = serde_json::json!({ - "address": addr, - "amount": amount.to_string(), - }); - obj.to_string().into() - }) - .collect() - } - - #[wasm_bindgen(js_name = setRBF)] - pub fn set_rbf(&mut self, rbf: bool) { - self.rbf = Some(rbf); - } - - #[wasm_bindgen(js_name = getRBF)] - pub fn get_rbf(&self) -> bool { - self.rbf.unwrap_or_default() - } -} - -impl BTCOptions { - pub fn dust_value(&self) -> BigNumber { - self.dust_value.clone().unwrap_or(BigNumber::from(546)) - } - - pub fn sats_per_bytes(&self) -> u64 { - self.sats_per_bytes.unwrap_or(1) - } - - pub fn receivers(&self) -> Vec<(String, BigNumber)> { - self.receivers.clone().unwrap_or_default() - } - - pub fn rbf(&self) -> bool { - self.rbf.unwrap_or(false) - } -} diff --git a/packages/kos-sdk/Cargo.toml b/packages/kos-sdk/Cargo.toml deleted file mode 100644 index 32004f1..0000000 --- a/packages/kos-sdk/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -name = "kos-sdk" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -default = ["serde"] -serde = ["bitcoin/serde"] - -[dependencies] -strum = { version = "0.25", features = ["derive"] } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -serde-wasm-bindgen = "0.5" -enum_delegate = { workspace = true } -enum_dispatch = "0.3" - -log = { workspace = true } -wasm-bindgen = { workspace = true } -base58 = { workspace = true } -bech32 = { workspace = true } -hmac = { workspace = true } -hex = { workspace = true } -rand = { workspace = true } -coins-bip39 = { workspace = true } -pem = "3" -web3 = { version = "0.19", default-features = false, features = ["http-tls", "wasm"] } -bitcoin = { version = "0.30" } -secp256k1 = { workspace = true, features = ["serde", "bitcoin_hashes"] } -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 } -kos-utils = { workspace = true } -pbjson = { workspace = true } -pbjson-types = { workspace = true } -prost = { workspace = true } - -[dev-dependencies] -tokio-test = "*" -dotenvy = "0.15.7" - -[build-dependencies] -dotenv-build = "0.1" diff --git a/packages/kos-sdk/build.rs b/packages/kos-sdk/build.rs deleted file mode 100644 index a819e0c..0000000 --- a/packages/kos-sdk/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - let config = dotenv_build::Config { - filename: std::path::Path::new(".env.nodes"), - recursive_search: false, - fail_if_missing_dotenv: false, - }; - - dotenv_build::output(config).unwrap(); -} diff --git a/packages/kos-sdk/env.nodes.example b/packages/kos-sdk/env.nodes.example deleted file mode 100644 index 8374279..0000000 --- a/packages/kos-sdk/env.nodes.example +++ /dev/null @@ -1,7 +0,0 @@ - -# NODES -NODE_KLV= -NODE_TRX= -NODE_BTC= -NODE_ETH= -NODE_MATIC= diff --git a/packages/kos-sdk/src/chain.rs b/packages/kos-sdk/src/chain.rs deleted file mode 100644 index 1a7087b..0000000 --- a/packages/kos-sdk/src/chain.rs +++ /dev/null @@ -1,248 +0,0 @@ -use crate::chains::*; -use crate::models::{self, BroadcastResult, PathOptions, Transaction}; -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::*; - -macro_rules! createChains { - ($($name:ident),*) => { - #[wasm_bindgen] - #[derive(Serialize, Deserialize, PartialEq, Debug, Copy, Clone, EnumCount, IntoStaticStr)] - pub enum Chain { - $($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 { - $(Chain::$name => $name::base_chain(),)* - } - } - - pub fn new_keypair(&self) -> Result { - match self { - $(Chain::$name => $name::random(),)* - } - } - - pub fn keypair_from_bytes(&self, private_key: &[u8]) -> Result { - match self { - $(Chain::$name => $name::keypair_from_bytes(private_key),)* - } - } - - pub fn keypair_from_mnemonic(&self, mnemonic: &str, path: &str, password: Option) -> Result { - match self { - $(Chain::$name => $name::keypair_from_mnemonic(mnemonic, path, password),)* - } - } - - pub fn get_address_from_keypair(&self, privatekey: &KeyPair) -> Result { - match self { - $(Chain::$name => $name::get_address_from_keypair(privatekey),)* - } - } - - pub fn get_path(&self, options: &PathOptions) -> Result { - match self { - $(Chain::$name => $name::get_path(options),)* - } - } - - 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 { - $(Chain::$name => $name::sign_digest(digest, keypair),)* - } - } - - /// Verify Message signature - pub fn verify_digest(&self, digest: &[u8], signature: &[u8], address: &str) -> Result { - match self { - $(Chain::$name => $name::verify_digest(digest, signature, address),)* - } - } - - /// Hash and Sign data with the private key. - pub fn sign(&self, data: Transaction, keypair: &KeyPair) -> Result { - match self { - $(Chain::$name => $name::sign(data, keypair),)* - } - } - - /// Append prefix and hash the message - pub fn message_hash(&self, message: &[u8]) -> Result, Error> { - match self { - $(Chain::$name => $name::message_hash(message),)* - } - } - - /// Sign Message with the private key. - pub fn sign_message(&self, message: &[u8], keypair: &KeyPair) -> Result, Error> { - match self { - $(Chain::$name => $name::sign_message(message, keypair),)* - } - } - - /// Verify Message signature - pub fn verify_message_signature(&self, message: &[u8], signature: &[u8], address: &str) -> Result { - match self { - $(Chain::$name => $name::verify_message_signature(message, signature, address),)* - } - } - - /// Get balance of address and token - /// If token is None, it will return balance of native token - /// If token is Some, it will return balance of token - /// If node_url is None, it will use default node url - pub async fn get_balance(&self, address: &str, token: Option, node_url: Option) -> Result { - match self { - $(Chain::$name => $name::get_balance(address, token, node_url).await,)* - } - } - - pub async fn send( - &self, - sender: String, - receiver: String, - amount: BigNumber, - options: Option, - node_url: Option, - ) -> Result { - match self { - $(Chain::$name => $name::send(sender, receiver, amount, options, node_url).await,)* - } - } - - pub async fn broadcast(&self, data: Transaction, node_url: Option) -> Result { - match self { - $(Chain::$name => $name::broadcast(data, node_url).await,)* - } - } - - /// validate chain address format with options - pub fn validate_address(&self, address: &str, option: Option) -> Result { - match self { - $(Chain::$name => $name::validate_address(address, option),)* - } - } - } - } -} - -createChains!(NONE, KLV, TRX, BTC, ETH, MATIC); - -// pub enum Chain { -// NONE, // 0 -// TRX, -// BTC, -// ETH, -// XRP, -// LTC, // 5 -// XLM, -// COSMOS, -// EOS, -// ONT, -// KLAY, // 10 -// DASH, -// DOGE, -// IOTX, -// ONE, -// SYS, // 15 -// DGB, -// BNB, -// BCH, -// VET, -// ADA, // 20 -// DOT, -// XMR, -// XTZ, -// EGLD, -// NEO, // 25 -// BSC, -// KSM, -// MATIC, -// REEF, -// HT, // 30 -// ICP, -// MOVR, -// TKLV, // 33 -// GLMR, -// SDN, // 35 -// ASTR, -// SYSNEVM, -// KLV, -// AVAX, -// SOL, // 40 -// KAR, -// AIR, -// FLOW, -// KILT, -// CUDOS, //45 -// ACA, -// CFG, -// TIA, -// AURA, -// APT, //50 -// } - -#[wasm_bindgen] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct BaseChain { - #[wasm_bindgen(skip)] - pub name: &'static str, - #[wasm_bindgen(skip)] - pub symbol: &'static str, - #[wasm_bindgen(skip)] - pub precision: u8, - #[wasm_bindgen(skip)] - pub chain_code: u8, -} - -#[wasm_bindgen] -impl BaseChain { - #[wasm_bindgen(js_name = getName)] - pub fn get_name(&self) -> String { - self.name.to_string() - } - - #[wasm_bindgen(js_name = getSymbol)] - pub fn get_symbol(&self) -> String { - self.symbol.to_string() - } - - #[wasm_bindgen(js_name = getPrecision)] - pub fn get_precision(&self) -> u8 { - self.precision - } - - #[wasm_bindgen(js_name = getChainCode)] - pub fn get_chain_code(&self) -> u8 { - self.chain_code - } -} diff --git a/packages/kos-sdk/src/chains/bitcoin/mod.rs b/packages/kos-sdk/src/chains/bitcoin/mod.rs deleted file mode 100644 index a32bac6..0000000 --- a/packages/kos-sdk/src/chains/bitcoin/mod.rs +++ /dev/null @@ -1,551 +0,0 @@ -mod requests; -pub mod transaction; - -use crate::chain::{BaseChain, Chain}; -use crate::models::{self, BroadcastResult, PathOptions, Transaction, TransactionRaw}; - -use kos_crypto::{keypair::KeyPair, secp256k1::Secp256k1KeyPair}; -use kos_proto::options::BTCOptions; -use kos_types::{error::Error, hash::Hash, number::BigNumber}; - -use bitcoin::{network::constants::Magic, Address, Network}; - -use std::{ops::Add, str::FromStr}; -use wasm_bindgen::prelude::*; - -#[derive(Debug, Copy, Clone)] -#[wasm_bindgen] -pub struct BTC {} - -pub const SIGN_PREFIX: &[u8; 25] = b"\x18Bitcoin Signed Message:\n"; -pub const BIP44_PATH: u32 = 0; - -pub const BASE_CHAIN: BaseChain = BaseChain { - name: "Bitcoin", - symbol: "BTC", - precision: 8, - chain_code: 2, -}; - -const DEFAULT_NETWORK: Network = Network::Bitcoin; - -pub fn get_network(option: &BTCOptions) -> Result { - match option.network.clone() { - Some(hex_magic) => { - let magic_bytes = hex::decode(hex_magic)?; - if magic_bytes.len() != 4 { - return Err(Error::UnsupportedChain("invalid magic for network length")); - } - - let array: [u8; 4] = [ - magic_bytes[0], - magic_bytes[1], - magic_bytes[2], - magic_bytes[3], - ]; - let magic = Magic::from_bytes(array); - - Network::from_magic(magic).ok_or(Error::UnsupportedChain("invalid magic for network")) - } - _ => Ok(DEFAULT_NETWORK), - } -} - -fn get_options(options: Option) -> BTCOptions { - match options.and_then(|opt| opt.data) { - Some(crate::models::Options::Bitcoin(op)) => op, - _ => BTCOptions::default(), - } -} - -#[wasm_bindgen] -impl BTC { - #[wasm_bindgen(js_name = "baseChain")] - pub fn base_chain() -> BaseChain { - BASE_CHAIN - } - - #[wasm_bindgen(js_name = "random")] - pub fn random() -> Result { - let mut rng = rand::thread_rng(); - let kp = Secp256k1KeyPair::random(&mut rng).set_compressed(true); - Ok(KeyPair::new_secp256k1(kp)) - } - - #[wasm_bindgen(js_name = "keypairFromBytes")] - pub fn keypair_from_bytes(private_key: &[u8]) -> Result { - // copy to fixed length array - let mut pk_slice = [0u8; 32]; - pk_slice.copy_from_slice(private_key); - - let kp = Secp256k1KeyPair::new(pk_slice).set_compressed(true); - Ok(KeyPair::new_secp256k1(kp)) - } - - #[wasm_bindgen(js_name = "keypairFromMnemonic")] - pub fn keypair_from_mnemonic( - mnemonic: &str, - path: &str, - password: Option, - ) -> Result { - let kp = Secp256k1KeyPair::new_from_mnemonic_phrase_with_path( - mnemonic, - path, - password.as_deref(), - )? - .set_compressed(true); - - Ok(KeyPair::new_secp256k1(kp)) - } - - #[wasm_bindgen(js_name = "getAddressFromKeyPair")] - pub fn get_address_from_keypair(kp: &KeyPair) -> Result { - let address = BTC::get_address(kp, DEFAULT_NETWORK)?; - Ok(address.to_string()) - } - - #[wasm_bindgen(js_name = "getPath")] - pub fn get_path(options: &PathOptions) -> Result { - Ok(format!("m/84'/{}'/0'/0/{}", BIP44_PATH, options.index)) - } - - #[wasm_bindgen(js_name = "signDigest")] - /// Sign digest data with the private key. - pub fn sign_digest(digest: &[u8], keypair: &KeyPair) -> Result, Error> { - let raw = keypair.sign_digest(digest); - Ok(raw) - } - - #[wasm_bindgen(js_name = "verifyDigest")] - /// Verify Message signature - pub fn verify_digest(_digest: &[u8], _signature: &[u8], _address: &str) -> Result { - todo!() - } - - #[wasm_bindgen(js_name = "sign")] - /// Hash and Sign data with the private key. - /// P2WPKH address is used as the signature address. - pub fn sign(tx: Transaction, keypair: &KeyPair) -> Result { - match tx.data { - // get bitcoin transaction from raw - Some(TransactionRaw::Bitcoin(btc_tx)) => { - let mut btc_tx = btc_tx; - - // sign tx - btc_tx.sign(keypair)?; - - // redeem script - btc_tx.finalize()?; - - let signature = btc_tx.get_signature().unwrap_or("".to_string()); - - Ok(Transaction { - hash: btc_tx.txid_hash()?, - data: Some(TransactionRaw::Bitcoin(btc_tx)), - signature: Some(signature), - ..tx - }) - } - _ => Err(Error::InvalidMessage( - "not a bitcoin transaction".to_string(), - )), - } - } - - #[wasm_bindgen(js_name = "hash")] - /// hash digest - pub fn hash(message: &[u8]) -> Result, Error> { - let digest = kos_crypto::hash::keccak256(message); - Ok(digest.to_vec()) - } - - #[wasm_bindgen(js_name = "messageHash")] - /// Append prefix and hash the message - pub fn message_hash(message: &[u8]) -> Result, Error> { - let to_sign = [SIGN_PREFIX, message.len().to_string().as_bytes(), message].concat(); - - BTC::hash(&to_sign) - } - - #[wasm_bindgen(js_name = "signMessage")] - /// Sign Message with the private key. - pub fn sign_message(message: &[u8], keypair: &KeyPair) -> Result, Error> { - let m = BTC::message_hash(message)?; - BTC::sign_digest(&m, keypair) - } - - #[wasm_bindgen(js_name = "verifyMessageSignature")] - /// Verify Message signature - pub fn verify_message_signature( - message: &[u8], - signature: &[u8], - address: &str, - ) -> Result { - let m = BTC::message_hash(message)?; - BTC::verify_digest(&m, signature, address) - } - - #[wasm_bindgen(js_name = "getBalance")] - pub async fn get_balance( - address: &str, - _token: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("BTC")); - requests::balance(&node, address, 0).await - } - - fn get_receiver( - receiver: String, - amount: &BigNumber, - network: Network, - ) -> Result<(Address, BigNumber), Error> { - let addr = - Address::from_str(&receiver).map_err(|e| Error::InvalidAddress(e.to_string()))?; - Ok(( - addr.require_network(network) - .map_err(|e| Error::InvalidAddress(e.to_string()))?, - amount.clone(), - )) - } - - pub async fn send( - sender: String, - receiver: String, - amount: BigNumber, - options: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("BTC")); - let options = get_options(options); - - let network = get_network(&options)?; - - let mut total_amount = amount.clone(); - - let mut receivers: Vec<(Address, BigNumber)> = Vec::new(); - if !receiver.is_empty() { - receivers.push(Self::get_receiver(receiver, &amount, network)?); - } else { - // check if amount is provided without receiver - if !amount.is_zero() { - return Err(Error::InvalidTransaction(format!( - "receiver is required for amount {}", - amount.to_string() - ))); - } - } - // add outputs from options - for output in options.receivers() { - receivers.push(Self::get_receiver(output.0, &output.1, network)?); - total_amount = total_amount.add(output.1); - } - - let sender_address = Address::from_str(&sender.clone()) - .map_err(|e| Error::InvalidAddress(e.to_string()))? - .require_network(network) - .map_err(|e| Error::InvalidAddress(e.to_string()))?; - - let change_address = - Address::from_str(&options.change_address.clone().unwrap_or(sender.clone())) - .map_err(|e| Error::InvalidAddress(e.to_string()))? - .require_network(network) - .map_err(|e| Error::InvalidAddress(e.to_string()))?; - - // get utoxs - let sender_utxos = - requests::select_utxos(&node, &sender, &total_amount, 1, 1, 10, false, false).await?; - - // create transaction - let tx = transaction::create_transaction( - sender_address, - sender_utxos, - receivers, - change_address, - &options, - )?; - - Ok(crate::models::Transaction { - chain: Chain::BTC, - sender, - hash: Hash::new(&tx.txid().to_string())?, - data: Some(TransactionRaw::Bitcoin(tx)), - signature: None, - }) - } - - #[wasm_bindgen(js_name = "broadcast")] - pub async fn broadcast( - tx: crate::models::Transaction, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("BTC")); - - match &tx.data { - Some(TransactionRaw::Bitcoin(btc_tx)) => { - let txid = requests::broadcast(&node, &btc_tx.btc_serialize_hex()).await?; - // check if tx hash is same as txid - if btc_tx.txid() != txid { - return Err(Error::InvalidTransaction(format!( - "invalid transaction hash: {}/{}", - txid, - btc_tx.txid() - ))); - } - Ok(BroadcastResult { tx }) - } - _ => Err(Error::InvalidTransaction( - "not a bitcoin transaction".to_string(), - )), - } - } - - #[wasm_bindgen(js_name = "serializeTx")] - pub fn serialize_tx(tx: Transaction) -> Result { - match tx.data { - Some(TransactionRaw::Bitcoin(btc_tx)) => Ok(btc_tx.btc_serialize_hex()), - _ => Err(Error::InvalidTransaction( - "not a bitcoin transaction".to_string(), - )), - } - } - - fn decode_magic(hex_magic: String) -> Result { - let magic_bytes = hex::decode(hex_magic)?; - if magic_bytes.len() != 4 { - return Err(Error::UnsupportedChain("invalid magic for network length")); - } - - let array: [u8; 4] = [ - magic_bytes[0], - magic_bytes[1], - magic_bytes[2], - magic_bytes[3], - ]; - let magic = Magic::from_bytes(array); - - let network = Network::from_magic(magic) - .ok_or(Error::UnsupportedChain("invalid magic for network"))?; - - Ok(network) - } - - #[wasm_bindgen(js_name = "validateAddress")] - pub fn validate_address( - address: &str, - options: Option, - ) -> Result { - let addr = Address::from_str(address); - if addr.is_err() { - return Ok(false); - } - - let addr = addr.unwrap(); - - // get network from options - let network = match options { - Some(opt) => match opt.network { - Some(hex_magic) => BTC::decode_magic(hex_magic)?, - _ => DEFAULT_NETWORK, - }, - _ => DEFAULT_NETWORK, - }; - - Ok(addr.is_valid_for_network(network)) - } -} - -impl BTC { - #[inline] - pub fn get_address(kp: &KeyPair, network: Network) -> Result { - let pubkey = BTC::get_pubkey(&kp.public_key())?; - - Address::p2wpkh(&pubkey, network).map_err(|e| Error::InvalidAddress(e.to_string())) - } - - #[inline] - pub fn get_pubkey(data: &[u8]) -> Result { - bitcoin::PublicKey::from_slice(data) - .map_err(|e| Error::InvalidPublicKey(format!("Invalid public key: {}", e))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use dotenvy; - use hex::{FromHex, ToHex}; - use kos_types::Bytes32; - use std::sync::Once; - - const DEFAULT_PRIVATE_KEY: &str = - "4604b4b710fe91f584fff084e1a9159fe4f8408fff380596a604948474ce4fa3"; - const DEFAULT_ADDRESS: &str = "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"; - const DEFAULT_MNEMONIC: &str = - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - - static _INIT: Once = Once::new(); - - fn _init() { - _INIT.call_once(|| { - dotenvy::from_filename(".env.nodes").ok(); - }); - } - - fn get_default_secret() -> KeyPair { - let b = Bytes32::from_hex(DEFAULT_PRIVATE_KEY).unwrap(); - let kp = Secp256k1KeyPair::new(b.into()).set_compressed(true); - KeyPair::new_secp256k1(kp) - } - - #[test] - fn test_address_from_private_key() { - let address = BTC::get_address_from_keypair(&get_default_secret()).unwrap(); - - assert_eq!(DEFAULT_ADDRESS, address); - } - - #[test] - fn test_address_from_private_key_bytes() { - // convert hex to [u8] - let pk_bytes = Bytes32::from_hex(DEFAULT_PRIVATE_KEY).unwrap(); - let kp = BTC::keypair_from_bytes(pk_bytes.as_ref()).unwrap(); - let address = BTC::get_address_from_keypair(&kp).unwrap(); - - assert_eq!(DEFAULT_ADDRESS, address); - } - - #[test] - fn test_validate_bip44() { - let v = vec![ - (0, "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"), - (1, "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g"), - (2, "bc1qp59yckz4ae5c4efgw2s5wfyvrz0ala7rgvuz8z"), - (3, "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3"), - (4, "bc1qm97vqzgj934vnaq9s53ynkyf9dgr05rargr04n"), - ]; - - for (index, expected_addr) in v { - let path = BTC::get_path(&PathOptions::new(index)).unwrap(); - let kp = BTC::keypair_from_mnemonic(DEFAULT_MNEMONIC, &path, None).unwrap(); - let addr = BTC::get_address_from_keypair(&kp).unwrap(); - - assert_eq!(expected_addr, addr); - } - } - - #[test] - fn test_get_balance() { - let balance = tokio_test::block_on(BTC::get_balance( - "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", - None, - None, - )) - .unwrap(); - - assert!(balance.to_i64() > 100); - } - - #[test] - fn test_send_and_sign() { - let btc_address_sender = "tb1q09hyefvam4x5hrnnavx6sphv797f0l5xcqt7cl"; - let btc_address_receiver = "tb1qgg29y2z8xsvav65j5kx2pztqff4g2ctn6a4x0u"; - - let testnet_magic = "0b110907"; - - let option = models::SendOptions { - data: Some(models::Options::Bitcoin(BTCOptions { - sats_per_bytes: Some(1), - network: Some(testnet_magic.to_string()), - ..Default::default() - })), - }; - - let send_tx = tokio_test::block_on(BTC::send( - btc_address_sender.to_string(), - btc_address_receiver.to_string(), - BigNumber::from(1000), - Some(option), - None, - )) - .unwrap(); - - let sign_tx = BTC::sign(send_tx, &get_default_secret()).unwrap(); - - let tx = match sign_tx.data.unwrap() { - TransactionRaw::Bitcoin(tx) => tx, - _ => panic!("invalid transaction"), - }; - - assert!(tx.total_send.to_u64() == 1000); - assert!(tx.fee.to_u64() == 226); - // let _ = tokio_test::block_on(BTC::broadcast(sign_tx, Some(node.to_string()))).unwrap(); - } - - #[test] - fn test_validate_address_ok() { - let list = [ - "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", - "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g", - "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", - "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", - "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3", - ]; - - for addr in list.iter() { - let valid = BTC::validate_address(addr, None).unwrap(); - assert_eq!(valid, true, "address {} should be valid", addr); - } - - // network mainnet - for addr in list.iter() { - let valid = BTC::validate_address( - addr, - Some(models::AddressOptions::new( - Some(Network::Bitcoin.magic().encode_hex()), - None, - None, - )), - ) - .unwrap(); - assert_eq!(valid, true, "address {} should be valid", addr); - } - } - - #[test] - fn test_validate_address_fail() { - let list = [ - "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", - "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g", - "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", - "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", - "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3", - ]; - - // wrong network - for addr in list.iter() { - let valid = BTC::validate_address( - addr, - Some(models::AddressOptions::new( - Some(Network::Testnet.magic().encode_hex()), - None, - None, - )), - ) - .unwrap(); - assert_eq!(valid, false, "address {} should be invalid", addr); - } - - let list = [ - "qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", // no prefix - "ltc1qz9vvmr3q2s6m4drd9y9plzs0l38u9z4p96wwxz", // wrong prefix - "BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", - "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN", - ]; - - for addr in list.iter() { - let valid = BTC::validate_address(addr, None).unwrap(); - assert_eq!(valid, false, "address {} should be invalid", addr); - } - } -} diff --git a/packages/kos-sdk/src/chains/bitcoin/requests.rs b/packages/kos-sdk/src/chains/bitcoin/requests.rs deleted file mode 100644 index 82cb3d1..0000000 --- a/packages/kos-sdk/src/chains/bitcoin/requests.rs +++ /dev/null @@ -1,193 +0,0 @@ -use super::transaction::{estimate_fee, UTXO}; -use crate::utils; - -use kos_types::{error::Error, number::BigNumber}; -use std::ops::Add; - -pub async fn fetch_utxos( - node_url: &str, - address: &str, - confirmations: u64, -) -> Result, Error> { - let url = format!("{}/api/v2/utxo/{}", node_url, address); - let mut list: Vec = utils::http_get_auth::>(url) - .await? - .into_iter() - .filter(|utxo| utxo.confirmations >= confirmations) - .collect(); - - list.sort_by_key(|a| a.amount()); - - Ok(list) -} - -// Fetch UTXOs and sum up the total amount. -pub async fn balance( - node_url: &str, - address: &str, - confirmations: u64, -) -> Result { - let unspent = fetch_utxos(node_url, address, confirmations) - .await? - .into_iter() - .map(|utxo| BigNumber::from_string(&utxo.value).unwrap_or_default()) - .reduce(|a, b| a.add(b)); - - Ok(unspent.unwrap_or_default()) -} - -// Fetch UTXOs from Bitcoin node and select UTXOs to cover the desired amount. -#[allow(clippy::too_many_arguments)] -pub async fn select_utxos( - node_url: &str, - address: &str, - desired_amount: &BigNumber, - outputs: u64, - confirmations: u64, - sats_per_bytes: u64, - spend_biggest_first: bool, - spend_all: bool, -) -> Result, Error> { - let mut unspent: Vec = fetch_utxos(node_url, address, confirmations).await?; - - if spend_all { - return Ok(unspent); - } - - // min TX Value defined by dust - let min_value = BigNumber::from(148 * sats_per_bytes); - - let tx_fee_min = estimate_fee(1, outputs, sats_per_bytes).add(desired_amount.clone()); - let tx_fee_max = estimate_fee(1, outputs + 1, sats_per_bytes).add(desired_amount.clone()); - - // check if there is a UTXO that better match the desired amount - for utxo in &unspent { - let value = utxo.amount(); - - // if utxo match the desired amount, stop selecting UTXOs. - // todo!("revisit this logic") - if value.ge(&tx_fee_min) && value.le(&tx_fee_max) { - return Ok(vec![utxo.clone()]); - } - } - - // accumulate UTXOs until we have enough value - // reverse order to reduce the number of UTXOs - if spend_biggest_first { - unspent.sort_by_key(|b| std::cmp::Reverse(b.amount())) - } - - // Vector to hold selected UTXOs. - let mut selected_utxos = Vec::new(); - // Variable to keep track of the total amount in the selected UTXOs. - let mut total_amount = BigNumber::from(0); - - for utxo in unspent { - let value = BigNumber::from_string(&utxo.value)?; - - // Ignore dust values - if value.lt(&min_value) { - continue; - } - - // Add the value of the UTXO to the total amount. - total_amount = total_amount.add(value); - - // Add the UTXO to the selected UTXOs. - selected_utxos.push(utxo); - - // if we have select utxos with enough value, stop looping (but keep processing utxos for a better match) - if total_amount.ge(&tx_fee_min) { - break; - } - } - - Ok(selected_utxos) -} - -// Broadcast raw hex transaction to Bitcoin node. -pub async fn broadcast(node_url: &str, hex_tx: &str) -> Result { - let url = format!("{}/api/v2/sendtx/", node_url); - let data = hex_tx.as_bytes().to_vec(); - - let result = utils::http_post_auth::(url, &data).await; - - let result = result?; - - if let Some(err) = result.get("error") { - if let Some(err) = err.as_str() { - return Err(Error::ReqwestError(err.to_string())); - } - } - - if let Some(txid_value) = result.get("result") { - if let Some(txid) = txid_value.as_str() { - return Ok(txid.to_string()); - } else { - return Err(Error::ReqwestError("txid is not a string".to_string())); - } - } - - Err(Error::ReqwestError("missing txid".to_string())) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_select_utxos() { - let node = "https://bitcoin.explorer.klever.io"; - - let amount = BigNumber::from_string("20000000").unwrap(); - let list = tokio_test::block_on(select_utxos( - node, - "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", - &amount, - 0, - 0, - 10, - false, - false, - )) - .unwrap(); - - // computer total - let total = list - .iter() - .map(|utxo| BigNumber::from_string(&utxo.value).unwrap_or_default()) - .reduce(|a, b| a.add(b)) - .unwrap_or_default(); - - println!("total: {:?}", total); - - assert!(total.ge(&amount)); - } - - #[test] - fn test_spend_all() { - let node = "https://bitcoin.explorer.klever.io"; - - let selected = tokio_test::block_on(select_utxos( - node, - "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", - &BigNumber::from(0), - 0, - 0, - 10, - false, - true, - )) - .unwrap(); - - // get all utxos - let list = tokio_test::block_on(fetch_utxos( - "https://bitcoin.explorer.klever.io", - "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", - 0, - )) - .unwrap(); - - assert_eq!(selected.len(), list.len()); - } -} diff --git a/packages/kos-sdk/src/chains/bitcoin/transaction.rs b/packages/kos-sdk/src/chains/bitcoin/transaction.rs deleted file mode 100644 index 5024ca4..0000000 --- a/packages/kos-sdk/src/chains/bitcoin/transaction.rs +++ /dev/null @@ -1,265 +0,0 @@ -use super::BTC; - -use kos_crypto::keypair::KeyPair; -use kos_proto::options; -use kos_types::{error::Error, hash::Hash, number::BigNumber}; - -use bitcoin::{ - absolute::LockTime, - blockdata::transaction::{Transaction, TxIn, TxOut}, - ecdsa, - psbt::{self, PartiallySignedTransaction}, - sighash, Address, OutPoint, Sequence, -}; - -use serde::{Deserialize, Serialize}; -use std::{ - ops::{Add, Div, Sub}, - str::FromStr, -}; - -// Structure to hold Transaction data. -#[derive(Serialize, Debug, Clone)] -pub struct BTCTransaction { - pub sender_address: Address, - pub tx: PartiallySignedTransaction, - pub total_inputs: BigNumber, - pub total_outputs: BigNumber, - pub total_send: BigNumber, - pub change_amount: BigNumber, - pub change_address: Address, - pub fee: BigNumber, - pub sats_per_bytes: BigNumber, -} - -impl BTCTransaction { - pub fn txid(&self) -> String { - let btc_tx = self.tx.clone().extract_tx(); - btc_tx.txid().to_string() - } - - pub fn txid_hash(&self) -> Result { - Hash::new(&self.txid()) - } - - pub fn btc_serialize_hex(&self) -> String { - bitcoin::consensus::encode::serialize_hex(&self.tx.clone().extract_tx()) - } - - pub fn get_signature(&self) -> Result { - let sig = self.tx.clone().extract_tx().wtxid(); - Ok(sig.to_string()) - } - - pub fn finalize(&mut self) -> Result<(), Error> { - for inp_idx in 0..self.tx.inputs.len() { - // todo!("multi sig and redeem type"); - let script_witness = { - match self.tx.inputs[inp_idx].partial_sigs.first_key_value() { - Some((pubkey, sig)) => { - let mut script_witness = bitcoin::Witness::new(); - script_witness.push(sig.to_vec()); - script_witness.push(pubkey.to_bytes()); - script_witness - } - _ => return Err(Error::TransportError("No signature found".to_string())), - } - }; - self.tx.inputs[inp_idx].final_script_witness = Some(script_witness); - } - - Ok(()) - } - - pub fn sign(&mut self, keypair: &KeyPair) -> Result<(), Error> { - // get BIP143 hasher - let mut cache = sighash::SighashCache::new(&self.tx.unsigned_tx); - let secp = &secp256k1::Secp256k1::new(); - let sk = bitcoin::secp256k1::SecretKey::from_slice(&keypair.secret_key()) - .map_err(|_| Error::InvalidSignature("private key error"))?; - let pk = BTC::get_pubkey(&keypair.public_key())?; - - // sign inputs - for inp_idx in 0..self.tx.inputs.len() { - // compute sighash - let msg_sighash_ty_res = self.tx.sighash_ecdsa(inp_idx, &mut cache); - - // Only return the error if we have a secret key to sign this input. - let (msg, sighash_ty) = match msg_sighash_ty_res { - Err(e) => return Err(Error::InvalidTransaction(format!("{:?}", e))), - Ok((msg, sighash_ty)) => (msg, sighash_ty), - }; - - // sign - let sig = ecdsa::Signature { - sig: secp.sign_ecdsa(&msg, &sk), - hash_ty: sighash_ty, - }; - - // insert signature - self.tx.inputs[inp_idx].partial_sigs.insert(pk, sig); - } - - Ok(()) - } -} - -pub fn create_transaction( - sender_address: Address, - sender_utxos: Vec, - receivers: Vec<(Address, BigNumber)>, - change_address: Address, - options: &options::BTCOptions, -) -> Result { - let total_send = receivers - .iter() - .fold(BigNumber::from(0), |acc, (_, amount)| { - acc.add(amount.clone()) - }); - - let mut total_inputs = BigNumber::from(0); - let mut total_outputs = BigNumber::from(0); - - let mut tx_inputs: Vec = Vec::new(); - let sequence = if options.rbf() { - Sequence::ENABLE_RBF_NO_LOCKTIME - } else { - Sequence::MAX - }; - - let spk = sender_address.script_pubkey(); - let mut psbt_input = Vec::new(); - for utxo in sender_utxos { - // check for RBF flag - tx_inputs.push(TxIn { - previous_output: utxo.to_outpoint()?, - sequence, - ..Default::default() - }); - - let amount = BigNumber::from_str(&utxo.value)?; - total_inputs = total_inputs.add(amount.clone()); - - // todo!("allow non segwit inputs") - psbt_input.push(psbt::Input { - witness_utxo: Some(TxOut { - script_pubkey: spk.clone(), - value: amount.to_u64(), - }), - non_witness_utxo: None, - ..Default::default() - }); - } - - let sats_per_bytes = options.sats_per_bytes(); - - let mut tx_outputs: Vec = Vec::new(); - - // Output to receiver - for (receiver, amount) in receivers { - tx_outputs.push(TxOut { - script_pubkey: receiver.script_pubkey(), - value: amount.to_u64(), - }); - - total_outputs = total_outputs.add(amount); - } - - let change_output = if total_inputs.gt(&total_send) { 1 } else { 0 }; - - // estimate fee - let fee = estimate_fee( - tx_inputs.len() as u64, - tx_outputs.len() as u64 + change_output, - sats_per_bytes, - ); - - let send_plus_estimated_fee = total_send.clone().add(fee.clone()); - - // error if not enough funds to cover fee and amount send - if total_inputs.lt(&send_plus_estimated_fee) { - return Err(Error::TransportError( - format!( - "Not enough funds to cover fee and amount send. Total inputs: {}, Total send: {}, Fee: {}", - total_inputs.to_string(), - total_send.to_string(), - fee.to_string(), - ))); - } - - // Output to self (change) if applicable - // todo add fee calculation - let change_amount = total_inputs.clone().sub(send_plus_estimated_fee); - // compute dust value if not provided - let dust_value = options.dust_value(); - if change_amount.gt(&dust_value) { - tx_outputs.push(TxOut { - script_pubkey: change_address.script_pubkey(), - value: change_amount.clone().to_u64(), - }); - - total_outputs = total_outputs.add(change_amount.clone()); - } - - let fee = total_inputs.clone().sub(total_outputs.clone()); - let size = estimate_size(tx_inputs.len() as u64, tx_outputs.len() as u64); - let sats_per_bytes = fee.clone().div(BigNumber::from(size)); - - // Build Transaction - let mut tx = PartiallySignedTransaction::from_unsigned_tx(Transaction { - version: 2, - lock_time: LockTime::ZERO, - input: tx_inputs, - output: tx_outputs, - }) - .map_err(|e| Error::InvalidTransaction(format!("creating psbt: {}", e)))?; - - // update inputs - tx.inputs = psbt_input; - - Ok(BTCTransaction { - sender_address, - tx, - total_inputs, - total_outputs, - total_send, - fee, - change_amount, - change_address, - sats_per_bytes, - }) -} - -// Structure to hold UTXO data. -#[allow(clippy::upper_case_acronyms)] -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UTXO { - pub txid: String, - pub vout: u32, - pub value: String, - #[serde(default)] - pub height: u64, - pub confirmations: u64, -} - -impl UTXO { - pub fn to_outpoint(&self) -> Result { - let txid = bitcoin::Txid::from_str(&self.txid) - .map_err(|e| Error::InvalidTransaction(e.to_string()))?; - let outpoint = OutPoint::new(txid, self.vout); - Ok(outpoint) - } - - pub fn amount(&self) -> BigNumber { - BigNumber::from_string(&self.value).unwrap_or_default() - } -} - -pub fn estimate_size(tx_in: u64, tx_out: u64) -> u64 { - (148 * tx_in) + (34 * tx_out + 10) -} - -// EstimateFee based on Transaction size -pub fn estimate_fee(tx_in: u64, tx_out: u64, sats_per_bytes: u64) -> BigNumber { - BigNumber::from(estimate_size(tx_in, tx_out) * sats_per_bytes) -} diff --git a/packages/kos-sdk/src/chains/default/mod.rs b/packages/kos-sdk/src/chains/default/mod.rs deleted file mode 100644 index 42cc590..0000000 --- a/packages/kos-sdk/src/chains/default/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::chain::BaseChain; -use crate::models::{self, BroadcastResult, PathOptions, Transaction}; -use kos_crypto::keypair::KeyPair; -use kos_types::error::Error; -use kos_types::number::BigNumber; - -use wasm_bindgen::prelude::*; - -#[derive(Debug, Copy, Clone)] -#[wasm_bindgen] -pub struct NONE; -pub const BASE_CHAIN: BaseChain = BaseChain { - name: "None", - symbol: "NONE", - precision: 0, - chain_code: 0, -}; - -#[wasm_bindgen] -impl NONE { - #[wasm_bindgen(js_name = "baseChain")] - pub fn base_chain() -> BaseChain { - BASE_CHAIN - } - - #[wasm_bindgen(js_name = "random")] - pub fn random() -> Result { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "keypairFromBytes")] - pub fn keypair_from_bytes(_private_key: &[u8]) -> Result { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "keypairFromMnemonic")] - pub fn keypair_from_mnemonic( - _mnemonic: &str, - _path: &str, - _password: Option, - ) -> Result { - Ok(KeyPair::new_default()) - } - - #[wasm_bindgen(js_name = "getAddressFromKeyPair")] - pub fn get_address_from_keypair(_private_key: &KeyPair) -> Result { - Ok("NONE".into()) - } - - #[wasm_bindgen(js_name = "getPath")] - pub fn get_path(_options: &PathOptions) -> Result { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "signDigest")] - /// Sign digest data with the private key. - pub fn sign_digest(_digest: &[u8], _keypair: &KeyPair) -> Result, Error> { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "verifyDigest")] - /// Verify Message signature - pub fn verify_digest(_digest: &[u8], _signature: &[u8], _address: &str) -> Result { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "sign")] - /// Hash and Sign data with the private key. - pub fn sign(_data: Transaction, _keypair: &KeyPair) -> Result { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "messageHash")] - /// Append prefix and hash the message - pub fn message_hash(_message: &[u8]) -> Result, Error> { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "signMessage")] - /// Sign Message with the private key. - pub fn sign_message(_message: &[u8], _keypair: &KeyPair) -> Result, Error> { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "verifyMessageSignature")] - /// Verify Message signature - pub fn verify_message_signature( - _message: &[u8], - _signature: &[u8], - _address: &str, - ) -> Result { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "getBalance")] - /// Get balance of address and token - /// If token is None, it will return balance of native token - /// If token is Some, it will return balance of token - /// If node_url is None, it will use default node url - pub async fn get_balance( - _address: &str, - _token: Option, - _node_url: Option, - ) -> Result { - Err(Error::UnsupportedChain("NONE")) - } - - pub async fn send( - _sender: String, - _receiver: String, - _amount: BigNumber, - _options: Option, - _node_url: Option, - ) -> Result { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "broadcast")] - pub async fn broadcast( - _data: crate::models::Transaction, - _node_url: Option, - ) -> Result { - Err(Error::UnsupportedChain("NONE")) - } - - #[wasm_bindgen(js_name = "validateAddress")] - pub fn validate_address( - _address: &str, - _option: Option, - ) -> Result { - Err(Error::UnsupportedChain("NONE")) - } -} diff --git a/packages/kos-sdk/src/chains/ethereum/address.rs b/packages/kos-sdk/src/chains/ethereum/address.rs deleted file mode 100644 index d7ec3cc..0000000 --- a/packages/kos-sdk/src/chains/ethereum/address.rs +++ /dev/null @@ -1,193 +0,0 @@ -use kos_crypto::keypair::KeyPair; -use kos_types::error::Error; - -use hex::FromHex; -use rlp::{DecoderError, Rlp}; -use std::{fmt, str::FromStr}; -use web3::types::Address as Web3Address; - -use wasm_bindgen::prelude::*; - -const ADDRESS_LEN: usize = 20; - -#[derive(PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[repr(transparent)] -#[wasm_bindgen(js_name = "ETHAddress")] -pub struct Address([u8; ADDRESS_LEN]); - -impl Address { - /// Address of a public key. - pub fn from_public(public: [u8; 64]) -> Address { - let digest = kos_crypto::hash::keccak256(&public); - - let mut raw = [0u8; ADDRESS_LEN]; - raw.copy_from_slice(&digest[digest.len() - ADDRESS_LEN..]); - - Address(raw) - } - - /// Address of a private key. - pub fn from_keypair(kp: &KeyPair) -> Address { - Address::from_public(kp.public_key().try_into().unwrap()) - } - - pub fn as_bytes(&self) -> &[u8] { - &self.0 - } - - pub fn from_bytes(raw: &[u8]) -> &Address { - assert!(raw.len() == ADDRESS_LEN); - - unsafe { std::mem::transmute(&raw[0]) } - } - - /// To hex address - pub fn to_hex_address(self) -> String { - Address::to_hex_checksum(&hex::encode(self.0)) - } - - pub fn to_hex_checksum(address: &str) -> String { - let address = address.trim_start_matches("0x").to_lowercase(); - - let address_hash = hex::encode(kos_crypto::hash::keccak256(address.as_bytes())); - - address - .char_indices() - .fold(String::from("0x"), |mut acc, (index, address_char)| { - // this cannot fail since it's Keccak256 hashed - let n = u16::from_str_radix(&address_hash[index..index + 1], 16).unwrap(); - - if n > 7 { - // make char uppercase if ith character is 9..f - acc.push_str(&address_char.to_uppercase().to_string()) - } else { - // already lowercased - acc.push(address_char) - } - - acc - }) - } -} - -impl fmt::Display for Address { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_hex_address().fmt(f) - } -} - -impl ::std::fmt::Debug for Address { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_tuple("Address").field(&self.to_string()).finish() - } -} - -impl TryFrom<&[u8]> for Address { - type Error = Error; - - fn try_from(value: &[u8]) -> Result { - if value.len() != ADDRESS_LEN { - Err(Error::InvalidAddress(format!( - "invalid length, {}", - value.len() - ))) - } else { - let mut raw = [0u8; ADDRESS_LEN]; - raw[..ADDRESS_LEN].copy_from_slice(value); - Ok(Address(raw)) - } - } -} - -impl TryFrom> for Address { - type Error = Error; - - fn try_from(value: Vec) -> Result { - Self::try_from(&value[..]) - } -} - -impl TryFrom<&Vec> for Address { - type Error = Error; - - fn try_from(value: &Vec) -> Result { - Self::try_from(&value[..]) - } -} - -impl TryFrom<&str> for Address { - type Error = Error; - - fn try_from(value: &str) -> Result { - Address::from_str(value) - } -} - -impl FromHex for Address { - type Error = Error; - - fn from_hex>(hex: T) -> Result { - Address::try_from(hex.as_ref()) - } -} - -impl FromStr for Address { - type Err = Error; - - fn from_str(s: &str) -> Result - where - Self: Sized, - { - // remove "0x"|"0X" if exists - let s = if s.len() > 2 && (s.starts_with("0x") || s.starts_with("0X")) { - &s[2..] - } else { - s - }; - - if s.len() == 40 { - return Vec::from_hex(s) - .map_err(|e| Error::InvalidAddress(e.to_string())) - .and_then(Address::try_from); - } - - eprintln!("len={} prefix={:x}", s.len(), s.as_bytes()[0]); - Err(Error::InvalidAddress( - "invalid ethereum address".to_string(), - )) - } -} - -// NOTE: AsRef<[u8]> implies ToHex -impl AsRef<[u8]> for Address { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl rlp::Decodable for Address { - fn decode(rlp: &Rlp) -> Result { - let mut data: Vec = rlp.as_val()?; - let mut bytes: [u8; ADDRESS_LEN] = [0; ADDRESS_LEN]; - while data.len() < ADDRESS_LEN { - data.push(0); - } - bytes.copy_from_slice(&data[..]); - Ok(Address(bytes)) - } -} - -impl TryFrom<&Web3Address> for Address { - type Error = Error; - - fn try_from(value: &Web3Address) -> Result { - Address::try_from(value.as_bytes()) - } -} - -impl From
for Web3Address { - fn from(value: Address) -> Self { - Web3Address::from(value.0) - } -} diff --git a/packages/kos-sdk/src/chains/ethereum/mod.rs b/packages/kos-sdk/src/chains/ethereum/mod.rs deleted file mode 100644 index 502eed3..0000000 --- a/packages/kos-sdk/src/chains/ethereum/mod.rs +++ /dev/null @@ -1,749 +0,0 @@ -pub mod address; -pub mod request; -pub mod transaction; - -use crate::chain::{self, BaseChain}; -use crate::chains::evm20; -use crate::models::{self, BroadcastResult, PathOptions, Transaction, TransactionRaw}; - -use kos_crypto::keypair::KeyPair; -use kos_crypto::secp256k1::Secp256k1KeyPair; -use kos_proto::options::ETHOptions; -use kos_types::error::Error; -use kos_types::hash::Hash; -use kos_types::number::BigNumber; - -use rlp::Rlp; -use secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; -use std::{ops::Div, str::FromStr}; -use wasm_bindgen::prelude::*; -use web3::ethabi; -use web3::types::U256; - -#[derive(Debug, Copy, Clone)] -#[wasm_bindgen] -pub struct ETH {} - -pub const SIGN_PREFIX: &[u8; 26] = b"\x19Ethereum Signed Message:\n"; -pub const BIP44_PATH: u32 = 60; -pub const CHAIN_ID: u64 = 1; -pub const DEFAULT_GAS_TRANSFER: u64 = 21000; - -/// hash digest -pub fn hash_transaction(eth_tx: &transaction::Transaction) -> Result, Error> { - let bytes = eth_tx.encode()?; - let digest = ETH::hash(&bytes)?; - Ok(digest) -} - -pub const BASE_CHAIN: BaseChain = BaseChain { - name: "Ethereum", - symbol: "ETH", - precision: 18, - chain_code: 3, -}; - -#[wasm_bindgen] -impl ETH { - #[wasm_bindgen(js_name = "baseChain")] - pub fn base_chain() -> BaseChain { - BASE_CHAIN - } - - #[wasm_bindgen(js_name = "random")] - pub fn random() -> Result { - let mut rng = rand::thread_rng(); - let kp = Secp256k1KeyPair::random(&mut rng); - Ok(KeyPair::new_secp256k1(kp)) - } - - #[wasm_bindgen(js_name = "keypairFromBytes")] - pub fn keypair_from_bytes(private_key: &[u8]) -> Result { - // copy to fixed length array - let mut pk_slice = [0u8; 32]; - pk_slice.copy_from_slice(private_key); - - let kp = Secp256k1KeyPair::new(pk_slice); - Ok(KeyPair::new_secp256k1(kp)) - } - - #[wasm_bindgen(js_name = "keypairFromMnemonic")] - pub fn keypair_from_mnemonic( - mnemonic: &str, - path: &str, - password: Option, - ) -> Result { - let kp = Secp256k1KeyPair::new_from_mnemonic_phrase_with_path( - mnemonic, - path, - password.as_deref(), - )?; - - Ok(KeyPair::new_secp256k1(kp)) - } - - #[wasm_bindgen(js_name = "getAddressFromKeyPair")] - pub fn get_address_from_keypair(kp: &KeyPair) -> Result { - Ok(address::Address::from_keypair(kp).to_string()) - } - - #[wasm_bindgen(js_name = "getPath")] - pub fn get_path(options: &PathOptions) -> Result { - Ok(format!("m/44'/{}'/0'/0/{}", BIP44_PATH, options.index)) - } - - #[wasm_bindgen(js_name = "signDigest")] - /// Sign digest data with the private key. - pub fn sign_digest(digest: &[u8], keypair: &KeyPair) -> Result, Error> { - let raw = keypair.sign_digest(digest); - Ok(raw) - } - - #[wasm_bindgen(js_name = "verifyDigest")] - /// Verify Message signature - pub fn verify_digest(_digest: &[u8], _signature: &[u8], _address: &str) -> Result { - // let message = Message::from_slice(message).map_err(|_| RecoveryError::InvalidMessage)?; - // let recovery_id = RecoveryId::from_i32(recovery_id).map_err(|_| RecoveryError::InvalidSignature)?; - // let signature = - // RecoverableSignature::from_compact(signature, recovery_id).map_err(|_| RecoveryError::InvalidSignature)?; - // let public_key = CONTEXT - // .recover_ecdsa(&message, &signature) - // .map_err(|_| RecoveryError::InvalidSignature)?; - - // Ok(public_key_address(&public_key)) - todo!() - } - - #[wasm_bindgen(js_name = "sign")] - /// Hash and Sign data with the private key. - pub fn sign(tx: Transaction, keypair: &KeyPair) -> Result { - match tx.data { - Some(TransactionRaw::Ethereum(eth_tx)) => { - let mut new_tx = eth_tx.clone(); - - // remove signature if any - let mut eth_tx = eth_tx.clone(); - eth_tx.signature = None; - - let digest = hash_transaction(ð_tx)?; - let sig = ETH::sign_digest(digest.as_slice(), keypair)?; - - new_tx.signature = Some(RecoverableSignature::from_compact( - &sig[..64], - RecoveryId::from_i32(sig[64] as i32)?, - )?); - - let new_hash = hash_transaction(&new_tx)?; - let result = Transaction { - chain: tx.chain, - sender: tx.sender, - hash: Hash::from_vec(new_hash)?, - data: Some(TransactionRaw::Ethereum(new_tx)), - signature: Some(hex::encode(sig)), - }; - - Ok(result) - } - _ => Err(Error::InvalidMessage( - "not a ethereum transaction".to_string(), - )), - } - } - - #[wasm_bindgen(js_name = "hash")] - /// hash digest - pub fn hash(message: &[u8]) -> Result, Error> { - let digest = kos_crypto::hash::keccak256(message); - Ok(digest.to_vec()) - } - - #[wasm_bindgen(js_name = "messageHash")] - /// Append prefix and hash the message - pub fn message_hash(message: &[u8]) -> Result, Error> { - let to_sign = [SIGN_PREFIX, message.len().to_string().as_bytes(), message].concat(); - - ETH::hash(&to_sign) - } - - #[wasm_bindgen(js_name = "signMessage")] - /// Sign Message with the private key. - pub fn sign_message(message: &[u8], keypair: &KeyPair) -> Result, Error> { - let m = ETH::message_hash(message)?; - ETH::sign_digest(&m, keypair) - } - - #[wasm_bindgen(js_name = "verifyMessageSignature")] - /// Verify Message signature - pub fn verify_message_signature( - message: &[u8], - signature: &[u8], - address: &str, - ) -> Result { - let m = ETH::message_hash(message)?; - ETH::verify_digest(&m, signature, address) - } - - #[wasm_bindgen(js_name = "getBalance")] - pub async fn get_balance( - address: &str, - token: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("ETH")); - - let from: address::Address = address.try_into()?; - - match token { - Some(key) if key != "ETH" => { - let contract_address: address::Address = key.as_str().try_into()?; - let contract = evm20::get_contract_evm20(); - let func = contract.function("balanceOf").map_err(|e| { - Error::InvalidMessage(format!("failed to get balanceOf function: {}", e)) - })?; - - let data = func - .encode_input(&[ethabi::Token::Address(from.into())]) - .map_err(|e| Error::InvalidMessage(format!("failed to encode input: {}", e)))?; - - let result = request::call(&node, from, contract_address, data).await?; - - // Decode the output (the balance). - let balance = match func.decode_output(&result).unwrap()[0].clone().into_uint() { - Some(b) => b, - _ => return Err(Error::ReqwestError("failed to decode output".to_string())), - }; - - Ok(balance.to_string().try_into()?) - } - _ => request::get_balance(&node, from).await, - } - } - - #[wasm_bindgen(js_name = "getGasPrice")] - pub async fn gas_price(node_url: Option) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("ETH")); - let gas_price = request::gas_price(&node).await?; - BigNumber::from_string(&gas_price.to_string()) - } - - fn get_options(options: Option) -> kos_proto::options::ETHOptions { - match options.and_then(|opt| opt.data) { - Some(crate::models::Options::Ethereum(op)) => op, - _ => kos_proto::options::ETHOptions::default(), - } - } - - pub async fn send( - sender: String, - receiver: String, - amount: BigNumber, - options: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("ETH")); - - // validate sender address - let addr_sender = address::Address::from_str(&sender)?; - let mut addr_receiver = address::Address::from_str(&receiver)?; - let mut options = ETH::get_options(options); - - let token = options.token.as_deref().unwrap_or("ETH"); - let is_eth_token = token == "ETH"; - - let mut amount_eth = amount.clone(); - // Update addr_receiver for non-ETH token. - if !is_eth_token { - // update contract data for token transfer - let contract = evm20::get_contract_evm20(); - let func = contract.function("transfer").map_err(|e| { - Error::InvalidMessage(format!("failed to get transfer function: {}", e)) - })?; - - let encoded = func - .encode_input(&[ - ethabi::Token::Address(addr_receiver.into()), - ethabi::Token::Uint( - U256::from_dec_str(&amount.to_string()) - .map_err(|e| Error::InvalidNumberParse(e.to_string()))?, - ), - ]) - .map_err(|e| Error::InvalidTransaction(e.to_string()))?; - - addr_receiver = address::Address::from_str(token)?; - options.contract_data = Some(encoded); - amount_eth = 0.into(); - } else if options.gas_limit.is_none() { - options.gas_limit = Some(BigNumber::from(DEFAULT_GAS_TRANSFER)); - } - - let tx = ETH::build_tx(&node, addr_sender, addr_receiver, amount_eth, options).await?; - - let digest = hash_transaction(&tx)?; - - Ok(crate::models::Transaction { - chain: chain::Chain::ETH, - sender, - hash: Hash::from_vec(digest)?, - data: Some(TransactionRaw::Ethereum(tx)), - signature: None, - }) - } - - async fn build_tx( - node: &str, - sender: address::Address, - receiver: address::Address, - amount: BigNumber, - options: ETHOptions, - ) -> Result { - // update chain id if none - let chain_id = options.chain_id.unwrap_or(CHAIN_ID); - - // compute nonce if none - let nonce = match options.nonce { - Some(value) if value != 0 => U256::from_dec_str(&value.to_string()) - .map_err(|e| Error::InvalidNumberParse(e.to_string()))?, - _ => { - let nonce = request::get_nonce(node, sender).await?; - U256::from(nonce) - } - }; - - let gas_price: Option = match options.gas_price { - Some(value) => Some( - U256::from_dec_str(&value.to_string()) - .map_err(|e| Error::InvalidNumberParse(e.to_string()))?, - ), - None => { - if options.legacy_type.unwrap_or(false) { - Some(request::gas_price(node).await?) - } else { - None - } - } - }; - - let value = U256::from_dec_str(&amount.to_string()) - .map_err(|e| Error::InvalidNumberParse(e.to_string()))?; - - let gas_limit: U256 = match options.gas_limit { - Some(value) => U256::from_dec_str(&value.to_string()) - .map_err(|e| Error::InvalidNumberParse(e.to_string()))?, - None => { - request::estimate_gas( - node, - sender, - receiver, - gas_price, - Some(value), - options.contract_data.to_owned(), - ) - .await? - } - }; - - let max_fee_per_gas: Option = match options.max_fee_per_gas { - Some(value) => Some( - U256::from_dec_str(&value.to_string()) - .map_err(|e| Error::InvalidNumberParse(e.to_string()))?, - ), - None => { - if !options.legacy_type.unwrap_or(false) { - Some(request::base_fee(node).await?) - } else { - None - } - } - }; - - let max_priority_fee_per_gas: Option = match options.max_priority_fee_per_gas { - Some(value) => Some( - U256::from_dec_str(&value.to_string()) - .map_err(|e| Error::InvalidNumberParse(e.to_string()))?, - ), - None => { - if !options.legacy_type.unwrap_or(false) { - // use 10% of max_fee_per_gas for max_priority_fee_per_gas as default - Some(max_fee_per_gas.unwrap_or_default().div(U256::from(10))) - } else { - None - } - } - }; - - Ok(transaction::Transaction { - transaction_type: Some(if options.legacy_type.unwrap_or(false) { - transaction::TransactionType::Legacy - } else { - transaction::TransactionType::EIP1559 - }), - chain_id: Some(chain_id), - nonce, - from: Some(sender), - to: Some(receiver), - value, - data: options.contract_data.unwrap_or_default(), - gas: gas_limit, - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - signature: None, - }) - } - - #[wasm_bindgen(js_name = "broadcast")] - pub async fn broadcast( - tx: crate::models::Transaction, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("ETH")); - - let eth_tx = match tx.data.to_owned() { - Some(TransactionRaw::Ethereum(tx)) => tx, - _ => return Err(Error::InvalidTransaction("Invalid transaction type".into())), - }; - - _ = request::broadcast(node.as_str(), eth_tx.encode()?).await?; - - Ok(BroadcastResult::new(tx)) - } - - #[wasm_bindgen(js_name = "validateAddress")] - pub fn validate_address( - address: &str, - option: Option, - ) -> Result { - let addr = address::Address::from_str(address); - let addr = match addr { - Ok(addr) => addr.to_string().trim_start_matches("0x").to_string(), - Err(_) => return Ok(false), - }; - - let option = option.unwrap_or_default(); - - // Check if address checksum is required and if the address matches the expected format - if option.check_summed.unwrap_or(false) - && addr != address.to_string().trim_start_matches("0x") - { - return Ok(false); - } - - Ok(true) - } - - #[wasm_bindgen(js_name = "txFromRaw")] - pub fn tx_from_raw(raw: &str) -> Result { - let hex_tx = hex::decode(raw)?; - let rlp = Rlp::new(&hex_tx); - - let tx = match transaction::Transaction::decode_legacy(&rlp) { - Ok(tx) => tx, - Err(_) => { - let rlp = Rlp::new(&hex_tx[2..]); - self::transaction::Transaction::decode_eip155(rlp).map_err(|e| { - Error::InvalidTransaction(format!("failed to decode transaction: {}", e)) - })? - } - }; - - let signature = tx.signature.map(|sig| sig.to_standard().to_string()); - - let digest = hash_transaction(&tx)?; - - Ok(crate::models::Transaction { - chain: chain::Chain::ETH, - sender: "".to_string(), //TODO: implement sender on eth decode - hash: Hash::from_vec(digest)?, - data: Some(TransactionRaw::Ethereum(tx)), - signature, - }) - } - - #[wasm_bindgen(js_name = "txFromJson")] - pub fn tx_from_json(raw: &str) -> Result { - // build expected send result - let tx: transaction::Transaction = serde_json::from_str(raw)?; - - let digest = hash_transaction(&tx)?; - - let sender = match tx.from { - Some(addr) => addr.to_string(), - None => "".to_string(), - }; - - let signature = tx.signature.map(|sig| sig.to_standard().to_string()); - - Ok(crate::models::Transaction { - chain: chain::Chain::ETH, - sender, - hash: Hash::from_vec(digest)?, - data: Some(TransactionRaw::Ethereum(tx)), - signature, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use dotenvy; - use hex::FromHex; - use kos_types::Bytes32; - use std::sync::Once; - - const DEFAULT_PRIVATE_KEY: &str = - "1ab42cc412b618bdea3a599e3c9bae199ebf030895b039e9db1e30dafb12b727"; - const DEFAULT_ADDRESS: &str = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"; - const DEFAULT_MNEMONIC: &str = - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - - static _INIT: Once = Once::new(); - - fn _init() { - _INIT.call_once(|| { - dotenvy::from_filename(".env.nodes").ok(); - }); - } - - fn get_default_secret() -> KeyPair { - let b = Bytes32::from_hex(DEFAULT_PRIVATE_KEY).unwrap(); - let kp = Secp256k1KeyPair::new(b.into()); - KeyPair::new_secp256k1(kp) - } - - #[test] - fn test_address_from_private_key() { - let address = ETH::get_address_from_keypair(&get_default_secret()).unwrap(); - - assert_eq!(DEFAULT_ADDRESS, address); - } - - #[test] - fn test_validate_bip44() { - let v = vec![ - (0, "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"), - (1, "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0"), - (2, "0xb6716976A3ebe8D39aCEB04372f22Ff8e6802D7A"), - (3, "0xF3f50213C1d2e255e4B2bAD430F8A38EEF8D718E"), - (4, "0x51cA8ff9f1C0a99f88E86B8112eA3237F55374cA"), - ]; - - for (index, expected_addr) in v { - let path = ETH::get_path(&PathOptions::new(index)).unwrap(); - let kp = ETH::keypair_from_mnemonic(DEFAULT_MNEMONIC, &path, None).unwrap(); - let addr = ETH::get_address_from_keypair(&kp).unwrap(); - - assert_eq!(expected_addr, addr); - } - } - - #[test] - fn test_send_and_sign() { - let options = models::SendOptions { - data: Some(models::Options::Ethereum(kos_proto::options::ETHOptions { - chain_id: Some(1), - nonce: Some(100), - max_fee_per_gas: Some("1000000000".try_into().unwrap()), - max_priority_fee_per_gas: Some("1000000000".try_into().unwrap()), - ..Default::default() - })), - }; - - let tx = tokio_test::block_on(ETH::send( - DEFAULT_ADDRESS.to_string(), - "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0".to_string(), - "1000".try_into().unwrap(), - Some(options), - None, - )) - .unwrap(); - - assert_eq!( - tx.hash.to_string(), - "c2c9b955cf8394fa9434ba812e69f2297c23c16a261a915fa3f1a18adf3fae63" - ); - - let signed = ETH::sign(tx, &get_default_secret()); - assert!(signed.is_ok()); - assert_eq!( - signed.unwrap().hash.to_string(), - "87eab8c8201462ac4872200dfebe841aa77bdc0cc0e5310542c7f319dd304fdf" - ); - } - - #[test] - fn test_send_erc20() { - let options = models::SendOptions { - data: Some(models::Options::Ethereum(kos_proto::options::ETHOptions { - chain_id: Some(1), - nonce: Some(100), - token: Some("0xc12d1c73ee7dc3615ba4e37e4abfdbddfa38907e".to_string()), - gas_limit: Some(1000000.into()), - gas_price: Some(0.into()), - max_fee_per_gas: Some(0.into()), - max_priority_fee_per_gas: Some(0.into()), - ..Default::default() - })), - }; - - let tx = tokio_test::block_on(ETH::send( - DEFAULT_ADDRESS.to_string(), - "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0".to_string(), - "1000".try_into().unwrap(), - Some(options), - None, - )) - .unwrap(); - - assert_eq!( - tx.hash.to_string(), - "fa2b84b45d28888c43c0bda80000e4a4d2040017f3b6e4a31e7d8a4ace27db29" - ); - - let eth_tx = match tx.data.clone() { - Some(models::TransactionRaw::Ethereum(tx)) => tx, - _ => panic!("invalid tx"), - }; - - assert_eq!(eth_tx.value.to_string(), "0"); - assert_eq!(hex::encode(eth_tx.data), "a9059cbb0000000000000000000000006fac4d18c912343bf86fa7049364dd4e424ab9c000000000000000000000000000000000000000000000000000000000000003e8"); - - let signed = ETH::sign(tx, &get_default_secret()); - assert!(signed.is_ok()); - assert_eq!( - signed.unwrap().hash.to_string(), - "f4c27caf4a9e3718d217f315e8fed3f9b739fa61c78e5ab43a7ca7d4fd1c010f" - ); - } - - #[test] - fn test_get_balance() { - let balance = tokio_test::block_on(ETH::get_balance(DEFAULT_ADDRESS, None, None)).unwrap(); - - assert!(balance.to_i64() > 0); - } - - #[test] - fn test_get_balance_erc20() { - let balance = tokio_test::block_on(ETH::get_balance( - DEFAULT_ADDRESS, - Some("0xC12D1c73eE7DC3615BA4e37E4ABFdbDDFA38907E".to_string()), - None, - )); - assert!(balance.is_ok()); - - assert!(balance.unwrap().to_i64() > 100); - } - - #[test] - fn test_validate_address_ok() { - let list = [ - "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", - "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0", - "9858EfFD232B4033E47d90003D41EC34EcaEda94", // no 0x prefix as valid - ]; - - for addr in list { - let valid = ETH::validate_address(addr, None).unwrap(); - assert!(valid); - } - - // check summed - for addr in list { - let valid = ETH::validate_address( - addr, - Some(models::AddressOptions::new(None, None, Some(true))), - ) - .unwrap(); - assert_eq!(valid, true, "address: {}", addr); - } - } - - #[test] - fn test_validate_address_fail() { - let list = [ - "0x9858EfFD232B4033E47d90003D41EC34EcaEda95", // wrong check sum - &"0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0".to_lowercase(), // all lower case - ]; - - // check summed - for addr in list { - let valid = ETH::validate_address( - addr, - Some(models::AddressOptions::new(None, None, Some(true))), - ) - .unwrap(); - assert_eq!(valid, false, "address: {}", addr); - } - - // wrong size - let list = [ - "0x9858EfFD232B4033E47d90003D41EC34EcaEda9", // hex convert wrong parity - "0x9858EfFD232B4033E47d90003D41EC34EcaEda941", // hex convert wrong parity - "0x9858EfFD232B4033E47d90003D41EC34EcaEda94Ab", // hex convert ok, wrong length - ]; - - // check summed - for addr in list { - let valid = ETH::validate_address(&addr, None).unwrap(); - assert_eq!(valid, false, "address: {}", addr); - } - } - - #[test] - fn test_decode_rlp_tx() { - let raw_tx = "af02ed0182012884019716f7850e60f86055827530944cbeee256240c92a9ad920ea6f4d7df6466d2cdc0180c0808080"; - let tx = ETH::tx_from_raw(raw_tx).unwrap(); - - assert_eq!(tx.chain, chain::Chain::ETH); - - let eth_tx = match tx.data { - Some(TransactionRaw::Ethereum(tx)) => tx, - _ => panic!("invalid tx"), - }; - - assert_eq!(eth_tx.chain_id, Some(1)); - assert_eq!(eth_tx.nonce, U256::from_dec_str("296").unwrap()); - assert_eq!( - eth_tx.to.unwrap().to_string(), - "0x4cBeee256240c92A9ad920ea6f4d7Df6466D2Cdc" - ); - assert_eq!(eth_tx.gas, U256::from(30000)); - assert_eq!(eth_tx.value, U256::from_dec_str("1").unwrap()); - assert_eq!(eth_tx.signature, None); - } - - #[test] - fn test_decode_json() { - let json = r#"{ - "from":"0x4cbeee256240c92a9ad920ea6f4d7df6466d2cdc", - "maxPriorityFeePerGas":null,"maxFeePerGas":null, - "gas": "0x00", - "value": "0x00", - "data":"0xa9059cbb000000000000000000000000ac4145fef6c828e8ae017207ad944c988ccb2cf700000000000000000000000000000000000000000000000000000000000f4240", - "to":"0xdac17f958d2ee523a2206206994597c13d831ec7", - "nonce":"0x00"}"#; - let tx = ETH::tx_from_json(json).unwrap(); - - assert_eq!(tx.chain, chain::Chain::ETH); - - let eth_tx = match tx.data { - Some(TransactionRaw::Ethereum(tx)) => tx, - _ => panic!("invalid tx"), - }; - - assert_eq!(eth_tx.chain_id, None); - assert_eq!(eth_tx.nonce, U256::from_dec_str("0").unwrap()); - assert_eq!( - eth_tx.from.unwrap().to_string(), - "0x4cBeee256240c92A9ad920ea6f4d7Df6466D2Cdc" - ); - assert_eq!( - eth_tx.to.unwrap().to_string(), - "0xdAC17F958D2ee523a2206206994597C13D831ec7" - ); - assert_eq!(eth_tx.gas, U256::from(0)); - assert_eq!(eth_tx.value, U256::from(0)); - assert_eq!(eth_tx.signature, None); - } -} diff --git a/packages/kos-sdk/src/chains/ethereum/request.rs b/packages/kos-sdk/src/chains/ethereum/request.rs deleted file mode 100644 index 70fe080..0000000 --- a/packages/kos-sdk/src/chains/ethereum/request.rs +++ /dev/null @@ -1,121 +0,0 @@ -use super::address::Address; - -use kos_types::{error::Error, number::BigNumber}; - -use web3::{transports::Http, types::U256, Web3}; - -pub fn new_transport(url: &str) -> Result { - let transport = - web3::transports::Http::new(url).map_err(|e| Error::TransportError(e.to_string()))?; - Ok(transport) -} - -pub fn get_web3(url: &str) -> Result, Error> { - let transport = new_transport(url)?; - let web3 = Web3::new(transport); - Ok(web3) -} - -pub async fn call(url: &str, from: Address, to: Address, data: Vec) -> Result, Error> { - let call = web3::types::CallRequest { - from: Some(from.into()), - to: Some(to.into()), - gas: None, - gas_price: None, - value: None, - data: Some(data.into()), - ..Default::default() - }; - - let web3 = get_web3(url)?; - let result = web3 - .eth() - .call(call, None) - .await - .map_err(|e| Error::TransportError(e.to_string()))?; - - Ok(result.0) -} - -pub async fn get_balance(url: &str, address: Address) -> Result { - let web3 = get_web3(url)?; - let balance = web3 - .eth() - .balance(address.into(), None) - .await - .map_err(|e| Error::TransportError(e.to_string()))?; - balance.to_string().try_into() -} - -pub async fn get_nonce(url: &str, address: Address) -> Result { - let web3 = get_web3(url)?; - let nonce = web3 - .eth() - .transaction_count(address.into(), None) - .await - .map_err(|e| Error::TransportError(e.to_string()))?; - Ok(nonce.as_u64()) -} - -/// Call a contract without changing the state of the blockchain to estimate gas usage. -pub async fn estimate_gas( - url: &str, - from: Address, - to: Address, - gas_price: Option, - value: Option, - data: Option>, -) -> Result { - let req = web3::types::CallRequest { - from: Some(from.into()), - to: Some(to.into()), - gas: None, - gas_price, - value, - data: data.map(|data| data.into()), - ..Default::default() - }; - - let web3 = get_web3(url)?; - web3.eth() - .estimate_gas(req, None) - .await - .map_err(|e| Error::TransportError(e.to_string())) -} - -/// Get current recommended gas price -pub async fn gas_price(url: &str) -> Result { - let web3 = get_web3(url)?; - web3.eth() - .gas_price() - .await - .map_err(|e| Error::TransportError(e.to_string())) -} - -pub async fn base_fee(url: &str) -> Result { - let web3 = get_web3(url)?; - let block = web3 - .eth() - .block(web3::types::BlockId::Number( - web3::types::BlockNumber::Pending, - )) - .await - .map_err(|e| Error::TransportError(e.to_string()))? - .unwrap(); - - // get block base fee - let base_fee = block.base_fee_per_gas.unwrap_or(U256::zero()); - - Ok(base_fee) -} - -/// Broadcast ETH transaction -pub async fn broadcast(url: &str, tx_raw: Vec) -> Result { - let web3 = get_web3(url)?; - Ok(web3 - .eth() - .send_raw_transaction(tx_raw.into()) - .await - .map_err(|e| Error::TransportError(e.to_string()))? - .to_string()) -} diff --git a/packages/kos-sdk/src/chains/ethereum/transaction.rs b/packages/kos-sdk/src/chains/ethereum/transaction.rs deleted file mode 100644 index 20ac348..0000000 --- a/packages/kos-sdk/src/chains/ethereum/transaction.rs +++ /dev/null @@ -1,281 +0,0 @@ -use super::address::Address; -use std::str::FromStr; - -use kos_types::error::Error; - -use rlp::{DecoderError, Rlp, RlpStream}; -use secp256k1::ecdsa::RecoverableSignature; -use serde::{Deserialize, Deserializer, Serialize}; -use web3::types::U256; - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub enum TransactionType { - Legacy, - EIP1559, -} - -fn signature_serialize(x: &Option, s: S) -> Result -where - S: serde::Serializer, -{ - if let Some(signature) = x { - let serialized = format!("{:?}", signature); - s.serialize_str(&serialized) - } else { - s.serialize_none() - } -} - -pub fn deserialize_addr<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - if s.is_empty() { - Ok(None) - } else { - Ok(Some( - Address::from_str(&s).map_err(serde::de::Error::custom)?, - )) - } -} - -pub fn deserialize_data<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - if s.is_empty() { - Ok(Vec::new()) - } else { - let s = if s.len() > 2 && (s.starts_with("0x") || s.starts_with("0X")) { - &s[2..] - } else { - &s - }; - - Ok(hex::decode(s).map_err(serde::de::Error::custom)?) - } -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct Transaction { - pub transaction_type: Option, - pub nonce: U256, - #[serde(deserialize_with = "deserialize_addr")] - pub from: Option
, - #[serde(deserialize_with = "deserialize_addr")] - pub to: Option
, - pub gas: U256, - pub gas_price: Option, - pub value: U256, - #[serde(deserialize_with = "deserialize_data")] - pub data: Vec, - pub chain_id: Option, - pub max_fee_per_gas: Option, - pub max_priority_fee_per_gas: Option, - #[serde(serialize_with = "signature_serialize", skip_deserializing)] - pub signature: Option, -} - -impl Transaction { - fn rlp_append_legacy(&self, stream: &mut RlpStream) { - stream.append(&self.nonce); - stream.append(&self.gas_price.unwrap_or_default()); - stream.append(&self.gas); - if let Some(to) = self.to { - stream.append(&to.as_bytes()); - } else { - stream.append(&""); - } - stream.append(&self.value); - stream.append(&self.data); - } - - fn encode_legacy(&self) -> RlpStream { - let mut stream = RlpStream::new(); - stream.begin_list(9); - - self.rlp_append_legacy(&mut stream); - - if let Some(signature) = self.signature { - self.rlp_append_signature(&mut stream, signature); - } else { - stream.append(&self.chain_id.unwrap_or(0)); - stream.append(&0u8); - stream.append(&0u8); - }; - - stream - } - - fn encode_eip1559_payload(&self) -> RlpStream { - let mut stream = RlpStream::new(); - - let list_size = if self.signature.is_some() { 12 } else { 9 }; - stream.begin_list(list_size); - - // append chain_id. from EIP-2930: chainId is defined to be an integer of arbitrary size. - stream.append(&self.chain_id.unwrap_or(0)); - - stream.append(&self.nonce); - stream.append(&self.max_priority_fee_per_gas.unwrap_or_default()); - - let gas_price = self.max_fee_per_gas.or(self.gas_price).unwrap_or_default(); - stream.append(&gas_price); - - stream.append(&self.gas); - if let Some(to) = self.to { - stream.append(&to.as_bytes()); - } else { - stream.append(&""); - } - stream.append(&self.value); - stream.append(&self.data); - - self.rlp_append_access_list(&mut stream); - - if let Some(signature) = self.signature { - self.rlp_append_signature(&mut stream, signature); - } - - stream - } - - fn rlp_append_access_list(&self, stream: &mut RlpStream) { - stream.begin_list(0); - // todo!(access_list) - } - - fn rlp_append_signature(&self, stream: &mut RlpStream, signature: RecoverableSignature) { - // Deconstruct the signature into r, s, and v - let (rec_id, raw_sig) = signature.serialize_compact(); - let v = self.rlp_adjust_v_value(rec_id.to_i32()); - let r = &raw_sig[0..32]; - let s = &raw_sig[32..64]; - stream.append(&v); - stream.append(&U256::from_big_endian(r)); - stream.append(&U256::from_big_endian(s)); - } - - fn rlp_adjust_v_value(&self, v: i32) -> u64 { - match self.transaction_type { - Some(TransactionType::Legacy) | None => { - let chain_id = self.chain_id.unwrap_or(0); - chain_id * 2 + 35 + (v as u64) - } - _ => v as u64, - } - } - - pub fn encode(&self) -> Result, Error> { - match self.transaction_type { - Some(TransactionType::Legacy) | None => { - let stream = self.encode_legacy(); - Ok(stream.out().to_vec()) - } - - Some(TransactionType::EIP1559) => { - let stream = self.encode_eip1559_payload(); - Ok([&[2], stream.as_raw()].concat()) - } - } - } - - pub fn decode_legacy(rlp: &Rlp) -> Result { - Ok(Transaction { - transaction_type: Some(TransactionType::Legacy), - nonce: rlp.val_at(0)?, - gas_price: Some(rlp.val_at(1)?), - gas: rlp.val_at(2)?, - from: None, - to: Some(rlp.val_at(3)?), - value: rlp.val_at(4)?, - data: rlp.val_at(5)?, - chain_id: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - signature: None, - }) - } - - pub fn decode_eip155(rlp: Rlp) -> Result { - Ok(Transaction { - transaction_type: Some(TransactionType::EIP1559), - chain_id: Some(rlp.val_at(0)?), - nonce: rlp.val_at(1)?, - max_priority_fee_per_gas: Some(rlp.val_at(2)?), - max_fee_per_gas: Some(rlp.val_at(3)?), - gas: rlp.val_at(4)?, - from: None, - to: Some(rlp.val_at(5)?), // Convert to Option - value: rlp.val_at(6)?, - data: rlp.val_at(7)?, - signature: None, - gas_price: None, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::chains::ethereum::address::Address; - use secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; - - #[test] - fn test_encode_legacy() { - let tx = Transaction { - transaction_type: Some(TransactionType::Legacy), - nonce: U256::from_dec_str("691").unwrap(), - from: None, - to: Some(Address::try_from("0x4592D8f8D7B001e72Cb26A73e4Fa1806a51aC79d").unwrap()), - gas: U256::from(21000), - gas_price: Some(U256::from_dec_str("2000000000").unwrap()), - value: U256::from_dec_str("1000000000000000000").unwrap(), - data: Vec::new(), - chain_id: Some(4), - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - signature: Some( - RecoverableSignature::from_compact( - &hex::decode("699ff162205967ccbabae13e07cdd4284258d46ec1051a70a51be51ec2bc69f34e6944d508244ea54a62ebf9a72683eeadacb73ad7c373ee542f1998147b220e").unwrap(), - RecoveryId::from_i32(0).unwrap(), - ).unwrap(), - ) - }; - - let raw = tx.encode().unwrap(); - let expected = hex::decode("f86d8202b38477359400825208944592d8f8d7b001e72cb26a73e4fa1806a51ac79d880de0b6b3a7640000802ba0699ff162205967ccbabae13e07cdd4284258d46ec1051a70a51be51ec2bc69f3a04e6944d508244ea54a62ebf9a72683eeadacb73ad7c373ee542f1998147b220e"); - - assert_eq!(raw, expected.unwrap()); - } - - #[test] - fn test_encode_eip1559() { - let tx = Transaction { - transaction_type: Some(TransactionType::EIP1559), - nonce: U256::from_dec_str("241").unwrap(), - from: None, - to: Some(Address::try_from("0xe0e5d2B4EDcC473b988b44b4d13c3972cb6694cb").unwrap()), - gas: U256::from(21000), - gas_price: None, - value: U256::from_dec_str("138078072511761950").unwrap(), - data: Vec::new(), - chain_id: Some(1), - max_fee_per_gas: Some(U256::from_dec_str("91097072255").unwrap()), - max_priority_fee_per_gas: Some(U256::from_dec_str("1000000000").unwrap()), - signature: Some( - RecoverableSignature::from_compact( - &hex::decode("7eb3335f4fd4de25ec3452c08882f28fb098b2eaa37a332941f918d869f5c2ad59b9d4aa997c7fa34f1b167f98a12432bb1a4a35660d723a9c19bb76b4cd025d").unwrap(), - RecoveryId::from_i32(1).unwrap(), - ).unwrap(), - ) - }; - - let raw = tx.encode().unwrap(); - let expected = hex::decode("02f8740181f1843b9aca00851535cf027f82520894e0e5d2b4edcc473b988b44b4d13c3972cb6694cb8801ea8d467f558e1e80c001a07eb3335f4fd4de25ec3452c08882f28fb098b2eaa37a332941f918d869f5c2ada059b9d4aa997c7fa34f1b167f98a12432bb1a4a35660d723a9c19bb76b4cd025d"); - - assert_eq!(raw, expected.unwrap()); - } -} diff --git a/packages/kos-sdk/src/chains/evm20.rs b/packages/kos-sdk/src/chains/evm20.rs deleted file mode 100644 index d3a10b0..0000000 --- a/packages/kos-sdk/src/chains/evm20.rs +++ /dev/null @@ -1,82 +0,0 @@ -use web3::ethabi::Contract; - -// ERC20 contract ABI. This is a simplified ABI with only the `balanceOf` function. -const EVM20_CONTRACT_ABI: &str = r#" -[ - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "name": "balance", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_from", - "type": "address" - }, - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - - ], - "name": "transferFrom", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant":false, - "inputs":[ - { - "name":"_to", - "type":"address" - }, - { - "name":"_value", - "type":"uint256" - } - ], - "name":"transfer", - "outputs":[ - { - "name":"", - "type":"bool" - } - ], - "payable":false, - "stateMutability":"nonpayable", - "type":"function" - } -] -"#; - -pub fn get_contract_evm20() -> Contract { - // Parse the ABI. - Contract::load(EVM20_CONTRACT_ABI.as_bytes()).unwrap() -} diff --git a/packages/kos-sdk/src/chains/klever/address.rs b/packages/kos-sdk/src/chains/klever/address.rs deleted file mode 100644 index a3dfecb..0000000 --- a/packages/kos-sdk/src/chains/klever/address.rs +++ /dev/null @@ -1,157 +0,0 @@ -use kos_crypto::keypair::KeyPair; -use kos_types::error::Error; - -use bech32::{FromBase32, ToBase32}; -use hex::FromHex; -use std::{fmt, str::FromStr}; - -use wasm_bindgen::prelude::*; - -const ADDRESS_LEN: usize = 32; -const ADDRESS_HRP: &str = "klv"; - -#[derive(PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[repr(transparent)] -#[wasm_bindgen(js_name = "KLVAddress")] -pub struct Address([u8; ADDRESS_LEN]); - -impl Address { - /// Address of a public key. - pub fn from_public(public: [u8; 32]) -> Address { - // pubkey to address use first 32 bytes - Address(public) - } - - pub fn public_key(&self) -> [u8; 32] { - self.0 - } - - /// Address of a keypair. - pub fn from_keypair(kp: &KeyPair) -> Address { - Address::from_public(kp.public_key().try_into().unwrap()) - } - - /// As raw 32-byte address. - pub fn as_bytes(&self) -> &[u8] { - &self.0 - } - - /// Address from raw 32-byte. - pub fn from_bytes(raw: &[u8]) -> &Address { - assert!(raw.len() == ADDRESS_LEN); - - unsafe { std::mem::transmute(&raw[0]) } - } - - /// To hex address - pub fn to_hex_address(self) -> String { - hex::encode(self.0) - } -} -impl fmt::Display for Address { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let st = bech32::encode(ADDRESS_HRP, self.0.to_base32(), bech32::Variant::Bech32).unwrap(); - st.fmt(f) - } -} - -impl ::std::fmt::Debug for Address { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_tuple("Address").field(&self.to_string()).finish() - } -} - -impl TryFrom<&[u8]> for Address { - type Error = Error; - - fn try_from(value: &[u8]) -> Result { - if value.len() != ADDRESS_LEN { - Err(Error::InvalidAddress(format!( - "invalid length: {}", - value.len() - ))) - } else { - let mut raw = [0u8; ADDRESS_LEN]; - raw[..ADDRESS_LEN].copy_from_slice(value); - Ok(Address(raw)) - } - } -} - -impl TryFrom> for Address { - type Error = Error; - - fn try_from(value: Vec) -> Result { - Self::try_from(&value[..]) - } -} - -impl TryFrom<&Vec> for Address { - type Error = Error; - - fn try_from(value: &Vec) -> Result { - Self::try_from(&value[..]) - } -} - -impl TryFrom<&str> for Address { - type Error = Error; - - fn try_from(value: &str) -> Result { - Address::from_str(value) - } -} - -impl FromHex for Address { - type Error = Error; - - fn from_hex>(hex: T) -> Result { - Address::try_from(hex.as_ref()) - } -} - -impl FromStr for Address { - type Err = Error; - - fn from_str(s: &str) -> Result - where - Self: Sized, - { - if s.starts_with(ADDRESS_HRP) { - return bech32::decode(s) - .map_err(|e| Error::InvalidAddress(e.to_string())) - .and_then(|(_, data, _)| { - Vec::from_base32(&data) - .map_err(|e| Error::InvalidAddress(e.to_string())) - .and_then(Address::try_from) - }); - } - - if s.len() == 64 { - return Vec::from_hex(s) - .map_err(|e| Error::InvalidAddress(e.to_string())) - .and_then(Address::try_from); - } - - if s.len() == 66 && (s.starts_with("0x") || s.starts_with("0X")) { - return Vec::from_hex(&s.as_bytes()[2..]) - .map_err(|e| Error::InvalidAddress(e.to_string())) - .and_then(Address::try_from); - } - - if s == "_" || s == "0x0" || s == "/0" { - return Ok(Address([0u8; ADDRESS_LEN])); - } - - eprintln!("len={} prefix={:x}", s.len(), s.as_bytes()[0]); - Err(Error::InvalidAddress("invalid klever address".to_string())) - } -} - -// NOTE: AsRef<[u8]> implies ToHex -impl AsRef<[u8]> for Address { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} diff --git a/packages/kos-sdk/src/chains/klever/klever_test.rs b/packages/kos-sdk/src/chains/klever/klever_test.rs deleted file mode 100644 index 3ab6849..0000000 --- a/packages/kos-sdk/src/chains/klever/klever_test.rs +++ /dev/null @@ -1,250 +0,0 @@ -#[cfg(test)] -mod klever_test { - use dotenvy; - use std::assert_eq; - use std::str; - - use crate::chains::klever::*; - use crate::models::PathOptions; - use crate::models::SendOptions; - use hex::FromHex; - use kos_types::Bytes32; - use std::sync::Once; - - const DEFAULT_KAPP_FEE: i64 = 1000000; - const DEFAULT_BANDWIDTH_FEE: i64 = 2000000; - - const DEFAULT_PRIVATE_KEY: &str = - "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d"; - const DEFAULT_ADDRESS: &str = "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy"; - - static _INIT: Once = Once::new(); - - fn _init() { - _INIT.call_once(|| { - dotenvy::from_filename(".env.nodes").ok(); - }); - } - - fn get_default_secret() -> KeyPair { - let b = Bytes32::from_hex(DEFAULT_PRIVATE_KEY).unwrap(); - let kp = Ed25519KeyPair::new(b.into()); - KeyPair::from(kp) - } - - #[test] - fn test_get_balance() { - let balance = tokio_test::block_on(KLV::get_balance( - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - Some("KLV".to_string()), - None, - )) - .unwrap(); - println!("balance: {}", balance.to_string()); - println!("balance: {}", balance.with_precision(6)); - - assert!(balance > BigNumber::from(0)); - } - - #[test] - fn test_broadcast() { - let klv_tx: kos_proto::klever::Transaction = serde_json::from_str( - "{\"RawData\":{\"Nonce\":13,\"Sender\":\"5BsyOlcf2VXgnNQWYP9EZcP0RpPIfy+upKD8QIcnyOo=\",\"Contract\":[{\"Parameter\":{\"type_url\":\"type.googleapis.com/proto.TransferContract\",\"value\":\"CiAysyg0Aj8xj/rr5XGU6iJ+ATI29mnRHS0W0BrC1vz0CBIDS0xWGAo=\"}}],\"KAppFee\":500000,\"BandwidthFee\":1000000,\"Version\":1,\"ChainID\":\"MTAwNDIw\"},\"Signature\":[\"O7C2MjTUMauWl8kfeJjgwDnFLkiDqY2U23s6AWzTstut63FnZeKC3EcxY0DiAgzf5PQ1+jeC2dIx3+pP7BHlBQ==\"]}", - ).unwrap(); - - let to_broadcast = crate::models::Transaction { - chain: crate::chain::Chain::KLV, - sender: "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy".to_string(), - hash: Hash::new("0x0000000000000000000000000000000000000000000000000000000000000000") - .unwrap(), - data: Some(TransactionRaw::Klever(klv_tx)), - signature: None, - }; - - let result = tokio_test::block_on(KLV::broadcast(to_broadcast, None)); - - assert!(result.is_err()); - assert!(result - .unwrap_err() - .to_string() - .contains("invalid transaction fees")) - } - - #[test] - fn test_send() { - let result = tokio_test::block_on(KLV::send( - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy".to_string(), - "klv1x2ejsdqz8uccl7htu4cef63z0cqnydhkd8g36tgk6qdv94hu7syqms3spm".to_string(), - BigNumber::from(10), - None, - None, - )); - - assert!(result.is_ok()); - match result.unwrap().data { - Some(TransactionRaw::Klever(tx)) => { - let raw = &tx.raw_data.unwrap(); - assert!(raw.nonce > 0); - assert_eq!(raw.k_app_fee, DEFAULT_KAPP_FEE); - assert_eq!(raw.bandwidth_fee, DEFAULT_BANDWIDTH_FEE); - assert!(raw.kda_fee.is_none()); - - assert_eq!(raw.contract.len(), 1); - let c: kos_proto::klever::TransferContract = - kos_proto::unpack_from_option_any(&raw.contract.get(0).unwrap().parameter) - .unwrap(); - - assert_eq!(c.amount, 10); - assert_eq!(c.asset_id.len(), 0); - } - _ => assert!(false), - } - } - - #[test] - fn test_send_kda() { - let kda = "TAS-1I86".to_string(); - // create KLV send options - let klv_options = kos_proto::options::KLVOptions { - kda: Some(kda.clone()), - ..Default::default() - }; - - let options = SendOptions::new_klever_send_options(klv_options); - - let result = tokio_test::block_on(KLV::send( - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy".to_string(), - "klv1x2ejsdqz8uccl7htu4cef63z0cqnydhkd8g36tgk6qdv94hu7syqms3spm".to_string(), - BigNumber::from(10), - Some(options), - None, - )); - - assert!(result.is_ok()); - match result.unwrap().data { - Some(TransactionRaw::Klever(tx)) => { - let raw = &tx.raw_data.unwrap(); - assert!(raw.nonce > 0); - assert_eq!(raw.k_app_fee, DEFAULT_KAPP_FEE); - assert_eq!(raw.bandwidth_fee, DEFAULT_BANDWIDTH_FEE); - assert!(raw.kda_fee.is_none()); - - assert_eq!(raw.contract.len(), 1); - let c: kos_proto::klever::TransferContract = - kos_proto::unpack_from_option_any(&raw.contract.get(0).unwrap().parameter) - .unwrap(); - - assert_eq!(c.amount, 10); - assert_eq!(str::from_utf8(&c.asset_id).unwrap(), kda); - } - _ => assert!(false), - } - } - - #[test] - fn test_address_from_mnemonic() { - let path = KLV::get_path(&PathOptions::new(0)).unwrap(); - let kp = KLV::keypair_from_mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", &path, None).unwrap(); - let address = KLV::get_address_from_keypair(&kp).unwrap(); - - assert_eq!(DEFAULT_ADDRESS, address); - } - - #[test] - fn test_address_from_private_key() { - let address = KLV::get_address_from_keypair(&get_default_secret()).unwrap(); - - assert_eq!(DEFAULT_ADDRESS, address.to_string()); - } - - #[test] - fn test_sign_message() { - let message = "Hello World"; - let kp = get_default_secret(); - let signature = KLV::sign_message(message.as_bytes(), &kp).unwrap(); - assert_eq!( - "38b3fd1e4d5a34291dddb2c6ca66e857c1696f3160981ca6abb8a78087f86b6163314cadd16179239d38201ba91c97aa201b7f38ecfff50c7f0448da67bf5a05", - hex::encode(signature) - ); - } - - #[test] - fn test_verify_message() { - let message = "Hello World"; - let kp = get_default_secret(); - let signature = hex::decode("38b3fd1e4d5a34291dddb2c6ca66e857c1696f3160981ca6abb8a78087f86b6163314cadd16179239d38201ba91c97aa201b7f38ecfff50c7f0448da67bf5a05").unwrap() ; - let address = KLV::get_address_from_keypair(&kp).unwrap(); - let result = KLV::verify_message_signature(message.as_bytes(), &signature, &address); - - assert!(result.is_ok()); - assert_eq!(result.unwrap(), true); - } - - #[test] - fn test_tx_from_raw() { - let raw = "{\"RawData\":{\"BandwidthFee\":1000000,\"ChainID\":\"MTAwNDIw\",\"Contract\":[{\"Parameter\":{\"type_url\":\"type.googleapis.com/proto.TransferContract\",\"value\":\"CiAysyg0Aj8xj/rr5XGU6iJ+ATI29mnRHS0W0BrC1vz0CBgK\"}}],\"KAppFee\":500000,\"Nonce\":39,\"Sender\":\"5BsyOlcf2VXgnNQWYP9EZcP0RpPIfy+upKD8QIcnyOo=\",\"Version\":1}}"; - - let tx = KLV::tx_from_raw(raw); - assert!(tx.is_ok()); - let tx = tx.unwrap(); - assert_eq!(tx.chain, crate::chain::Chain::KLV); - assert_eq!( - tx.sender, - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy" - ); - assert_eq!( - tx.hash.to_string(), - "1e61c51f0d230f4855dc9b8935b47b9019887baf02be75d364a4068083833c15" - ); - match tx.data.unwrap() { - TransactionRaw::Klever(klv_tx) => { - let raw = &klv_tx.raw_data.unwrap(); - assert_eq!(raw.nonce, 39); - assert_eq!(raw.contract.len(), 1); - assert_eq!(raw.k_app_fee, 500000); - assert_eq!(raw.bandwidth_fee, 1000000); - - let c: kos_proto::klever::TransferContract = - kos_proto::unpack_from_option_any(&raw.contract.get(0).unwrap().parameter) - .unwrap(); - - assert_eq!(c.amount, 10); - } - _ => assert!(false), - } - } - - #[test] - fn test_validate_address_ok() { - let list = [ - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "klv1x2ejsdqz8uccl7htu4cef63z0cqnydhkd8g36tgk6qdv94hu7syqms3spm", - ]; - - for addr in list.iter() { - let result = KLV::validate_address(addr, None); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), true, "address: {}", addr); - } - } - - #[test] - fn test_validate_address_fail() { - let list = [ - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlaz", - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy1", - "klv2usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "klvusdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "bnb1ztx5rf7jx28k3xnemftcq3kfgm3yhfvfmhm456", - "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", - ]; - - for addr in list.iter() { - let result = KLV::validate_address(addr, None); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), false, "address: {}", addr); - } - } -} diff --git a/packages/kos-sdk/src/chains/klever/mod.rs b/packages/kos-sdk/src/chains/klever/mod.rs deleted file mode 100644 index 00e9229..0000000 --- a/packages/kos-sdk/src/chains/klever/mod.rs +++ /dev/null @@ -1,303 +0,0 @@ -pub mod address; -pub mod models; -pub mod requests; -use crate::{ - chain::BaseChain, - models::{AddressOptions, BroadcastResult, PathOptions, Transaction, TransactionRaw}, -}; -use kos_crypto::{ed25519::Ed25519KeyPair, keypair::KeyPair}; -use kos_types::{error::Error, hash::Hash, number::BigNumber}; - -use pbjson::private::base64; -use std::str::FromStr; -use wasm_bindgen::prelude::*; - -#[derive(Debug, Copy, Clone)] -#[wasm_bindgen()] -pub struct KLV {} - -pub const SIGN_PREFIX: &[u8; 24] = b"\x17Klever Signed Message:\n"; -pub const BIP44_PATH: u32 = 690; -pub const BASE_CHAIN: BaseChain = BaseChain { - name: "Klever", - symbol: "KLV", - precision: 6, - chain_code: 38, -}; - -#[wasm_bindgen] -impl KLV { - #[wasm_bindgen(js_name = "baseChain")] - pub fn base_chain() -> BaseChain { - BASE_CHAIN - } - - #[wasm_bindgen(js_name = "random")] - pub fn random() -> Result { - let mut rng = rand::thread_rng(); - let kp = Ed25519KeyPair::random(&mut rng); - Ok(KeyPair::new_ed25519(kp)) - } - - #[wasm_bindgen(js_name = "keypairFromBytes")] - pub fn keypair_from_bytes(private_key: &[u8]) -> Result { - // copy to fixed length array - let mut pk_slice = [0u8; 32]; - pk_slice.copy_from_slice(private_key); - - let kp = Ed25519KeyPair::new(pk_slice); - Ok(KeyPair::new_ed25519(kp)) - } - - #[wasm_bindgen(js_name = "keypairFromMnemonic")] - pub fn keypair_from_mnemonic( - mnemonic: &str, - path: &str, - password: Option, - ) -> Result { - let kp = Ed25519KeyPair::new_from_mnemonic_phrase_with_path( - mnemonic, - path, - password.as_deref(), - )?; - - Ok(KeyPair::new_ed25519(kp)) - } - - #[wasm_bindgen(js_name = "getAddressFromKeyPair")] - pub fn get_address_from_keypair(keypair: &KeyPair) -> Result { - Ok(address::Address::from_keypair(keypair).to_string()) - } - - #[wasm_bindgen(js_name = "getPath")] - pub fn get_path(options: &PathOptions) -> Result { - Ok(format!("m/44'/{}'/0'/0'/{}'", BIP44_PATH, options.index)) - } - - #[wasm_bindgen(js_name = "signDigest")] - /// Sign digest data with the private key. - pub fn sign_digest(digest: &[u8], keypair: &KeyPair) -> Result, Error> { - let sig = keypair.sign_digest(digest); - Ok(sig) - } - - #[wasm_bindgen(js_name = "verifyDigest")] - /// Verify Message signature - pub fn verify_digest(digest: &[u8], signature: &[u8], address: &str) -> Result { - let addr = address::Address::from_str(address)?; - let kp = Ed25519KeyPair::default(); - - if kp.verify_digest(digest, signature, &addr.public_key()) { - Ok(true) - } else { - Err(Error::InvalidSignature("message verification fail")) - } - } - - #[wasm_bindgen(js_name = "sign")] - /// Hash and Sign data with the private key. - pub fn sign(tx: Transaction, keypair: &KeyPair) -> Result { - match tx.data { - Some(TransactionRaw::Klever(klv_tx)) => { - let mut new_tx = klv_tx.clone(); - let digest = KLV::hash_transaction(&klv_tx)?; - let sig = KLV::sign_digest(digest.as_slice(), keypair)?; - - let signature = base64::encode(sig.clone()); - - new_tx.signature.push(sig); - let result = Transaction { - chain: tx.chain, - sender: tx.sender, - hash: Hash::from_vec(digest)?, - data: Some(TransactionRaw::Klever(new_tx)), - signature: Some(signature), - }; - - Ok(result) - } - _ => Err(Error::InvalidMessage( - "not a klever transaction".to_string(), - )), - } - } - - fn hash_transaction(tx: &kos_proto::klever::Transaction) -> Result, Error> { - if let Some(raw_data) = &tx.raw_data { - let bytes = kos_proto::write_message(raw_data); - KLV::hash(&bytes) - } else { - Err(Error::InvalidTransaction("klv raw".to_string())) - } - } - - #[wasm_bindgen(js_name = "hash")] - /// hash digest - pub fn hash(message: &[u8]) -> Result, Error> { - let digest = kos_crypto::hash::blake2b256(message); - Ok(digest.to_vec()) - } - - #[wasm_bindgen(js_name = "messageHash")] - /// Append prefix and hash the message - pub fn message_hash(message: &[u8]) -> Result, Error> { - let to_sign = [SIGN_PREFIX, message.len().to_string().as_bytes(), message].concat(); - - let digest = kos_crypto::hash::keccak256(&to_sign); - Ok(digest.to_vec()) - } - - #[wasm_bindgen(js_name = "signMessage")] - /// Sign Message with the private key. - pub fn sign_message(message: &[u8], keypair: &KeyPair) -> Result, Error> { - let m = KLV::message_hash(message)?; - KLV::sign_digest(&m, keypair) - } - - #[wasm_bindgen(js_name = "verifyMessageSignature")] - /// Verify Message signature - pub fn verify_message_signature( - message: &[u8], - signature: &[u8], - address: &str, - ) -> Result { - let m = KLV::message_hash(message)?; - KLV::verify_digest(&m, signature, address) - } - - #[wasm_bindgen(js_name = "getBalance")] - /// Get balance of address and token - /// If token is None, it will return balance of native token - /// If token is Some, it will return balance of token - /// If node_url is None, it will use default node url - pub async fn get_balance( - address: &str, - token: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("KLV")); - let acc = requests::get_account(node.as_str(), address).await?; - - Ok(match token { - Some(key) if key != "KLV" => match acc.assets.unwrap().get(&key) { - Some(asset) => BigNumber::from(asset.balance), - None => BigNumber::from(0), - }, - _ => BigNumber::from(acc.balance.unwrap_or(0)), - }) - } - - #[wasm_bindgen(js_name = "broadcast")] - pub async fn broadcast( - tx: crate::models::Transaction, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("KLV")); - let raw = tx - .data - .clone() - .ok_or_else(|| Error::ReqwestError("Missing transaction data".into()))?; - - let result = requests::broadcast(node.as_str(), raw.try_into()?).await?; - - match result.get("data").and_then(|v| v.as_object()) { - Some(v) => { - let tx_hash_str = v - .get("txHash") - .and_then(|v| v.as_str()) - .ok_or_else(|| Error::ReqwestError("Missing transaction hash".into()))?; - - let tx_hash = Hash::new(tx_hash_str)?; - - Ok(BroadcastResult::new(crate::models::Transaction { - chain: tx.chain, - sender: tx.sender, - hash: tx_hash, - data: tx.data, - signature: None, - })) - } - None => match result.get("error") { - Some(err) => Err(Error::ReqwestError(err.to_string())), - None => Err(Error::ReqwestError("Unknown error".into())), - }, - } - } - - fn get_options(options: Option) -> kos_proto::options::KLVOptions { - match options.and_then(|opt| opt.data) { - Some(crate::models::Options::Klever(op)) => op, - _ => kos_proto::options::KLVOptions::default(), - } - } - - /// create a send transaction network - #[wasm_bindgen(js_name = "send")] - pub async fn send( - sender: String, - receiver: String, - amount: BigNumber, - options: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("KLV")); - let options = KLV::get_options(options); - - let contract = models::TransferTXRequest { - receiver, - amount: amount.to_i64(), - kda: options.kda.clone(), - kda_royalties: options.kda_royalties, - }; - - requests::make_request(sender, contract, &options, node.as_str()).await - } - - #[wasm_bindgen(js_name = "validateAddress")] - pub fn validate_address( - address: &str, - _options: Option, - ) -> Result { - let addr = address::Address::from_str(address); - if addr.is_err() { - return Ok(false); - } - - if addr.unwrap().to_string() == address { - return Ok(true); - } - - Ok(false) - } -} - -#[wasm_bindgen] -impl KLV { - /// import raw TX rom JSValue to Transaction model - #[wasm_bindgen(js_name = "txFromRaw")] - pub fn tx_from_raw(raw: &str) -> Result { - // build expected send result - let tx: kos_proto::klever::Transaction = serde_json::from_str(raw)?; - - // unwrap raw_data - let data = tx - .raw_data - .clone() - .ok_or_else(|| Error::InvalidTransaction("no raw TX found".to_string()))?; - - let sender = address::Address::from_bytes(&data.sender); - let digest = KLV::hash_transaction(&tx)?; - let signature = tx.signature.first().map(|sig| base64::encode(sig.clone())); - - Ok(crate::models::Transaction { - chain: crate::chain::Chain::KLV, - sender: sender.to_string(), - hash: Hash::from_slice(&digest)?, - data: Some(TransactionRaw::Klever(tx)), - signature, - }) - } -} - -#[cfg(test)] -mod klever_test; diff --git a/packages/kos-sdk/src/chains/klever/models.rs b/packages/kos-sdk/src/chains/klever/models.rs deleted file mode 100644 index 826f06c..0000000 --- a/packages/kos-sdk/src/chains/klever/models.rs +++ /dev/null @@ -1,148 +0,0 @@ -use kos_types::error::Error; - -use serde::{Deserialize, Serialize}; -use serde_json; -use std::{ - collections, - fmt::{self, Display, Formatter}, -}; -use wasm_bindgen::prelude::*; - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct ResultAccount { - pub data: DataAccount, -} - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct DataAccount { - pub account: Account, -} - -impl std::fmt::Display for ResultAccount { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match serde_json::to_string(&self) { - Ok(json_str) => write!(f, "({})", json_str), - Err(e) => write!(f, "{}", e), - } - } -} - -#[derive(Deserialize, Serialize, Clone, Debug)] -#[serde(rename_all = "camelCase")] -#[wasm_bindgen(getter_with_clone)] -pub struct Asset { - pub asset_id: String, - pub collection: String, - pub asset_name: String, - pub asset_type: u8, - pub balance: u64, - pub precision: u8, - pub frozen_balance: u64, - pub unfrozen_balance: u64, -} - -#[derive(Deserialize, Serialize, Clone, Debug)] -#[serde(rename_all = "camelCase")] -#[wasm_bindgen(getter_with_clone)] -pub struct Account { - // pub address: String, - #[serde(rename = "Nonce")] - pub nonce: Option, - #[serde(rename = "Balance")] - pub balance: Option, - // pub frozen_balance: u64, - #[serde(rename = "Allowance")] - pub allowance: Option, - #[wasm_bindgen(skip)] - pub assets: Option>, -} - -pub struct TransactionResult { - pub tx_hash: String, - pub tx: kos_proto::klever::Transaction, -} - -impl TryFrom for TransactionResult { - type Error = kos_types::error::Error; - - fn try_from(value: serde_json::value::Value) -> Result { - if let Some(v) = value.get("data") { - if let Some(obj) = v.as_object() { - let hash = obj.get("txHash").unwrap().as_str().unwrap(); - let result = obj.get("result").unwrap().to_string(); - let tx: kos_proto::klever::Transaction = serde_json::from_str(&result)?; - return Ok(Self { - tx_hash: hash.to_string(), - tx, - }); - } - } - - match value.get("error") { - Some(err) => Err(Error::ReqwestError(err.to_string())), - None => Err(Error::ReqwestError("Unknown error".to_string())), - } - } -} - -// SendTXRequest - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct SendTXRequest { - #[serde(rename = "type")] - pub tx_type: u32, - #[serde(rename = "sender")] - pub sender: String, - #[serde(rename = "nonce")] - pub nonce: Option, - #[serde(rename = "permID")] - pub perm_id: Option, - #[serde(rename = "data")] - pub data: Option>>, - #[serde(rename = "contract")] - pub contract: Option, - #[serde(rename = "contracts")] - pub contracts: Option>, - #[serde(rename = "kdaFee")] - pub kda_fee: Option, -} - -impl SendTXRequest { - pub fn set_contract(&mut self, contract: impl Serialize) -> Result<(), Error> { - let data = serde_json::to_value(&contract)?; - self.contract = Some(data); - Ok(()) - } - - pub fn to_vec(&self) -> Result, Error> { - let data = serde_json::to_vec(&self).map_err(Error::from)?; - Ok(data) - } -} - -impl Display for SendTXRequest { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) - } -} - -#[derive(Deserialize, Serialize, Clone, Debug)] -// TransferTXRequest - -pub struct TransferTXRequest { - #[serde(rename = "receiver")] - pub receiver: String, - #[serde(rename = "amount")] - pub amount: i64, - #[serde(rename = "kda")] - pub kda: Option, - #[serde(rename = "kdaRoyalties")] - pub kda_royalties: Option, -} - -impl std::fmt::Display for TransferTXRequest { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match serde_json::to_string(&self) { - Ok(json_str) => write!(f, "({})", json_str), - Err(e) => write!(f, "{}", e), - } - } -} diff --git a/packages/kos-sdk/src/chains/klever/requests.rs b/packages/kos-sdk/src/chains/klever/requests.rs deleted file mode 100644 index 318625f..0000000 --- a/packages/kos-sdk/src/chains/klever/requests.rs +++ /dev/null @@ -1,89 +0,0 @@ -use super::models; -use crate::{chain, models::TransactionRaw, utils}; -use kos_types::{error::Error, hash::Hash}; -use serde::Serialize; - -pub async fn get_account(node_url: &str, address: &str) -> Result { - let url = format!("{}/address/{}", node_url, address); - - utils::http_get::(url) - .await - .map(|r| r.data.account) -} - -pub async fn broadcast( - node_url: &str, - tx: kos_proto::klever::Transaction, -) -> Result { - let url = format!("{}/transaction/broadcast", node_url); - log::debug!( - "Broadcasting to {}\nData: {} ", - url, - serde_json::to_string(&tx)?, - ); - - // adjust to kleverchain format - let data = format!( - "{{\"tx\": {}}}", - serde_json::to_string(&tx)?.replace("typeUrl", "type_url"), - ) - .as_bytes() - .to_vec(); - - utils::http_post::(url, &data).await -} - -pub async fn send_request( - node_url: &str, - request: &mut models::SendTXRequest, -) -> Result { - let url = format!("{}/transaction/send", node_url); - - if request.nonce.unwrap_or(0) == 0 { - let account = get_account(node_url, &request.sender).await?; - request.nonce = account.nonce; - } - - log::debug!("Send request: {}", request.to_string()); - - // to json - let data = request.to_vec().unwrap(); - - utils::http_post::(url, &data).await -} - -pub async fn make_request( - sender: String, - contract: impl Serialize, - options: &kos_proto::options::KLVOptions, - node: &str, -) -> Result { - let data = options.memo.clone().map(|memo| { - memo.into_iter() - .map(|m| m.into_bytes()) - .collect::>>() - }); - - let mut tx_request = models::SendTXRequest { - tx_type: 0, - sender: sender.to_owned(), - nonce: options.nonce, - perm_id: None, - data, - contract: None, - contracts: None, - kda_fee: options.kda_fee.clone(), - }; - - tx_request.set_contract(contract)?; - let result = send_request(node, &mut tx_request).await?; - let tx = models::TransactionResult::try_from(result)?; - - Ok(crate::models::Transaction { - chain: chain::Chain::KLV, - sender, - hash: Hash::new(&tx.tx_hash)?, - data: Some(TransactionRaw::Klever(tx.tx)), - signature: None, - }) -} diff --git a/packages/kos-sdk/src/chains/mod.rs b/packages/kos-sdk/src/chains/mod.rs deleted file mode 100644 index f6bc78f..0000000 --- a/packages/kos-sdk/src/chains/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod bitcoin; -mod default; -mod ethereum; -mod evm20; -mod klever; -mod polygon; -mod tron; - -pub use self::bitcoin::BTC; -pub use default::NONE; -pub use ethereum::ETH; -pub use klever::KLV; -pub use polygon::MATIC; -pub use tron::TRX; - -pub use ethereum::transaction::Transaction as ETHTransaction; -pub use polygon::Transaction as MATICTransaction; - -pub use self::bitcoin::transaction::BTCTransaction; diff --git a/packages/kos-sdk/src/chains/polygon/mod.rs b/packages/kos-sdk/src/chains/polygon/mod.rs deleted file mode 100644 index a7fac20..0000000 --- a/packages/kos-sdk/src/chains/polygon/mod.rs +++ /dev/null @@ -1,357 +0,0 @@ -use super::ETHTransaction; -use super::ETH; - -use crate::chain::{BaseChain, Chain}; -use crate::models::PathOptions; -use crate::models::{self, BroadcastResult, TransactionRaw}; - -use kos_crypto::keypair::KeyPair; -use kos_types::error::Error; -use kos_types::number::BigNumber; - -use serde::Serialize; -use wasm_bindgen::prelude::*; - -#[derive(Debug, Copy, Clone)] -#[wasm_bindgen] -pub struct MATIC {} - -pub const CHAIN_ID: u64 = 137; - -pub const BASE_CHAIN: BaseChain = BaseChain { - name: "Polygon", - symbol: "MATIC", - precision: 18, - chain_code: 28, -}; - -#[derive(Serialize, Clone, Debug, PartialEq)] -pub struct Transaction { - pub eth: ETHTransaction, -} - -fn convert_options( - options: Option, -) -> Option { - let mut data = match options.and_then(|opt| opt.data) { - Some(crate::models::Options::Polygon(op)) => op.eth, - _ => kos_proto::options::ETHOptions::default(), - }; - - if data.chain_id.is_none() { - data.chain_id = Some(CHAIN_ID) - }; - - Some(crate::models::SendOptions::new( - crate::models::Options::Ethereum(data), - )) -} - -fn convert_transaction(tx: models::Transaction) -> Result { - match tx.data.clone() { - Some(TransactionRaw::Polygon(tx_polygon)) => { - Ok(tx.new_data(Chain::MATIC, TransactionRaw::Ethereum(tx_polygon.eth))) - } - Some(TransactionRaw::Ethereum(tx_eth)) => Ok(tx.new_data( - Chain::MATIC, - TransactionRaw::Polygon(Transaction { eth: tx_eth }), - )), - _ => Err(Error::InvalidTransaction( - "Invalid Transaction Type".to_string(), - )), - } -} - -#[wasm_bindgen] -impl MATIC { - #[wasm_bindgen(js_name = "baseChain")] - pub fn base_chain() -> BaseChain { - BASE_CHAIN - } - - #[wasm_bindgen(js_name = "random")] - pub fn random() -> Result { - ETH::random() - } - - #[wasm_bindgen(js_name = "keypairFromBytes")] - pub fn keypair_from_bytes(private_key: &[u8]) -> Result { - ETH::keypair_from_bytes(private_key) - } - - #[wasm_bindgen(js_name = "keypairFromMnemonic")] - pub fn keypair_from_mnemonic( - mnemonic: &str, - path: &str, - password: Option, - ) -> Result { - ETH::keypair_from_mnemonic(mnemonic, path, password) - } - - #[wasm_bindgen(js_name = "getAddressFromKeyPair")] - pub fn get_address_from_keypair(kp: &KeyPair) -> Result { - ETH::get_address_from_keypair(kp) - } - - #[wasm_bindgen(js_name = "getPath")] - pub fn get_path(options: &PathOptions) -> Result { - ETH::get_path(options) - } - - #[wasm_bindgen(js_name = "signDigest")] - /// Sign digest data with the private key. - pub fn sign_digest(digest: &[u8], keypair: &KeyPair) -> Result, Error> { - ETH::sign_digest(digest, keypair) - } - - #[wasm_bindgen(js_name = "verifyDigest")] - /// Verify Message signature - pub fn verify_digest(digest: &[u8], signature: &[u8], address: &str) -> Result { - ETH::verify_digest(digest, signature, address) - } - - #[wasm_bindgen(js_name = "sign")] - /// Hash and Sign data with the private key. - pub fn sign(tx: models::Transaction, keypair: &KeyPair) -> Result { - let result = ETH::sign(convert_transaction(tx)?, keypair); - - // convert back to polygon tx enum - convert_transaction(result?) - } - - #[wasm_bindgen(js_name = "hash")] - /// hash digest - pub fn hash(message: &[u8]) -> Result, Error> { - ETH::hash(message) - } - - #[wasm_bindgen(js_name = "messageHash")] - /// Append prefix and hash the message - pub fn message_hash(message: &[u8]) -> Result, Error> { - ETH::message_hash(message) - } - - #[wasm_bindgen(js_name = "signMessage")] - /// Sign Message with the private key. - pub fn sign_message(message: &[u8], keypair: &KeyPair) -> Result, Error> { - ETH::sign_message(message, keypair) - } - - #[wasm_bindgen(js_name = "verifyMessageSignature")] - /// Verify Message signature - pub fn verify_message_signature( - message: &[u8], - signature: &[u8], - address: &str, - ) -> Result { - ETH::verify_message_signature(message, signature, address) - } - - #[wasm_bindgen(js_name = "getBalance")] - pub async fn get_balance( - address: &str, - token: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("MATIC")); - ETH::get_balance(address, token, Some(node)).await - } - - #[wasm_bindgen(js_name = "getGasPrice")] - pub async fn gas_price(node_url: Option) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("MATIC")); - ETH::gas_price(Some(node)).await - } - - pub async fn send( - sender: String, - receiver: String, - amount: BigNumber, - options: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("MATIC")); - let result = ETH::send( - sender, - receiver, - amount, - convert_options(options), - Some(node), - ) - .await?; - convert_transaction(result) - } - - #[wasm_bindgen(js_name = "broadcast")] - pub async fn broadcast( - tx: models::Transaction, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("MATIC")); - let result = ETH::broadcast(tx, Some(node)).await?; - Ok(BroadcastResult::new(convert_transaction(result.tx)?)) - } - - #[wasm_bindgen(js_name = "validateAddress")] - pub fn validate_address( - address: &str, - option: Option, - ) -> Result { - ETH::validate_address(address, option) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use hex::FromHex; - use kos_crypto::secp256k1::Secp256k1KeyPair; - use kos_types::Bytes32; - - const DEFAULT_PRIVATE_KEY: &str = - "1ab42cc412b618bdea3a599e3c9bae199ebf030895b039e9db1e30dafb12b727"; - const DEFAULT_ADDRESS: &str = "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"; - const DEFAULT_MNEMONIC: &str = - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - - fn init() { - std::env::set_var("NODE_MATIC", "https://matic.node.klever.io"); - } - - fn get_default_secret() -> KeyPair { - let b = Bytes32::from_hex(DEFAULT_PRIVATE_KEY).unwrap(); - let kp = Secp256k1KeyPair::new(b.into()); - KeyPair::new_secp256k1(kp) - } - - #[test] - fn test_address_from_private_key() { - let address = MATIC::get_address_from_keypair(&get_default_secret()).unwrap(); - - assert_eq!(DEFAULT_ADDRESS, address); - } - - #[test] - fn test_validate_bip44() { - let v = vec![ - (0, "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"), - (1, "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0"), - (2, "0xb6716976A3ebe8D39aCEB04372f22Ff8e6802D7A"), - (3, "0xF3f50213C1d2e255e4B2bAD430F8A38EEF8D718E"), - (4, "0x51cA8ff9f1C0a99f88E86B8112eA3237F55374cA"), - ]; - - for (index, expected_addr) in v { - let path = MATIC::get_path(&PathOptions::new(index)).unwrap(); - let kp = MATIC::keypair_from_mnemonic(DEFAULT_MNEMONIC, &path, None).unwrap(); - let addr = MATIC::get_address_from_keypair(&kp).unwrap(); - - assert_eq!(expected_addr, addr); - } - } - - #[test] - fn test_send_and_sign() { - let options = models::SendOptions { - data: Some(models::Options::Polygon(kos_proto::options::MATICOptions { - eth: kos_proto::options::ETHOptions { - chain_id: Some(CHAIN_ID), - nonce: Some(100), - max_fee_per_gas: Some("1000000000".try_into().unwrap()), - max_priority_fee_per_gas: Some("1000000000".try_into().unwrap()), - ..Default::default() - }, - })), - }; - - let tx = tokio_test::block_on(MATIC::send( - DEFAULT_ADDRESS.to_string(), - "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0".to_string(), - "1000".try_into().unwrap(), - Some(options), - None, - )) - .unwrap(); - - assert_eq!( - tx.hash.to_string(), - "ce4fdd2a2d7767a4d4df8533bd1218a51755e6d15f814a20d3fa834e27c8602c" - ); - - let signed = MATIC::sign(tx, &get_default_secret()); - assert!(signed.is_ok()); - assert_eq!( - signed.unwrap().hash.to_string(), - "df531bd12423bc8c2a047c01a30b1513a41b47e81d75904a703bdb1d8962bd22" - ); - } - - #[test] - fn test_send_erc20() { - std::env::set_var("NODE_MATIC", "https://matic.node.klever.io"); - let options = models::SendOptions { - data: Some(models::Options::Polygon(kos_proto::options::MATICOptions { - eth: kos_proto::options::ETHOptions { - chain_id: Some(CHAIN_ID), - nonce: Some(100), - token: Some("0xc12d1c73ee7dc3615ba4e37e4abfdbddfa38907e".to_string()), - gas_limit: Some(1000000.into()), - gas_price: Some(0.into()), - max_fee_per_gas: Some(0.into()), - max_priority_fee_per_gas: Some(0.into()), - ..Default::default() - }, - })), - }; - - let tx = tokio_test::block_on(MATIC::send( - DEFAULT_ADDRESS.to_string(), - "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0".to_string(), - "1000".try_into().unwrap(), - Some(options), - None, - )) - .unwrap(); - assert_eq!( - tx.hash.to_string(), - "531d345f8aa0a5ff3625d758e53ff601fa8e2a060bc7a3a1b83e50e927fbb5f5" - ); - - let polygon_tx = match tx.data.clone() { - Some(models::TransactionRaw::Polygon(tx)) => tx, - _ => panic!("invalid tx"), - }; - - assert_eq!(polygon_tx.eth.value.to_string(), "0"); - assert_eq!(hex::encode(polygon_tx.eth.data), "a9059cbb0000000000000000000000006fac4d18c912343bf86fa7049364dd4e424ab9c000000000000000000000000000000000000000000000000000000000000003e8"); - - let signed = MATIC::sign(tx, &get_default_secret()); - assert!(signed.is_ok()); - assert_eq!( - signed.unwrap().hash.to_string(), - "1b570ed15ef11db9860618fce69ac0a6b50cef5403cefa94c86483103ae2bde3" - ); - } - - #[test] - fn test_get_balance() { - init(); - let balance = - tokio_test::block_on(MATIC::get_balance(DEFAULT_ADDRESS, None, None)).unwrap(); - - assert!(balance.to_i64() >= 0); - } - - #[test] - fn test_get_balance_erc20() { - init(); - let balance = tokio_test::block_on(MATIC::get_balance( - DEFAULT_ADDRESS, - Some("0x19a935cbaaa4099072479d521962588d7857d717".to_string()), - None, - )); - assert!(balance.is_ok()); - - assert_eq!(balance.unwrap().to_string(), "1000000000000000000000"); - } -} diff --git a/packages/kos-sdk/src/chains/tron/address.rs b/packages/kos-sdk/src/chains/tron/address.rs deleted file mode 100644 index 7d06e2b..0000000 --- a/packages/kos-sdk/src/chains/tron/address.rs +++ /dev/null @@ -1,196 +0,0 @@ -use kos_crypto::keypair::KeyPair; -use kos_types::error::Error; - -use base58::{FromBase58, ToBase58}; -use hex::FromHex; -use std::{fmt, str::FromStr}; - -use wasm_bindgen::prelude::*; - -/// The mainnet uses 0x41('A') as address type prefix. -pub const ADDRESS_TYPE_PREFIX: u8 = 0x41; -pub const ADDRESS_LEN: usize = 21; -pub const ADDRESS_LEN_STR: usize = 34; -pub const ADDRESS_TYPE_PREFIX_STR: &str = "T"; - -/// Address of Tron, saved in 21-byte format. -#[derive(PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[repr(transparent)] -#[wasm_bindgen(js_name = "TRXAddress")] -pub struct Address([u8; ADDRESS_LEN]); - -impl Address { - /// Address of a public key. - pub fn from_public(public: [u8; 64]) -> Address { - let digest = kos_crypto::hash::keccak256(&public); - - let mut raw = [ADDRESS_TYPE_PREFIX; ADDRESS_LEN]; - raw[1..ADDRESS_LEN].copy_from_slice(&digest[digest.len() - 20..]); - - Address(raw) - } - - /// Address of a private key. - pub fn from_keypair(kp: &KeyPair) -> Address { - Address::from_public(kp.public_key().try_into().unwrap()) - } - - /// As raw 21-byte address. - pub fn as_bytes(&self) -> &[u8] { - &self.0 - } - - /// As 20-byte address that compatiable with Ethereum. - pub fn as_tvm_bytes(&self) -> &[u8] { - &self.0[1..] - } - - /// Address from 20-byte address that compatiable with Ethereum. - pub fn from_tvm_bytes(raw: &[u8]) -> Self { - assert!(raw.len() == 20); - - let mut inner = [ADDRESS_TYPE_PREFIX; ADDRESS_LEN]; - inner[1..ADDRESS_LEN].copy_from_slice(raw); - Address(inner) - } - - /// Address from raw 21-byte. - pub fn from_bytes(raw: &[u8]) -> &Address { - assert!(raw.len() == ADDRESS_LEN); - - unsafe { std::mem::transmute(&raw[0]) } - } - - /// To hex address, i.e. 41-address. - pub fn to_hex_address(self) -> String { - hex::encode(self.0) - } -} - -impl fmt::Display for Address { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - b58encode_check(self.0).fmt(f) - } -} - -impl ::std::fmt::Debug for Address { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_tuple("Address").field(&self.to_string()).finish() - } -} - -impl TryFrom<&[u8]> for Address { - type Error = Error; - - fn try_from(value: &[u8]) -> Result { - if value.len() != ADDRESS_LEN { - Err(Error::InvalidAddress(format!( - "invalid length: {}", - value.len() - ))) - } else { - let mut raw = [0u8; ADDRESS_LEN]; - raw[..ADDRESS_LEN].copy_from_slice(value); - Ok(Address(raw)) - } - } -} - -impl TryFrom> for Address { - type Error = Error; - - fn try_from(value: Vec) -> Result { - Self::try_from(&value[..]) - } -} - -impl TryFrom<&Vec> for Address { - type Error = Error; - - fn try_from(value: &Vec) -> Result { - Self::try_from(&value[..]) - } -} - -impl TryFrom<&str> for Address { - type Error = Error; - - fn try_from(value: &str) -> Result { - Address::from_str(value) - } -} - -impl FromHex for Address { - type Error = Error; - - fn from_hex>(hex: T) -> Result { - Address::try_from(hex.as_ref()) - } -} - -impl FromStr for Address { - type Err = Error; - - fn from_str(s: &str) -> Result - where - Self: Sized, - { - if s.len() == ADDRESS_LEN_STR && s.starts_with(ADDRESS_TYPE_PREFIX_STR) { - return b58decode_check(s).and_then(Address::try_from); - } - - if s.len() == 42 && s[..2] == hex::encode([ADDRESS_TYPE_PREFIX]) { - return Vec::from_hex(s) - .map_err(|e| Error::InvalidAddress(e.to_string())) - .and_then(Address::try_from); - } - - if s.len() == 44 && (s.starts_with("0x") || s.starts_with("0X")) { - return Vec::from_hex(&s.as_bytes()[2..]) - .map_err(|e| Error::InvalidAddress(e.to_string())) - .and_then(Address::try_from); - } - - if s == "_" || s == "0x0" || s == "/0" { - return "410000000000000000000000000000000000000000".parse(); - } - - eprintln!("len={} prefix={:x}", s.len(), s.as_bytes()[0]); - Err(Error::InvalidAddress("invalid tron address".to_string())) - } -} - -// NOTE: AsRef<[u8]> implies ToHex -impl AsRef<[u8]> for Address { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -/// Base58check encode. -pub fn b58encode_check>(raw: T) -> String { - let digest1 = kos_crypto::hash::sha256(raw.as_ref()); - let digest2 = kos_crypto::hash::sha256(&digest1); - - let mut raw = raw.as_ref().to_owned(); - raw.extend(&digest2[..4]); - raw.to_base58() -} - -/// Base58check decode. -pub fn b58decode_check(s: &str) -> Result, Error> { - let mut result = s - .from_base58() - .map_err(|e| Error::InvalidAddress(format!("base58: {:?}", e)))?; - - let check = result.split_off(result.len() - 4); - let digest1 = kos_crypto::hash::sha256(&result); - let digest2 = kos_crypto::hash::sha256(&digest1); - - if check != digest2[..4] { - Err(Error::InvalidChecksum("base58check")) - } else { - Ok(result) - } -} diff --git a/packages/kos-sdk/src/chains/tron/mod.rs b/packages/kos-sdk/src/chains/tron/mod.rs deleted file mode 100644 index 6cddd0d..0000000 --- a/packages/kos-sdk/src/chains/tron/mod.rs +++ /dev/null @@ -1,722 +0,0 @@ -pub mod address; -pub mod requests; - -use std::str::FromStr; - -use crate::{ - chain::{self, BaseChain}, - chains::ethereum::address::Address as ETHAddress, - chains::evm20, - models::{BroadcastResult, PathOptions, Transaction, TransactionRaw}, -}; -use kos_crypto::{keypair::KeyPair, secp256k1::Secp256k1KeyPair}; -use kos_types::error::Error; -use kos_types::hash::Hash; -use kos_types::number::BigNumber; - -use kos_proto::tron::transaction::contract::ContractType; -use wasm_bindgen::prelude::*; -use web3::{ethabi, types::U256}; - -#[derive(Debug, Copy, Clone)] -#[wasm_bindgen] -pub struct TRX {} - -pub const SIGN_PREFIX: &[u8; 22] = b"\x19TRON Signed Message:\n"; -pub const BIP44_PATH: u32 = 195; -pub const BASE_CHAIN: BaseChain = BaseChain { - name: "Tron", - symbol: "TRX", - precision: 6, - chain_code: 1, -}; - -#[wasm_bindgen] -impl TRX { - #[wasm_bindgen(js_name = "baseChain")] - pub fn base_chain() -> BaseChain { - BASE_CHAIN - } - - fn get_options(options: Option) -> kos_proto::options::TRXOptions { - match options.and_then(|opt| opt.data) { - Some(crate::models::Options::Tron(op)) => op, - _ => kos_proto::options::TRXOptions::default(), - } - } - - #[wasm_bindgen(js_name = "random")] - pub fn random() -> Result { - let mut rng = rand::thread_rng(); - let kp = Secp256k1KeyPair::random(&mut rng); - Ok(KeyPair::new_secp256k1(kp)) - } - - #[wasm_bindgen(js_name = "keypairFromBytes")] - pub fn keypair_from_bytes(private_key: &[u8]) -> Result { - // copy to fixed length array - let mut pk_slice = [0u8; 32]; - pk_slice.copy_from_slice(private_key); - - let kp = Secp256k1KeyPair::new(pk_slice); - Ok(KeyPair::new_secp256k1(kp)) - } - - #[wasm_bindgen(js_name = "keypairFromMnemonic")] - pub fn keypair_from_mnemonic( - mnemonic: &str, - path: &str, - password: Option, - ) -> Result { - let kp = Secp256k1KeyPair::new_from_mnemonic_phrase_with_path( - mnemonic, - path, - password.as_deref(), - )?; - - Ok(KeyPair::new_secp256k1(kp)) - } - - #[wasm_bindgen(js_name = "getAddressFromKeyPair")] - pub fn get_address_from_keypair(kp: &KeyPair) -> Result { - Ok(address::Address::from_keypair(kp).to_string()) - } - - #[wasm_bindgen(js_name = "getPath")] - pub fn get_path(options: &PathOptions) -> Result { - let index = options.index; - - let is_legacy = options.is_legacy.unwrap_or(false); - - if is_legacy { - Ok(format!("m/44'/{}'/{}'", BIP44_PATH, index)) - } else { - // use account 0 index X - Ok(format!("m/44'/{}'/0'/0/{}", BIP44_PATH, index)) - } - } - - #[wasm_bindgen(js_name = "signDigest")] - /// Sign digest data with the private key. - pub fn sign_digest(digest: &[u8], keypair: &KeyPair) -> Result, Error> { - let raw = keypair.sign_digest(digest); - Ok(raw) - } - - #[wasm_bindgen(js_name = "verifyDigest")] - /// Verify Message signature - pub fn verify_digest(_digest: &[u8], _signature: &[u8], _address: &str) -> Result { - todo!() - } - - #[wasm_bindgen(js_name = "sign")] - /// Hash and Sign data with the private key. - pub fn sign(tx: Transaction, keypair: &KeyPair) -> Result { - match tx.data { - Some(TransactionRaw::Tron(trx_tx)) => { - let mut new_tx = trx_tx.clone(); - let digest = TRX::hash_transaction(&trx_tx)?; - let sig = TRX::sign_digest(digest.as_slice(), keypair)?; - let hex_sig = hex::encode(sig.clone()); - - new_tx.signature.push(sig); - let result = Transaction { - chain: tx.chain, - sender: tx.sender, - hash: Hash::from_vec(digest)?, - data: Some(TransactionRaw::Tron(new_tx)), - signature: Some(hex_sig), - }; - - Ok(result) - } - _ => Err(Error::InvalidMessage("not a tron transaction".to_string())), - } - } - - fn hash_transaction(tx: &kos_proto::tron::Transaction) -> Result, Error> { - if let Some(raw_data) = &tx.raw_data { - let bytes = kos_proto::write_message(raw_data); - TRX::hash(&bytes) - } else { - Err(Error::InvalidTransaction("trx raw_data".to_string())) - } - } - - #[wasm_bindgen(js_name = "hash")] - /// hash digest - pub fn hash(message: &[u8]) -> Result, Error> { - let digest = kos_crypto::hash::sha256(message); - Ok(digest.to_vec()) - } - - #[wasm_bindgen(js_name = "messageHash")] - /// Append prefix and hash the message - pub fn message_hash(message: &[u8]) -> Result, Error> { - let to_sign = [SIGN_PREFIX, message.len().to_string().as_bytes(), message].concat(); - - TRX::hash(&to_sign) - } - - #[wasm_bindgen(js_name = "signMessage")] - /// Sign Message with the private key. - pub fn sign_message(message: &[u8], keypair: &KeyPair) -> Result, Error> { - let m = TRX::message_hash(message)?; - TRX::sign_digest(&m, keypair) - } - - #[wasm_bindgen(js_name = "verifyMessageSignature")] - /// Verify Message signature - pub fn verify_message_signature( - message: &[u8], - signature: &[u8], - address: &str, - ) -> Result { - let m = TRX::message_hash(message)?; - TRX::verify_digest(&m, signature, address) - } - - #[wasm_bindgen(js_name = "getBalance")] - /// Get balance of address and token - /// If token is None, it will return balance of native token - /// If token is Some, it will return balance of token - /// If node_url is None, it will use default node url - pub async fn get_balance( - addr: &str, - token: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("TRX")); - - let acc_address = address::Address::from_str(addr)?; - - // check if TRC20 -> trigger contract instead todo!() - let acc = requests::get_account(&node, &acc_address.to_hex_address()).await?; - - Ok(match token { - Some(key) if key != "TRX" => match acc.asset_v2.get(&key) { - Some(value) => BigNumber::from(*value), - None => BigNumber::from(0), - }, - _ => BigNumber::from(acc.balance), - }) - } - - async fn trigger_asset_transfer( - addr_sender: &address::Address, - addr_receiver: &address::Address, - amount: &BigNumber, - token: &str, - node: &str, - ) -> Result { - let contract = kos_proto::tron::TransferAssetContract { - owner_address: addr_sender.as_bytes().to_vec(), - to_address: addr_receiver.as_bytes().to_vec(), - amount: amount.to_i64(), - asset_name: token.as_bytes().to_vec(), - }; - let transaction = requests::create_asset_transfer(node, contract).await?; - Ok(transaction) - } - - async fn trigger_trc20_transfer( - addr_sender: &address::Address, - addr_receiver: &address::Address, - amount: &BigNumber, - token: &str, - node: &str, - fee_limit: &i64, - ) -> Result { - let contract = evm20::get_contract_evm20(); - let func = contract.function("transfer").map_err(|e| { - Error::InvalidMessage(format!("failed to get transfer function: {}", e)) - })?; - - let to_address = *ETHAddress::from_bytes(addr_receiver.as_tvm_bytes()); - let encoded = func - .encode_input(&[ - ethabi::Token::Address(to_address.into()), - ethabi::Token::Uint( - U256::from_dec_str(&amount.to_string()) - .map_err(|e| Error::InvalidNumberParse(e.to_string()))?, - ), - ]) - .map_err(|e| Error::InvalidTransaction(e.to_string()))?; - let contract_address = address::Address::from_str(token)?; - - let contract = kos_proto::tron::TriggerSmartContract { - owner_address: addr_sender.as_bytes().to_vec(), - contract_address: contract_address.as_bytes().to_vec(), - data: encoded, - call_token_value: 0, - call_value: 0, - token_id: 0, - }; - - let extended = requests::ContractOptions { - contract, - // TODO: estimate fee limit, for now use 100 TRX - fee_limit: fee_limit | 100000000, - }; - let transaction = requests::trigger_smartcontract(node, extended).await?; - Ok(transaction) - } - - /// create a send transaction network - #[wasm_bindgen(js_name = "send")] - pub async fn send( - sender: String, - receiver: String, - amount: BigNumber, - options: Option, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("TRX")); - let addr_sender = address::Address::from_str(&sender)?; - let addr_receiver = address::Address::from_str(&receiver)?; - - let options = TRX::get_options(options); - - let fee_limit = options.fee_limit.unwrap_or(0); - - let tx: kos_proto::tron::Transaction = match options.token { - Some(token) if token != "TRX" => { - // Check if TRC20 transfer - let valid_address = TRX::validate_address(&token, None)?; - if valid_address { - TRX::trigger_trc20_transfer( - &addr_sender, - &addr_receiver, - &amount, - &token, - &node, - &fee_limit, - ) - .await? - } else { - TRX::trigger_asset_transfer( - &addr_sender, - &addr_receiver, - &amount, - &token, - &node, - ) - .await? - } - } - _ => { - let contract = kos_proto::tron::TransferContract { - owner_address: addr_sender.as_bytes().to_vec(), - to_address: addr_receiver.as_bytes().to_vec(), - amount: amount.to_i64(), - }; - - requests::create_transfer(&node, contract).await? - } - }; - // update memo field - let tx = match options.memo { - Some(memo) => { - let mut tx = tx.clone(); - tx.raw_data.as_mut().unwrap().data = memo.as_bytes().to_vec(); - tx - } - None => tx, - }; - - let digest = TRX::hash_transaction(&tx)?; - - Ok(crate::models::Transaction { - chain: chain::Chain::TRX, - sender, - hash: Hash::from_vec(digest)?, - data: Some(TransactionRaw::Tron(tx)), - signature: None, - }) - } - - #[wasm_bindgen(js_name = "broadcast")] - pub async fn broadcast( - tx: crate::models::Transaction, - node_url: Option, - ) -> Result { - let node = node_url.unwrap_or_else(|| crate::utils::get_node_url("TRX")); - - let raw = tx - .data - .clone() - .ok_or_else(|| Error::ReqwestError("Missing transaction data".into()))?; - - let result = requests::broadcast(node.as_str(), raw.try_into()?).await?; - - if let Some(false) = result.get("result").and_then(|v| v.as_bool()) { - let error_message = result - .get("message") - .and_then(|m| m.as_str()) - .unwrap_or("no message"); - return Err(Error::InvalidTransaction(format!( - "Expected successful broadcast, got: {:?}", - error_message - ))); - } - - Ok(BroadcastResult::new(crate::models::Transaction { - chain: tx.chain, - sender: tx.sender, - hash: tx.hash, - data: tx.data, - signature: tx.signature, - })) - } - - #[wasm_bindgen(js_name = "validateAddress")] - pub fn validate_address( - addr: &str, - _option: Option, - ) -> Result { - if addr.len() == address::ADDRESS_LEN_STR - && addr.starts_with(address::ADDRESS_TYPE_PREFIX_STR) - { - let check = address::b58decode_check(addr); - if check.is_err() { - return Ok(false); - } - - let check = check.unwrap(); - - // check mainnet prefix - if check[0] == address::ADDRESS_TYPE_PREFIX { - return Ok(true); - } - } - - Ok(false) - } - - #[wasm_bindgen(js_name = "serializeTxIntoRawHex")] - pub fn serialize_tx_into_raw_hex(raw: &str) -> Result { - let raw: kos_proto::tron::Transaction = serde_json::from_str(raw)?; - - let bytes = kos_proto::write_message(&raw); - Ok(hex::encode(bytes)) - } - - #[wasm_bindgen(js_name = "serializeRawDataIntoHexString")] - pub fn serialize_raw_data_into_hex_string(tx: &str) -> Result { - let t: kos_proto::tron::Transaction = serde_json::from_str(tx)?; - let raw_data = t.raw_data.ok_or(Error::InvalidTransaction( - "Transaction raw data is missing.".to_string(), - ))?; - let bytes = kos_proto::write_message(&raw_data); - Ok(hex::encode(bytes)) - } - - pub fn tx_from_raw(data: &str) -> Result { - let raw_data_bytes = hex::decode(data)?; - let raw_data: kos_proto::tron::transaction::Raw = - kos_proto::from_bytes(raw_data_bytes).unwrap(); - - let contract = raw_data - .contract - .first() - .ok_or_else(|| Error::InvalidTransaction("Missing contract".to_string()))?; - - let sender: String = TRX::extract_address_from_contract(contract)?; - - let tx = kos_proto::tron::Transaction { - raw_data: Some(raw_data), - signature: Vec::new(), - ret: Vec::new(), - }; - - let hash = Hash::from_vec(TRX::hash_transaction(&tx)?)?; - - let signature = tx.signature.first().map(hex::encode); - - Ok(Transaction { - chain: chain::Chain::TRX, - sender, - hash, - data: Some(TransactionRaw::Tron(tx)), - signature, - }) - } - - fn extract_address_from_contract( - contract: &kos_proto::tron::transaction::Contract, - ) -> Result { - let contract_type = ContractType::from_i32(contract.r#type) - .ok_or_else(|| Error::InvalidTransaction("Invalid contract type".to_string()))?; - - let address = match contract_type { - ContractType::TransferContract => { - let parameter: kos_proto::tron::TransferContract = - kos_proto::unpack_from_option_any(&contract.parameter).unwrap(); - address::Address::from_bytes(parameter.owner_address.as_slice()).to_string() - } - ContractType::TransferAssetContract => { - let parameter: kos_proto::tron::TransferAssetContract = - kos_proto::unpack_from_option_any(&contract.parameter).unwrap(); - address::Address::from_bytes(parameter.owner_address.as_slice()).to_string() - } - ContractType::TriggerSmartContract => { - let parameter: kos_proto::tron::TriggerSmartContract = - kos_proto::unpack_from_option_any(&contract.parameter).unwrap(); - address::Address::from_bytes(parameter.owner_address.as_slice()).to_string() - } - _ => "".to_string(), - }; - - Ok(address) - } -} - -#[cfg(test)] -mod tests { - use std::assert_eq; - use std::str; - - use hex::FromHex; - - use kos_types::Bytes32; - - use crate::models::SendOptions; - - use super::*; - - const DEFAULT_PRIVATE_KEY: &str = - "b5a4cea271ff424d7c31dc12a3e43e401df7a40d7412a15750f3f0b6b5449a28"; - const DEFAULT_ADDRESS: &str = "TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH"; - - fn get_default_secret() -> KeyPair { - let b = Bytes32::from_hex(DEFAULT_PRIVATE_KEY).unwrap(); - let kp = Secp256k1KeyPair::new(b.into()); - KeyPair::new_secp256k1(kp) - } - - #[test] - fn test_address_from_mnemonic() { - let path = TRX::get_path(&PathOptions::new(0)).unwrap(); - let kp = TRX::keypair_from_mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", &path, None).unwrap(); - let address = TRX::get_address_from_keypair(&kp).unwrap(); - - assert_eq!(DEFAULT_ADDRESS, address); - } - - #[test] - fn test_address_from_private_key() { - let address = TRX::get_address_from_keypair(&get_default_secret()).unwrap(); - - assert_eq!(DEFAULT_ADDRESS, address); - } - - #[test] - fn test_get_balance() { - let balance = tokio_test::block_on(TRX::get_balance( - "TWd4WrZ9wn84f5x1hZhL4DHvk738ns5jwb", - Some("TRX".to_string()), - None, - )) - .unwrap(); - println!("balance: {}", balance.to_string()); - - assert!(balance.to_number() > 0 as f64); - } - - #[test] - fn test_send() { - let result = tokio_test::block_on(TRX::send( - "TAUN6FwrnwwmaEqYcckffC7wYmbaS6cBiX".to_string(), - DEFAULT_ADDRESS.to_string(), - BigNumber::from(10), - None, - Some("https://tron.node.klever.io".to_string()), - )); - - assert!(result.is_ok()); - let t = result.unwrap().clone(); - match t.clone().data { - Some(TransactionRaw::Tron(tx)) => { - let raw = &tx.raw_data.unwrap(); - assert_eq!(raw.contract.len(), 1); - let c: kos_proto::tron::TransferContract = - kos_proto::unpack_from_option_any(&raw.contract.get(0).unwrap().parameter) - .unwrap(); - - assert_eq!(c.amount, 10); - } - _ => assert!(false), - } - } - - #[test] - fn test_send_trc20() { - // create TRX send options - let trx_options = kos_proto::options::TRXOptions { - token: Some("TKk6DLX1xWRKHjDhHfdyQKefnP1WUppEXB".to_string()), - ..Default::default() - }; - - let options = SendOptions::new_tron_send_options(trx_options); - - let result = tokio_test::block_on(TRX::send( - "TCwwZeH6so1X4R5kcdbKqa4GWuzF53xPqG".to_string(), - DEFAULT_ADDRESS.to_string(), - BigNumber::from(1000000), - Some(options), - None, - )); - - assert!(result.is_ok()); - let t = result.unwrap().clone(); - match t.clone().data { - Some(TransactionRaw::Tron(tx)) => { - let raw = &tx.raw_data.unwrap(); - assert_eq!(raw.contract.len(), 1); - let c: kos_proto::tron::TriggerSmartContract = - kos_proto::unpack_from_option_any(&raw.contract.get(0).unwrap().parameter) - .unwrap(); - let data: String = c.data.iter().map(|b| format!("{:02X}", b)).collect(); - let owner_address = address::Address::from_bytes(&c.owner_address); - let contract_address = address::Address::from_bytes(&c.contract_address); - assert!(data.starts_with("A9059CBB")); - assert_eq!( - owner_address.to_string(), - "TCwwZeH6so1X4R5kcdbKqa4GWuzF53xPqG".to_string() - ); - assert_eq!( - contract_address.to_string(), - "TKk6DLX1xWRKHjDhHfdyQKefnP1WUppEXB".to_string() - ); - } - _ => assert!(false), - } - } - - #[test] - fn test_validate_bip44() { - let default_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let v = vec![ - (0, "TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH"), - (1, "TSeJkUh4Qv67VNFwY8LaAxERygNdy6NQZK"), - (2, "TYJPRrdB5APNeRs4R7fYZSwW3TcrTKw2gx"), - (3, "TRhVWK5XEDkQBDevcdCWW7RW51aRncty4W"), - (4, "TT2X2yyubp7qpAWYYNE5JQWBtoZ7ikQFsY"), - ]; - - for (index, expected_addr) in v { - let path = TRX::get_path(&PathOptions::new(index)).unwrap(); - let kp = TRX::keypair_from_mnemonic(default_mnemonic, &path, None).unwrap(); - let addr = TRX::get_address_from_keypair(&kp).unwrap(); - - assert_eq!(expected_addr, addr); - } - } - - #[test] - fn test_validate_address_ok() { - // valid addresses - let list = [ - "TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH", - "TSeJkUh4Qv67VNFwY8LaAxERygNdy6NQZK", - "TYJPRrdB5APNeRs4R7fYZSwW3TcrTKw2gx", - "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", - ]; - - for addr in list { - let result = TRX::validate_address(addr, None); - assert!(result.is_ok()); - - let result = result.unwrap(); - assert_eq!(result, true, "address: {}", addr); - } - } - - #[test] - fn test_validate_address_invalid() { - // invalid addresses - let list = [ - "TuEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH", - "TronEnergyioE1Z3ukeRv38sYkv5Jn55bL", - "TronEnergyioNijNo8g3LF2ABKUAae6D2Z", - "TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH1", - "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", - ]; - - for addr in list { - let result = TRX::validate_address(addr, None); - assert!(result.is_ok()); - - let result = result.unwrap(); - assert_eq!(result, false, "address: {}", addr); - } - } - - #[test] - fn test_serialize_raw_data_into_hex_string() { - let result = tokio_test::block_on(TRX::send( - "TAUN6FwrnwwmaEqYcckffC7wYmbaS6cBiX".to_string(), - DEFAULT_ADDRESS.to_string(), - BigNumber::from(10), - None, - Some("https://tron.node.klever.io".to_string()), - )); - - let t = result.unwrap().clone(); - match t.clone().data { - Some(TransactionRaw::Tron(tx)) => { - let raw = tx.clone().raw_data.unwrap(); - assert_eq!(raw.contract.len(), 1); - let c: kos_proto::tron::TransferContract = - kos_proto::unpack_from_option_any(&raw.contract.get(0).unwrap().parameter) - .unwrap(); - - assert_eq!(c.amount, 10); - let raw_data_string = t.get_raw().unwrap(); - let hex_string_tx = - TRX::serialize_raw_data_into_hex_string(&raw_data_string).unwrap(); - println!("tx: {:?}", hex_string_tx); - assert!(hex_string_tx.len() > 0); - } - _ => assert!(false), - } - } - - #[test] - fn test_serialize_raw_data_empty_input() { - let result = TRX::serialize_raw_data_into_hex_string(""); - assert!(result.is_err()); - } - - #[test] - fn test_serialize_raw_data_invalid_format() { - let invalid_tx = "invalid_json_format"; - let result = TRX::serialize_raw_data_into_hex_string(invalid_tx); - assert!(result.is_err()); - } - - #[test] - fn test_tx_from_raw() { - let tx = "0a02d8372208e9c73b516bcd78844088c6e8ad9a325a67080112630a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412320a1541e825d52582eec346c839b4875376117904a76cbc12154120ab1300cf70c048e4cf5d5b1b33f59653ed662618c0843d70fdfee4ad9a32"; - let result = TRX::tx_from_raw(tx); - assert!(result.is_ok()); - let t = result.unwrap(); - assert_eq!(t.chain, chain::Chain::TRX); - assert_eq!(t.sender, "TX8h6Df74VpJsXF6sTDz1QJsq3Ec8dABc3"); - assert_eq!( - t.hash.to_string(), - "3e6c463b2e88d78e912dee3aa31a14aa71c0e56bbdfbdbba012c8af4e45b8834" - ); - - let trigger_smart_contract_tx = "0a029af222085e42eee56c43056140d0c8aeec9d325ad402081f12cf020a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e74726163741299020a15415ad61eb25c69f75711f059e106bc1ca1b338a814121541ff7155b5df8008fbf3834922b2d52430b27874f518c0843d22e4017ff36ab500000000000000000000000000000000000000000000099d6ced5f1867f3579900000000000000000000000000000000000000000000000000000000000000800000000000000000000000005ad61eb25c69f75711f059e106bc1ca1b338a8140000000000000000000000000000000000000000000000000000000066e0a6d40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000891cdb91d149f23b1a45d9c5ca78a88d0cb44c18000000000000000000000000691a8e6e5b03bf79e133be5ac6cdc0986142e684708afdaaec9d3290018094ebdc03"; - let result = TRX::tx_from_raw(trigger_smart_contract_tx); - assert!(result.is_ok()); - let t = result.unwrap(); - assert_eq!(t.chain, chain::Chain::TRX); - assert_eq!(t.sender, "TJFWKdqCxgqu4LngZrC6mRvrpT4fGAsznp"); - assert_eq!( - t.hash.to_string(), - "066b49fab01944699516efc5c3d636f150c12d7e157e4867c155b29d94edb018" - ); - } -} diff --git a/packages/kos-sdk/src/chains/tron/requests.rs b/packages/kos-sdk/src/chains/tron/requests.rs deleted file mode 100644 index abea6f8..0000000 --- a/packages/kos-sdk/src/chains/tron/requests.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::utils; -use kos_types::error::Error; -use serde::Serialize; -use serde_json::json; - -#[derive(Serialize)] -pub struct ContractOptions { - #[serde(flatten)] - pub contract: kos_proto::tron::TriggerSmartContract, - pub fee_limit: i64, -} - -pub async fn get_account(node_url: &str, address: &str) -> Result { - let url = format!("{}/wallet/getaccount", node_url); - - let data = json!({ "address": address }).to_string().into_bytes(); - - let mut r: serde_json::Value = utils::http_post::(url, &data).await?; - - // remove hash map for now todo!(): fix this - let v = r.as_object_mut().unwrap(); - for key in [ - "assetV2", - "asset", - "free_asset_net_usage", - "free_asset_net_usageV2", - ] - .into_iter() - { - v.remove(key); - } - - let acc: kos_proto::tron::Account = serde_json::from_str(&r.to_string())?; - - Ok(acc) -} - -pub async fn broadcast( - node_url: &str, - tx: kos_proto::tron::Transaction, -) -> Result { - let url = format!("{}/wallet/broadcasthex", node_url); - - log::debug!( - "Broadcasting to {}\nData: {} ", - url, - serde_json::to_string(&tx)?, - ); - - // hex encode tx data - let tx_raw = kos_proto::write_message(&tx); - - // adjust to tron format - let data = format!("{{\"transaction\": \"{}\"}}", hex::encode(tx_raw),); - - utils::http_post::(url, data.as_bytes()).await -} - -pub async fn create_transfer( - node_url: &str, - contract: kos_proto::tron::TransferContract, -) -> Result { - let url = format!("{}/wallet/createtransaction", node_url); - - create_transaction(url, contract).await -} - -pub async fn create_asset_transfer( - node_url: &str, - contract: kos_proto::tron::TransferAssetContract, -) -> Result { - let url = format!("{}/wallet/transferasset", node_url); - - create_transaction(url, contract).await -} - -pub async fn trigger_smartcontract( - node_url: &str, - contract: ContractOptions, -) -> Result { - let url = format!("{}/wallet/triggersmartcontract", node_url); - - create_transaction(url, contract).await -} - -async fn create_transaction( - url: String, - contract: impl serde::Serialize, -) -> Result { - let data = serde_json::to_string(&contract)?.as_bytes().to_vec(); - let result = utils::http_post::(url, &data).await?; - let raw_hex = unpack_result(result)?; - pack_tx(&raw_hex) -} - -fn pack_tx(raw_hex: &str) -> Result { - // encode raw data - let raw_data_bytes = hex::decode(raw_hex)?; - let raw_data: kos_proto::tron::transaction::Raw = kos_proto::from_bytes(raw_data_bytes) - .map_err(|e| Error::InvalidTransaction(e.to_string()))?; - - Ok(kos_proto::tron::Transaction { - raw_data: Some(raw_data), - signature: Vec::new(), - ret: Vec::new(), - }) -} - -fn unpack_result(value: serde_json::Value) -> Result { - if let Some(v) = value.get("raw_data_hex").and_then(|v| v.as_str()) { - return Ok(v.to_string()); - } - - if let Some(transaction) = value.get("transaction") { - if let Some(v) = transaction.get("raw_data_hex").and_then(|v| v.as_str()) { - return Ok(v.to_string()); - } - } - - match value.get("Error") { - Some(err) => Err(Error::ReqwestError(err.to_string())), - None => Err(Error::ReqwestError("Unknown error".to_string())), - } -} diff --git a/packages/kos-sdk/src/lib.rs b/packages/kos-sdk/src/lib.rs deleted file mode 100644 index e5adaff..0000000 --- a/packages/kos-sdk/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod chain; -pub mod chains; -pub mod models; -pub mod wallet; -pub mod wm; - -mod utils; diff --git a/packages/kos-sdk/src/models.rs b/packages/kos-sdk/src/models.rs deleted file mode 100644 index 70fdd51..0000000 --- a/packages/kos-sdk/src/models.rs +++ /dev/null @@ -1,276 +0,0 @@ -use crate::chain::Chain; -use crate::chains::{ETH, KLV, TRX}; -use kos_types::{error::Error, hash::Hash}; -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -#[derive(Debug, Clone, Serialize)] -#[wasm_bindgen] -pub struct BroadcastResult { - #[wasm_bindgen(skip)] - pub tx: Transaction, -} - -#[wasm_bindgen] -impl BroadcastResult { - #[wasm_bindgen(constructor)] - pub fn new(tx: Transaction) -> Self { - Self { tx } - } - - #[wasm_bindgen(js_name = hash)] - pub fn hash(&self) -> Hash { - self.tx.hash - } - - #[allow(clippy::inherent_to_string)] - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - serde_json::to_string(&self).unwrap() - } -} -#[derive(Default, Deserialize, Serialize, Clone, Debug)] -#[wasm_bindgen] -pub struct PathOptions { - #[wasm_bindgen(skip)] - pub index: u32, - #[wasm_bindgen(skip)] - pub is_legacy: Option, -} - -#[wasm_bindgen] -impl PathOptions { - #[wasm_bindgen(constructor)] - pub fn constructor() -> Self { - Self::default() - } - - pub fn new(index: u32) -> Self { - Self { - index, - is_legacy: Some(false), - } - } - #[wasm_bindgen(js_name = setIndex)] - pub fn set_index(&mut self, index: u32) { - self.index = index; - } - #[wasm_bindgen(js_name = setLegacy)] - pub fn set_legacy(&mut self, is_legacy: bool) { - self.is_legacy = Some(is_legacy); - } -} - -// create enum variant list of transaction types supported -kos_types::enum_thing! { - enum TransactionRaw { - Klever(kos_proto::klever::Transaction), - Tron(kos_proto::tron::Transaction), - Ethereum(super::chains::ETHTransaction), - Polygon(super::chains::MATICTransaction), - Bitcoin(super::chains::BTCTransaction), - } -} - -// create enum variant list of transaction types supported -kos_types::enum_thing! { - enum Options { - Klever(kos_proto::options::KLVOptions), - Tron(kos_proto::options::TRXOptions), - Ethereum(kos_proto::options::ETHOptions), - Polygon(kos_proto::options::MATICOptions), - Bitcoin(kos_proto::options::BTCOptions), - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[wasm_bindgen] -/// Transaction Handler -pub struct SendOptions { - #[wasm_bindgen(skip)] - #[serde(skip)] - pub data: Option, -} - -impl SendOptions { - pub fn new(data: Options) -> Self { - Self { data: Some(data) } - } -} - -#[wasm_bindgen] -impl SendOptions { - #[wasm_bindgen(js_name = newKleverSendOptions)] - pub fn new_klever_send_options(option: kos_proto::options::KLVOptions) -> Self { - Self { - data: Some(Options::Klever(option)), - } - } - - #[wasm_bindgen(js_name = newTronSendOptions)] - pub fn new_tron_send_options(option: kos_proto::options::TRXOptions) -> Self { - Self { - data: Some(Options::Tron(option)), - } - } - - #[wasm_bindgen(js_name = newEthereumSendOptions)] - pub fn new_ethereum_send_options(option: &kos_proto::options::ETHOptions) -> Self { - Self { - data: Some(Options::Ethereum(option.clone())), - } - } - - #[wasm_bindgen(js_name = newPolygonSendOptions)] - pub fn new_polygon_send_options(option: &kos_proto::options::MATICOptions) -> Self { - Self { - data: Some(Options::Polygon(option.clone())), - } - } - - #[wasm_bindgen(js_name = newBitcoinSendOptions)] - pub fn new_bitcoin_send_options(option: &kos_proto::options::BTCOptions) -> Self { - Self { - data: Some(Options::Bitcoin(option.clone())), - } - } -} - -#[derive(Debug, Clone, Serialize)] -#[wasm_bindgen] -/// Transaction Handler -pub struct Transaction { - #[wasm_bindgen(skip)] - pub chain: Chain, - #[wasm_bindgen(skip)] - pub sender: String, - #[wasm_bindgen(js_name = hash)] - pub hash: Hash, - #[wasm_bindgen(skip)] - pub data: Option, - #[wasm_bindgen(skip)] - pub signature: Option, -} - -#[wasm_bindgen] -impl Transaction { - #[wasm_bindgen(js_name = chain)] - pub fn chain(&self) -> Chain { - self.chain - } - - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> Result { - serde_json::to_string(&self).map_err(|e| e.into()) - } - - #[wasm_bindgen(js_name = getRaw)] - pub fn get_raw(&self) -> Result { - match &self.data { - Some(data) => match data { - TransactionRaw::Klever(data) => serde_json::to_string(&data).map_err(|e| e.into()), - TransactionRaw::Tron(data) => serde_json::to_string(&data).map_err(|e| e.into()), - TransactionRaw::Ethereum(data) => { - let encoded = data.encode()?; - Ok(hex::encode(encoded)) - } - TransactionRaw::Polygon(data) => { - let encoded = data.eth.encode()?; - Ok(hex::encode(encoded)) - } - TransactionRaw::Bitcoin(data) => { - serde_json::to_string(&data.tx).map_err(|e| e.into()) - } - }, - None => Err(Error::InvalidTransaction("no data found".to_string())), - } - } - - #[wasm_bindgen(js_name = getSignature)] - pub fn get_signature(&self) -> Option { - self.signature.clone() - } -} -impl Transaction { - pub fn new_data(&self, chain: Chain, data: TransactionRaw) -> Transaction { - let mut tx = self.clone(); - tx.chain = chain; - tx.data = Some(data); - tx - } -} - -#[wasm_bindgen] -impl Transaction { - #[wasm_bindgen(js_name = fromRaw)] - pub fn from_raw(chain: Chain, data: &str) -> Result { - match chain { - Chain::KLV => KLV::tx_from_raw(data), - Chain::TRX => TRX::tx_from_raw(data), - Chain::ETH => ETH::tx_from_json(data), - Chain::MATIC => Err(Error::InvalidTransaction( - "MATIC chain not implemented".to_string(), - )), - Chain::BTC => Err(Error::InvalidTransaction( - "BTC chain not implemented".to_string(), - )), - Chain::NONE => Err(Error::InvalidTransaction("Invalid chain".to_string())), - } - } -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -#[wasm_bindgen] -pub struct AddressOptions { - #[wasm_bindgen(skip)] - pub network: Option, - #[wasm_bindgen(skip)] - pub prefix: Option, - #[wasm_bindgen(skip)] - pub check_summed: Option, -} - -#[wasm_bindgen] -impl AddressOptions { - pub fn new( - network: Option, - prefix: Option, - check_summed: Option, - ) -> Self { - Self { - network, - prefix, - check_summed, - } - } - - #[wasm_bindgen(js_name = setNetwork)] - pub fn set_network(&mut self, network: Option) { - self.network = network; - } - - #[wasm_bindgen(js_name = setPrefix)] - pub fn set_prefix(&mut self, prefix: Option) { - self.prefix = prefix; - } - - #[wasm_bindgen(js_name = setCheckSummed)] - pub fn set_check_summed(&mut self, check_summed: Option) { - self.check_summed = check_summed; - } - - #[wasm_bindgen(js_name = getNetwork)] - pub fn get_network(&self) -> Option { - self.network.clone() - } - - #[wasm_bindgen(js_name = getPrefix)] - pub fn get_prefix(&self) -> Option { - self.prefix.clone() - } - - #[wasm_bindgen(js_name = getCheckSummed)] - pub fn get_check_summed(&self) -> Option { - self.check_summed - } -} diff --git a/packages/kos-sdk/src/utils.rs b/packages/kos-sdk/src/utils.rs deleted file mode 100644 index b3f4ab4..0000000 --- a/packages/kos-sdk/src/utils.rs +++ /dev/null @@ -1,88 +0,0 @@ -use kos_types::error::Error; - -use serde::de::DeserializeOwned; - -static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); - -#[allow(dead_code)] -#[cfg(not(target_arch = "wasm32"))] -pub fn http_get_block(url: String) -> Result { - let body = reqwest::blocking::get(url).unwrap(); - body.json::().map_err(Error::from) -} - -pub fn get_client() -> Result { - let mut client = reqwest::Client::builder(); - - #[cfg(not(target_arch = "wasm32"))] - { - client = client - .user_agent(APP_USER_AGENT) - .connect_timeout(std::time::Duration::from_secs(20)) - .timeout(std::time::Duration::from_secs(100)); - } - - client - .build() - .map_err(|e| Error::ReqwestError(e.to_string())) -} - -/// HTTP GET request -pub async fn http_get(url: String) -> Result { - let client = get_client()?; - - let body = client.get(url).send().await?; - body.json::().await.map_err(Error::from) -} - -/// HTTP GET request with basic auth -pub async fn http_get_auth(url: String) -> Result { - let client = get_client()?; - - let body = basic_auth(client.get(url)).send().await?; - body.json::().await.map_err(Error::from) -} - -/// HTTP POST request -pub async fn http_post(url: String, data: &[u8]) -> Result { - let client = get_client()?; - - client - .post(url) - .header("Content-Type", "application/json") - .body(data.to_vec()) - .send() - .await - .map_err(Error::from)? - .json::() - .await - .map_err(Error::from) -} - -/// HTTP POST request with basic auth -pub async fn http_post_auth(url: String, data: &[u8]) -> Result { - let client = get_client()?; - - basic_auth(client.post(url)) - .header("Content-Type", "application/json") - .body(data.to_vec()) - .send() - .await - .map_err(Error::from)? - .json::() - .await - .map_err(Error::from) -} - -pub fn get_node_url(name: &str) -> String { - std::env::var(format!("NODE_{}", name)).unwrap_or("".to_string()) -} - -pub fn basic_auth(client: reqwest::RequestBuilder) -> reqwest::RequestBuilder { - if let Ok(user) = std::env::var("KOS_API_USER") { - let pass: Option = std::env::var("KOS_API_PASS").ok(); - return client.basic_auth(user, pass); - } - - client -} diff --git a/packages/kos-sdk/src/wallet.rs b/packages/kos-sdk/src/wallet.rs deleted file mode 100644 index aac57f0..0000000 --- a/packages/kos-sdk/src/wallet.rs +++ /dev/null @@ -1,674 +0,0 @@ -use crate::{ - chain::{BaseChain, Chain}, - models::{self, BroadcastResult, PathOptions, Transaction}, -}; - -use kos_crypto::keypair::KeyPair; -use kos_types::{error::Error, number::BigNumber}; -use kos_utils::{pack, unpack}; - -use pem::{encode as encode_pem, parse as parse_pem, Pem}; -use serde::{Deserialize, Serialize}; -use strum::{EnumCount, IntoStaticStr}; - -use kos_crypto::cipher::CipherAlgo; -use wasm_bindgen::prelude::*; - -// todo!("allow change of default algo") -const DEFAULT_ALGO: CipherAlgo = kos_crypto::cipher::CipherAlgo::GMC; -// todo!("implement wallet auto lock") - -#[wasm_bindgen] -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy, EnumCount, IntoStaticStr)] -pub enum AccountType { - Mnemonic, - PrivateKey, - KleverSafe, - ReadOnly, -} - -#[wasm_bindgen] -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Wallet { - chain: Chain, - account_type: AccountType, - public_address: String, - is_locked: bool, - node_url: Option, - index: Option, - - encrypted_data: Option>, - mnemonic: Option, - path: Option, - keypair: Option, -} - -#[wasm_bindgen] -impl Wallet { - pub fn wallet_key(chain: crate::chain::Chain, address: &str) -> String { - format!("{}-{}", chain.base_chain().symbol, address) - } - - pub fn get_key(&self) -> String { - Wallet::wallet_key(self.chain, &self.public_address) - } - - /// lock wallet privatekey with password - pub fn lock(&mut self, password: String) -> Result<(), Error> { - // return if is locked - if self.is_locked { - return Ok(()); - } - - // Verify password if encrypted_data is present, else serialize and encrypt wallet - match self.encrypted_data { - Some(_) => self.verify_password(password.clone())?, - None => { - let serialized = pack(self)?; - - let encrypted_data = - kos_crypto::cipher::encrypt(DEFAULT_ALGO, &serialized, &password)?; - self.encrypted_data = Some(encrypted_data); - } - } - - // reset secrets - self.keypair = None; - self.mnemonic = None; - self.path = None; - self.is_locked = true; - - Ok(()) - } - - /// unlock wallet privatekey with password - pub fn unlock(&mut self, password: String) -> Result<(), Error> { - // return if is unlocked - if !self.is_locked { - return Ok(()); - } - - let encrypted_data = match self.encrypted_data { - Some(ref data) => data, - None => return Err(Error::WalletManagerError("No encrypted data".to_string())), - }; - // decrypt encrypted_data - let data = kos_crypto::cipher::decrypt(encrypted_data, &password)?; - // restore values - let wallet: Wallet = - unpack(&data[..]).map_err(|e| Error::CipherError(format!("deserialize: {}", e)))?; - - // restore secrets - self.keypair = wallet.keypair; - self.mnemonic = wallet.mnemonic; - self.path = wallet.path; - self.is_locked = false; - - Ok(()) - } - - #[wasm_bindgen(js_name = "isLocked")] - /// check if wallet is locked - pub fn is_locked(&self) -> bool { - self.is_locked - } - - #[wasm_bindgen(js_name = "verifyPassword")] - pub fn verify_password(&self, password: String) -> Result<(), Error> { - match self.encrypted_data { - Some(ref encrypted_data) => { - // verify password - // decrypt encrypted_data - _ = kos_crypto::cipher::decrypt(encrypted_data, &password)?; - - Ok(()) - } - None => Err(Error::WalletManagerError("No encrypted data".to_string())), - } - } - - #[wasm_bindgen(constructor)] - /// create a random private key wallet - pub fn new(chain: Chain) -> Result { - let kp = chain.new_keypair()?; - - Wallet::from_keypair(chain, kp) - } - - #[wasm_bindgen(js_name = "fromKeyPair")] - /// restore wallet from keypair - pub fn from_keypair(chain: Chain, kp: KeyPair) -> Result { - let address = chain.get_address_from_keypair(&kp)?; - - Ok(Wallet { - chain, - account_type: AccountType::PrivateKey, - public_address: address, - is_locked: false, - node_url: None, - index: None, - - encrypted_data: None, - mnemonic: Some(String::new()), - path: Some(String::new()), - keypair: Some(kp), - }) - } - - #[wasm_bindgen(js_name = "fromMnemonic")] - /// restore wallet from mnemonic - pub fn from_mnemonic( - chain: Chain, - mnemonic: String, - path: String, - password: Option, - ) -> Result { - // validate mnemonic entropy - kos_crypto::mnemonic::validate_mnemonic(&mnemonic)?; - - let kp = chain.keypair_from_mnemonic(&mnemonic, &path, password)?; - let address = chain.get_address_from_keypair(&kp)?; - - Ok(Wallet { - chain, - account_type: AccountType::Mnemonic, - public_address: address, - is_locked: false, - node_url: None, - index: None, - - encrypted_data: None, - mnemonic: Some(mnemonic), - path: Some(path), - keypair: Some(kp), - }) - } - - #[wasm_bindgen(js_name = "fromPrivateKey")] - /// restore wallet from mnemonic - pub fn from_private_key(chain: Chain, private_key: String) -> Result { - // convert hex to bytes - let private_key = hex::decode(private_key)?; - - // check size of private key - if private_key.len() != 32 { - return Err(Error::WalletManagerError("Invalid private key".to_string())); - } - - // crete keypair from private key - let kp = chain.keypair_from_bytes(&private_key)?; - - // create wallet from keypair - Wallet::from_keypair(chain, kp) - } - - #[wasm_bindgen(js_name = "fromKCPem")] - /// restore wallet from mnemonic - pub fn from_kc_pem(chain: Chain, data: &[u8]) -> Result { - // decode pem file - let pem = parse_pem(data) - .map_err(|_| Error::WalletManagerError("Invalid PEM data".to_string()))?; - - let content = String::from_utf8(pem.contents().to_vec()) - .map_err(|_| Error::WalletManagerError("Invalid PEM data".to_string()))?; - - let pk_hex = content.chars().take(64).collect::(); - - // import from private key - Wallet::from_private_key(chain, pk_hex) - } - - #[wasm_bindgen(js_name = "fromMnemonicIndex")] - /// restore wallet from mnemonic - pub fn from_mnemonic_index( - chain: Chain, - mnemonic: String, - path_options: &PathOptions, - password: Option, - ) -> Result { - let path = chain.get_path(path_options)?; - let mut wallet = Wallet::from_mnemonic(chain, mnemonic, path, password)?; - wallet.index = Some(path_options.index); - - Ok(wallet) - } - - #[wasm_bindgen(js_name = "fromPem")] - pub fn from_pem(data: &[u8]) -> Result { - // parse pem - let pem = parse_pem(data) - .map_err(|_| Error::WalletManagerError("Invalid PEM data".to_string()))?; - - Wallet::import(pem) - } - - #[wasm_bindgen(js_name = "toPem")] - pub fn to_pem(&self, password: String) -> Result, Error> { - let pem = self.export(password)?; - - Ok(encode_pem(&pem).as_bytes().to_vec()) - } -} - -// wallet properties -impl Wallet { - pub fn import(pem: Pem) -> Result { - // Deserialize decrypted bytes to WalletManager - let wallet: Wallet = unpack(pem.contents()) - .map_err(|e| Error::CipherError(format!("deserialize data: {}", e)))?; - - Ok(wallet) - } - - pub fn export(&self, password: String) -> Result { - // validate password and lock wallet - if !self.is_locked() { - return Err(Error::WalletManagerError( - "Wallet is not locked".to_string(), - )); - } - - self.verify_password(password)?; - - // serialize wallet manager - let serialized = pack(self)?; - - let pem = kos_crypto::cipher::to_pem(self.get_key(), &serialized)?; - - Ok(pem) - } -} - -#[wasm_bindgen] -// wallet properties -impl Wallet { - #[wasm_bindgen(js_name = "getChain")] - /// get wallet chain type - pub fn get_chain(&self) -> Chain { - self.chain - } - - #[wasm_bindgen(js_name = "getAccountType")] - /// get wallet account type - pub fn get_account_type(&self) -> AccountType { - self.account_type - } - - #[wasm_bindgen(js_name = "getAddress")] - /// get wallet address - pub fn get_address(&self) -> String { - self.public_address.clone() - } - - #[wasm_bindgen(js_name = "getPublicKey")] - /// get wallet public key - pub fn get_public_key(&self) -> String { - match self.keypair { - Some(ref kp) => kp.public_key_hex(), - None => String::new(), - } - } - - #[wasm_bindgen(js_name = "getPath")] - /// get wallet path if wallet is created from mnemonic - pub fn get_path(&self) -> String { - match self.path { - Some(ref path) => path.clone(), - None => String::new(), - } - } - - #[wasm_bindgen(js_name = "getIndex")] - /// get wallet index if wallet is created from mnemonic index - pub fn get_index(&self) -> Result { - self.index.ok_or(Error::WalletManagerError( - "Wallet is not created from mnemonic index".to_string(), - )) - } - - #[wasm_bindgen(js_name = "getPrivateKey")] - /// get wallet private key - pub fn get_private_key(&self) -> String { - match self.keypair { - Some(ref kp) => kp.secret_key_hex(), - None => String::new(), - } - } - - #[wasm_bindgen(js_name = "getMnemonic")] - /// get wallet mnemonic if wallet is created from mnemonic - pub fn get_mnemonic(&self) -> String { - match self.mnemonic { - Some(ref mnemonic) => mnemonic.clone(), - None => String::new(), - } - } - - #[wasm_bindgen(js_name = "getNodeUrl")] - /// get node url setting for wallet - pub fn get_node_url(&self) -> String { - match self.node_url { - Some(ref node_url) => node_url.clone(), - None => crate::utils::get_node_url(self.chain.base_chain().symbol), - } - } - - #[wasm_bindgen(js_name = "setNodeUrl")] - /// set node url setting for wallet - pub fn set_node_url(&mut self, node_url: String) { - self.node_url = Some(node_url.clone()); - } -} - -#[wasm_bindgen] -// wallet methods -impl Wallet { - #[wasm_bindgen(js_name = "getBaseChain")] - /// sign message with private key - pub fn base_chain(&self) -> Result { - Ok(self.chain.base_chain()) - } - - #[wasm_bindgen(js_name = "signMessage")] - /// sign message with keypair - pub fn sign_message(&self, message: &[u8]) -> Result, Error> { - if self.is_locked { - return Err(Error::WalletManagerError("Wallet is locked".to_string())); - } - - match self.keypair { - Some(ref kp) => self.chain.sign_message(message, kp), - None => Err(Error::WalletManagerError("no keypair".to_string())), - } - } - - #[wasm_bindgen(js_name = "signDigest")] - /// sign digest with raw keypair - pub fn sign_digest(&self, hash: &[u8]) -> Result, Error> { - if self.is_locked { - return Err(Error::WalletManagerError("Wallet is locked".to_string())); - } - - match self.keypair { - Some(ref kp) => self.chain.sign_digest(hash, kp), - None => Err(Error::WalletManagerError("no keypair".to_string())), - } - } - - #[wasm_bindgen(js_name = "sign")] - pub fn sign(&self, tx: Transaction) -> Result { - if self.is_locked { - return Err(Error::WalletManagerError("Wallet is locked".to_string())); - } - - match self.keypair { - Some(ref kp) => self.chain.sign(tx, kp), - None => Err(Error::WalletManagerError("no keypair".to_string())), - } - } -} - -#[wasm_bindgen] -impl Wallet { - #[wasm_bindgen(js_name = "getBalance")] - pub async fn get_balance( - &self, - address: &str, - token: Option, - node_url: Option, - ) -> Result { - self.chain - .get_balance(address, token, node_url.or(self.node_url.clone())) - .await - } - - #[wasm_bindgen(js_name = "send")] - /// create a send transaction network - pub async fn send( - &self, - receiver: String, - amount: BigNumber, - options: Option, - node_url: Option, - ) -> Result { - self.chain - .send( - self.get_address(), - receiver, - amount, - options, - node_url.or(self.node_url.clone()), - ) - .await - } - - #[wasm_bindgen(js_name = "broadcast")] - /// broadcast transaction to network - pub async fn broadcast( - &self, - data: Transaction, - node_url: Option, - ) -> Result { - self.chain - .broadcast(data, node_url.or(self.node_url.clone())) - .await - } - - #[wasm_bindgen(js_name = "validateAddress")] - pub fn validate_address( - &self, - address: &str, - option: Option, - ) -> Result { - self.chain.validate_address(address, option) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_sign_broadcast() { - let mut w1 = Wallet::from_mnemonic( - Chain::KLV, - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(), - Chain::KLV.get_path(&PathOptions::new(0)).unwrap(), - None, - ).unwrap(); - - w1.set_node_url("https://node.testnet.klever.finance".to_string()); - - let tx = tokio_test::block_on(w1.send( - "klv1x2ejsdqz8uccl7htu4cef63z0cqnydhkd8g36tgk6qdv94hu7syqms3spm".to_string(), - BigNumber::from(10), - None, - None, - )) - .unwrap(); - - let to_broadcast = w1.sign(tx).unwrap(); - let result = tokio_test::block_on(w1.broadcast(to_broadcast, None)); - - assert!(result.is_ok()) - } - - #[test] - fn test_export_import() { - let default_password = "password"; - // create wallet - let mut w1 = Wallet::from_mnemonic( - Chain::KLV, - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(), - Chain::KLV.get_path(&PathOptions::new(0)).unwrap(), - None, - ).unwrap(); - - // check if wallet is unlocked (nearly created wallet) - assert!(!w1.is_locked()); - // lock wallet - let result = w1.lock(default_password.to_string()); - assert!(result.is_ok()); - assert!(w1.is_locked()); - // check if secret keys are removed - assert!(w1.get_private_key().is_empty()); - assert!(w1.get_mnemonic().is_empty()); - assert!(w1.get_path().is_empty()); - - // export wallet - let result = w1.to_pem(default_password.to_string()); - assert!(result.is_ok()); - let pem = result.unwrap(); - println!("{}", String::from_utf8(pem.clone()).unwrap()); - - // export wrong password - let result = w1.to_pem("wrong password".to_string()); - assert!(result.is_err()); - - // unlock wallet - let result = w1.unlock(default_password.to_string()); - println!("{:?}", result); - assert!(result.is_ok()); - assert!(!w1.is_locked()); - // check if secret keys restored - assert_eq!( - w1.get_private_key(), - "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d" - ); - assert_eq!(w1.get_mnemonic(), "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"); - assert_eq!(w1.get_path(), "m/44'/690'/0'/0'/0'"); - - // try to export wallet unlocked - let result = w1.export(default_password.to_string()); - assert!(result.is_err()); - - // try to lock with wrong password - let result = w1.lock("wrong password".to_string()); - assert!(result.is_err()); - } - - #[test] - fn test_import_pem() { - let pem_str = - "-----BEGIN KLV-klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy----- -AQA+a2x2MXVzZG55d2pocmx2NHRjeXU2c3R4cGw2eXZocGxnMzVuZXBsamx0NHk1 -cjd5cHBlOGVyNHF1amxhenkBAAABvAIAOJCsBrwdU4FmaoxoUm790L+QMK4zTixR -pX8Nich/FhH53iI1cgoAyySPnzUVpvdSrsYZgpALJHO1Iv5FYBMYOqIIOJfLgMYp -N4C8Qw2LCgx4L/VIFoU+nbxJhvnx8Xd9mmkyWhbcoBSAHxzQ/teEfTRAMu9l8H33 -tN5xf3N7KRFsjZ3vDSW9w/xoiOCsi1QbTpE7SHB0KGLPaW1kgJ57J0gPC1QHI8Nk -csPM/08jVmOb1OIIs51qmo/FOsewPwb5aPEji6panHN3aJiYYv5XZCAxbWQqu2oY -Q4mznxvSHZLyGLsTGDmrticCzCL2i+3nXh7a07PsTMguY8IqUNRZpF68TqcYw/Bf -56tx4+0OgQ2ujSMWWeR3uN95K6o7rzIMpRbLxrcGfTkfozbGMe/H0Ur+5YI0hVY/ -qeVTAAAA ------END KLV-klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy-----"; - - let wallet = Wallet::from_pem(pem_str.as_bytes()).unwrap(); - assert!(wallet.is_locked()); - assert_eq!( - wallet.get_address(), - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy" - ) - } - - #[test] - fn test_wallet_mnemonic_without_index() { - let w1 = Wallet::from_mnemonic( - Chain::KLV, - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(), - Chain::KLV.get_path(&PathOptions::new(0)).unwrap(), - None, - ).unwrap(); - - let result = w1.get_index(); - assert!(result.is_err()); - } - - #[test] - fn test_wallet_mnemonic_with_index() { - let w1 = Wallet::from_mnemonic_index( - Chain::KLV, - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(), - &PathOptions::new(10), - None, - ).unwrap(); - let result = w1.get_index(); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), 10); - } - - #[test] - fn test_validate_address_ok() { - let list_klv = [ - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "klv1x2ejsdqz8uccl7htu4cef63z0cqnydhkd8g36tgk6qdv94hu7syqms3spm", - ]; - - let list_btc = [ - "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", - "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g", - "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", - "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", - "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3", - ]; - - let w1 = Wallet::new(Chain::KLV).unwrap(); - - for address in list_klv.iter() { - let result = w1.validate_address(address, None); - assert!(result.is_ok()); - assert!(result.unwrap()); - } - - let w2 = Wallet::new(Chain::BTC).unwrap(); - for address in list_btc.iter() { - let result = w2.validate_address(address, None); - assert!(result.is_ok()); - assert!(result.unwrap()); - } - } - - #[test] - fn test_validate_address_fail() { - let list_klv = [ - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlaz", - "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy1", - "klv2usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "klvusdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy", - "bnb1ztx5rf7jx28k3xnemftcq3kfgm3yhfvfmhm456", - "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", - ]; - - let list_btc = [ - "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", - "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g", - "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", - "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", - "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3", - ]; - - let w1 = Wallet::new(Chain::KLV).unwrap(); - - for address in list_klv.iter() { - let result = w1.validate_address(address, None); - assert!(result.is_ok()); - assert!(!result.unwrap()); - } - - let w2 = Wallet::new(Chain::BTC).unwrap(); - for address in list_btc.iter() { - let result = w2.validate_address( - address, - Some(models::AddressOptions::new( - Some("0B110907".to_string()), - None, - None, - )), - ); - assert!(result.is_ok()); - assert!(!result.unwrap()); - } - } -} diff --git a/packages/kos-sdk/src/wm.rs b/packages/kos-sdk/src/wm.rs deleted file mode 100644 index 25d72e4..0000000 --- a/packages/kos-sdk/src/wm.rs +++ /dev/null @@ -1,650 +0,0 @@ -use crate::{models::PathOptions, wallet::Wallet}; -use kos_types::error::Error; -use kos_utils::{pack, unpack}; - -use pem::{ - encode as encode_pem, encode_many as encode_many_pem, parse as parse_pem, - parse_many as parse_many_pem, Pem, -}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -use wasm_bindgen::prelude::*; - -const KOS_WM_TAG: &str = "KOS WALLET MANAGER"; -const KOS_WM_TAG_DEFAULT: &str = "KOS WM DEFAULT"; - -#[wasm_bindgen] -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct WalletManager { - is_locked: bool, - encrypted_data: Option>, - checksum: Option>, - // skip serializing wallets, all data must be encrypted into encrypted_data - #[serde(skip)] - wallets: HashMap, -} - -#[wasm_bindgen] -impl WalletManager { - #[wasm_bindgen(constructor)] - pub fn new() -> WalletManager { - WalletManager { - is_locked: false, - encrypted_data: None, - checksum: None, - wallets: HashMap::new(), - } - } - - #[wasm_bindgen(js_name = "verifyPassword")] - pub fn verify_password(&self, password: String) -> Result<(), Error> { - _ = self - .encrypted_data - .as_ref() - .ok_or_else(|| Error::WalletManagerError("No encrypted data".to_string()))?; - let checksum = self - .checksum - .as_ref() - .ok_or_else(|| Error::WalletManagerError("No checksum".to_string()))?; - - let checksum_str = String::from_utf8(checksum.clone()) - .map_err(|_| Error::WalletManagerError("Invalid checksum".to_string()))?; - - if !kos_crypto::cipher::check_checksum(&password, checksum_str) { - return Err(Error::WalletManagerError("Invalid password".to_string())); - } - - Ok(()) - } - - #[wasm_bindgen(js_name = "fromPem")] - pub fn from_pem(data: &[u8]) -> Result { - // parse pem - let pem = parse_pem(data) - .map_err(|_| Error::WalletManagerError("Invalid PEM data".to_string()))?; - - WalletManager::import(pem) - } - - #[wasm_bindgen(js_name = "toPem")] - pub fn to_pem(&self, password: String) -> Result, Error> { - let pem = self.export(password)?; - - Ok(encode_pem(&pem).as_bytes().to_vec()) - } - - #[wasm_bindgen(js_name = "isLocked")] - pub fn is_locked(&self) -> bool { - self.is_locked - } - - #[wasm_bindgen(js_name = "unlock")] - pub fn unlock(&mut self, password: String) -> Result<(), Error> { - // return if already unlocked - if !self.is_locked() { - return Ok(()); - } - - // verify password - self.verify_password(password.clone())?; - - // reload encrypted wallets from encrypted_data - let wallets = parse_many_pem(self.encrypted_data.as_ref().unwrap()) - .map_err(|_| Error::WalletManagerError("Invalid encrypted data".to_string()))?; - - // deserialize all wallets and save to encrypted_data - for pem in wallets.iter() { - let wallet = Wallet::import(pem.clone())?; - self.wallets - .insert(pem.tag().to_string(), wallet.to_owned()); - } - - // unlock status - self.is_locked = false; - - Ok(()) - } - - #[wasm_bindgen(js_name = "lock")] - pub fn lock(&mut self, password: String) -> Result<(), Error> { - // return if is locked - if self.is_locked() { - return Ok(()); - } - - // verify password if encrypted data is present - if self.encrypted_data.is_some() { - self.verify_password(password.clone())?; - } - - let wallets: Result, Error> = self - .wallets - .iter_mut() - .map(|(tag, wallet)| { - wallet.lock(password.clone())?; - let w = wallet.export(password.clone())?; - - // replace default tag with KOS_WM_TAG_DEFAULT - Ok(if tag == KOS_WM_TAG_DEFAULT { - Pem::new(KOS_WM_TAG_DEFAULT, w.contents()) - } else { - w - }) - }) - .collect(); - - let wallets = wallets?; - - self.encrypted_data = Some(encode_many_pem(&wallets).as_bytes().to_vec()); - self.checksum = Some( - kos_crypto::cipher::create_checksum(&password) - .as_bytes() - .to_vec(), - ); - - // reset secrets - self.wallets.clear(); - self.is_locked = true; - - Ok(()) - } -} - -impl WalletManager { - pub fn import(pem: Pem) -> Result { - // check tag - if pem.tag() != KOS_WM_TAG { - return Err(Error::WalletManagerError("Invalid PEM tag".to_string())); - } - - // Deserialize decrypted bytes to WalletManager - let wm: WalletManager = unpack(pem.contents()) - .map_err(|e| Error::CipherError(format!("deserialize wm: {}", e)))?; - - Ok(wm) - } - - pub fn export(&self, password: String) -> Result { - // validate password and lock wallet - if !self.is_locked() { - return Err(Error::WalletManagerError( - "WalletManager is not locked".to_string(), - )); - } - - self.verify_password(password.clone())?; - - let data = pack(self).map_err(|e| Error::CipherError(format!("serialize wm: {}", e)))?; - - let pem = kos_crypto::cipher::to_pem(KOS_WM_TAG.to_owned(), &data)?; - - Ok(pem) - } -} - -impl Default for WalletManager { - fn default() -> Self { - Self::new() - } -} - -#[wasm_bindgen] -impl WalletManager { - fn ensure_unlocked(&mut self, password: String) -> Result<(), Error> { - if !self.is_locked() { - self.unlock(password) - } else { - self.verify_password(password) - } - } - - #[wasm_bindgen(js_name = "addWallet")] - pub fn add_wallet(&mut self, wallet: Wallet, password: String) -> Result { - self.ensure_unlocked(password.clone())?; - - let mut wallet = wallet.clone(); - - // check if wallet is locked - if !wallet.is_locked() { - wallet.lock(password.clone())?; - } else { - wallet.verify_password(password.clone())?; - } - - let wallet_name = wallet.get_key(); - - // error if exists - if self.wallets.contains_key(&wallet_name) { - return Err(Error::WalletManagerError( - "Wallet already exists".to_string(), - )); - } - - self.wallets.insert(wallet_name, wallet.clone()); - - Ok(wallet) - } - - #[wasm_bindgen(js_name = "newWallet")] - pub fn new_wallet( - &mut self, - chain: crate::chain::Chain, - password: String, - ) -> Result { - self.ensure_unlocked(password.clone())?; - - let index = self - .wallets - .values() - .filter(|wallet| { - wallet.get_chain() == chain - && wallet.get_account_type() == crate::wallet::AccountType::Mnemonic - }) - .filter_map(|wallet| wallet.get_index().ok()) - .max() - .map(|x| x + 1) - .unwrap_or(0); - - let mut wallet = Wallet::from_mnemonic_index( - chain, - self.get_mnemonic(password.to_owned())?, - &PathOptions::new(index), - None, - )?; - - wallet.lock(password.clone())?; - - self.add_wallet(wallet, password) - } - - #[wasm_bindgen(js_name = "removeWallet")] - pub fn remove_wallet( - &mut self, - chain: crate::chain::Chain, - address: String, - ) -> Result<(), Error> { - let wallet_name = Wallet::wallet_key(chain, &address); - match self.wallets.remove(&wallet_name) { - Some(_) => Ok(()), - None => Err(Error::WalletManagerError(format!( - "Wallet with address {} not found", - address - ))), - } - } - - #[wasm_bindgen(js_name = "getWallet")] - pub fn get_wallet(&self, chain: crate::chain::Chain, address: String) -> Result { - let wallet_name = Wallet::wallet_key(chain, &address); - match self.wallets.get(&wallet_name) { - Some(wallet) => Ok(wallet.clone()), - None => Err(Error::WalletManagerError(format!( - "Wallet with address {} not found", - address - ))), - } - } - - #[wasm_bindgen(js_name = "getMnemonic")] - pub fn get_mnemonic(&self, password: String) -> Result { - // error if unlocked - if self.is_locked() { - return Err(Error::WalletManagerError( - "WalletManager is locked".to_string(), - )); - } - - // reload encrypted wallets from encrypted_data - let mut w_mnemonic = self - .wallets - .get(KOS_WM_TAG_DEFAULT) - .ok_or(Error::WalletManagerError( - "Default mnemonic not found".to_string(), - ))? - .to_owned(); - - // decrypt mnemonic - w_mnemonic.unlock(password.clone())?; - let mnemonic_str = w_mnemonic.get_mnemonic(); - if mnemonic_str.is_empty() { - return Err(Error::WalletManagerError( - "Default mnemonic not found".to_string(), - )); - } - - Ok(mnemonic_str) - } - - #[wasm_bindgen(js_name = "setMnemonic")] - pub fn set_mnemonic(&mut self, mnemonic: String, password: String) -> Result<(), Error> { - // error if unlocked - if self.is_locked() { - return Err(Error::WalletManagerError( - "WalletManager is locked".to_string(), - )); - } - - // error if default mnemonic exists - if self.wallets.contains_key(KOS_WM_TAG_DEFAULT) { - return Err(Error::WalletManagerError( - "Default mnemonic already exists".to_string(), - )); - } - - // create wallet - let mut wallet = Wallet::from_mnemonic( - crate::chain::Chain::NONE, - mnemonic.clone(), - "".to_string(), - None, - )?; - - wallet.lock(password.clone())?; - self.wallets.insert(KOS_WM_TAG_DEFAULT.to_owned(), wallet); - - Ok(()) - } - - #[wasm_bindgen(js_name = "viewWallets")] - pub fn view_wallets(&self) -> Result { - let wallets = self.list_wallets(); - serde_wasm_bindgen::to_value(&wallets).map_err(|e| Error::JSONSerde(e.to_string())) - } -} - -impl WalletManager { - pub fn list_wallets(&self) -> Vec { - let mut wallets = Vec::new(); - for w in self.wallets.values() { - // skip default mnemonic wallet - if w.get_chain() == crate::chain::Chain::NONE { - continue; - } - - wallets.push(WalletView { - chain: w.get_chain(), - address: w.get_address(), - // todo!("add name to wallet struct") - name: w.get_key(), - }); - } - wallets - } -} - -#[derive(Deserialize, Serialize)] -pub struct WalletView { - pub chain: crate::chain::Chain, - pub address: String, - pub name: String, -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::chain::Chain; - const DEFAULT_MNEMONIC: &str = - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - - #[test] - fn test_wallet_manager_export_import() { - let default_password = "password".to_string(); - - let mut wm = WalletManager::new(); - let wallet = Wallet::new(Chain::KLV).unwrap(); - let result = wm.add_wallet(wallet.to_owned(), default_password.to_owned()); - assert!(result.is_ok()); - - // set default mnemonic - wm.set_mnemonic(DEFAULT_MNEMONIC.to_string(), default_password.to_string()) - .unwrap(); - - let result = wm.export(default_password.to_owned()); - assert_eq!( - result.err(), - Some(Error::WalletManagerError( - "WalletManager is not locked".to_string() - )) - ); - - let result = wm.lock(default_password.to_owned()); - assert!(result.is_ok()); - let pem = wm.export(default_password.to_owned()); - assert!(pem.is_ok()); - - let mut wm2 = WalletManager::import(pem.unwrap()).unwrap(); - // unlock wallet manager - let result = wm2.unlock(default_password.to_owned()); - assert!(result.is_ok()); - // count should be 2 dues to default mnemonic wallet - assert_eq!(wm2.wallets.len(), 2); - - // check if wallet address matches - let wallet2 = wm2 - .get_wallet(wallet.get_chain(), wallet.get_address()) - .unwrap(); - assert_eq!(wallet2.get_address(), wallet.get_address()); - - // check default mnemonic matchs - let mnemonic = wm2.get_mnemonic(default_password.to_owned()).unwrap(); - assert_eq!(mnemonic, DEFAULT_MNEMONIC); - } - - #[test] - fn test_wallet_manager_lock_unlock() { - let default_password = "password".to_string(); - - let mut wm = WalletManager::new(); - let wallet = Wallet::new(Chain::KLV).unwrap(); - let test_privete_key = wallet.get_private_key(); - - let resul = wm.add_wallet(wallet.clone(), default_password.to_owned()); - assert!(resul.is_ok()); - let result = wm.lock(default_password.to_owned()); - assert!(result.is_ok()); - assert!(wm.is_locked()); - assert_eq!(wm.wallets.len(), 0); - // try unlock with wrong password - let result = wm.unlock("wrong_password".to_owned()); - assert_eq!( - result.err(), - Some(Error::WalletManagerError("Invalid password".to_string())) - ); - // unlock with correct password - let result = wm.unlock(default_password.to_owned()); - assert!(result.is_ok()); - assert!(!wm.is_locked()); - assert_eq!(wm.wallets.len(), 1); - - // check if wallet is locked - let mut wallet = wm - .get_wallet(wallet.get_chain(), wallet.get_address()) - .unwrap(); - assert!(wallet.is_locked()); - assert!(wallet.get_private_key().is_empty()); - - // unlock wallet - let result = wallet.unlock("password".to_owned()); - assert!(result.is_ok()); - assert_eq!(wallet.get_private_key(), test_privete_key); - } - - #[test] - fn test_wallet_manager_wallet_timing() { - let default_password = "password".to_string(); - const WALLET_COUNT: usize = 10; - - let start_time = std::time::Instant::now(); - let mut wm = WalletManager::new(); - println!("New Manger: {:?}", start_time.elapsed()); - - let start_time = std::time::Instant::now(); - for _ in 0..WALLET_COUNT { - let wallet = Wallet::new(Chain::KLV).unwrap(); - _ = wm.add_wallet(wallet.to_owned(), default_password.to_owned()); - } - println!("{} New Wallets: {:?}", WALLET_COUNT, start_time.elapsed()); - - // lock wallet manager with WALLET_COUNT wallets - let start_time = std::time::Instant::now(); - let result = wm.lock(default_password.to_owned()); - assert!(result.is_ok()); - println!( - "Lock {} New Wallets: {:?}", - WALLET_COUNT, - start_time.elapsed() - ); - - // unlock wallet manager - let start_time = std::time::Instant::now(); - let result = wm.unlock(default_password.to_owned()); - assert!(result.is_ok()); - println!( - "Unlock Wallet Manager with {} wallets: {:?}", - WALLET_COUNT, - start_time.elapsed() - ); - - // lock wallet manager with WALLET_COUNT wallets all locked - let start_time = std::time::Instant::now(); - let result = wm.lock(default_password.to_owned()); - assert!(result.is_ok()); - println!( - "Lock {} New Wallets all locked: {:?}", - WALLET_COUNT, - start_time.elapsed() - ); - - // export wallet manager - let start_time = std::time::Instant::now(); - let pem = wm.export(default_password.to_owned()).unwrap(); - println!( - "Export Wallet Manager with {} wallets: {:?}", - WALLET_COUNT, - start_time.elapsed() - ); - println!("Pem size: {:?} bytes", pem.contents().len()); - - // import wallet manager - let start_time = std::time::Instant::now(); - let mut wm2 = WalletManager::import(pem).unwrap(); - println!( - "Import Wallet Manager with {} wallets: {:?}", - WALLET_COUNT, - start_time.elapsed() - ); - assert!(wm2.is_locked()); - assert_eq!(wm2.wallets.len(), 0); - wm2.unlock(default_password.to_owned()).unwrap(); - assert_eq!(wm2.wallets.len(), WALLET_COUNT); - } - - #[test] - fn test_add_existent_wallet_should_fail() { - let default_password = "password"; - - let mut wm = WalletManager::new(); - let wallet = Wallet::new(Chain::KLV).unwrap(); - - let result = wm.add_wallet(wallet.clone(), default_password.to_owned()); - assert!(result.is_ok()); - let result = wm.add_wallet(wallet.clone(), default_password.to_owned()); - assert_eq!( - result.err(), - Some(Error::WalletManagerError( - "Wallet already exists".to_string() - )) - ); - } - - #[test] - fn test_set_mnemonic_should_work() { - let default_password = "password".to_string(); - - let mut wm = WalletManager::new(); - - let result = wm.set_mnemonic(DEFAULT_MNEMONIC.to_string(), default_password); - assert!(result.is_ok()); - } - - #[test] - fn test_set_mnemonic_should_fail() { - let default_password = "password".to_string(); - - let mut wm = WalletManager::new(); - - let result = wm.set_mnemonic("invalid mnemonic".to_string(), default_password); - assert_eq!( - result.err(), - Some(Error::InvalidMnemonic("Invalid mnemonic")) - ); - } - - #[test] - fn test_set_mnemonic_already_set_should_fail() { - let default_password = "password".to_string(); - - let mut wm = WalletManager::new(); - - let result = wm.set_mnemonic(DEFAULT_MNEMONIC.to_string(), default_password.clone()); - assert!(result.is_ok()); - - let result = wm.set_mnemonic(DEFAULT_MNEMONIC.to_string(), default_password); - assert_eq!( - result.err(), - Some(Error::WalletManagerError( - "Default mnemonic already exists".to_string() - )) - ); - } - - #[test] - fn test_get_mnemonic_not_set_should_fail() { - let default_password = "password".to_string(); - - let wm = WalletManager::new(); - let result = wm.get_mnemonic(default_password); - assert_eq!( - result.err(), - Some(Error::WalletManagerError( - "Default mnemonic not found".to_string() - )) - ); - } - - #[test] - fn test_get_mnemonic_should_work() { - let default_password = "password".to_string(); - - let mut wm = WalletManager::new(); - - let result = wm.set_mnemonic(DEFAULT_MNEMONIC.to_string(), default_password.clone()); - assert!(result.is_ok()); - - let result = wm.get_mnemonic(default_password); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), DEFAULT_MNEMONIC); - } - - #[test] - fn test_get_mnemonic_wallet() { - let default_password = "password".to_string(); - - let mut wm = WalletManager::new(); - - let result = wm.set_mnemonic(DEFAULT_MNEMONIC.to_string(), default_password.clone()); - assert!(result.is_ok()); - - let mut w_default = wm.wallets.get(KOS_WM_TAG_DEFAULT).unwrap().to_owned(); - assert!(w_default.is_locked()); - // get mnemonic shoudl be empty - let result = w_default.get_mnemonic(); - assert_eq!(result, ""); - - // unlock wallet and retrive mnemonic - let _ = w_default.unlock(default_password); - let result = w_default.get_mnemonic(); - assert_eq!(result, DEFAULT_MNEMONIC); - } -} diff --git a/packages/kos-types/Cargo.toml b/packages/kos-types/Cargo.toml deleted file mode 100644 index 1dac0bb..0000000 --- a/packages/kos-types/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "kos-types" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[features] -alloc = ["hex/alloc"] -serde = ["dep:serde", "alloc"] -std = ["alloc", "serde?/std"] -random = ["rand"] - -[dependencies] -rand = { workspace = true, optional = true } -coins-bip32 = { workspace = true } -coins-bip39 = { workspace = true } -secp256k1 = { workspace = true, features = ["recovery", "rand"] } -ed25519-dalek = { workspace = true } -log = { workspace = true } -serde = { workspace = true, features = ["derive", "alloc"], optional = true } -serde_json = { workspace = true } -hex = { workspace = true } -reqwest = { workspace = true, default-features = false } -wasm-bindgen = { workspace = true } -num-bigint = "0.4" -num-traits = "0.2" diff --git a/packages/kos-types/src/array_types.rs b/packages/kos-types/src/array_types.rs deleted file mode 100644 index 175ccfc..0000000 --- a/packages/kos-types/src/array_types.rs +++ /dev/null @@ -1,298 +0,0 @@ -// serde-big-array doesn't allow documentation of its generated structure -#![allow(missing_docs)] -use core::array::TryFromSliceError; -use core::borrow::Borrow; -use core::borrow::BorrowMut; -use core::convert::TryFrom; -use core::ops::{Deref, DerefMut}; -use core::{fmt, str}; -use hex::{FromHex, FromHexError}; - -#[cfg(feature = "alloc")] -extern crate alloc; - -#[cfg(feature = "random")] -use rand::{ - distributions::{Distribution, Standard}, - Rng, -}; - -use crate::hex_val; - -macro_rules! key { - ($i:ident, $s:expr) => { - #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - /// atomic array type. - #[repr(transparent)] - pub struct $i([u8; $s]); - - key_methods!($i, $s); - - #[cfg(feature = "random")] - impl Distribution<$i> for Standard { - fn sample(&self, rng: &mut R) -> $i { - $i(rng.gen()) - } - } - }; -} - -macro_rules! key_with_big_array { - ($i:ident, $s:expr) => { - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - /// atomic type. - #[repr(transparent)] - pub struct $i([u8; $s]); - - key_methods!($i, $s); - - impl Default for $i { - fn default() -> $i { - $i([0u8; $s]) - } - } - - #[cfg(feature = "random")] - impl Distribution<$i> for Standard { - fn sample(&self, rng: &mut R) -> $i { - let mut bytes = $i::default(); - - rng.fill_bytes(bytes.as_mut()); - - bytes - } - } - }; -} - -macro_rules! key_methods { - ($i:ident, $s:expr) => { - impl $i { - /// Memory length of the type - pub const LEN: usize = $s; - - /// Bytes constructor. - pub const fn new(bytes: [u8; $s]) -> Self { - Self(bytes) - } - - /// Zeroes bytes constructor. - pub const fn zeroed() -> $i { - $i([0; $s]) - } - - #[allow(unsafe_code)] - /// Add a conversion from arbitrary slices into owned - /// - /// # Safety - /// - /// This function will not panic if the length of the slice is smaller than - /// `Self::LEN`. Instead, it will cause undefined behavior and read random disowned - /// bytes - pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self { - $i($crate::bytes::from_slice_unchecked(bytes)) - } - - /// Copy-free reference cast - /// # Safety - /// Assumes the type is `repr[transparent]`. - pub fn from_bytes_ref_checked(bytes: &[u8]) -> Option<&Self> { - let bytes: &[u8; $s] = bytes.get(..$s)?.try_into().ok()?; - Some(Self::from_bytes_ref(bytes)) - } - - /// Copy-free reference cast - /// # Safety - /// Assumes the type is `repr[transparent]`. - pub fn from_bytes_ref(bytes: &[u8; $s]) -> &Self { - // The interpreter will frequently make references to keys and values using - // logically checked slices. - // - // This function will save unnecessary copy to owned slices for the interpreter - // access - #[allow(unsafe_code)] - unsafe { - &*(bytes.as_ptr() as *const Self) - } - } - - /// The memory size of the type by the method. - pub const fn size(&self) -> usize { - Self::LEN - } - } - - #[cfg(feature = "random")] - impl rand::Fill for $i { - fn try_fill(&mut self, rng: &mut R) -> Result<(), rand::Error> { - rng.fill_bytes(self.as_mut()); - - Ok(()) - } - } - - impl Deref for $i { - type Target = [u8; $s]; - - fn deref(&self) -> &[u8; $s] { - &self.0 - } - } - - impl Borrow<[u8; $s]> for $i { - fn borrow(&self) -> &[u8; $s] { - &self.0 - } - } - - impl BorrowMut<[u8; $s]> for $i { - fn borrow_mut(&mut self) -> &mut [u8; $s] { - &mut self.0 - } - } - - impl DerefMut for $i { - fn deref_mut(&mut self) -> &mut [u8; $s] { - &mut self.0 - } - } - - impl AsRef<[u8]> for $i { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for $i { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - impl From<[u8; $s]> for $i { - fn from(bytes: [u8; $s]) -> Self { - Self(bytes) - } - } - - impl From<$i> for [u8; $s] { - fn from(salt: $i) -> [u8; $s] { - salt.0 - } - } - - impl TryFrom<&[u8]> for $i { - type Error = TryFromSliceError; - - fn try_from(bytes: &[u8]) -> Result<$i, TryFromSliceError> { - <[u8; $s]>::try_from(bytes).map(|b| b.into()) - } - } - - impl FromHex for $i { - type Error = FromHexError; - - fn from_hex>(hex: T) -> Result { - let b: [u8; Self::LEN] = FromHex::from_hex(hex)?; - b.try_into().map_err(|_| FromHexError::InvalidStringLength) - } - } - - impl fmt::LowerHex for $i { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - write!(f, "0x")? - } - - match f.width() { - Some(w) if w > 0 => self.0.chunks(2 * Self::LEN / w).try_for_each(|c| { - write!(f, "{:02x}", c.iter().fold(0u8, |acc, x| acc ^ x)) - }), - - _ => self.0.iter().try_for_each(|b| write!(f, "{:02x}", &b)), - } - } - } - - impl fmt::UpperHex for $i { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - write!(f, "0x")? - } - - match f.width() { - Some(w) if w > 0 => self.0.chunks(2 * Self::LEN / w).try_for_each(|c| { - write!(f, "{:02X}", c.iter().fold(0u8, |acc, x| acc ^ x)) - }), - - _ => self.0.iter().try_for_each(|b| write!(f, "{:02X}", &b)), - } - } - } - - impl fmt::Debug for $i { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ::fmt(&self, f) - } - } - - impl fmt::Display for $i { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ::fmt(&self, f) - } - } - - impl str::FromStr for $i { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - const ERR: &str = "Invalid encoded byte"; - - let alternate = s.starts_with("0x"); - - let mut b = s.bytes(); - let mut ret = $i::zeroed(); - - if alternate { - b.next(); - b.next(); - } - - for r in ret.as_mut() { - let h = b.next().and_then(hex_val).ok_or(ERR)?; - let l = b.next().and_then(hex_val).ok_or(ERR)?; - - *r = h << 4 | l; - } - - Ok(ret) - } - } - - #[cfg(feature = "serde")] - impl serde::Serialize for $i { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use alloc::format; - serializer.serialize_str(&format!("{:x}", &self)) - } - } - - #[cfg(feature = "serde")] - impl<'de> serde::Deserialize<'de> for $i { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use serde::de::Error; - let s: &str = serde::Deserialize::deserialize(deserializer)?; - s.parse().map_err(D::Error::custom) - } - } - }; -} - -key!(Bytes32, 32); -key_with_big_array!(Bytes64, 64); diff --git a/packages/kos-types/src/bytes.rs b/packages/kos-types/src/bytes.rs deleted file mode 100644 index fe13b76..0000000 --- a/packages/kos-types/src/bytes.rs +++ /dev/null @@ -1,35 +0,0 @@ -#[derive(Debug, PartialEq, Clone, Eq)] -pub struct Bytes(pub Vec); - -impl From for Vec { - fn from(raw_slice: Bytes) -> Vec { - raw_slice.0 - } -} - -impl PartialEq> for Bytes { - fn eq(&self, other: &Vec) -> bool { - self.0 == *other - } -} - -impl PartialEq for Vec { - fn eq(&self, other: &Bytes) -> bool { - *self == other.0 - } -} - -#[allow(unsafe_code)] -/// Add a conversion from arbitrary slices into arrays -/// -/// # Safety -/// -/// This function will not panic if the length of the slice is smaller than `N`. Instead, it will -/// cause undefined behavior and read random disowned bytes. -pub unsafe fn from_slice_unchecked(buf: &[u8]) -> [u8; N] { - let ptr = buf.as_ptr() as *const [u8; N]; - - // Static assertions are not applicable to runtime length check (e.g. slices). - // This is safe if the size of `bytes` is consistent to `N` - *ptr -} diff --git a/packages/kos-types/src/hash.rs b/packages/kos-types/src/hash.rs deleted file mode 100644 index bb4628d..0000000 --- a/packages/kos-types/src/hash.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::error::Error; -use crate::Bytes32; - -use std::fmt::{Display, Formatter}; -use std::str::FromStr; -use wasm_bindgen::prelude::*; - -#[derive(Debug, Default, Copy, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[wasm_bindgen] -pub struct Hash { - data: Bytes32, -} - -#[wasm_bindgen] -impl Hash { - #[wasm_bindgen(constructor)] - pub fn new(data: &str) -> Result { - log::debug!("Hash::new({})", data); - - let value = Bytes32::from_str(data).map_err(|e| Error::InvalidString(e.to_string()))?; - - Ok(Self { data: value }) - } - - #[allow(clippy::inherent_to_string_shadow_display)] - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - self.data.to_string() - } - - #[wasm_bindgen(js_name = bytes)] - pub fn bytes(&self) -> Vec { - self.data.to_vec() - } - - #[wasm_bindgen(js_name = fromSlice)] - pub fn from_slice(data: &[u8]) -> Result { - // check length - if data.len() != Bytes32::LEN { - return Err(Error::InvalidLen(format!( - "expected {}, found {}", - Bytes32::LEN, - data.len() - ))); - } - let value = unsafe { Bytes32::from_slice_unchecked(data) }; - - Ok(Self { data: value }) - } - - #[wasm_bindgen(js_name = fromVec)] - pub fn from_vec(data: Vec) -> Result { - Hash::from_slice(data.as_slice()) - } -} - -impl From for Hash { - fn from(data: Bytes32) -> Self { - Self { data } - } -} - -impl From for Bytes32 { - fn from(hash: Hash) -> Self { - hash.data - } -} - -impl Display for Hash { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.data) - } -} diff --git a/packages/kos-types/src/lib.rs b/packages/kos-types/src/lib.rs deleted file mode 100644 index 818fb92..0000000 --- a/packages/kos-types/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -pub mod bytes; -pub mod error; -pub mod hash; -pub mod number; -pub mod vectorize; - -mod array_types; -pub use array_types::*; - -pub(crate) const fn hex_val(c: u8) -> Option { - match c { - b'A'..=b'F' => Some(c - b'A' + 10), - b'a'..=b'f' => Some(c - b'a' + 10), - b'0'..=b'9' => Some(c - b'0'), - _ => None, - } -} - -#[macro_export] -macro_rules! enum_thing { - ( - enum $EnumName:ident { - $($EnumVariant:ident($EnumType:ty)),* $(,)? - } - ) => { - #[derive(serde::Serialize, Clone, Debug)] - pub enum $EnumName { - $($EnumVariant($EnumType),)* - } - - $( - impl TryFrom<$EnumName> for $EnumType { - type Error = kos_types::error::Error; - - fn try_from(other: $EnumName) -> Result { - match other { - $EnumName::$EnumVariant(v) => Ok(v), - _ => Err(kos_types::error::Error::InvalidEnumVariant(stringify!($EnumName).to_string())), - } - } - } - )* - }; -} diff --git a/packages/kos-types/src/vectorize.rs b/packages/kos-types/src/vectorize.rs deleted file mode 100644 index 50c82f7..0000000 --- a/packages/kos-types/src/vectorize.rs +++ /dev/null @@ -1,26 +0,0 @@ -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::iter::FromIterator; - -#[cfg(feature = "serde")] -pub fn serialize<'a, T, K, V, S>(target: T, ser: S) -> Result -where - S: Serializer, - T: IntoIterator, - K: Serialize + 'a, - V: Serialize + 'a, -{ - let container: Vec<_> = target.into_iter().collect(); - serde::Serialize::serialize(&container, ser) -} - -#[cfg(feature = "serde")] -pub fn deserialize<'de, T, K, V, D>(des: D) -> Result -where - D: Deserializer<'de>, - T: FromIterator<(K, V)>, - K: Deserialize<'de>, - V: Deserialize<'de>, -{ - let container: Vec<_> = serde::Deserialize::deserialize(des)?; - Ok(T::from_iter(container)) -} diff --git a/packages/kos-utils/Cargo.toml b/packages/kos-utils/Cargo.toml deleted file mode 100644 index 01e45a1..0000000 --- a/packages/kos-utils/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "kos-utils" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -kos-types = { workspace = true } - -log = { workspace = true, features=["std"] } -wasm-bindgen = { workspace = true } -web-sys = { version="0.3", features=["console"] } -postcard = { version = "1.0.6", features=["use-std"] } -serde = { version="1.0", features=["derive"] } diff --git a/packages/kos-utils/src/logger.rs b/packages/kos-utils/src/logger.rs deleted file mode 100644 index bd86bde..0000000 --- a/packages/kos-utils/src/logger.rs +++ /dev/null @@ -1,179 +0,0 @@ -use log::{Level, Log, Metadata, Record}; -use wasm_bindgen::prelude::*; -use web_sys::console; - -/// Specify what to be logged -pub struct Config { - level: Level, - module_prefix: Option, - message_location: MessageLocation, -} - -/// Specify where the message will be logged. -pub enum MessageLocation { - /// The message will be on the same line as other info (level, path...) - SameLine, - /// The message will be on its own line, a new after other info. - NewLine, -} - -impl Default for Config { - fn default() -> Self { - Self { - level: Level::Debug, - module_prefix: None, - message_location: MessageLocation::SameLine, - } - } -} - -impl Config { - /// Specify the maximum level you want to log - pub fn new(level: Level) -> Self { - Self { - level, - module_prefix: None, - message_location: MessageLocation::SameLine, - } - } - - /// Initialize the logger with the given config - pub fn with_prefix(level: Level, prefix: &str) -> Self { - Config::new(level).module_prefix(prefix) - } - - /// Configure the `target` of the logger. If specified, the logger - /// only output for `log`s in module that its path starts with - /// `module_prefix`. logger only supports single prefix. Only - /// the last call to `module_prefix` has effect if you call it multiple times. - pub fn module_prefix(mut self, module_prefix: &str) -> Self { - self.module_prefix = Some(module_prefix.to_string()); - self - } - - /// Put the message on a new line, separated from other information - /// such as level, file path, line number. - pub fn message_on_new_line(mut self) -> Self { - self.message_location = MessageLocation::NewLine; - self - } -} - -/// The log styles -struct Style { - lvl_trace: String, - lvl_debug: String, - lvl_info: String, - lvl_warn: String, - lvl_error: String, - tgt: String, - args: String, -} - -impl Style { - fn new() -> Style { - let base = String::from("color: white; padding: 0 3px; background:"); - Style { - lvl_trace: format!("{} gray;", base), - lvl_debug: format!("{} blue;", base), - lvl_info: format!("{} green;", base), - lvl_warn: format!("{} orange;", base), - lvl_error: format!("{} darkred;", base), - tgt: String::from("font-weight: bold; color: inherit"), - args: String::from("background: inherit; color: inherit"), - } - } -} - -/// The logger -struct Logger { - config: Config, - style: Style, -} - -impl Log for Logger { - fn enabled(&self, metadata: &Metadata<'_>) -> bool { - if let Some(ref prefix) = self.config.module_prefix { - metadata.target().starts_with(prefix) - } else { - true - } - } - - fn log(&self, record: &Record<'_>) { - if self.enabled(record.metadata()) { - let style = &self.style; - let message_separator = match self.config.message_location { - MessageLocation::NewLine => "\n", - MessageLocation::SameLine => " ", - }; - let s = format!( - "%c{}%c {}:{}%c{}{}", - record.level(), - record.file().unwrap_or_else(|| record.target()), - record - .line() - .map_or_else(|| "[Unknown]".to_string(), |line| line.to_string()), - message_separator, - record.args(), - ); - let s = JsValue::from_str(&s); - let tgt_style = JsValue::from_str(&style.tgt); - let args_style = JsValue::from_str(&style.args); - - match record.level() { - Level::Trace => console::debug_4( - &s, - &JsValue::from(&style.lvl_trace), - &tgt_style, - &args_style, - ), - Level::Debug => console::log_4( - &s, - &JsValue::from(&style.lvl_debug), - &tgt_style, - &args_style, - ), - Level::Info => { - console::info_4(&s, &JsValue::from(&style.lvl_info), &tgt_style, &args_style) - } - Level::Warn => { - console::warn_4(&s, &JsValue::from(&style.lvl_warn), &tgt_style, &args_style) - } - Level::Error => console::error_4( - &s, - &JsValue::from(&style.lvl_error), - &tgt_style, - &args_style, - ), - } - } - } - - fn flush(&self) {} -} - -/// Initialize the logger which the given config. If failed, it will log a message to the the browser console. -/// -/// ## Examples -/// ```rust -/// use kos_utils::logger::*; -/// init(Config::new(log::Level::Debug)); -/// ``` -/// or -/// ```rust -/// use kos_utils::logger::*; -/// init(Config::with_prefix(log::Level::Debug, "some::module")); -/// ``` -pub fn init(config: Config) { - let max_level = config.level; - let wl = Logger { - config, - style: Style::new(), - }; - - match log::set_boxed_logger(Box::new(wl)) { - Ok(_) => log::set_max_level(max_level.to_level_filter()), - Err(e) => console::error_1(&JsValue::from(e.to_string())), - } -} diff --git a/packages/kos-web/Cargo.toml b/packages/kos-web/Cargo.toml new file mode 100644 index 0000000..1eccf7d --- /dev/null +++ b/packages/kos-web/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "kos-web" +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", "rlib"] + +[features] +default = ["serde"] + +[dependencies] +strum = { version = "0.25", features = ["derive"] } +serde = { workspace = true, features = ["derive"], optional = true } +serde_json = { workspace = true } +serde-wasm-bindgen = "0.5" +qrcode-generator = "4.1" +enum_delegate = { workspace = true } +hex = { workspace = true } +enum_dispatch = "0.3" +pem = "3" +postcard = { version = "1.0.6", features = ["use-std"] } +lazy_static = { workspace = true } +kos = { workspace = true } +wasm-bindgen = { workspace = true } +num-bigint = "0.4" +num-traits = "0.2" diff --git a/packages/kos-types/src/error.rs b/packages/kos-web/src/error.rs similarity index 58% rename from packages/kos-types/src/error.rs rename to packages/kos-web/src/error.rs index a73cb16..8e55b7a 100644 --- a/packages/kos-types/src/error.rs +++ b/packages/kos-web/src/error.rs @@ -1,7 +1,4 @@ -use core::convert::Infallible; -use ed25519_dalek::SignatureError; -use reqwest::Error as ReqwestError; -use secp256k1::Error as Secp256k1Error; +use kos::chains::ChainError; use std::{error, fmt, str}; use wasm_bindgen::JsValue; @@ -33,8 +30,6 @@ pub enum Error { InvalidMessage(String), /// Out of preallocated memory NotEnoughMemory(String), - /// Reqwest error - ReqwestError(String), /// Invalid Enum Variant InvalidEnumVariant(String), /// Invalid Len @@ -44,11 +39,13 @@ pub enum Error { /// InvalidTransaction InvalidTransaction(String), /// WalletManagerError - WalletManagerError(String), + WalletManager(String), /// CipherError - CipherError(String), + Cipher(String), /// TransportError - TransportError(String), + Transport(String), + /// DelegateError + Delegate(String), } impl fmt::Display for Error { @@ -66,54 +63,18 @@ impl fmt::Display for Error { Error::InvalidSignature(e) => write!(f, "Invalid signature: {}", e), Error::InvalidMessage(e) => write!(f, "Invalid message: {}", e), Error::NotEnoughMemory(e) => write!(f, "Not enough memory: {}", e), - Error::ReqwestError(e) => write!(f, "Reqwest error: {}", e), Error::InvalidEnumVariant(e) => write!(f, "Invalid Enum Variant error: {}", e), Error::InvalidLen(e) => write!(f, "Invalid Len: {}", e), Error::InvalidNumberParse(e) => write!(f, "Invalid number parse: {}", e), Error::InvalidTransaction(e) => write!(f, "Invalid transaction: {}", e), - Error::WalletManagerError(e) => write!(f, "WalletManager error: {}", e), - Error::CipherError(e) => write!(f, "Cipher error: {}", e), - Error::TransportError(e) => write!(f, "Transport error: {}", e), + Error::WalletManager(e) => write!(f, "WalletManager error: {}", e), + Error::Cipher(e) => write!(f, "Cipher error: {}", e), + Error::Transport(e) => write!(f, "Transport error: {}", e), + Error::Delegate(e) => write!(f, "Delegate error: {}", e), } } } -impl From for Error { - fn from(secp: Secp256k1Error) -> Self { - match secp { - Secp256k1Error::IncorrectSignature - | Secp256k1Error::InvalidSignature - | Secp256k1Error::InvalidTweak - | Secp256k1Error::InvalidSharedSecret - | Secp256k1Error::InvalidPublicKeySum - | Secp256k1Error::InvalidParityValue(_) - | Secp256k1Error::InvalidRecoveryId => Self::InvalidSignature("Secp256k1Error"), - Secp256k1Error::InvalidMessage => Self::InvalidMessage(secp.to_string()), - Secp256k1Error::InvalidPublicKey => Self::InvalidPublicKey(secp.to_string()), - Secp256k1Error::InvalidSecretKey => Self::InvalidPrivateKey("Secp256k1Error"), - Secp256k1Error::NotEnoughMemory => Self::NotEnoughMemory(secp.to_string()), - } - } -} - -impl From for Error { - fn from(_: SignatureError) -> Self { - Self::InvalidSignature("Invalid signature") - } -} - -impl From for Error { - fn from(_: coins_bip39::MnemonicError) -> Self { - Self::InvalidMnemonic("Invalid mnemonic") - } -} - -impl From for Error { - fn from(_: coins_bip32::Bip32Error) -> Self { - Self::InvalidPath("Invalid path") - } -} - impl From for Error { fn from(e: serde_json::Error) -> Self { Self::JSONSerde(e.to_string()) @@ -132,26 +93,14 @@ impl From for JsValue { } } -impl From for Error { - fn from(e: ReqwestError) -> Self { - Self::ReqwestError(e.to_string()) - } -} - impl From for Error { fn from(e: hex::FromHexError) -> Self { Self::InvalidString(e.to_string()) } } -impl From for Infallible { - fn from(_: Error) -> Infallible { - unreachable!() - } -} - -impl From for Error { - fn from(_: Infallible) -> Error { - unreachable!() +impl From for Error { + fn from(err: ChainError) -> Self { + Error::Delegate(err.to_string()) } } diff --git a/packages/kos-web/src/lib.rs b/packages/kos-web/src/lib.rs new file mode 100644 index 0000000..a99d418 --- /dev/null +++ b/packages/kos-web/src/lib.rs @@ -0,0 +1,69 @@ +use crate::error::Error; +use kos::crypto::cipher; +use kos::crypto::mnemonic::generate_mnemonic; +use qrcode_generator::QrCodeEcc; +use wasm_bindgen::prelude::*; + +mod models; +mod utils; + +mod error; +mod number; +pub mod wallet; + +/// Generates a random mnemonic phrase given the number of words to generate, `count`. +#[wasm_bindgen(js_name = "generateMnemonicPhrase")] +pub fn generate_mnemonic_phrase(count: usize) -> Result { + Ok(generate_mnemonic(count)?.to_phrase()) +} + +/// Converts the given string to bytes. +#[wasm_bindgen(js_name = "toBytes")] +pub fn to_bytes(data: &str) -> Result, Error> { + Ok(data.as_bytes().to_vec()) +} + +/// Converts the given bytes to a string. +#[wasm_bindgen(js_name = "toString")] +pub fn to_string(data: &[u8]) -> Result { + String::from_utf8(data.to_vec()) + .map_err(|e| Error::InvalidString(format!("Invalid UTF-8 string: {}", e))) +} + +/// Decrypts the given data with the given password. +/// Data will have the algorithm tag prepended to it (1 byte). +#[wasm_bindgen(js_name = "decrypt")] +pub fn decrypt(data: &[u8], password: &str) -> Result, Error> { + cipher::decrypt(data, password).map_err(|e| Error::Cipher(format!("{}", e))) +} + +/// Create pem file from tag and data +#[wasm_bindgen(js_name = "toPem")] +pub fn to_pem(tag: String, data: &[u8]) -> Result { + let result = cipher::to_pem(tag, data)?; + Ok(result.to_string()) +} + +/// Decrypt pem file to bytes +#[wasm_bindgen(js_name = "fromPem")] +pub fn from_pem(data: &str, password: &str) -> Result, Error> { + let pem = cipher::string_to_pem(data)?; + decrypt(pem.contents(), password) +} + +/// Create QRCode based on data +#[wasm_bindgen(js_name = "generateQR")] +pub fn generate_qr(data: &str) -> Result, Error> { + qrcode_generator::to_png_to_vec(data, QrCodeEcc::Low, 1024) + .map_err(|e| Error::InvalidString(format!("Invalid QRCode data: {}", e))) +} + +#[wasm_bindgen(js_name = "isChainSupported")] +pub fn is_chain_supported(chain: u32) -> bool { + kos::chains::is_chain_supported(chain) +} + +#[wasm_bindgen(js_name = "getSupportedChains")] +pub fn get_supported_chains() -> Vec { + kos::chains::get_supported_chains() +} diff --git a/packages/kos-web/src/models.rs b/packages/kos-web/src/models.rs new file mode 100644 index 0000000..ea1b6a8 --- /dev/null +++ b/packages/kos-web/src/models.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Serialize}; +use wasm_bindgen::prelude::*; + +#[derive(Default, Deserialize, Serialize, Clone, Debug)] +#[wasm_bindgen] +pub struct PathOptions { + #[wasm_bindgen(skip)] + pub index: u32, + #[wasm_bindgen(skip)] + pub is_legacy: Option, +} + +#[wasm_bindgen] +impl PathOptions { + #[wasm_bindgen(constructor)] + pub fn constructor() -> Self { + Self::default() + } + + pub fn new(index: u32) -> Self { + Self { + index, + is_legacy: Some(false), + } + } + #[wasm_bindgen(js_name = setIndex)] + pub fn set_index(&mut self, index: u32) { + self.index = index; + } + #[wasm_bindgen(js_name = setLegacy)] + pub fn set_legacy(&mut self, is_legacy: bool) { + self.is_legacy = Some(is_legacy); + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[wasm_bindgen] +pub struct Transaction { + #[wasm_bindgen(skip)] + pub raw_data: Vec, + #[wasm_bindgen(skip)] + pub tx_hash: Vec, + #[wasm_bindgen(skip)] + pub signature: Vec, +} + +#[wasm_bindgen] +impl Transaction { + #[wasm_bindgen(js_name = getRawData)] + pub fn get_raw_data(&self) -> Vec { + self.raw_data.clone() + } + + #[wasm_bindgen(js_name = getTxHash)] + pub fn get_tx_hash(&self) -> Vec { + self.tx_hash.clone() + } + + #[wasm_bindgen(js_name = getSignature)] + pub fn get_signature(&self) -> Vec { + self.signature.clone() + } +} diff --git a/packages/kos-types/src/number.rs b/packages/kos-web/src/number.rs similarity index 100% rename from packages/kos-types/src/number.rs rename to packages/kos-web/src/number.rs diff --git a/packages/kos-utils/src/lib.rs b/packages/kos-web/src/utils.rs similarity index 50% rename from packages/kos-utils/src/lib.rs rename to packages/kos-web/src/utils.rs index 4c5f9bd..eb5e28e 100644 --- a/packages/kos-utils/src/lib.rs +++ b/packages/kos-web/src/utils.rs @@ -1,8 +1,7 @@ -pub mod logger; -use kos_types::error::Error; - +use crate::error::Error; use serde::{Deserialize, Serialize}; +#[allow(dead_code)] pub fn pack(t: &T) -> Result, Error> where T: Serialize + ?Sized, @@ -13,16 +12,3 @@ where pub fn unpack<'a, T: Deserialize<'a>>(bytes: &'a [u8]) -> Result { postcard::from_bytes(bytes).map_err(|e| Error::InvalidString(e.to_string())) } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_pack_unpack() { - let to_serialize = "data to serialize"; - let serialized = pack(&to_serialize).unwrap(); - let deserialized: String = unpack(&serialized).unwrap(); - assert_eq!(to_serialize, deserialized); - } -} diff --git a/packages/kos-web/src/wallet.rs b/packages/kos-web/src/wallet.rs new file mode 100644 index 0000000..0b46425 --- /dev/null +++ b/packages/kos-web/src/wallet.rs @@ -0,0 +1,365 @@ +use crate::models::{PathOptions, Transaction}; + +use pem::{parse as parse_pem, Pem}; +use serde::{Deserialize, Serialize}; +use strum::{EnumCount, IntoStaticStr}; + +use crate::error::Error; +use crate::utils::unpack; +use kos::chains::{get_chain_by_base_id, ChainOptions, Transaction as KosTransaction}; +use kos::crypto::base64; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy, EnumCount, IntoStaticStr)] +pub enum AccountType { + Mnemonic, + PrivateKey, + KleverSafe, + ReadOnly, +} + +#[wasm_bindgen] +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Wallet { + chain: u32, + account_type: AccountType, + public_address: String, + index: Option, + encrypted_data: Option>, + mnemonic: Option, + private_key: Option, + path: Option, +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct TransactionChainOptions { + #[wasm_bindgen(skip)] + pub data: ChainOptions, +} + +#[wasm_bindgen] +impl TransactionChainOptions { + #[wasm_bindgen(js_name = "newBitcoinSignOptions")] + pub fn new_bitcoin_sign_options( + input_amounts: Vec, + prev_scripts: Vec, + ) -> TransactionChainOptions { + let prev_scripts = prev_scripts + .iter() + .map(|s| base64::simple_base64_decode(s).unwrap_or_default()) + .collect(); + + TransactionChainOptions { + data: ChainOptions::BTC { + prev_scripts, + input_amounts, + }, + } + } +} + +#[wasm_bindgen] +impl Wallet { + #[wasm_bindgen(js_name = "fromMnemonic")] + /// restore wallet from mnemonic + pub fn from_mnemonic( + chain_id: u32, + mnemonic: String, + path: String, + password: Option, + ) -> Result { + // validate mnemonic entropy + kos::crypto::mnemonic::validate_mnemonic(&mnemonic)?; + + let chain = get_chain_by_base_id(chain_id) + .ok_or_else(|| Error::WalletManager("Invalid chain".to_string()))?; + + let seed = chain + .mnemonic_to_seed(mnemonic.clone(), password.unwrap_or_default()) + .map_err(|e| Error::WalletManager(format!("mnemonic to seed: {}", e)))?; + let private_key = chain + .derive(seed, path.clone()) + .map_err(|e| Error::WalletManager(format!("derive keypair: {}", e)))?; + + let public_key = chain + .get_pbk(private_key.clone()) + .map_err(|e| Error::WalletManager(format!("get public key: {}", e)))?; + let address = chain + .get_address(public_key) + .map_err(|e| Error::WalletManager(format!("get address: {}", e)))?; + + Ok(Wallet { + chain: chain_id, + account_type: AccountType::Mnemonic, + public_address: address, + index: None, + encrypted_data: None, + private_key: Some(hex::encode(private_key)), + mnemonic: Some(mnemonic), + path: Some(path), + }) + } + + #[wasm_bindgen(js_name = "fromMnemonicIndex")] + /// restore wallet from mnemonic + pub fn from_mnemonic_index( + chain_id: u32, + mnemonic: String, + path_options: &PathOptions, + password: Option, + ) -> Result { + let chain = get_chain_by_base_id(chain_id) + .ok_or_else(|| Error::WalletManager("Invalid chain".to_string()))?; + let path = chain.get_path(path_options.index, path_options.is_legacy.unwrap()); + + let mut wallet = Wallet::from_mnemonic(chain_id, mnemonic, path, password)?; + wallet.index = Some(path_options.index); + + Ok(wallet) + } + + #[wasm_bindgen(js_name = "fromPrivateKey")] + /// restore wallet from mnemonic + pub fn from_private_key(chain_id: u32, private_key: String) -> Result { + // convert hex to bytes + let private_key_bytes = hex::decode(private_key.clone())?; + + // check size of private key + if private_key_bytes.len() != 32 { + return Err(Error::WalletManager("Invalid private key".to_string())); + } + + let chain = get_chain_by_base_id(chain_id) + .ok_or_else(|| Error::WalletManager("Invalid chain".to_string()))?; + + let public_key = chain + .get_pbk(hex::decode(private_key_bytes.clone())?) + .map_err(|e| Error::WalletManager(format!("get public key: {}", e)))?; + let address = chain + .get_address(public_key.clone()) + .map_err(|e| Error::WalletManager(format!("get address: {}", e)))?; + + // create wallet from keypair + Ok(Wallet { + chain: chain_id, + account_type: AccountType::PrivateKey, + public_address: address, + index: None, + encrypted_data: None, + mnemonic: None, + private_key: Some(private_key), + path: None, + }) + } + + #[wasm_bindgen(js_name = "fromKCPem")] + /// restore wallet from mnemonic + pub fn from_kc_pem(chain: u32, data: &[u8]) -> Result { + // decode pem file + let pem = + parse_pem(data).map_err(|_| Error::WalletManager("Invalid PEM data".to_string()))?; + + let content = String::from_utf8(pem.contents().to_vec()) + .map_err(|_| Error::WalletManager("Invalid PEM data".to_string()))?; + + let pk_hex = content.chars().take(64).collect::(); + + // import from private key + Wallet::from_private_key(chain, pk_hex) + } + + #[wasm_bindgen(js_name = "fromPem")] + pub fn from_pem(data: &[u8]) -> Result { + // parse pem + let pem = + parse_pem(data).map_err(|_| Error::WalletManager("Invalid PEM data".to_string()))?; + + Wallet::import(pem) + } +} + +// wallet properties +impl Wallet { + pub fn import(pem: Pem) -> Result { + // Deserialize decrypted bytes to WalletManager + let wallet: Wallet = unpack(pem.contents()) + .map_err(|e| Error::Cipher(format!("deserialize data: {}", e)))?; + + Ok(wallet) + } +} + +#[wasm_bindgen] +// wallet properties +impl Wallet { + #[wasm_bindgen(js_name = "getChain")] + // /// get wallet chain type + pub fn get_chain(&self) -> u32 { + self.chain + } + + #[wasm_bindgen(js_name = "getAccountType")] + /// get wallet account type + pub fn get_account_type(&self) -> AccountType { + self.account_type + } + + #[wasm_bindgen(js_name = "getAddress")] + /// get wallet address + pub fn get_address(&self) -> String { + self.public_address.clone() + } + + #[wasm_bindgen(js_name = "getPath")] + /// get wallet path if wallet is created from mnemonic + pub fn get_path(&self) -> String { + match self.path { + Some(ref path) => path.clone(), + None => String::new(), + } + } + + #[wasm_bindgen(js_name = "getIndex")] + /// get wallet index if wallet is created from mnemonic index + pub fn get_index(&self) -> Result { + self.index.ok_or(Error::WalletManager( + "Wallet is not created from mnemonic index".to_string(), + )) + } + + #[wasm_bindgen(js_name = "getPrivateKey")] + /// get wallet private key + pub fn get_private_key(&self) -> String { + match self.private_key { + Some(ref pk) => pk.clone(), + None => String::new(), + } + } + + #[wasm_bindgen(js_name = "getMnemonic")] + /// get wallet mnemonic if wallet is created from mnemonic + pub fn get_mnemonic(&self) -> String { + match self.mnemonic { + Some(ref mnemonic) => mnemonic.clone(), + None => String::new(), + } + } +} + +#[wasm_bindgen] +// wallet methods +impl Wallet { + #[wasm_bindgen(js_name = "signMessage")] + /// sign message with keypair + pub fn sign_message(&self, message: &[u8]) -> Result, Error> { + match self.private_key { + Some(ref pk_hex) => { + let pk_bytes = hex::decode(pk_hex)?; + let chain = get_chain_by_base_id(self.chain) + .ok_or_else(|| Error::WalletManager("Invalid chain".to_string()))?; + + chain + .sign_message(pk_bytes, message.to_vec()) + .map_err(|e| Error::WalletManager(format!("sign message: {}", e))) + } + None => Err(Error::WalletManager("no keypair".to_string())), + } + } + + #[wasm_bindgen(js_name = "sign")] + /// sign transaction with keypair + pub fn sign( + &self, + tx_raw: &[u8], + options: Option, + ) -> Result { + match self.private_key { + Some(ref pk_hex) => { + let pk_bytes = hex::decode(pk_hex)?; + + let options = options.map(|o| o.data); + + let tx = KosTransaction { + raw_data: tx_raw.to_vec(), + signature: vec![], + tx_hash: vec![], + options, + }; + + let chain = get_chain_by_base_id(self.chain) + .ok_or_else(|| Error::WalletManager("Invalid chain".to_string()))?; + + let signed_tx = chain + .sign_tx(pk_bytes, tx) + .map_err(|e| Error::WalletManager(format!("sign transaction: {}", e)))?; + + Ok(Transaction { + raw_data: signed_tx.raw_data, + tx_hash: signed_tx.tx_hash, + signature: signed_tx.signature, + }) + } + None => Err(Error::WalletManager("no private key".to_string())), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use kos::chains::get_chain_by_base_id; + + #[test] + fn test_export_import() { + let chain = get_chain_by_base_id(38).unwrap(); + + // create wallet + let w1 = Wallet::from_mnemonic( + 38, + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(), + chain.get_path(0, false), + None, + ).unwrap(); + + // check if secret keys restored + assert_eq!( + w1.get_private_key(), + "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d" + ); + assert_eq!(w1.get_mnemonic(), "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"); + assert_eq!(w1.get_path(), "m/44'/690'/0'/0'/0'"); + } + + #[test] + fn test_sign_transaction() { + let chain = get_chain_by_base_id(2).unwrap(); + + let w1 = Wallet::from_mnemonic( + 2, + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(), + chain.get_path(0, false), + None, + ).unwrap(); + + let sign_options = TransactionChainOptions::new_bitcoin_sign_options( + vec![5000, 10000], + vec![ + "ABRUbV+OhmQeTR7sW5FVpUDZUyReSg==".to_string(), + "ABRUbV+OhmQeTR7sW5FVpUDZUyReSg==".to_string(), + ], + ); + + let raw_tx = hex::decode("0100000002badfa0606bc6a1738d8ddf951b1ebf9e87779934a5774b836668efb5a6d643970000000000fffffffffe60fbeb66791b10c765a207c900a08b2a9bd7ef21e1dd6e5b2ef1e9d686e5230000000000ffffffff028813000000000000160014e4132ab9175345e24b344f50e6d6764a651a89e6c21f000000000000160014546d5f8e86641e4d1eec5b9155a540d953245e4a00000000").unwrap(); + + let signed_tx = w1.sign(&raw_tx, Some(sign_options)).unwrap(); + + assert_eq!(signed_tx.raw_data.len(), 372); + + assert_eq!(signed_tx.tx_hash.len(), 32); + + assert_eq!(signed_tx.signature.len(), 32); + } +} diff --git a/packages/kos/Cargo.toml b/packages/kos/Cargo.toml index a842941..6afd4ee 100644 --- a/packages/kos/Cargo.toml +++ b/packages/kos/Cargo.toml @@ -1,47 +1,76 @@ [package] name = "kos" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = false -repository = { workspace = true } -rust-version = { workspace = true } +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true +build = "build.rs" +## Only necessary if using Protobuf well-known types: [lib] -crate-type = ["cdylib", "rlib"] +name = "kos" +crate-type = ["staticlib", "cdylib", "rlib"] -[features] -# If you uncomment this line, it will enable `wee_alloc`: -#default = ["wee_alloc"] +# +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.release] +opt-level = 'z' # Optimize for size +lto = true # Enable Link Time Optimization +panic = 'abort' # Generally reduces size for embedded systems +codegen-units = 1 # more aggressive code merging +strip = true # Strip symbols from the final binary +[profile.dev] +panic = 'abort' [dependencies] -log = { workspace = true } -wasm-bindgen = { workspace = true } -console_error_panic_hook = "0.1" -qrcode-generator = "4.1" -hex = { workspace = true } - -# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size -# compared to the default allocator's ~10K. However, it is slower than the default -# allocator, so it's not enabled by default. +hmac = { version = "0.12", default-features = false } +sha2 = { version = "0.10", default-features = false } +sha3 = { version = "0.10", default-features = false } +prost = { version = "0.12.1", default-features = false, features = ["prost-derive"] } +prost-types = { version = "0.12.1", default-features = false } +bech32 = { version = "0.9.1", default-features = false } +blake2b-ref = { version = "0.3.1", default-features = false } +rlp = { version = "0.5.0", default-features = false } +hex = { version = "0.4.3", features = ["alloc"], default-features = false } +tiny-json-rs = "0.2.5" +ripemd = { version = "0.1.3", default-features = false } +schnorrkel = { version = "0.11.4", default-features = false } +ed25519-dalek = { version = "2.1.0", features = ["hazmat"], default-features = false } +pbkdf2 = { version = "0.12.2 ", features = ["sha2", "hmac", "password-hash"], default-features = false } +bip39-dict = { version = "0.1.0", features = ["english"], default-features = false } +libsecp256k1 = { version = "0.7.1", features = ["hmac", "static-context"], default-features = false } +rand_core = { version = "0.6.4", default-features = false } +parity-scale-codec = { version = "3.6.9", default-features = false, features = ["derive"] } +bitcoin = { version = "0.32.5", default-features = false } -kos-types = { workspace = true } -kos-crypto = { workspace = true } -kos-sdk = { workspace = true, features = ["serde"] } -kos-utils = { workspace = true } +[target.'cfg(not(feature = "ksafe"))'.dependencies] +rand = { workspace = true } +coins-bip32 = { workspace = true } +coins-bip39 = { workspace = true } +rand_core = { version = "0.6.4", features = ["getrandom"], default-features = false } +getrandom = { version = "0.2", features = ["js"] } +alloy-dyn-abi = { version = "0.8.5", features = ["eip712"] } +serde_json = { workspace = true } +aes-gcm = "0.10" +aes = { version = "0.8" } +pem = "3" +cfb-mode = "0.8" +cbc = { version = "0.1", features = ["block-padding", "std"] } +pbkdf2 = { version = "0.12", features = ["simple"] } +[build-dependencies] +prost-build = "0.12.1" +quote = "1.0.33" +prost-wkt-build = "0.5.0" +prost-wkt = { version = "0.5.0", default-features = false } +prost-wkt-types = { version = "0.5.0", default-features = false } +heck = "0.4.1" -# The `web-sys` crate allows you to interact with the various browser APIs, -# like the DOM. -[dependencies.web-sys] -version = "0.3.66" -features = ["console"] - -[dev-dependencies] -wasm-bindgen-test = "0.3" -futures = "0.3" -js-sys = "0.3.66" +[features] +ksafe = [] +android = [] diff --git a/packages/kos/build.rs b/packages/kos/build.rs new file mode 100644 index 0000000..5cba003 --- /dev/null +++ b/packages/kos/build.rs @@ -0,0 +1,49 @@ +use std::path::PathBuf; + +fn main() { + let mut config = prost_build::Config::new(); + config.btree_map(["."]); + + let protos = vec![ + "src/protos/tron/core/Discover.proto", + "src/protos/tron/core/Tron.proto", + "src/protos/tron/core/TronInventoryItems.proto", + "src/protos/tron/core/contract/account_contract.proto", + "src/protos/tron/core/contract/asset_issue_contract.proto", + "src/protos/tron/core/contract/balance_contract.proto", + "src/protos/tron/core/contract/common.proto", + "src/protos/tron/core/contract/exchange_contract.proto", + "src/protos/tron/core/contract/market_contract.proto", + "src/protos/tron/core/contract/proposal_contract.proto", + "src/protos/tron/core/contract/shield_contract.proto", + "src/protos/tron/core/contract/smart_contract.proto", + "src/protos/tron/core/contract/storage_contract.proto", + "src/protos/tron/core/contract/vote_asset_contract.proto", + "src/protos/tron/core/contract/witness_contract.proto", + ]; + + // Specify the output directory where the generated Rust code should be placed. + config.out_dir("src/protos/generated/trx"); + + config + .compile_protos(&protos, &["src/protos/tron"]) + .unwrap(); + + let out_dir = PathBuf::from("src/protos/generated/klv"); + + config.extern_path(".google.protobuf", "crate::protos"); + config.protoc_arg("--experimental_allow_proto3_optional"); + config.compile_well_known_types(); + + let klv_protos = vec![ + "src/protos/klever/contracts.proto", + "src/protos/klever/transaction.proto", + "src/protos/klever/userAccountData.proto", + ]; + + config.out_dir(out_dir.clone()); + + config + .compile_protos(&klv_protos, &["src/protos/klever"]) + .unwrap(); +} diff --git a/packages/kos/src/api.rs b/packages/kos/src/api.rs deleted file mode 100644 index 37482c0..0000000 --- a/packages/kos/src/api.rs +++ /dev/null @@ -1,121 +0,0 @@ -use kos_sdk::chains; -use kos_types::error::Error; - -use wasm_bindgen::prelude::*; - -/// API Signature model -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct APISignature { - #[wasm_bindgen(skip)] - pub user: String, - #[wasm_bindgen(skip)] - pub nonce: u64, - #[wasm_bindgen(skip)] - pub message_hash: String, - #[wasm_bindgen(skip)] - pub signature: String, -} - -#[wasm_bindgen] -impl APISignature { - #[wasm_bindgen(js_name = "getUser")] - pub fn get_user(&self) -> String { - self.user.clone() - } - - #[wasm_bindgen(js_name = "getNonce")] - pub fn get_nonce(&self) -> u64 { - self.nonce - } - - #[wasm_bindgen(js_name = "getMessageHash")] - pub fn get_message_hash(&self) -> String { - self.message_hash.clone() - } - - #[wasm_bindgen(js_name = "getSignature")] - pub fn get_signature(&self) -> String { - self.signature.clone() - } -} - -/// Create QRCode based on data -#[wasm_bindgen(js_name = "apiSignature")] -pub fn api_signature( - private_key: String, - client_id: String, - nonce: u64, -) -> Result { - let nonce = if nonce == 0 { - // current timestamp in ms - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() as u64 - } else { - nonce - }; - - println!("nonce: {}", nonce); - - // message prefix - let prefix = "\x17KLEVER Signed Message:\n".as_bytes(); - - // create token nonce - let token_nonce = format!("{}/{}", nonce, client_id); - // sha256 token nonce - let token_nonce_hash = kos_crypto::hash::sha256(token_nonce.as_bytes()); - let token_hash_len: [u8; 4] = [0, 0, 0, 32]; - // append prefix. message length and message - let to_sign = [prefix, &token_hash_len, &token_nonce_hash].concat(); - // keccak256 hash - let hash = kos_crypto::hash::keccak256(&to_sign); - - // convert hex prive key to bytes - let pk = hex::decode(private_key).unwrap(); - // KLV KeyPair - let mut pk_array: [u8; 32] = [0; 32]; - pk_array.copy_from_slice(&pk); - - let kp = kos_crypto::keypair::KeyPair::new_ed25519(kos_crypto::ed25519::Ed25519KeyPair::new( - pk_array, - )); - - // sign message - let signature = kp.sign_digest(&hash); - - let addr = chains::KLV::get_address_from_keypair(&kp)?; - - Ok(APISignature { - user: addr, - nonce, - message_hash: hex::encode(hash), - signature: hex::encode(signature), - }) -} - -#[cfg(test)] -mod test { - use super::api_signature; - - #[test] - fn test_api_signature() { - const DEFAULT_PRIVATE_KEY: &str = - "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d"; - const CLIENT_ID: &str = "web-extension"; - - let nonce = 1692031458346; - - let v = api_signature( - DEFAULT_PRIVATE_KEY.to_string(), - CLIENT_ID.to_string(), - nonce, - ) - .unwrap(); - - println!("{:?}", v); - - assert_eq!(v.signature, "c24b408e710ca3be41fc6814e48857c226f869b13bc269a2a3c84cbd7ad89bc0433674fb816c92b656c1ea3fd18d1895d4fe56ab5427f8a50536060d5c400809") - } -} diff --git a/packages/kos/src/chains/ada/address.rs b/packages/kos/src/chains/ada/address.rs new file mode 100644 index 0000000..bbbabc9 --- /dev/null +++ b/packages/kos/src/chains/ada/address.rs @@ -0,0 +1,122 @@ +use crate::chains::ChainError; +use crate::crypto::hash::blake244_digest; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use bech32::{u5, Variant}; + +pub enum AddressType { + Base, + Pointer, + Enterprise, +} + +impl AddressType { + pub fn from_u8(val: u8) -> Self { + match val { + 0..=3 => AddressType::Base, + 4..=5 => AddressType::Pointer, + _ => AddressType::Enterprise, + } + } + + pub fn to_u8(&self) -> u8 { + match self { + AddressType::Base => 0, + AddressType::Pointer => 4, + AddressType::Enterprise => 6, + } + } +} + +#[derive(Clone)] +#[allow(dead_code)] +pub enum CredentialType { + Key, + Script, +} + +#[derive(Clone)] +pub struct StakeCredential { + _type: CredentialType, + key_hash: Option<[u8; 28]>, + script_hash: Option<[u8; 28]>, +} + +impl StakeCredential { + pub fn new(pbk: &[u8]) -> Self { + let key_hash = blake244_digest(pbk); + StakeCredential::from_key_hash(key_hash) + } + + pub fn from_key_hash(key_hash: [u8; 28]) -> Self { + StakeCredential { + _type: CredentialType::Key, + key_hash: Some(key_hash), + script_hash: None, + } + } + + pub fn get_hash(&self) -> Result<[u8; 28], ChainError> { + match self._type { + CredentialType::Key => match self.key_hash { + Some(hash) => Ok(hash), + None => Err(ChainError::InvalidCredential), + }, + CredentialType::Script => match self.script_hash { + Some(hash) => Ok(hash), + None => Err(ChainError::InvalidCredential), + }, + } + } +} + +pub struct Address { + pub network: u8, + pub _type: u8, + pub payment_cred: Option, + pub stake_cred: Option, + //TODO: Pointer attrs +} + +impl Address { + pub fn serialize(&self) -> Result, ChainError> { + let mut addr_bytes = vec![self._type << 4 | self.network]; + let addr_type = AddressType::from_u8(self._type); + match addr_type { + AddressType::Base => { + let payment_cred = self + .payment_cred + .as_ref() + .ok_or(ChainError::InvalidCredential)?; + let stake_cred = self + .stake_cred + .as_ref() + .ok_or(ChainError::InvalidCredential)?; + addr_bytes.append(&mut payment_cred.get_hash()?.to_vec()); + addr_bytes.append(&mut stake_cred.get_hash()?.to_vec()); + Ok(addr_bytes) + } + AddressType::Enterprise => { + let payment_cred = self + .payment_cred + .as_ref() + .ok_or(ChainError::InvalidCredential)?; + addr_bytes.append(&mut payment_cred.get_hash()?.to_vec()); + Ok(addr_bytes) + } + AddressType::Pointer => Err(ChainError::NotSupported), + } + } + + pub fn encode_bech32(&self) -> Result { + let addr_bytes = self.serialize()?; + let addr_encoded = bech32::convert_bits(addr_bytes.as_ref(), 8, 5, true)?; + let mut addr_u5: Vec = Vec::new(); + for i in addr_encoded { + addr_u5.push(u5::try_from_u8(i)?); + } + let res = bech32::encode("addr", addr_u5, Variant::Bech32)?; + Ok(res) + } +} diff --git a/packages/kos/src/chains/ada/mod.rs b/packages/kos/src/chains/ada/mod.rs new file mode 100644 index 0000000..bcc9ec0 --- /dev/null +++ b/packages/kos/src/chains/ada/mod.rs @@ -0,0 +1,161 @@ +mod address; + +use crate::chains::ada::address::{Address, AddressType, StakeCredential}; +use crate::chains::util::private_key_from_vec; +use crate::chains::{util, Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::bip32::{derive_ed25519_bip32, mnemonic_to_seed_ed25519_bip32}; +use crate::crypto::ed25519; +use crate::crypto::ed25519::Ed25519Trait; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +pub struct ADA {} + +impl Chain for ADA { + fn get_id(&self) -> u32 { + 20 + } + + fn get_name(&self) -> &str { + "Cardano" + } + + fn get_symbol(&self) -> &str { + "ADA" + } + + fn get_decimals(&self) -> u32 { + todo!() + } + + fn mnemonic_to_seed(&self, mnemonic: String, _password: String) -> Result, ChainError> { + Ok(mnemonic_to_seed_ed25519_bip32(mnemonic)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + if seed.len() != 96 { + return Err(ChainError::InvalidSeed); + } + + let mut seed_arr = [0u8; 96]; + seed_arr.copy_from_slice(&seed[..]); + + Ok(derive_ed25519_bip32(seed_arr, path)?.to_vec()) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/1852'/1815'/0'/0/{}", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + match private_key.len() { + 64 => { + let mut pvk = private_key_from_vec(&private_key)?; + let pbk = ed25519::Ed25519::public_from_extended(&pvk)?; + pvk.fill(0); + if pbk.len() < 32 { + return Err(ChainError::InvalidPrivateKey); + } + + Ok(pbk[0..32].to_vec()) + } + 96 => { + let mut pvk = [0u8; 64]; + let mut cc = [0u8; 32]; + pvk.copy_from_slice(&private_key[..64]); + cc.copy_from_slice(&private_key[64..]); + let vk = self.get_pbk(pvk.to_vec())?; + let mut xvk = Vec::new(); + xvk.append(&mut vk.to_vec()); + xvk.append(&mut cc.to_vec()); + + pvk.fill(0); + cc.fill(0); + + Ok(xvk) + } + _ => Err(ChainError::InvalidPrivateKey), + } + } + + fn get_address(&self, public_key: Vec) -> Result { + match public_key.len() { + 128 => { + let pbk = &public_key[..32]; + let pbk_stake = &public_key[32..64]; + + let payment = StakeCredential::new(pbk); + let stake = StakeCredential::new(pbk_stake); + let address = Address { + network: 1, + _type: AddressType::Base.to_u8(), + payment_cred: Some(payment), + stake_cred: Some(stake), + }; + + Ok(address.encode_bech32()?.to_string()) + } + 64 => { + let payment = StakeCredential::new(&public_key[0..32]); + let address = Address { + network: 1, + _type: AddressType::Enterprise.to_u8(), + payment_cred: Some(payment), + stake_cred: None, + }; + + Ok(address.encode_bech32()?.to_string()) + } + _ => Err(ChainError::InvalidPublicKey), + } + } + + fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_message( + &self, + _private_key: Vec, + _message: Vec, + ) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut private_key_bytes = util::private_key_from_vec(&private_key)?; + let sig = ed25519::Ed25519::sign_extended(&private_key_bytes, &payload)?; + private_key_bytes.fill(0); + Ok(sig) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use crate::chains::Chain; + use alloc::string::ToString; + + #[test] + fn test_address() { + let mnemonic = + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + .to_string(); + let ada = super::ADA {}; + + let seed = ada.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let path = ada.get_path(0, false); + + let pvk = ada.derive(seed, path).unwrap(); + let pbk = ada.get_pbk(pvk).unwrap(); + let addr = ada.get_address(pbk).unwrap(); + assert_eq!( + addr, + "addr1vy8ac7qqy0vtulyl7wntmsxc6wex80gvcyjy33qffrhm7ss7lxrqp" + ); + } +} diff --git a/packages/kos/src/chains/apt/mod.rs b/packages/kos/src/chains/apt/mod.rs new file mode 100644 index 0000000..d239b4a --- /dev/null +++ b/packages/kos/src/chains/apt/mod.rs @@ -0,0 +1,110 @@ +use crate::chains::util::private_key_from_vec; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::bip32; +use crate::crypto::ed25519::{Ed25519, Ed25519Trait}; +use crate::crypto::hash::sha3_digest; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +#[allow(clippy::upper_case_acronyms)] +pub struct APT {} + +impl Chain for APT { + fn get_id(&self) -> u32 { + 50 + } + + fn get_name(&self) -> &str { + "Aptos" + } + + fn get_symbol(&self) -> &str { + "APT" + } + + fn get_decimals(&self) -> u32 { + 8 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let result = bip32::derive_ed25519(&seed, path)?; + Ok(Vec::from(result)) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/637'/0'/0'/{}'", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let pbk = Ed25519::public_from_private(&pvk_bytes)?; + + pvk_bytes.fill(0); + Ok(pbk) + } + + fn get_address(&self, mut public_key: Vec) -> Result { + if public_key.len() != 32 { + return Err(ChainError::InvalidPublicKey); + } + + public_key.push(0); + let checksum = sha3_digest(&public_key[..]); + let hex_encode = hex::encode(checksum); + let mut addr = "0x".to_string(); + addr.push_str(&hex_encode[..64]); + Ok(addr) + } + + fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_message( + &self, + _private_key: Vec, + _message: Vec, + ) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let signature = Ed25519::sign(&pvk_bytes, &payload)?; + + pvk_bytes.fill(0); + Ok(signature) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use crate::chains::Chain; + use alloc::string::{String, ToString}; + + #[test] + fn test_derive() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + + let apt = super::APT {}; + let seed = apt.mnemonic_to_seed(mnemonic, String::new()).unwrap(); + let pvk = apt + .derive(seed.clone(), "m/44'/637'/0'/0'/0'".to_string()) + .unwrap(); + let pbk = apt.get_pbk(pvk.clone()).unwrap(); + let addr = apt.get_address(pbk.clone()).unwrap(); + assert_eq!( + addr, + "0xeb663b681209e7087d681c5d3eed12aaa8e1915e7c87794542c3f96e94b3d3bf" + ); + } +} diff --git a/packages/kos/src/chains/atom/mod.rs b/packages/kos/src/chains/atom/mod.rs new file mode 100644 index 0000000..cad2bbe --- /dev/null +++ b/packages/kos/src/chains/atom/mod.rs @@ -0,0 +1,144 @@ +use crate::chains::util::{private_key_from_vec, slice_from_vec}; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::hash::ripemd160_digest; +use crate::crypto::secp256k1::{Secp256K1, Secp256k1Trait}; +use crate::crypto::{bip32, secp256k1}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use bech32::{u5, Variant}; + +#[allow(clippy::upper_case_acronyms)] +pub(crate) struct ATOM { + pub addr_prefix: String, + #[allow(dead_code)] + pub network_str: String, + pub name: String, + pub symbol: String, +} + +impl ATOM { + pub fn new() -> Self { + Self { + addr_prefix: "cosmos".to_string(), + network_str: "cosmoshub-4".to_string(), + name: "Cosmos".to_string(), + symbol: "ATOM".to_string(), + } + } + + pub fn new_cosmos_based( + addr_prefix: &str, + network_str: &str, + name: &str, + symbol: &str, + ) -> Self { + Self { + addr_prefix: addr_prefix.to_string(), + network_str: network_str.to_string(), + name: name.to_string(), + symbol: symbol.to_string(), + } + } +} + +impl Chain for ATOM { + fn get_id(&self) -> u32 { + 7 + } + + fn get_name(&self) -> &str { + self.name.as_str() + } + + fn get_symbol(&self) -> &str { + self.symbol.as_str() + } + + fn get_decimals(&self) -> u32 { + 6 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let pvk = bip32::derive(&seed, path)?; + Ok(Vec::from(pvk)) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/118'/0'/0/{}", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk = private_key_from_vec(&private_key)?; + let pbk = Secp256K1::private_to_public_compressed(&pvk)?; + pvk.fill(0); + Ok(Vec::from(pbk)) + } + + fn get_address(&self, public_key: Vec) -> Result { + if public_key.len() != 33 { + return Err(ChainError::InvalidPublicKey); + } + + let mut pubkey_bytes = [0; 33]; + pubkey_bytes.copy_from_slice(&public_key[..33]); + + let hash = ripemd160_digest(&pubkey_bytes); + let addr_bytes = hash.to_vec(); + let add_encoded = bech32::convert_bits(addr_bytes.as_ref(), 8, 5, true)?; + let mut addr_u5: Vec = Vec::new(); + for i in add_encoded { + addr_u5.push(u5::try_from_u8(i)?); + } + let res = bech32::encode(self.addr_prefix.as_str(), addr_u5, Variant::Bech32)?; + Ok(res) + } + + fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_message( + &self, + _private_key: Vec, + _message: Vec, + ) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let payload_bytes = slice_from_vec(&payload)?; + + let sig = secp256k1::Secp256K1::sign(&payload_bytes, &pvk_bytes)?; + + pvk_bytes.fill(0); + Ok(sig.to_vec()) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_get_addr() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + + let atom = ATOM::new(); + let seed = atom.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let path = atom.get_path(0, false); + let pvk = atom.derive(seed, path).unwrap(); + let pbk = atom.get_pbk(pvk).unwrap(); + let addr = atom.get_address(pbk).unwrap(); + assert_eq!(addr, "cosmos19rl4cm2hmr8afy4kldpxz3fka4jguq0auqdal4"); + } +} diff --git a/packages/kos/src/chains/bch/mod.rs b/packages/kos/src/chains/bch/mod.rs new file mode 100644 index 0000000..3d7519e --- /dev/null +++ b/packages/kos/src/chains/bch/mod.rs @@ -0,0 +1,185 @@ +use crate::chains::util::{private_key_from_vec, slice_from_vec}; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::bip32; +use crate::crypto::hash::ripemd160_digest; +use crate::crypto::secp256k1::{Secp256K1, Secp256k1Trait}; +use alloc::string::String; +use alloc::vec::Vec; +use alloc::{format, vec}; + +const BCH_CHARSET: &str = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; +const BCH_PREFIX: &str = "bitcoincash"; +const BCH_MASK: u8 = 0x1f; + +#[allow(clippy::upper_case_acronyms)] +pub struct BCH {} + +impl BCH { + #[allow(clippy::needless_range_loop)] + fn expand_prefix(prefix: &str) -> Result, ChainError> { + let mut prefix_bytes = prefix.as_bytes().to_vec(); + for i in 0..prefix_bytes.len() { + prefix_bytes[i] &= BCH_MASK; + } + + prefix_bytes.push(0u8); + Ok(prefix_bytes) + } + + #[allow(clippy::needless_range_loop)] + fn create_checksum(prefix: &str, payload: &[u8]) -> Result, ChainError> { + let expanded_prefix = BCH::expand_prefix(prefix)?; + let to_encode = [expanded_prefix, payload.to_vec(), vec![0u8; 8]].concat(); + let check = BCH::poly_mod(&to_encode); + let mut ret = [0u8; 8]; + for i in 0..ret.len() { + ret[i] = ((check >> (5 * (7 - i))) & (BCH_MASK as u64)) as u8; + } + + Ok(ret.to_vec()) + } + + fn poly_mod(p_input: &[u8]) -> u64 { + const POLY_TABLE: [u64; 5] = [ + 0x98f2bc8e61, + 0x79b76d99e2, + 0xf33e5fb3c4, + 0xae2eabe2a8, + 0x1e4f43e470, + ]; + let mut check: u64 = 1; + + for &item in p_input.iter() { + let check0 = (check >> 35) as u8; // Assuming C0_SHIFT_COUNT is 35, as it's not defined in the original code + + check = ((check & 0x07ffffffff) << 5) ^ (item as u64); + + if (check0 & 0x01) > 0 { + check ^= POLY_TABLE[0]; + } + if (check0 & 0x02) > 0 { + check ^= POLY_TABLE[1]; + } + if (check0 & 0x04) > 0 { + check ^= POLY_TABLE[2]; + } + if (check0 & 0x08) > 0 { + check ^= POLY_TABLE[3]; + } + if (check0 & 0x10) > 0 { + check ^= POLY_TABLE[4]; + } + } + + check ^ 1 + } +} + +impl Chain for BCH { + fn get_id(&self) -> u32 { + 18 + } + fn get_name(&self) -> &str { + "Bitcoin Cash" + } + + fn get_symbol(&self) -> &str { + "BCH" + } + + fn get_decimals(&self) -> u32 { + 8 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let pvk = bip32::derive(&seed, path)?; + Ok(pvk.to_vec()) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/145'/0'/0/{}", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let pbk = Secp256K1::private_to_public_compressed(&pvk_bytes)?; + pvk_bytes.fill(0); + Ok(pbk.to_vec()) + } + + #[allow(clippy::needless_range_loop)] + fn get_address(&self, public_key: Vec) -> Result { + if public_key.len() != 33 { + return Err(ChainError::InvalidPublicKey); + } + + let mut pubkey_bytes = [0; 33]; + pubkey_bytes.copy_from_slice(&public_key[..33]); + + let hash = ripemd160_digest(&pubkey_bytes); + let to_base_58 = [vec![0x00], hash[..].to_vec()].concat(); + let addr_converted = bech32::convert_bits(&to_base_58, 8, 5, true)?; + let checksum = BCH::create_checksum(BCH_PREFIX, &addr_converted)?; + + let mut final_address = Vec::new(); + for i in 0..addr_converted.len() { + final_address.push(BCH_CHARSET.as_bytes()[addr_converted[i] as usize]); + } + + for i in 0..checksum.len() { + final_address.push(BCH_CHARSET.as_bytes()[checksum[i] as usize]); + } + + Ok(String::from_utf8(final_address)?) + } + + fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_message( + &self, + _private_key: Vec, + _message: Vec, + ) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let payload_bytes = slice_from_vec(&payload)?; + + let sig = Secp256K1::sign(&payload_bytes, &pvk_bytes)?; + + pvk_bytes.fill(0); + Ok(sig.to_vec()) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use crate::chains::Chain; + use alloc::string::ToString; + + #[test] + fn test_bch_address() { + let bch = super::BCH {}; + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + + let seed = bch.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let path = bch.get_path(0, false); + let pvk = bch.derive(seed, path).unwrap(); + let pbk = bch.get_pbk(pvk).unwrap(); + let addr = bch.get_address(pbk).unwrap(); + + assert_eq!(addr, "qqyx49mu0kkn9ftfj6hje6g2wfer34yfnq5tahq3q6"); + } +} diff --git a/packages/kos/src/chains/bnb/mod.rs b/packages/kos/src/chains/bnb/mod.rs new file mode 100644 index 0000000..085ca40 --- /dev/null +++ b/packages/kos/src/chains/bnb/mod.rs @@ -0,0 +1,118 @@ +use crate::chains::util::{private_key_from_vec, slice_from_vec}; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::bip32; +use crate::crypto::hash::ripemd160_digest; +use crate::crypto::secp256k1::{Secp256K1, Secp256k1Trait}; +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; +use bech32::{u5, Variant}; + +const BNB_PREFIX: &str = "bnb"; + +#[allow(clippy::upper_case_acronyms)] +pub struct BNB {} + +impl Chain for BNB { + fn get_id(&self) -> u32 { + 17 + } + + fn get_name(&self) -> &str { + "Binance" + } + + fn get_symbol(&self) -> &str { + "BNB" + } + + fn get_decimals(&self) -> u32 { + todo!() + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let pvk = bip32::derive(&seed, path)?; + Ok(pvk.to_vec()) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/714'/0'/0/{}", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + + let pbk = Secp256K1::private_to_public_compressed(&pvk_bytes)?; + pvk_bytes.fill(0); + Ok(pbk.to_vec()) + } + + fn get_address(&self, public_key: Vec) -> Result { + if public_key.len() != 33 { + return Err(ChainError::InvalidPublicKey); + } + + let mut pubkey_bytes = [0; 33]; + pubkey_bytes.copy_from_slice(&public_key[..33]); + + let hash = ripemd160_digest(&pubkey_bytes); + let add_encoded = bech32::convert_bits(&hash, 8, 5, true)?; + let mut addr_u5: Vec = Vec::new(); + for i in add_encoded { + addr_u5.push(u5::try_from_u8(i)?); + } + let res = bech32::encode(BNB_PREFIX, addr_u5, Variant::Bech32)?; + Ok(res) + } + + fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_message( + &self, + _private_key: Vec, + _message: Vec, + ) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let payload_bytes = slice_from_vec(&payload)?; + + let sig = Secp256K1::sign(&payload_bytes, &pvk_bytes)?; + + pvk_bytes.fill(0); + Ok(sig.to_vec()) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::string::ToString; + + #[test] + fn test_mnemonic_to_seed() { + let bnb = BNB {}; + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + + let seed = bnb.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let path = bnb.get_path(0, false); + let pvk = bnb.derive(seed, path).unwrap(); + let pbk = bnb.get_pbk(pvk.clone()).unwrap(); + + let addr = bnb.get_address(pbk.clone()).unwrap(); + + assert_eq!(addr, "bnb1rxhz5vdv4fvdjye8gxqvfv0yvg20jtlwf4f38d"); + } +} diff --git a/packages/kos/src/chains/btc/mod.rs b/packages/kos/src/chains/btc/mod.rs new file mode 100644 index 0000000..a19be83 --- /dev/null +++ b/packages/kos/src/chains/btc/mod.rs @@ -0,0 +1,342 @@ +use crate::chains::util::{private_key_from_vec, slice_from_vec}; +use crate::chains::{Chain, ChainError, ChainOptions, Transaction, TxInfo}; +use crate::crypto::b58::b58enc; +use crate::crypto::bip32; +use crate::crypto::hash::{ripemd160_digest, sha256_digest}; +use crate::crypto::secp256k1::{Secp256K1, Secp256k1Trait}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use alloc::{format, vec}; +use bech32::{u5, Variant}; +use bitcoin::{ecdsa, secp256k1, sighash, Amount, Denomination, Psbt}; + +const BITCOIN_MESSAGE_PREFIX: &str = "\x18Bitcoin Signed Message:\n"; + +#[allow(clippy::upper_case_acronyms)] +pub struct BTC { + pub id: u32, + pub addr_prefix: String, + pub symbol: String, + pub name: String, + pub use_legacy_address: bool, + pub legacy_version: u8, +} + +impl BTC { + pub fn new() -> Self { + BTC::new_btc_based(2, "bc", "BTC", "Bitcoin") + } + + pub fn new_btc_based(id: u32, addr_prefix: &str, symbol: &str, name: &str) -> Self { + BTC { + id, + addr_prefix: addr_prefix.to_string(), + symbol: symbol.to_string(), + name: name.to_string(), + use_legacy_address: false, + legacy_version: 0, + } + } + + pub fn new_legacy_btc_based(id: u32, legacy_version: u8, symbol: &str, name: &str) -> Self { + BTC { + id, + addr_prefix: "".to_string(), + symbol: symbol.to_string(), + name: name.to_string(), + use_legacy_address: true, + legacy_version, + } + } +} + +impl BTC { + fn get_addr_new(&self, public_key: Vec) -> Result { + if public_key.len() != 33 { + return Err(ChainError::InvalidPublicKey); + } + + let mut pubkey_bytes = [0; 33]; + pubkey_bytes.copy_from_slice(&public_key[..33]); + + let hash = ripemd160_digest(&pubkey_bytes); + let addr_bytes = hash.to_vec(); + let addr_converted = bech32::convert_bits(&addr_bytes, 8, 5, true)?; + let mut addr_u5: Vec = Vec::from([u5::try_from_u8(0).unwrap(); 1]); + for i in addr_converted { + addr_u5.push(u5::try_from_u8(i)?); + } + + let res = bech32::encode(self.addr_prefix.as_str(), addr_u5, Variant::Bech32)?; + Ok(res) + } + + pub fn get_addr_legacy(&self, public_key: Vec) -> Result { + if public_key.len() != 33 { + return Err(ChainError::InvalidPublicKey); + } + + let mut pubkey_bytes = [0; 33]; + pubkey_bytes.copy_from_slice(&public_key[..33]); + + let hash = ripemd160_digest(&pubkey_bytes); + + let to_base_58 = [vec![self.legacy_version], hash[..].to_vec()].concat(); + let checksum = sha256_digest(&sha256_digest(&to_base_58)); + let checksum_bytes = checksum[..4].to_vec(); + let to_base_58 = [&to_base_58[..], &checksum_bytes[..]].concat(); + + let res = b58enc(&to_base_58); + let addr = String::from_utf8(res)?; + Ok(addr) + } + + pub fn prepare_message(message: Vec) -> [u8; 32] { + let mut msg = Vec::new(); + msg.extend_from_slice(BITCOIN_MESSAGE_PREFIX.as_bytes()); + msg.extend_from_slice(message.len().to_string().as_bytes()); + msg.extend_from_slice(&message); + + sha256_digest(&msg[..]) + } +} + +impl Chain for BTC { + fn get_id(&self) -> u32 { + self.id + } + fn get_name(&self) -> &str { + self.name.as_str() + } + + fn get_symbol(&self) -> &str { + self.symbol.as_str() + } + + fn get_decimals(&self) -> u32 { + 8 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let pvk = bip32::derive(&seed, path)?; + Ok(pvk.to_vec()) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/84'/0'/0'/0/{}", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + if private_key.len() != 32 { + return Err(ChainError::InvalidPrivateKey); + } + + let mut pk_bytes: [u8; 32] = [0; 32]; + pk_bytes.copy_from_slice(&private_key[..32]); + + let pbk = Secp256K1::private_to_public_compressed(&pk_bytes)?; + Ok(pbk.to_vec()) + } + + #[allow(clippy::needless_question_mark)] + fn get_address(&self, public_key: Vec) -> Result { + if self.use_legacy_address { + return Ok(self.get_addr_legacy(public_key)?); + } + + Ok(self.get_addr_new(public_key)?) + } + + fn sign_tx(&self, private_key: Vec, tx: Transaction) -> Result { + let mut tx = tx; + + let options = tx.options.clone().ok_or(ChainError::MissingOptions)?; + + let (prev_scripts, input_amounts) = match options { + ChainOptions::BTC { + prev_scripts, + input_amounts, + } => (prev_scripts, input_amounts), + _ => { + return Err(ChainError::InvalidOptions); + } + }; + + let transaction: bitcoin::Transaction = + bitcoin::consensus::deserialize(tx.raw_data.as_ref()).unwrap(); + + let mut psbt = Psbt::from_unsigned_tx(transaction.clone()).unwrap(); + + let mut cache = sighash::SighashCache::new(transaction); + + let sk = secp256k1::SecretKey::from_slice(private_key.clone().as_slice()).unwrap(); + + let btc = BTC::new(); + let pk = btc.get_pbk(private_key)?; + + let public_key = bitcoin::PublicKey::from_slice(pk.as_slice()).unwrap(); + + let secp = bitcoin::secp256k1::Secp256k1::new(); + + let values = input_amounts + .iter() + .map(|x| Amount::from_str_in(&x.to_string(), Denomination::Satoshi).unwrap()) + .collect::>(); + + for inp_idx in 0..psbt.inputs.len() { + let utxo = bitcoin::TxOut { + value: values[inp_idx], + script_pubkey: prev_scripts[inp_idx].clone().into(), + }; + psbt.inputs[inp_idx].witness_utxo = Some(utxo); + } + + // sign inputs + for inp_idx in 0..psbt.inputs.len() { + // compute sighash + let (msg, sighash_ty) = psbt.sighash_ecdsa(inp_idx, &mut cache).unwrap(); + + // sign + let sig = ecdsa::Signature { + signature: secp.sign_ecdsa(&msg, &sk), + sighash_type: sighash_ty, + }; + + // insert signature + psbt.inputs[inp_idx].partial_sigs.insert(public_key, sig); + } + + // finalize + for inp_idx in 0..psbt.inputs.len() { + let script_witness = { + match psbt.inputs[inp_idx].partial_sigs.first_key_value() { + Some((pubkey, sig)) => { + let mut script_witness = bitcoin::Witness::new(); + script_witness.push(sig.to_vec()); + script_witness.push(pubkey.to_bytes()); + script_witness + } + _ => bitcoin::Witness::default(), + } + }; + psbt.inputs[inp_idx].final_script_witness = Some(script_witness); + } + + let signed_tx = psbt.extract_tx().unwrap(); + + tx.raw_data = bitcoin::consensus::encode::serialize(&signed_tx); + + tx.signature = bitcoin::consensus::encode::serialize(&signed_tx.compute_wtxid()); + + tx.tx_hash = bitcoin::consensus::encode::serialize(&signed_tx.compute_txid()); + + Ok(tx) + } + + fn sign_message(&self, private_key: Vec, message: Vec) -> Result, ChainError> { + let prepared_message = BTC::prepare_message(message); + let signature = self.sign_raw(private_key, prepared_message.to_vec())?; + Ok(signature) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let payload_bytes = slice_from_vec(&payload)?; + let sig = Secp256K1::sign(&payload_bytes, &pvk_bytes)?; + + pvk_bytes.fill(0); + Ok(sig.to_vec()) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::string::ToString; + + #[test] + fn test_derive() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let expected = + hex::decode("4604b4b710fe91f584fff084e1a9159fe4f8408fff380596a604948474ce4fa3") + .unwrap(); + let btc = BTC::new(); + let path = btc.get_path(0, false); + let seed = btc.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let res = btc.derive(seed, path).unwrap(); + assert_eq!(res, expected); + } + + #[test] + fn test_get_addr() { + let pvk = hex::decode("4604b4b710fe91f584fff084e1a9159fe4f8408fff380596a604948474ce4fa3") + .unwrap(); + + let btc = BTC::new(); + let pbk = btc.get_pbk(pvk).unwrap(); + let addr = btc.get_address(pbk).unwrap(); + assert_eq!(addr, "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"); + } + + #[test] + fn test_get_addr_legacy() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let path = "m/44'/3'/0'/0/0".to_string(); + + let btc = BTC::new_legacy_btc_based(12, 0x1e, "DOGE", "Dogecoin"); + let seed = btc.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let pvk = btc.derive(seed, path).unwrap(); + let pbk = btc.get_pbk(pvk).unwrap(); + let addr = btc.get_address(pbk).unwrap(); + assert_eq!(addr, "DBus3bamQjgJULBJtYXpEzDWQRwF5iwxgC"); + } + + #[test] + fn test_sign_message() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let path = "m/44'/0'/0'/0/0".to_string(); + + let btc = BTC::new(); + let seed = btc.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let pvk = btc.derive(seed, path).unwrap(); + let message = "Hello, World!".as_bytes().to_vec(); + let signature = btc.sign_message(pvk, message).unwrap(); + assert_eq!(hex::encode(signature.clone()), "9d561a0ba6ea562e61606e7f3b6a92c889246eec2c05e86e3f465f43469ae9436d7e46accdcfaea848460e42c83c52238b6956c4bfb192e67023b6024e95bdcf01"); + assert_eq!(signature.len(), 65); + } + + #[test] + fn sign_transaction() { + let raw = hex::decode("0100000002badfa0606bc6a1738d8ddf951b1ebf9e87779934a5774b836668efb5a6d643970000000000fffffffffe60fbeb66791b10c765a207c900a08b2a9bd7ef21e1dd6e5b2ef1e9d686e5230000000000ffffffff028813000000000000160014e4132ab9175345e24b344f50e6d6764a651a89e6c21f000000000000160014546d5f8e86641e4d1eec5b9155a540d953245e4a00000000").unwrap(); + + let pvk = hex::decode("4604b4b710fe91f584fff084e1a9159fe4f8408fff380596a604948474ce4fa3") + .unwrap(); + let btc = BTC::new(); + let transaction = Transaction { + raw_data: raw, + signature: vec![], + tx_hash: vec![], + options: Option::from(ChainOptions::BTC { + prev_scripts: vec![ + hex::decode("0014546d5f8e86641e4d1eec5b9155a540d953245e4a").unwrap(), + hex::decode("0014546d5f8e86641e4d1eec5b9155a540d953245e4a").unwrap(), + ], + input_amounts: vec![5000, 10000], + }), + }; + + let signed_tx = btc.sign_tx(pvk, transaction).unwrap(); + + assert_eq!(signed_tx.signature.len(), 32); + assert_eq!(signed_tx.tx_hash.len(), 32); + assert_eq!(signed_tx.raw_data.len(), 372); + } +} diff --git a/packages/kos/src/chains/constants.rs b/packages/kos/src/chains/constants.rs new file mode 100644 index 0000000..e335334 --- /dev/null +++ b/packages/kos/src/chains/constants.rs @@ -0,0 +1,57 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused)] + +pub const RESERVED: u32 = 0; +pub const TRX: u32 = 1; +pub const BTC: u32 = 2; +pub const ETH: u32 = 3; +pub const BSC: u32 = 4; +pub const EGLD: u32 = 5; +pub const DOT: u32 = 6; +pub const KSM: u32 = 7; +pub const KLV: u32 = 8; +pub const LTC: u32 = 9; +pub const DOGE: u32 = 10; +pub const DASH: u32 = 11; +pub const POLYGON: u32 = 12; +pub const BNB: u32 = 13; +pub const REEF: u32 = 14; +pub const ICP: u32 = 15; +pub const DGB: u32 = 16; +pub const XRP: u32 = 17; +pub const MOVR: u32 = 18; +pub const GLMR: u32 = 19; +pub const SYS: u32 = 20; +pub const HT: u32 = 21; +pub const BCH: u32 = 22; +pub const SDN: u32 = 23; +pub const ASTR: u32 = 24; +pub const ONE: u32 = 25; +pub const SYS_NEVM: u32 = 26; +pub const KILT: u32 = 27; +pub const ALTAIR: u32 = 28; +pub const SOL: u32 = 29; +pub const XLM: u32 = 30; +pub const SUBSTRATE: u32 = 31; +pub const COSMOS: u32 = 32; +pub const CELESTIA: u32 = 33; +pub const SUI: u32 = 34; +pub const CUDOS: u32 = 35; +pub const AURA: u32 = 36; +pub const APT: u32 = 37; +pub const ADA: u32 = 38; +pub const AVAIL: u32 = 39; +pub const ROLLUX: u32 = 40; +pub const AVAX: u32 = 41; +pub const CFG: u32 = 42; +pub const ARB: u32 = 43; +pub const BASE: u32 = 44; +pub const NEAR: u32 = 45; +pub const FTM: u32 = 46; +pub const CHZ: u32 = 47; +pub const OP: u32 = 48; +pub const HBAR: u32 = 49; +pub const FLOW: u32 = 50; +pub const POLYGON_ZKEVM: u32 = 51; +pub const STOLZ: u32 = 52; diff --git a/packages/kos/src/chains/default/mod.rs b/packages/kos/src/chains/default/mod.rs new file mode 100644 index 0000000..fbaeab2 --- /dev/null +++ b/packages/kos/src/chains/default/mod.rs @@ -0,0 +1,51 @@ +pub struct NONE; + +impl Chain for NONE { + fn get_name(&self) -> &str { + return "NONE"; + } + + fn get_symbol(&self) -> &str { + return "NONE"; + } + + fn get_decimals(&self) -> u32 { + return 0; + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn get_address(&self, public_key: Vec) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_tx( + &self, + private_key: Vec, + mut tx: Transaction, + ) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_message(&self, private_key: Vec, message: Vec) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn get_tx_info(&self, raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} diff --git a/packages/kos/src/chains/egld/mod.rs b/packages/kos/src/chains/egld/mod.rs new file mode 100644 index 0000000..1ba113e --- /dev/null +++ b/packages/kos/src/chains/egld/mod.rs @@ -0,0 +1,105 @@ +use crate::chains::util::private_key_from_vec; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::bip32; +use crate::crypto::ed25519::{Ed25519, Ed25519Trait}; +use crate::crypto::hash::keccak256_digest; +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; +use bech32::{u5, Variant}; + +pub struct EGLD {} + +impl Chain for EGLD { + fn get_id(&self) -> u32 { + 5 + } + + fn get_name(&self) -> &str { + "Elrond" + } + + fn get_symbol(&self) -> &str { + "EGLD" + } + + fn get_decimals(&self) -> u32 { + 18 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let result = bip32::derive_ed25519(&seed, path)?; + Ok(Vec::from(result)) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/508'/0'/0'/{}'", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let pvk_bytes = private_key_from_vec(&private_key)?; + let pbk = Ed25519::public_from_private(&pvk_bytes)?; + Ok(pbk) + } + + fn get_address(&self, public_key: Vec) -> Result { + let pbk_hash = keccak256_digest(&public_key[..]); + let add_encoded = bech32::convert_bits(pbk_hash.as_ref(), 8, 5, true)?; + let mut addr_u5: Vec = Vec::new(); + for i in add_encoded { + addr_u5.push(u5::try_from_u8(i)?); + } + let res = bech32::encode("erd", addr_u5, Variant::Bech32)?; + Ok(res) + } + + fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_message( + &self, + _private_key: Vec, + _message: Vec, + ) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + + let signature = Ed25519::sign(&pvk_bytes, &payload)?; + pvk_bytes.fill(0); + Ok(signature) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use crate::chains::Chain; + use alloc::string::{String, ToString}; + + #[test] + fn test_derive() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + + let egld = super::EGLD {}; + let seed = egld.mnemonic_to_seed(mnemonic, String::new()).unwrap(); + let path = egld.get_path(0, false); + let pvk = egld.derive(seed.clone(), path).unwrap(); + let pbk = egld.get_pbk(pvk.clone()).unwrap(); + let addr = egld.get_address(pbk.clone()).unwrap(); + assert_eq!( + addr, + "erd19a76e058k023kss9zhg5zfwtc9m8qwnq0tq8v3cp9e0632q9437sxrxrec" + ); + } +} diff --git a/packages/kos/src/chains/eth/mod.rs b/packages/kos/src/chains/eth/mod.rs new file mode 100644 index 0000000..a290c74 --- /dev/null +++ b/packages/kos/src/chains/eth/mod.rs @@ -0,0 +1,319 @@ +mod models; + +use crate::chains::eth::models::EthereumTransaction; +use crate::chains::util::{private_key_from_vec, slice_from_vec}; +use crate::chains::{Chain, ChainError, Transaction, TxInfo, TxType}; +use crate::crypto::hash::keccak256_digest; +use crate::crypto::secp256k1::{Secp256K1, Secp256k1Trait}; +use crate::crypto::{bip32, secp256k1}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +#[cfg(not(feature = "ksafe"))] +use alloy_dyn_abi::TypedData; + +pub(crate) const ETH_ADDR_SIZE: usize = 20; +const ETH_MESSAGE_PREFIX: &[u8; 26] = b"\x19Ethereum Signed Message:\n"; + +#[allow(clippy::upper_case_acronyms)] +pub struct ETH { + pub id: u32, + pub chaincode: u32, + pub symbol: String, + pub name: String, +} + +impl ETH { + pub fn new() -> Self { + ETH::new_eth_based(3, 1, "ETH", "Ethereum") + } + + pub fn new_eth_based(id: u32, chaincode: u32, symbol: &str, name: &str) -> Self { + ETH { + id, + chaincode, + symbol: symbol.to_string(), + name: name.to_string(), + } + } + + pub(crate) fn addr_bytes_to_string( + address_bytes: [u8; ETH_ADDR_SIZE], + ) -> Result { + let addr_str = hex::encode(address_bytes); + + let address_hash = hex::encode(keccak256_digest(addr_str.as_bytes())); + + let address = addr_str.as_str(); + let address = + address + .char_indices() + .fold(String::from("0x"), |mut acc, (index, address_char)| { + // this cannot fail since it's Keccak256 hashed + let n = u16::from_str_radix(&address_hash[index..index + 1], 16).unwrap(); + + if n > 7 { + // make char uppercase if ith character is 9..f + acc.push_str(&address_char.to_uppercase().to_string()) + } else { + // already lowercased + acc.push(address_char) + } + + acc + }); + Ok(address.to_string()) + } +} + +impl Chain for ETH { + fn get_id(&self) -> u32 { + self.id + } + + fn get_name(&self) -> &str { + self.name.as_str() + } + + fn get_symbol(&self) -> &str { + self.symbol.as_str() + } + + fn get_decimals(&self) -> u32 { + 18 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let pvk = bip32::derive(&seed, path)?; + Ok(Vec::from(pvk)) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/60'/0'/0/{}", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk = private_key_from_vec(&private_key)?; + let pbk = Secp256K1::private_to_public_uncompressed(&pvk)?; + pvk.fill(0); + Ok(Vec::from(pbk)) + } + + fn get_address(&self, public_key: Vec) -> Result { + let pbk_hash = keccak256_digest(&public_key[1..]); + let mut address_bytes: [u8; ETH_ADDR_SIZE] = [0; ETH_ADDR_SIZE]; + address_bytes.copy_from_slice(&pbk_hash[12..]); + + ETH::addr_bytes_to_string(address_bytes) + } + + fn sign_tx( + &self, + private_key: Vec, + mut tx: Transaction, + ) -> Result { + let mut eth_tx = EthereumTransaction::decode(&tx.raw_data)?; + + //Ensure empty signature + eth_tx.signature = None; + if eth_tx.transaction_type == models::TransactionType::Legacy { + eth_tx.chain_id = Some(self.chaincode as u64); + } + + let new_rlp = eth_tx.encode()?; + let to_sign = keccak256_digest(&new_rlp[..]); + let signature = self.sign_raw(private_key, to_sign.to_vec())?; + if signature.len() != 65 { + return Err(ChainError::InvalidSignature); + } + + let _sig_hex = hex::encode(&signature[..]); + + let mut signature_bytes: [u8; 65] = [0; 65]; + signature_bytes.copy_from_slice(&signature[..]); + eth_tx.signature = Some(signature_bytes); + let signed_rlp = eth_tx.encode()?; + tx.raw_data = signed_rlp.clone(); + tx.tx_hash = Vec::from(keccak256_digest(&signed_rlp[..])); + tx.signature = signature.to_vec(); + Ok(tx) + } + + fn sign_message(&self, private_key: Vec, message: Vec) -> Result, ChainError> { + #[cfg(not(feature = "ksafe"))] + { + if let Ok(data) = std::str::from_utf8(&message) { + if let Ok(typed_data) = serde_json::from_str::(data) { + let digest = typed_data.eip712_signing_hash().unwrap(); + return self.sign_raw(private_key, digest.to_vec()); + } + } + } + + let to_sign = [ + ETH_MESSAGE_PREFIX, + message.len().to_string().as_bytes(), + &message[..], + ] + .concat(); + let hashed = keccak256_digest(&to_sign[..]); + let signature = self.sign_raw(private_key, hashed.to_vec())?; + Ok(signature) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let payload_bytes = slice_from_vec(&payload)?; + let sig = secp256k1::Secp256K1::sign(&payload_bytes, &pvk_bytes)?; + + pvk_bytes.fill(0); + Ok(sig.to_vec()) + } + + fn get_tx_info(&self, raw_tx: Vec) -> Result { + let eth_tx = EthereumTransaction::decode(raw_tx.as_slice())?; + let mut tx_info = TxInfo { + sender: String::new(), + receiver: String::new(), + value: 0.0, + tx_type: TxType::Transfer, + }; + + if eth_tx.to.is_some() { + tx_info.tx_type = TxType::TriggerContract; + let addr_vec = eth_tx.to.unwrap_or([0; ETH_ADDR_SIZE].to_vec()); + let mut address_bytes: [u8; ETH_ADDR_SIZE] = [0; ETH_ADDR_SIZE]; + address_bytes.copy_from_slice(&addr_vec[..]); + tx_info.receiver = ETH::addr_bytes_to_string(address_bytes)?; + } + + tx_info.value = eth_tx.value.to_f64(self.get_decimals()); + + Ok(tx_info) + } +} + +#[cfg(test)] +mod test { + use crate::chains::Chain; + use alloc::string::ToString; + use alloc::vec::Vec; + + #[test] + fn test_derive() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + + let eth = super::ETH::new(); + let seed = eth.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let path = eth.get_path(0, false); + let pvk = eth.derive(seed, path).unwrap(); + let pbk = eth.get_pbk(pvk).unwrap(); + let addr = eth.get_address(pbk).unwrap(); + assert_eq!(addr, "0x9858EfFD232B4033E47d90003D41EC34EcaEda94"); + } + + #[test] + fn test_sign_tx() { + let raw_tx = hex::decode( + "b302f101819e84ae7937b285035f6cccc58252089498de4c83810b87f0e2cd92d80c9fac28c4ded4818568c696991f80c0808080", + ) + .unwrap(); + let pvk = hex::decode("1ab42cc412b618bdea3a599e3c9bae199ebf030895b039e9db1e30dafb12b727") + .unwrap(); + let eth = super::ETH::new(); + + let tx = crate::chains::Transaction { + raw_data: raw_tx, + tx_hash: Vec::new(), + signature: Vec::new(), + options: None, + }; + + let _ = eth.sign_tx(pvk, tx).unwrap(); + } + + #[test] + fn test_sign_london_tx() { + let raw_tx = hex::decode("b87602f8730182014f84147b7eeb85084ec9f83f8301450994dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb0000000000000000000000004cbeee256240c92a9ad920ea6f4d7df6466d2cdc000000000000000000000000000000000000000000000000000000000000000ac0808080").unwrap(); + let pvk = hex::decode("1ab42cc412b618bdea3a599e3c9bae199ebf030895b039e9db1e30dafb12b727") + .unwrap(); + let eth = super::ETH::new_eth_based(3, 56, "ETH", "Ethereum"); + + let tx = crate::chains::Transaction { + raw_data: raw_tx, + tx_hash: Vec::new(), + signature: Vec::new(), + options: None, + }; + + let _ = eth.sign_tx(pvk, tx).unwrap(); + } + + #[test] + fn test_decode_tx() { + let raw_tx = hex::decode( + "ad02eb01038493a7d5d085068da15595825208944cbeee256240c92a9ad920ea6f4d7df6466d2cdc0a80c0808080", + ) + .unwrap(); + let eth = super::ETH::new(); + let tx_info = eth.get_tx_info(raw_tx).unwrap(); + assert_eq!( + tx_info.receiver, + "0x4cBeee256240c92A9ad920ea6f4d7Df6466D2Cdc" + ); + assert_eq!(tx_info.value, 4.523128485832664e57); + } + + #[test] + fn test_sign_typed_data() { + let data = r#"{ + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallet", "type": "address" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "Person" }, + { "name": "contents", "type": "string" } + ] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } + }"#; + + let eth = super::ETH::new(); + let pvk = hex::decode("1ab42cc412b618bdea3a599e3c9bae199ebf030895b039e9db1e30dafb12b727") + .unwrap(); + let message = data.as_bytes(); + + let signature = eth.sign_message(pvk, message.to_vec()).unwrap(); + assert_eq!(signature.len(), 65); + } +} diff --git a/packages/kos/src/chains/eth/models.rs b/packages/kos/src/chains/eth/models.rs new file mode 100644 index 0000000..53fccab --- /dev/null +++ b/packages/kos/src/chains/eth/models.rs @@ -0,0 +1,183 @@ +use crate::crypto::bignum::U256; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use rlp::{DecoderError, Rlp, RlpStream}; + +extern crate rlp; + +#[derive(Debug, PartialEq)] +pub enum TransactionType { + Legacy, + Eip155, +} + +pub struct EthereumTransaction { + pub transaction_type: TransactionType, + pub nonce: U256, + pub to: Option>, // Ethereum address (Option for contract creation) + pub gas: U256, + pub gas_price: Option, + pub value: U256, + pub data: Vec, + pub chain_id: Option, + pub max_fee_per_gas: Option, + pub max_priority_fee_per_gas: Option, + pub signature: Option<[u8; 65]>, +} + +impl EthereumTransaction { + #[allow(clippy::unnecessary_unwrap)] + pub fn decode(rlp_data: &[u8]) -> Result { + let rlp = Rlp::new(rlp_data); + + let tx_result = self::EthereumTransaction::decode_legacy(&rlp); + if tx_result.is_ok() { + return Ok(tx_result.unwrap()); + } + + let tx_result = self::EthereumTransaction::decode_eip_envelope(rlp); + if tx_result.is_ok() { + return Ok(tx_result.unwrap()); + } + + let rlp = Rlp::new(&rlp_data[2..]); + self::EthereumTransaction::decode_eip155(rlp) + } + + pub fn encode_eip25519(&self) -> Result { + let mut rlp = RlpStream::new(); + let list_size = if self.signature.is_some() { 12 } else { 9 }; + rlp.begin_list(list_size); + rlp.append(&self.chain_id.unwrap_or(0)); + rlp.append(&self.nonce); + rlp.append( + &self + .max_priority_fee_per_gas + .clone() + .unwrap_or(U256([0; 32])), + ); + let gas_price = self.max_fee_per_gas.clone().unwrap_or(U256([0; 32])); + + rlp.append(&gas_price); + rlp.append(&self.gas); + if let Some(to) = &self.to { + rlp.append(to); + } else { + rlp.append(&""); + } + rlp.append(&self.value); + rlp.append(&self.data); + + rlp.begin_list(0); + + if self.signature.is_some() { + let sig = self.signature.unwrap_or([0; 65]); + let mut r = U256([0; 32]); + r.0.copy_from_slice(&sig[..32]); + let mut s = U256([0; 32]); + s.0.copy_from_slice(&sig[32..64]); + let v = sig[64] as u64; + rlp.append(&v); + rlp.append(&r); + rlp.append(&s); + } + Ok(rlp) + } + + pub fn encode_legacy(&self) -> Result { + let mut rlp = RlpStream::new(); + rlp.begin_list(9); + rlp.append(&self.nonce); + rlp.append(&self.gas_price.clone().unwrap_or(U256([0; 32]))); + rlp.append(&self.gas); + if self.to.is_some() { + rlp.append(&self.to.clone().unwrap().as_slice()); + } else { + rlp.append(&""); + } + rlp.append(&self.value); + rlp.append(&self.data); + if self.chain_id.is_some() { + let cid = self.chain_id.unwrap_or(0); + if self.signature.is_some() { + let sig = self.signature.unwrap_or([0; 65]); + let mut r = U256([0; 32]); + r.0.copy_from_slice(&sig[..32]); + let mut s = U256([0; 32]); + s.0.copy_from_slice(&sig[32..64]); + let v = cid * 2 + 35 + (sig[64] as u64); + rlp.append(&v); + rlp.append(&r); + rlp.append(&s); + } else { + rlp.append(&cid); + rlp.append(&0u8); + rlp.append(&0u8); + } + } + + Ok(rlp) + } + + pub fn encode(&self) -> Result, DecoderError> { + match self.transaction_type { + TransactionType::Legacy => { + let stream = self.encode_legacy()?; + Ok(stream.out().to_vec()) + } + + TransactionType::Eip155 => { + let stream = self.encode_eip25519()?; + Ok([&[2], stream.as_raw()].concat()) + } + } + } + + pub fn decode_legacy(rlp: &Rlp) -> Result { + Ok(EthereumTransaction { + transaction_type: TransactionType::Legacy, + nonce: rlp.val_at(0)?, + gas_price: Some(rlp.val_at(1)?), + gas: rlp.val_at(2)?, + to: Some(rlp.val_at(3)?), + value: rlp.val_at(4)?, + data: rlp.val_at(5)?, + chain_id: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + signature: None, + }) + } + + pub fn decode_eip155(rlp: Rlp) -> Result { + Ok(EthereumTransaction { + transaction_type: TransactionType::Eip155, + chain_id: Some(rlp.val_at(0)?), + nonce: rlp.val_at(1)?, + max_priority_fee_per_gas: Some(rlp.val_at(2)?), + max_fee_per_gas: Some(rlp.val_at(3)?), + gas: rlp.val_at(4)?, + to: Some(rlp.val_at(5)?), // Convert to Option + value: rlp.val_at(6)?, + data: rlp.val_at(7)?, + signature: None, + gas_price: None, + }) + } + + pub fn decode_eip_envelope(rlp: Rlp) -> Result { + let mut string_tx: String = rlp.to_string(); + string_tx = string_tx[1..string_tx.len() - 1].to_string(); + if string_tx.starts_with("0x") { + string_tx = string_tx[2..].to_string(); + } + + let mut byte_tx = hex::decode(string_tx).map_err(|_| DecoderError::RlpExpectedToBeData)?; + if byte_tx[0] == 2 { + byte_tx.remove(0); + } + + let rlp = Rlp::new(&byte_tx); + EthereumTransaction::decode_eip155(rlp) + } +} diff --git a/packages/kos/src/chains/icp/mod.rs b/packages/kos/src/chains/icp/mod.rs new file mode 100644 index 0000000..0d0772a --- /dev/null +++ b/packages/kos/src/chains/icp/mod.rs @@ -0,0 +1,169 @@ +use crate::chains::util::private_key_from_vec; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::bip32; +use crate::crypto::ed25519::{Ed25519, Ed25519Trait}; +use crate::crypto::hash::sha224_digest; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use alloc::{format, vec}; + +const ASN1_ED25519_HEADER: [u8; 12] = [48u8, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0]; +const ICP_TAIL: u8 = 2; +const ACCOUNT_ID_STR: &str = "account-id"; +const ACCOUNT_ID_BYTE: u8 = 0x0A; + +#[allow(clippy::upper_case_acronyms)] +pub struct ICP {} + +impl Chain for ICP { + fn get_id(&self) -> u32 { + 31 + } + + fn get_name(&self) -> &str { + "Internet Computer" + } + + fn get_symbol(&self) -> &str { + "ICP" + } + + fn get_decimals(&self) -> u32 { + 8 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let result = bip32::derive_ed25519(&seed, path)?; + Ok(Vec::from(result)) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/223'/0'/0'/{}", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let pbk = Ed25519::public_from_private(&pvk_bytes)?; + pvk_bytes.fill(0); + Ok(pbk) + } + + fn get_address(&self, public_key: Vec) -> Result { + let mut der = Vec::new(); + der.extend_from_slice(&ASN1_ED25519_HEADER); + der.extend_from_slice(&public_key); + + let mut der_digest = sha224_digest(&der).to_vec(); + + let mut new_digest = vec![ACCOUNT_ID_BYTE]; + new_digest.append(&mut ACCOUNT_ID_STR.as_bytes().to_vec()); + new_digest.append(der_digest.as_mut()); + new_digest.push(ICP_TAIL); + new_digest.append(&mut vec![0u8; 32]); + + let out_digest = sha224_digest(&new_digest); + let crc_calc = crc_calc_singletable(&out_digest); + + let mut addr_bytes: Vec = Vec::new(); + addr_bytes.append(&mut crc_calc.to_be_bytes().to_vec()); + addr_bytes.append(&mut out_digest.to_vec()); + + let addr = hex::encode(addr_bytes).to_string(); + Ok(addr) + } + + fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { + todo!() + } + + fn sign_message( + &self, + _private_key: Vec, + _message: Vec, + ) -> Result, ChainError> { + todo!() + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + + let signature = Ed25519::sign(&pvk_bytes, &payload)?; + pvk_bytes.fill(0); + Ok(signature) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + todo!() + } +} + +const TABLE_0: [u32; 256] = [ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +]; + +pub fn crc_calc_singletable(buffer: &[u8]) -> u32 { + let mut crc: u32 = 0xFFFFFFFF; + + for &byte in buffer.iter() { + crc = TABLE_0[((crc as u8) ^ byte) as usize] ^ (crc >> 8); + } + + !crc +} + +#[cfg(test)] +mod test { + + use super::*; + #[test] + fn test_icp_get_address() { + let icp = ICP {}; + + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let seed = icp.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let path = icp.get_path(0, false); + let pvk = icp.derive(seed, path).unwrap(); + let pbk = icp.get_pbk(pvk).unwrap(); + + let addr = icp.get_address(pbk).unwrap(); + assert_eq!( + addr, + "11d238129427ef0e44d86bd27cb6d9da4d7e8934cb0306a93a540e657082d885" + ); + } +} diff --git a/packages/kos/src/chains/klv/mod.rs b/packages/kos/src/chains/klv/mod.rs new file mode 100644 index 0000000..e938d8f --- /dev/null +++ b/packages/kos/src/chains/klv/mod.rs @@ -0,0 +1,255 @@ +mod models; + +use crate::chains::{Chain, ChainError, Transaction, TxInfo, TxType}; +use crate::crypto::bip32; +use crate::crypto::ed25519::{Ed25519, Ed25519Trait}; +use crate::crypto::hash::{blake2b_digest, keccak256_digest}; +use crate::protos::generated::klv::proto; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use bech32::{u5, Variant}; + +use crate::chains::util::private_key_from_vec; +use crate::crypto::base64::simple_base64_encode; +use crate::crypto::bignum::U256; +use prost::Message; + +const KLEVER_MESSAGE_PREFIX: &str = "\x17Klever Signed Message:\n"; + +pub const BIP44_PATH: u32 = 690; + +pub struct KLV {} + +impl KLV { + pub fn prepare_message(message: Vec) -> [u8; 32] { + let mut msg = Vec::new(); + msg.extend_from_slice(KLEVER_MESSAGE_PREFIX.as_bytes()); + msg.extend_from_slice(message.len().to_string().as_bytes()); + msg.extend_from_slice(&message); + + keccak256_digest(&msg[..]) + } +} + +impl Chain for KLV { + fn get_id(&self) -> u32 { + 38 + } + + fn get_name(&self) -> &str { + "Klever" + } + + fn get_symbol(&self) -> &str { + "KLV" + } + + fn get_decimals(&self) -> u32 { + 6 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let result = bip32::derive_ed25519(&seed, path)?; + Ok(Vec::from(result)) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/{}'/0'/0'/{}'", BIP44_PATH, index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let pbk = Ed25519::public_from_private(&pvk_bytes)?; + pvk_bytes.fill(0); + Ok(pbk) + } + + fn get_address(&self, public_key: Vec) -> Result { + let add_encoded = bech32::convert_bits(public_key.as_ref(), 8, 5, true)?; + let mut addr_u5: Vec = Vec::new(); + for i in add_encoded { + addr_u5.push(u5::try_from_u8(i)?); + } + let res = bech32::encode("klv", addr_u5, Variant::Bech32)?; + Ok(res) + } + + fn sign_tx( + &self, + private_key: Vec, + mut tx: Transaction, + ) -> Result { + let raw_tx = tx.raw_data; + let mut js_tx: models::Transaction = + tiny_json_rs::decode(String::from_utf8(raw_tx.clone())?)?; + let klv_tx = proto::Transaction::try_from(js_tx.clone()) + .map_err(|_| ChainError::ProtoDecodeError)?; + + let raw_data = klv_tx + .raw_data + .clone() + .ok_or(ChainError::ProtoDecodeError)?; + let mut tx_raw = Vec::with_capacity(raw_data.encoded_len()); + raw_data.encode(&mut tx_raw)?; + let result_buffer = blake2b_digest(&tx_raw); + + let sig = self.sign_raw(private_key, result_buffer.to_vec())?; + + js_tx.signature = Some(Vec::from([simple_base64_encode(&sig)])); + + tx.raw_data = tiny_json_rs::encode(js_tx).into_bytes(); + tx.tx_hash = result_buffer.to_vec(); + tx.signature = sig.as_slice().to_vec(); + Ok(tx) + } + + fn sign_message(&self, private_key: Vec, message: Vec) -> Result, ChainError> { + let prepared_messafe = KLV::prepare_message(message); + let signature = self.sign_raw(private_key, prepared_messafe.to_vec())?; + Ok(signature) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let signature = Ed25519::sign(&pvk_bytes, &payload)?; + pvk_bytes.fill(0); + Ok(signature) + } + + fn get_tx_info(&self, raw_tx: Vec) -> Result { + let js_tx: models::Transaction = tiny_json_rs::decode(String::from_utf8(raw_tx)?)?; + let tx = proto::Transaction::try_from(js_tx).map_err(|_| ChainError::ProtoDecodeError)?; + let raw = tx.raw_data.ok_or(ChainError::ProtoDecodeError)?; + + if raw.contract.len() != 1 { + return Err(ChainError::ProtoDecodeError); + } + + let c_type: proto::tx_contract::ContractType = + proto::tx_contract::ContractType::try_from(raw.contract[0].r#type) + .map_err(|_| ChainError::ProtoDecodeError)?; + match c_type { + proto::tx_contract::ContractType::TransferContractType => { + let value = raw.contract[0].clone().parameter.unwrap().value; + let tc = proto::TransferContract::decode(value.as_slice()) + .map_err(|_| ChainError::ProtoDecodeError)?; + let sender = self.get_address(raw.sender)?; + let receiver = self.get_address(tc.to_address)?; + let value = U256::from_i64(tc.amount).to_f64(self.get_decimals()); + + Ok(TxInfo { + sender, + receiver, + value, + tx_type: TxType::Transfer, + }) + } + _ => Ok(TxInfo { + sender: String::new(), + receiver: String::new(), + value: 0.0, + tx_type: TxType::Unknown, + }), + } + } +} + +#[cfg(test)] +mod test { + use crate::chains::Chain; + use alloc::string::{String, ToString}; + use alloc::vec::Vec; + + #[test] + fn test_derive() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let path = crate::chains::klv::KLV {}.get_path(0, false); + + let seed = crate::chains::klv::KLV {} + .mnemonic_to_seed(mnemonic, String::new()) + .unwrap(); + let pvk = crate::chains::klv::KLV {}.derive(seed, path).unwrap(); + assert_eq!(pvk.len(), 32); + let pbk = crate::chains::klv::KLV {}.get_pbk(pvk).unwrap(); + assert_eq!(pbk.len(), 32); + let addr = crate::chains::klv::KLV {}.get_address(pbk).unwrap(); + assert_eq!( + addr, + "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy" + ); + } + + #[test] + fn test_sign_raw() { + let mnemonic = + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + .to_string(); + let path = String::from("m/44'/690'/0'/0'/0'"); + + let seed = crate::chains::klv::KLV {} + .mnemonic_to_seed(mnemonic, String::new()) + .unwrap(); + let pvk = crate::chains::klv::KLV {}.derive(seed, path).unwrap(); + + let digest = + hex::decode("0f47f28830f7aa9607a7a462b267003f94b4ef2c5c28ac8763cfc68e8fe10915"); + let signature = crate::chains::klv::KLV {} + .sign_raw(pvk, digest.unwrap()) + .unwrap(); + assert_eq!(signature.len(), 64) + } + + #[test] + fn test_sign_tx() { + let pvk = hex::decode("1ab42cc412b618bdea3a599e3c9bae199ebf030895b039e9db1e30dafb12b727") + .unwrap(); + + let raw_tx = hex::decode( + "7b2252617744617461223a7b224e6f6e6365223a3837312c2253656e646572223a226e506832763367457a636d41684b4c783630764d41734e65384a5871716f5a47695a30504e51434c2b55303d222c22436f6e7472616374223a5b7b22506172616d65746572223a7b22747970655f75726c223a22747970652e676f6f676c65617069732e636f6d2f70726f746f2e5472616e73666572436f6e7472616374222c2276616c7565223a224369417633486c46453731646170613948454e704a454b4671656e7468417a32306b67436c76776e46753076635249714d48686b59574d784e3259354e54686b4d6d566c4e54497a595449794d4459794d4459354f5451314f54646a4d544e6b4f444d785a574d3347416f3d227d7d5d2c224b417070466565223a313030303030302c2242616e647769647468466565223a323030303030302c2256657273696f6e223a312c22436861696e4944223a224d544134227d7d", + ) + .unwrap(); + + let tx = crate::chains::Transaction { + raw_data: raw_tx, + tx_hash: Vec::new(), + signature: Vec::new(), + options: None, + }; + + let result_tx = crate::chains::klv::KLV {}.sign_tx(pvk, tx).unwrap(); + assert_eq!( + result_tx.tx_hash, + hex::decode("0f47f28830f7aa9607a7a462b267003f94b4ef2c5c28ac8763cfc68e8fe10915") + .unwrap() + ) + } + + #[test] + fn test_decode_klv_tx() { + let raw_tx = hex::decode( + "7b225261774\ + 4617461223a7b224e6f6e6365223a322c2253656e646572223a2231427\ + 673447457583848784162506664437742686b6956767378446637354e7a\ + 4d4f6c44727357377034633d222c22436f6e7472616374223a5b7b22506\ + 172616d65746572223a7b22747970655f75726c223a22747970652e676f\ + 6f676c65617069732e636f6d2f70726f746f2e5472616e73666572436f6\ + e7472616374222c2276616c7565223a224369446653574f374e61687538\ + 74717056506b4d547645324a6a4649385752702f4d62452f326c702b385\ + 06f37786742227d7d5d2c224b417070466565223a3530303030302c2242\ + 616e647769647468466565223a313030303030302c2256657273696f6e2\ + 23a312c22436861696e4944223a224d544134227d7d", + ) + .unwrap(); + + let tx_info = crate::chains::klv::KLV {}.get_tx_info(raw_tx).unwrap(); + assert_eq!( + tx_info.sender, + "klv16sd7crk4jlc8csrv7lwskqrpjgjklvcsmlhexuesa9p6a3dm57rs5vh0hq" + ); + } +} diff --git a/packages/kos/src/chains/klv/models.rs b/packages/kos/src/chains/klv/models.rs new file mode 100644 index 0000000..bf98ebd --- /dev/null +++ b/packages/kos/src/chains/klv/models.rs @@ -0,0 +1,246 @@ +use crate::alloc::borrow::ToOwned; +use crate::alloc::string::ToString; +use crate::crypto::base64::simple_base64_decode; +use crate::protos::generated::klv::proto; +use crate::protos::generated::klv::proto::tx_contract::ContractType; +use crate::{chains, protos}; +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; +use tiny_json_rs::mapper; +use tiny_json_rs::serializer; +use tiny_json_rs::Deserialize; +use tiny_json_rs::Serialize; + +#[derive(Serialize, Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq)] +pub struct Transaction { + #[Rename = "RawData"] + pub raw_data: ::core::option::Option, + #[Rename = "Signature"] + pub signature: Option>, //Base64 encoded + #[Rename = "Result"] + pub result: Option, + #[Rename = "ResultCode"] + pub result_code: Option, + #[Rename = "Receipts"] + pub receipts: Option>, + #[Rename = "Block"] + pub block: Option, +} + +#[derive(Clone, PartialEq, Serialize, Deserialize)] +pub struct Raw { + #[Rename = "Nonce"] + pub nonce: u64, + #[Rename = "Sender"] + pub sender: String, + #[Rename = "Contract"] + pub contract: ::prost::alloc::vec::Vec, + #[Rename = "PermissionID"] + pub permission_id: Option, + #[Rename = "Data"] + pub data: Option>, + #[Rename = "KAppFee"] // Use this to match the exact JSON field name for this field + pub k_app_fee: Option, + #[Rename = "BandwidthFee"] // Use this to match the exact JSON field name for this field + pub bandwidth_fee: Option, + #[Rename = "Version"] + pub version: Option, + #[Rename = "ChainID"] + pub chain_id: String, + #[Rename = "KdaFee"] // Use this to match the exact JSON field name for this field + pub kda_fee: ::core::option::Option, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub struct TxContract { + #[Rename = "Parameter"] + pub parameter: Parameter, + // ... other fields +} + +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub struct Parameter { + pub type_url: String, + pub value: String, + // ... other fields +} + +#[derive(Clone, PartialEq, Serialize, Deserialize)] +pub struct KdaFee { + #[Rename = "Kda"] + pub kda: String, + #[Rename = "Amount"] + pub amount: i64, +} + +#[derive(Clone, PartialEq, Serialize, Deserialize)] +pub struct Receipt { + #[Rename = "Data"] + pub data: ::prost::alloc::vec::Vec, +} + +#[derive(Debug)] +pub enum ConversionError { + InvalidData(&'static str), + Base64Error, + // You can add more error types as needed for detailed error handling +} + +impl TryFrom for proto::Transaction { + type Error = ConversionError; + + #[allow(clippy::needless_update)] + fn try_from(value: chains::klv::models::Transaction) -> Result { + let raw_data = match value.raw_data { + Some(raw) => Some(proto::transaction::Raw::try_from(raw)?), + None => None, + }; + + let receipts = value + .receipts + .unwrap_or_default() + .into_iter() + .map(proto::transaction::Receipt::try_from) + .collect::>()?; + + let signatures = value + .signature + .unwrap_or_default() + .into_iter() + .map(|s| simple_base64_decode(&s).map_err(|_| ConversionError::Base64Error)) + .collect::, _>>()?; + + let proto_tx = proto::Transaction { + raw_data, + signature: signatures, + result: value.result.unwrap_or(0), + result_code: value.result_code.unwrap_or(0), + receipts, + block: value.block.unwrap_or(0), + ..Default::default() // Include other fields as necessary + }; + + Ok(proto_tx) + } +} + +impl TryFrom for proto::transaction::Raw { + type Error = ConversionError; + + #[allow(clippy::needless_update)] + fn try_from(value: chains::klv::models::Raw) -> Result { + let contracts = value + .contract + .into_iter() + .map(proto::TxContract::try_from) + .collect::>()?; + + let chain_id_bytes = + simple_base64_decode(&value.chain_id).map_err(|_| ConversionError::Base64Error)?; + + let datas = value + .data + .unwrap_or_default() + .into_iter() + .map(|d| simple_base64_decode(&d).map_err(|_| ConversionError::Base64Error)) + .collect::, _>>()?; + + let proto_raw = proto::transaction::Raw { + nonce: value.nonce, + sender: simple_base64_decode(&value.sender) + .map_err(|_| ConversionError::Base64Error)?, + contract: contracts, + permission_id: value.permission_id.unwrap_or(0), + data: datas, + k_app_fee: value.k_app_fee.unwrap_or(0), + bandwidth_fee: value.bandwidth_fee.unwrap_or(0), + version: value.version.unwrap_or(0), + chain_id: chain_id_bytes, + kda_fee: value + .kda_fee + .map(proto::transaction::KdaFee::try_from) + .transpose()?, + ..Default::default() + }; + + Ok(proto_raw) + } +} + +impl TryFrom for proto::TxContract { + type Error = ConversionError; + + fn try_from(value: chains::klv::models::TxContract) -> Result { + let contract_name = value.parameter.type_url.clone(); + //Remove the "type.googleapis.com/" prefix + let contract_name = contract_name + .strip_prefix("type.googleapis.com/proto.") + .ok_or(ConversionError::InvalidData("Invalid contract name"))?; + //Add Type at the end of str + let contract_name = format!("{}Type", contract_name); + let contract_type = ContractType::from_str_name(&contract_name) + .ok_or(ConversionError::InvalidData("Invalid contract type"))?; + let proto_contract = proto::TxContract { + r#type: contract_type as i32, + parameter: Option::from(protos::Any::try_from(value.parameter)?), + }; + + Ok(proto_contract) + } +} + +impl TryFrom for protos::Any { + type Error = ConversionError; + + #[allow(clippy::needless_update)] + fn try_from(value: chains::klv::models::Parameter) -> Result { + let proto_parameter = protos::Any { + type_url: value.type_url, + value: simple_base64_decode(&value.value).map_err(|_| ConversionError::Base64Error)?, + ..Default::default() + }; + + Ok(proto_parameter) + } +} + +impl TryFrom for proto::transaction::KdaFee { + type Error = ConversionError; + + #[allow(clippy::needless_update)] + fn try_from(value: chains::klv::models::KdaFee) -> Result { + let kda_bytes = + simple_base64_decode(&value.kda).map_err(|_| ConversionError::Base64Error)?; + + let proto_kda_fee = proto::transaction::KdaFee { + kda: kda_bytes, + amount: value.amount, + ..Default::default() + }; + + Ok(proto_kda_fee) + } +} + +impl TryFrom for proto::transaction::Receipt { + type Error = ConversionError; + + #[allow(clippy::needless_update)] + fn try_from(value: chains::klv::models::Receipt) -> Result { + let data = value + .data + .into_iter() + .map(|d| simple_base64_decode(&d).map_err(|_| ConversionError::Base64Error)) + .collect::>()?; + + let proto_receipt = proto::transaction::Receipt { + data, + ..Default::default() + }; + + Ok(proto_receipt) + } +} diff --git a/packages/kos/src/chains/mod.rs b/packages/kos/src/chains/mod.rs new file mode 100644 index 0000000..3c3397d --- /dev/null +++ b/packages/kos/src/chains/mod.rs @@ -0,0 +1,747 @@ +use crate::alloc::borrow::ToOwned; +use crate::crypto::bip32::Bip32Err; +use crate::crypto::ed25519::Ed25519Err; +use crate::crypto::secp256k1::Secp256Err; +use crate::crypto::sr25519::Sr25519Error; +use alloc::boxed::Box; +use alloc::format; +use alloc::string::{FromUtf8Error, String, ToString}; +use alloc::vec::Vec; +use core::fmt::Display; +use prost::{DecodeError, EncodeError}; +use rlp::DecoderError; +use tiny_json_rs::lexer::StringType; +use tiny_json_rs::mapper; +use tiny_json_rs::serializer; +use tiny_json_rs::serializer::Token; +use tiny_json_rs::Serialize; + +pub mod ada; +mod apt; +mod atom; +mod bch; +mod bnb; +mod btc; +pub mod constants; +pub mod egld; +mod eth; +mod icp; +pub mod klv; +mod sol; +mod substrate; +mod sui; +pub mod trx; +mod util; +mod xrp; + +#[derive(Debug)] +pub enum ChainError { + ErrDerive, + InvalidPrivateKey, + ProtoDecodeError, + CurveError(Secp256Err), + CurveErrorSr(Sr25519Error), + EncodeError(EncodeError), + DecodeError(DecodeError), + Ed25519Error, + Bech32EncodeError, + RlpError, + InvalidMessageSize, + InvalidSignature, + NotSupported, + InvalidPublicKey, + InvalidSeed, + InvalidCredential, + InvalidMnemonic, + CipherError(String), + InvalidString(String), + InvalidData(String), + MissingOptions, + InvalidOptions, +} + +impl Display for ChainError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + ChainError::ErrDerive => write!(f, "Derive error"), + ChainError::InvalidPrivateKey => write!(f, "Invalid private key"), + ChainError::CurveError(e) => write!(f, "Curve error: {}", e), + ChainError::ProtoDecodeError => { + write!(f, "Proto decode error") + } + ChainError::EncodeError(e) => { + write!(f, "encode error: {}", e) + } + ChainError::DecodeError(e) => { + write!(f, "decode error: {}", e) + } + ChainError::Ed25519Error => { + write!(f, "ed25519 error") + } + ChainError::Bech32EncodeError => { + write!(f, "bech32 encode error") + } + ChainError::RlpError => { + write!(f, "rlp error") + } + ChainError::InvalidMessageSize => { + write!(f, "invalid message size") + } + ChainError::InvalidSignature => { + write!(f, "invalid signature") + } + ChainError::NotSupported => { + write!(f, "not supported") + } + ChainError::InvalidPublicKey => { + write!(f, "invalid public key") + } + ChainError::CurveErrorSr(e) => { + write!(f, "curve error sr: {}", e) + } + ChainError::InvalidSeed => { + write!(f, "invalid seed") + } + ChainError::InvalidCredential => { + write!(f, "invalid credential") + } + ChainError::InvalidMnemonic => { + write!(f, "invalid mnemonic") + } + ChainError::CipherError(e) => { + write!(f, "cipher error: {}", e) + } + ChainError::InvalidString(e) => { + write!(f, "invalid string: {}", e) + } + ChainError::InvalidData(e) => { + write!(f, "invalid data: {}", e) + } + ChainError::MissingOptions => { + write!(f, "missing option") + } + ChainError::InvalidOptions => { + write!(f, "invalid option") + } + } + } +} + +impl From for ChainError { + fn from(value: Secp256Err) -> Self { + ChainError::CurveError(value) + } +} + +impl From for ChainError { + fn from(_: Ed25519Err) -> Self { + ChainError::Ed25519Error + } +} + +impl From for ChainError { + fn from(_: Bip32Err) -> Self { + ChainError::ErrDerive + } +} + +impl From for ChainError { + fn from(_: FromUtf8Error) -> Self { + ChainError::InvalidPrivateKey + } +} + +impl From for ChainError { + fn from(v: DecodeError) -> Self { + ChainError::DecodeError(v) + } +} + +impl From for ChainError { + fn from(v: EncodeError) -> Self { + ChainError::EncodeError(v) + } +} + +impl From for ChainError { + fn from(_: bech32::Error) -> Self { + ChainError::Bech32EncodeError + } +} + +impl From for ChainError { + fn from(_: DecoderError) -> Self { + ChainError::RlpError + } +} + +impl From for ChainError { + fn from(_: serializer::DecodeError) -> Self { + ChainError::ProtoDecodeError + } +} + +impl From for ChainError { + fn from(value: Sr25519Error) -> Self { + ChainError::CurveErrorSr(value) + } +} + +#[allow(dead_code)] +impl ChainError { + pub fn to_u32(&self) -> u32 { + match self { + ChainError::ErrDerive => 1, + ChainError::InvalidPrivateKey => 2, + ChainError::ProtoDecodeError => 3, + ChainError::CurveError(_) => 4, + ChainError::EncodeError(_) => 5, + ChainError::DecodeError(_) => 6, + ChainError::Ed25519Error => 7, + ChainError::Bech32EncodeError => 8, + ChainError::RlpError => 9, + ChainError::InvalidMessageSize => 10, + ChainError::InvalidSignature => 11, + ChainError::NotSupported => 12, + ChainError::InvalidPublicKey => 13, + ChainError::CurveErrorSr(_) => 14, + ChainError::InvalidSeed => 15, + ChainError::InvalidCredential => 16, + ChainError::InvalidMnemonic => 17, + ChainError::CipherError(_) => 18, + ChainError::InvalidString(_) => 19, + ChainError::InvalidData(_) => 20, + ChainError::MissingOptions => 21, + ChainError::InvalidOptions => 22, + } + } +} + +pub enum TxType { + Unknown, + Transfer, + TriggerContract, +} + +impl serializer::Serialize for TxType { + fn serialize(&self) -> mapper::Value { + let str = match self { + TxType::Unknown => "Unknown", + TxType::Transfer => "Transfer", + TxType::TriggerContract => "TriggerContract", + }; + let token = Token { + token_type: tiny_json_rs::lexer::TokenType::String(StringType::SimpleString), + literal: str.to_string(), + }; + + mapper::Value::Token(token) + } +} + +#[derive(Serialize)] +pub struct TxInfo { + pub sender: String, + pub receiver: String, + pub value: f64, + pub tx_type: TxType, +} + +pub struct Transaction { + pub raw_data: Vec, + pub tx_hash: Vec, + pub signature: Vec, + pub options: Option, +} + +#[derive(Clone)] +pub enum ChainOptions { + EVM { + chain_id: u32, + network_type: u32, + }, + BTC { + prev_scripts: Vec>, + input_amounts: Vec, + }, +} + +#[allow(dead_code)] +impl Transaction { + #[allow(clippy::should_implement_trait)] + pub fn default() -> Self { + Self { + raw_data: Vec::new(), + tx_hash: Vec::new(), + signature: Vec::new(), + options: None, + } + } +} + +pub trait Chain { + fn get_id(&self) -> u32; + fn get_name(&self) -> &str; + fn get_symbol(&self) -> &str; + fn get_decimals(&self) -> u32; + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError>; + fn derive(&self, seed: Vec, path: String) -> Result, ChainError>; + fn get_path(&self, index: u32, is_legacy: bool) -> String; + fn get_pbk(&self, private_key: Vec) -> Result, ChainError>; + fn get_address(&self, public_key: Vec) -> Result; + fn sign_tx(&self, private_key: Vec, tx: Transaction) -> Result; + fn sign_message(&self, private_key: Vec, message: Vec) -> Result, ChainError>; + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError>; + fn get_tx_info(&self, raw_tx: Vec) -> Result; +} + +type ChainFactory = fn() -> Box; + +struct ChainInfo { + factory: ChainFactory, + supported: bool, +} + +struct ChainRegistry { + registry: &'static [(u32, ChainInfo)], +} +impl ChainRegistry { + fn new() -> Self { + static REGISTRY: [(u32, ChainInfo); 46] = [ + ( + constants::ETH, + ChainInfo { + factory: || Box::new(eth::ETH::new()), + supported: true, + }, + ), + ( + constants::BSC, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(26, 56, "BSC", "BnbSmartChain")), + supported: true, + }, + ), + ( + constants::POLYGON, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(28, 137, "MATIC", "Polygon")), + supported: true, + }, + ), + ( + constants::HT, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(30, 128, "HT", "Huobi")), + supported: true, + }, + ), + ( + constants::SYS_NEVM, + ChainInfo { + factory: || { + Box::new(eth::ETH::new_eth_based(37, 57, "SYS_NEVM", "Syscoin Nevm")) + }, + supported: true, + }, + ), + ( + constants::TRX, + ChainInfo { + factory: || Box::new(trx::TRX {}), + supported: true, + }, + ), + ( + constants::KLV, + ChainInfo { + factory: || Box::new(klv::KLV {}), + supported: true, + }, + ), + ( + constants::BTC, + ChainInfo { + factory: || Box::new(btc::BTC::new()), + supported: false, + }, + ), + ( + constants::DOT, + ChainInfo { + factory: || Box::new(substrate::Substrate::new(21, 0, "DOT", "Polkadot")), + supported: true, + }, + ), + ( + constants::KSM, + ChainInfo { + factory: || Box::new(substrate::Substrate::new(27, 2, "KSM", "Kusama")), + supported: true, + }, + ), + ( + constants::LTC, + ChainInfo { + factory: || Box::new(btc::BTC::new_btc_based(5, "ltc", "LTC", "Litecoin")), + supported: false, + }, + ), + ( + constants::REEF, + ChainInfo { + factory: || Box::new(substrate::Substrate::new(29, 42, "REEF", "Reef")), + supported: true, + }, + ), + ( + constants::SDN, + ChainInfo { + factory: || Box::new(substrate::Substrate::new(35, 5, "SDN", "Shiden")), + supported: true, + }, + ), + ( + constants::ASTR, + ChainInfo { + factory: || Box::new(substrate::Substrate::new(36, 5, "ASTR", "Astar")), + supported: true, + }, + ), + ( + constants::CFG, + ChainInfo { + factory: || Box::new(substrate::Substrate::new(47, 36, "CFG", "Centrifuge")), + supported: true, + }, + ), + ( + constants::SYS, + ChainInfo { + factory: || Box::new(btc::BTC::new_btc_based(15, "sys", "SYS", "Syscoin")), + supported: false, + }, + ), + ( + constants::KILT, + ChainInfo { + factory: || Box::new(substrate::Substrate::new(44, 38, "KILT", "KILT")), + supported: true, + }, + ), + ( + constants::ALTAIR, + ChainInfo { + factory: || Box::new(substrate::Substrate::new(42, 136, "ALTAIR", "Altair")), + supported: true, + }, + ), + ( + constants::DOGE, + ChainInfo { + factory: || { + Box::new(btc::BTC::new_legacy_btc_based(12, 0x1E, "DOGE", "Dogecoin")) + }, + supported: false, + }, + ), + ( + constants::DASH, + ChainInfo { + factory: || Box::new(btc::BTC::new_legacy_btc_based(11, 0x4C, "DASH", "Dash")), + supported: false, + }, + ), + ( + constants::XRP, + ChainInfo { + factory: || Box::new(xrp::XRP::new()), + supported: false, + }, + ), + ( + constants::DGB, + ChainInfo { + factory: || Box::new(btc::BTC::new_btc_based(16, "dgb", "DGB", "Digibyte")), + supported: false, + }, + ), + ( + constants::COSMOS, + ChainInfo { + factory: || Box::new(atom::ATOM::new()), + supported: false, + }, + ), + ( + constants::CELESTIA, + ChainInfo { + factory: || { + Box::new(atom::ATOM::new_cosmos_based( + "celestia", "celestia", "Celestia", "TIA", + )) + }, + supported: false, + }, + ), + ( + constants::CUDOS, + ChainInfo { + factory: || { + Box::new(atom::ATOM::new_cosmos_based( + "cudos", "cudos-1", "Cudos", "CUDOS", + )) + }, + supported: false, + }, + ), + ( + constants::AURA, + ChainInfo { + factory: || { + Box::new(atom::ATOM::new_cosmos_based( + "aura", "xstaxy-1", "Aura", "AURA", + )) + }, + supported: false, + }, + ), + ( + constants::ICP, + ChainInfo { + factory: || Box::new(icp::ICP {}), + supported: false, + }, + ), + ( + constants::SOL, + ChainInfo { + factory: || Box::new(sol::SOL {}), + supported: false, + }, + ), + ( + constants::MOVR, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(32, 1285, "MOVR", "Moonriver")), + supported: true, + }, + ), + ( + constants::GLMR, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(34, 1284, "GLMR", "Moonbeam")), + supported: true, + }, + ), + ( + constants::BNB, + ChainInfo { + factory: || Box::new(bnb::BNB {}), + supported: false, + }, + ), + ( + constants::BCH, + ChainInfo { + factory: || Box::new(bch::BCH {}), + supported: false, + }, + ), + ( + constants::ADA, + ChainInfo { + factory: || Box::new(ada::ADA {}), + supported: false, + }, + ), + ( + constants::SUI, + ChainInfo { + factory: || Box::new(sui::SUI {}), + supported: true, + }, + ), + ( + constants::APT, + ChainInfo { + factory: || Box::new(apt::APT {}), + supported: false, + }, + ), + ( + constants::AVAIL, + ChainInfo { + factory: || Box::new(substrate::Substrate::new(62, 42, "AVAIL", "Avail")), + supported: true, + }, + ), + ( + constants::ROLLUX, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(63, 570, "ROLLUX", "Rollux")), + supported: true, + }, + ), + ( + constants::AVAX, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(39, 43114, "AVAX", "Avalanche")), + supported: true, + }, + ), + ( + constants::ARB, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(57, 42161, "ARB", "Arbitrum")), + supported: true, + }, + ), + ( + constants::BASE, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(60, 8453, "BASE", "Base")), + supported: true, + }, + ), + ( + constants::NEAR, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(64, 397, "NEAR", "Near")), + supported: true, + }, + ), + ( + constants::FTM, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(54, 250, "FTM", "Fantom")), + supported: true, + }, + ), + ( + constants::CHZ, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(61, 88888, "CHZ", "Chiliz")), + supported: true, + }, + ), + ( + constants::OP, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(53, 10, "OP", "Optimism")), + supported: true, + }, + ), + ( + constants::POLYGON_ZKEVM, + ChainInfo { + factory: || { + Box::new(eth::ETH::new_eth_based(52, 1101, "ZKEVM", "Polygon zkEVM")) + }, + supported: true, + }, + ), + ( + constants::STOLZ, + ChainInfo { + factory: || Box::new(eth::ETH::new_eth_based(67, 2344, "STOLZ", "Stolz")), + supported: true, + }, + ), + ]; + + Self { + registry: ®ISTRY, + } + } + + fn get_chain_by_id(&self, id: u32) -> Option> { + for &(chain_id, ref chain_info) in self.registry { + if chain_id == id { + return Some((chain_info.factory)()); + } + } + None + } + + fn get_chain_by_base_id(&self, base_id: u32) -> Option> { + for (_, chain_info) in self.registry { + let chain = (chain_info.factory)(); + if chain.get_id() == base_id { + return Some(chain); + } + } + None + } + + fn get_chains(&self) -> Vec { + let mut ids = Vec::new(); + for (_, chain_info) in self.registry { + let chain = (chain_info.factory)(); + ids.push(chain.get_id()); + } + ids + } + + fn is_chain_supported(&self, id: u32) -> bool { + for (_, chain_info) in self.registry { + let chain = (chain_info.factory)(); + if chain.get_id() == id { + return chain_info.supported; + } + } + false + } + + fn get_supported_chains(&self) -> Vec { + let mut ids = Vec::new(); + for (_, chain_info) in self.registry { + let chain = (chain_info.factory)(); + if chain_info.supported { + ids.push(chain.get_id()); + } + } + ids + } +} + +pub fn get_chain_by_id(id: u32) -> Option> { + ChainRegistry::new().get_chain_by_id(id) +} + +pub enum CustomChainType { + NotCustom(u32), + CustomEth(u32), + CustomSubstrate(u32), + CustomCosmos(String), +} + +pub fn get_chain_by_params(params: CustomChainType) -> Option> { + match params { + CustomChainType::NotCustom(c) => get_chain_by_id(c), + CustomChainType::CustomEth(chaincode) => Some(Box::new(eth::ETH::new_eth_based( + 0, + chaincode, + format!("ETH {}", chaincode).as_str(), + format!("Eth Based {}", chaincode).as_str(), + ))), + CustomChainType::CustomSubstrate(_) => None, + CustomChainType::CustomCosmos(_) => None, + } +} + +pub fn get_chain_by_base_id(base_id: u32) -> Option> { + ChainRegistry::new().get_chain_by_base_id(base_id) +} + +pub fn get_chains() -> Vec { + ChainRegistry::new().get_chains() +} + +pub fn is_chain_supported(id: u32) -> bool { + ChainRegistry::new().is_chain_supported(id) +} + +pub fn get_supported_chains() -> Vec { + ChainRegistry::new().get_supported_chains() +} diff --git a/packages/kos/src/chains/sol/mod.rs b/packages/kos/src/chains/sol/mod.rs new file mode 100644 index 0000000..d84bea2 --- /dev/null +++ b/packages/kos/src/chains/sol/mod.rs @@ -0,0 +1,96 @@ +use crate::chains::util::private_key_from_vec; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::b58::b58enc; +use crate::crypto::bip32; +use crate::crypto::ed25519::{Ed25519, Ed25519Trait}; +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; + +#[allow(clippy::upper_case_acronyms)] +pub struct SOL {} + +impl Chain for SOL { + fn get_id(&self) -> u32 { + 40 + } + + fn get_name(&self) -> &str { + "Solana" + } + + fn get_symbol(&self) -> &str { + "SOL" + } + + fn get_decimals(&self) -> u32 { + todo!() + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let result = bip32::derive_ed25519(&seed, path)?; + Ok(Vec::from(result)) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/501'/0'/0'/{}'", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let pbk = Ed25519::public_from_private(&pvk_bytes)?; + pvk_bytes.fill(0); + Ok(pbk) + } + + fn get_address(&self, public_key: Vec) -> Result { + let addr = b58enc(&public_key); + Ok(String::from_utf8(addr)?) + } + + fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_message( + &self, + _private_key: Vec, + _message: Vec, + ) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let signature = Ed25519::sign(&pvk_bytes, &payload)?; + pvk_bytes.fill(0); + Ok(signature) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::string::ToString; + + #[test] + fn test_derive() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + + let sol = SOL {}; + let seed = sol.mnemonic_to_seed(mnemonic, "".to_string()).unwrap(); + let path = sol.get_path(0, false); + let pvk = sol.derive(seed, path).unwrap(); + let pbk = sol.get_pbk(pvk).unwrap(); + let addr = sol.get_address(pbk).unwrap(); + assert_eq!(addr, "B9sVeu4rJU12oUrUtzjc6BSNuEXdfvurZkdcaTVkP2LY"); + } +} diff --git a/packages/kos/src/chains/substrate/mod.rs b/packages/kos/src/chains/substrate/mod.rs new file mode 100644 index 0000000..676086b --- /dev/null +++ b/packages/kos/src/chains/substrate/mod.rs @@ -0,0 +1,196 @@ +mod models; + +use crate::chains::util::private_key_from_vec; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::hash::blake2b_64_digest; +use crate::crypto::sr25519::Sr25519Trait; +use crate::crypto::{b58, bip32, sr25519}; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use models::{Call, CallArgs}; +use parity_scale_codec::Decode; + +const LOWER_MASK: u16 = 0x3FFF; +const TYPE1_ACCOUNT_ID: u16 = 63; +const IDENTIFIER_LOWER_MASK: u16 = 0x00FC; +const IDENTIFIER_UPPER_MASK: u16 = 0x0003; +const IDENTIFIER_LOWER_PREFIX: u8 = 0x40; +const SUBSTRATE_NETWORK_PREFIX: &str = "SS58PRE"; + +pub struct Substrate { + id: u32, + network_id: u16, + name: String, + symbol: String, +} + +impl Substrate { + pub fn new(id: u32, network_id: u16, name: &str, symbol: &str) -> Self { + Self { + id, + network_id, + name: name.to_string(), + symbol: symbol.to_string(), + } + } +} + +impl Chain for Substrate { + fn get_id(&self) -> u32 { + self.id + } + + fn get_name(&self) -> &str { + self.name.as_str() + } + + fn get_symbol(&self) -> &str { + self.symbol.as_str() + } + + fn get_decimals(&self) -> u32 { + 12 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed_substrate(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let pvk = bip32::derive_sr25519(&seed, path)?; + Ok(pvk.to_vec()) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + if index == 0 { + return "".to_string(); + } + format!("//{}", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk = private_key_from_vec(&private_key)?; + let pbk = sr25519::Sr25519::public_from_private(&pvk)?; + pvk.fill(0); + Ok(pbk) + } + + fn get_address(&self, public_key: Vec) -> Result { + let identifier = self.network_id & LOWER_MASK; + let mut prefix = Vec::new(); + if identifier < TYPE1_ACCOUNT_ID { + prefix.push(identifier as u8); + } else { + let lower_byte = ((identifier & IDENTIFIER_LOWER_MASK) >> 2) as u8; + let upper_byte = + ((identifier >> 8) | ((identifier & IDENTIFIER_UPPER_MASK) << 6)) as u8; + prefix.push(lower_byte | IDENTIFIER_LOWER_PREFIX); + prefix.push(upper_byte); + } + + let data_to_hash = [ + SUBSTRATE_NETWORK_PREFIX.as_bytes(), + &prefix[..], + &public_key[..], + ] + .concat(); + let digest = blake2b_64_digest(&data_to_hash); + + let to_base_58 = [&prefix[..], &public_key[..], &digest[..2]].concat(); + let encoded = b58::b58enc(&to_base_58); + let addr = String::from_utf8(encoded).unwrap(); + Ok(addr) + } + + fn sign_tx( + &self, + private_key: Vec, + mut tx: Transaction, + ) -> Result { + let sig = self.sign_raw(private_key, tx.raw_data.clone())?; + tx.signature = [[1u8].to_vec(), sig].concat(); + Ok(tx) + } + + fn sign_message(&self, private_key: Vec, message: Vec) -> Result, ChainError> { + self.sign_raw(private_key, message) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut private_key_bytes = private_key_from_vec(&private_key)?; + let sig = sr25519::Sr25519::sign(&payload, &private_key_bytes)?; + private_key_bytes.fill(0); + Ok(sig) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + let info = Call::decode(&mut &_raw_tx[..]).map_err(|_| ChainError::InvalidPrivateKey)?; + let args = info.args.to_vec(); + let tx_info = + CallArgs::decode(&mut &args[..]).map_err(|_| ChainError::InvalidPrivateKey)?; + let address_to = self.get_address(tx_info.addr_to.to_vec())?; + let new_tx_info = TxInfo { + receiver: address_to, + sender: "".to_string(), + tx_type: super::TxType::Unknown, + value: tx_info.amount.to_f64(self.get_decimals()), + }; + Ok(new_tx_info) + } +} + +#[cfg(test)] +mod test { + + use crate::chains::Chain; + use alloc::string::{String, ToString}; + + #[test] + fn test_get_addr() { + let dot = super::Substrate::new(21, 0, "Polkadot", "DOT"); + + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let path = dot.get_path(0, false); + + let seed = dot.mnemonic_to_seed(mnemonic, String::from("")).unwrap(); + let pvk = dot.derive(seed, path).unwrap(); + let pbk = dot.get_pbk(pvk).unwrap(); + let addr = dot.get_address(pbk).unwrap(); + assert_eq!(addr, "13KVd4f2a4S5pLp4gTTFezyXdPWx27vQ9vS6xBXJ9yWVd7xo"); + } + + #[test] + fn test_sign_raw() { + let dot = super::Substrate::new(21, 0, "Polkadot", "DOT"); + + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let path = String::from(""); + + let seed = dot.mnemonic_to_seed(mnemonic, String::from("")).unwrap(); + let pvk = dot.derive(seed, path).unwrap(); + let payload = [0; 32].to_vec(); + let _tx = dot.sign_raw(pvk, payload).unwrap(); + } + + #[test] + fn sign_tx() { + let dot = super::Substrate::new(21, 0, "Polkadot", "DOT"); + + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let path = String::from(""); + + let seed = dot.mnemonic_to_seed(mnemonic, String::from("")).unwrap(); + let _pvk = dot.derive(seed, path).unwrap(); + } + + #[test] + fn test_get_tx_info() { + let dot = super::Substrate::new(21, 0, "Polkadot", "DOT"); + let raw_data = "05030092fb4dc4e0790663aa4be18e6c49d62e8db091a6e1c4d0727c14906cf79f0f7a280000002b460f001900000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c391b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3".to_string(); + + let raw_data_hex = hex::decode(raw_data).unwrap(); + let tx_info = dot.get_tx_info(raw_data_hex); + assert!(tx_info.is_ok()); + } +} diff --git a/packages/kos/src/chains/substrate/models.rs b/packages/kos/src/chains/substrate/models.rs new file mode 100644 index 0000000..947a0ae --- /dev/null +++ b/packages/kos/src/chains/substrate/models.rs @@ -0,0 +1,138 @@ +use alloc::vec; +use alloc::vec::Vec; +use parity_scale_codec::{Decode, Input}; + +use crate::crypto::bignum::U256; + +#[derive(Decode)] +pub struct Call { + pub _call_index: CallIndex, + pub args: [u8; 100], +} + +#[derive(Decode)] +pub struct CallIndex { + _section_index: u8, + _method_index: u8, +} + +#[derive(Decode)] +pub struct CallArgs { + pub addr_to: MultiAddress, + pub amount: UIntCompact, +} + +pub struct MultiAddress { + pub is_id: bool, + pub as_id: [u8; 32], + pub is_index: bool, + pub as_index: u32, + pub is_raw: bool, + pub as_raw: [u8; 32], + pub is_address32: bool, + pub as_address32: [u8; 32], + pub is_address20: bool, + pub as_address20: [u8; 20], +} + +impl MultiAddress { + pub fn to_vec(&self) -> Vec { + if self.is_id { + return self.as_id.to_vec(); + } + if self.is_index { + return self.as_index.to_le_bytes().to_vec(); + } + if self.is_raw { + return self.as_raw.to_vec(); + } + if self.is_address32 { + return self.as_address32.to_vec(); + } + self.as_address20.to_vec() + } +} + +impl Decode for MultiAddress { + fn decode(input: &mut I) -> Result { + let mut address = MultiAddress { + is_id: false, + as_id: [0; 32], + is_index: false, + as_index: 0, + is_raw: false, + as_raw: [0; 32], + is_address32: false, + as_address32: [0; 32], + is_address20: false, + as_address20: [0; 20], + }; + let t = input.read_byte()?; + + match t { + 1 => { + address.is_index = true; + let mut as_index = [0u8; 4]; + let _ = input.read(&mut as_index); + address.as_index = u32::from_le_bytes(as_index); + Ok(address) + } + 2 => { + address.is_raw = true; + let _ = input.read(&mut address.as_raw); + Ok(address) + } + 3 => { + address.is_address32 = true; + let _ = input.read(&mut address.as_address32); + Ok(address) + } + 4 => { + address.is_address20 = true; + let _ = input.read(&mut address.as_address20); + Ok(address) + } + _ => { + address.is_id = true; + let _ = input.read(&mut address.as_id); + Ok(address) + } + } + } +} + +pub type UIntCompact = U256; + +impl Decode for UIntCompact { + fn decode(input: &mut I) -> Result { + let b = input.read_byte()?; + let mode = b & 3; + + match mode { + 1 => { + let bb = input.read_byte()?; + let mut r = bb as u64; + r <<= 6; + r += (b >> 2) as u64; + Ok(U256::from_u64(r)) + } + 2 => { + let mut buf = [0u8; 4]; + let _ = input.read(&mut buf); + let mut r = u32::from_le_bytes(buf); + r >>= 2; + Ok(U256::from_u64((r) as u64)) + } + 3 => { + let l = b >> 2; + if l > 63 { + todo!() + } + let mut buf = vec![0u8; (l + 4) as usize]; + input.read(&mut buf)?; + Ok(U256::read_data_as_le(buf)) + } + _ => Ok(U256::from_u64((b >> 2) as u64)), + } + } +} diff --git a/packages/kos/src/chains/sui/mod.rs b/packages/kos/src/chains/sui/mod.rs new file mode 100644 index 0000000..0564324 --- /dev/null +++ b/packages/kos/src/chains/sui/mod.rs @@ -0,0 +1,130 @@ +use crate::chains::util::private_key_from_vec; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::bip32; +use crate::crypto::ed25519::{Ed25519, Ed25519Trait}; +use crate::crypto::hash::blake2b_digest; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +#[allow(clippy::upper_case_acronyms)] +pub struct SUI {} + +impl Chain for SUI { + fn get_id(&self) -> u32 { + 51 + } + + fn get_name(&self) -> &str { + "Sui" + } + + fn get_symbol(&self) -> &str { + "SUI" + } + + fn get_decimals(&self) -> u32 { + todo!() + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let result = bip32::derive_ed25519(&seed, path)?; + Ok(Vec::from(result)) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/784'/0'/0'/{}'", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let pbk = Ed25519::public_from_private(&pvk_bytes)?; + pvk_bytes.fill(0); + Ok(pbk) + } + + fn get_address(&self, public_key: Vec) -> Result { + if public_key.len() != 32 { + return Err(ChainError::InvalidPublicKey); + } + + let mut signed_pbk = [0u8; 33]; + signed_pbk[1..].copy_from_slice(&public_key[..]); + + let checksum = blake2b_digest(&signed_pbk); + let hex_encode = hex::encode(checksum); + let mut addr = "0x".to_string(); + addr.push_str(&hex_encode[..64]); + Ok(addr) + } + + fn sign_tx( + &self, + private_key: Vec, + mut tx: Transaction, + ) -> Result { + let raw = tx.raw_data.clone(); + let signature = self.sign_message(private_key, raw)?; + tx.signature = signature; + Ok(tx) + } + + fn sign_message(&self, private_key: Vec, message: Vec) -> Result, ChainError> { + let mut intent_message = Vec::new(); + intent_message.append(&mut [0; 3].to_vec()); + intent_message.append(&mut message.clone()); + + let check_sum = blake2b_digest(&intent_message); + let mut pvk_bytes = private_key_from_vec(&private_key)?; + + let mut response = Vec::new(); + response.push(0x00); + + let signature = Ed25519::sign(&pvk_bytes, &check_sum)?; + response.append(&mut signature.clone()); + + let pbk = Ed25519::public_from_private(&pvk_bytes)?; + response.append(&mut pbk.clone()); + + pvk_bytes.fill(0); + Ok(response) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let signature = Ed25519::sign(&pvk_bytes, &payload)?; + + pvk_bytes.fill(0); + Ok(signature) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use crate::chains::Chain; + use alloc::string::{String, ToString}; + + #[test] + fn test_derive() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + + let sui = super::SUI {}; + let seed = sui.mnemonic_to_seed(mnemonic, String::new()).unwrap(); + let path = sui.get_path(0, false); + let pvk = sui.derive(seed.clone(), path).unwrap(); + let pbk = sui.get_pbk(pvk.clone()).unwrap(); + let addr = sui.get_address(pbk.clone()).unwrap(); + assert_eq!( + addr, + "0x5e93a736d04fbb25737aa40bee40171ef79f65fae833749e3c089fe7cc2161f1" + ); + } +} diff --git a/packages/kos/src/chains/trx/mod.rs b/packages/kos/src/chains/trx/mod.rs new file mode 100644 index 0000000..5d8e5f6 --- /dev/null +++ b/packages/kos/src/chains/trx/mod.rs @@ -0,0 +1,342 @@ +use crate::chains::util::{private_key_from_vec, slice_from_vec}; +use crate::chains::{Chain, ChainError, Transaction, TxInfo, TxType}; +use crate::crypto::b58::b58enc; +use crate::crypto::bignum::U256; +use crate::crypto::hash::{keccak256_digest, sha256_digest}; +use crate::crypto::secp256k1::Secp256k1Trait; +use crate::crypto::{bip32, secp256k1}; +use crate::protos::generated::trx::protocol; +use crate::protos::generated::trx::protocol::transaction::contract::ContractType; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use alloc::{format, vec}; +use prost::Message; + +const TRX_ADDR_PREFIX: u8 = 0x41; +const TRX_ADD_RAW_LEN: usize = 21; +const TRX_ADD_SIZE: usize = 25; + +pub const BIP44_PATH: u32 = 195; + +const TRON_MESSAGE_PREFIX: &str = "\x19TRON Signed Message:\n"; +pub struct TRX {} + +impl TRX { + pub fn prepare_message(message: Vec) -> [u8; 32] { + let mut msg = Vec::new(); + msg.extend_from_slice(TRON_MESSAGE_PREFIX.as_bytes()); + msg.extend_from_slice(message.len().to_string().as_bytes()); + msg.extend_from_slice(&message); + + keccak256_digest(&msg[..]) + } + + pub fn expand_address_with_checksum(address: &[u8; 21]) -> String { + let mut address_with_checksum: [u8; TRX_ADD_SIZE] = [0; TRX_ADD_SIZE]; + address_with_checksum[..TRX_ADD_RAW_LEN].copy_from_slice(&address[..]); + let hash = sha256_digest(&address_with_checksum[..TRX_ADD_RAW_LEN]); + let hash = sha256_digest(&hash[..]); + address_with_checksum[21..].copy_from_slice(&hash[0..4]); + let bytes_addr = b58enc(&address_with_checksum[..]); + String::from_utf8(bytes_addr).unwrap() + } + + #[allow(clippy::single_match)] + pub fn decode_transaction(raw_tx: Vec) -> Result { + let tx = protocol::Transaction::decode(raw_tx.as_slice()); + match tx { + Ok(t) => return Ok(t), + Err(_) => {} + } + + let raw_tx = protocol::transaction::Raw::decode(raw_tx.as_slice())?; + let tx = protocol::Transaction { + raw_data: Some(raw_tx), + signature: vec![], + ret: vec![], + }; + + Ok(tx) + } +} + +impl Chain for TRX { + fn get_id(&self) -> u32 { + 1 + } + + fn get_name(&self) -> &str { + "TRON" + } + + fn get_symbol(&self) -> &str { + "TRX" + } + + fn get_decimals(&self) -> u32 { + 6 + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let pvk = bip32::derive(&seed, path)?; + Ok(Vec::from(pvk)) + } + + fn get_path(&self, index: u32, is_legacy: bool) -> String { + if is_legacy { + format!("m/44'/{}'/{}'", BIP44_PATH, index) + } else { + format!("m/44'/{}'/0'/0/{}", BIP44_PATH, index) + } + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + let mut pvk = private_key_from_vec(&private_key)?; + let pbk = secp256k1::Secp256K1::private_to_public_uncompressed(&pvk)?; + pvk.fill(0); + Ok(Vec::from(pbk)) + } + + fn get_address(&self, public_key: Vec) -> Result { + let hash = keccak256_digest(&public_key[1..]); + + let mut address: [u8; TRX_ADD_RAW_LEN] = [0; TRX_ADD_RAW_LEN]; + address[0] = TRX_ADDR_PREFIX; + address[1..TRX_ADD_RAW_LEN].copy_from_slice(&hash[12..]); + Ok(TRX::expand_address_with_checksum(&address)) + } + + fn sign_tx( + &self, + private_key: Vec, + mut tx: Transaction, + ) -> Result { + let raw_tx = tx.raw_data; + + if private_key.len() != 32 { + return Err(ChainError::InvalidPrivateKey); + } + + let mut pvk_bytes: [u8; 32] = [0; 32]; + pvk_bytes.copy_from_slice(&private_key[..32]); + let mut tron_tx = TRX::decode_transaction(raw_tx)?; + + let raw_data_clone = tron_tx + .raw_data + .clone() + .ok_or(ChainError::ProtoDecodeError)?; + let mut tx_raw = Vec::new(); + raw_data_clone.encode(&mut tx_raw)?; + + let tx_id = sha256_digest(&tx_raw[..]); + let sig = secp256k1::Secp256K1::sign(&tx_id, &pvk_bytes)?; + + tron_tx.signature.push(sig.to_vec()); + + let mut tx_data = Vec::new(); + tron_tx.encode(&mut tx_data)?; + + tx.raw_data = tx_data; + tx.tx_hash = tx_id.to_vec(); + tx.signature = sig.as_slice().to_vec(); + Ok(tx) + } + + fn sign_message(&self, private_key: Vec, message: Vec) -> Result, ChainError> { + if private_key.len() != 32 { + return Err(ChainError::InvalidPrivateKey); + } + + let mut pvk_bytes: [u8; 32] = [0; 32]; + pvk_bytes.copy_from_slice(&private_key[..32]); + + let parsed_message = TRX::prepare_message(message); + let sig = self.sign_raw(private_key, parsed_message.to_vec())?; + Ok(sig.as_slice().to_vec()) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let mut pvk_bytes = private_key_from_vec(&private_key)?; + let payload_bytes = slice_from_vec(&payload)?; + + let sig = secp256k1::Secp256K1::sign(&payload_bytes, &pvk_bytes)?; + + pvk_bytes.fill(0); + Ok(sig.to_vec()) + } + + fn get_tx_info(&self, raw_tx: Vec) -> Result { + let tx = TRX::decode_transaction(raw_tx)?; + let raw = tx.raw_data.ok_or(ChainError::ProtoDecodeError)?; + if raw.contract.is_empty() { + return Err(ChainError::ProtoDecodeError); + } + + let contract = raw.contract[0].clone(); + + let contract_type = + ContractType::try_from(contract.r#type).map_err(|_| ChainError::ProtoDecodeError)?; + let parameter = contract.parameter.ok_or(ChainError::ProtoDecodeError)?; + + match contract_type { + ContractType::TransferContract => { + let transfer_contract = + protocol::TransferContract::decode(parameter.value.as_slice())?; + let mut owner_u8_addr: [u8; 21] = [0; 21]; + let mut owner_address = String::from(""); + + if transfer_contract.owner_address.len() >= TRX_ADD_RAW_LEN { + owner_u8_addr.copy_from_slice(&transfer_contract.owner_address[..]); + owner_address = TRX::expand_address_with_checksum(&owner_u8_addr); + } + + let mut to_u8_addr: [u8; 21] = [0; 21]; + let mut to_address = String::from(""); + + if transfer_contract.to_address.len() >= TRX_ADD_RAW_LEN { + to_u8_addr.copy_from_slice(&transfer_contract.to_address[..]); + to_address = TRX::expand_address_with_checksum(&to_u8_addr); + } + + let value = U256::from_i64(transfer_contract.amount).to_f64(self.get_decimals()); + return Ok(TxInfo { + sender: owner_address, + receiver: to_address, + value, + tx_type: TxType::Transfer, + }); + } + + ContractType::TransferAssetContract => { + let transfer_contract: protocol::TransferAssetContract = + protocol::TransferAssetContract::decode(parameter.value.as_slice())?; + let mut owner_u8_addr: [u8; 21] = [0; 21]; + let mut owner_address = String::from(""); + + if transfer_contract.owner_address.len() >= TRX_ADD_RAW_LEN { + owner_u8_addr.copy_from_slice(&transfer_contract.owner_address[..]); + owner_address = TRX::expand_address_with_checksum(&owner_u8_addr); + } + + let mut to_u8_addr: [u8; 21] = [0; 21]; + let mut to_address = String::from(""); + + if transfer_contract.to_address.len() >= TRX_ADD_RAW_LEN { + to_u8_addr.copy_from_slice(&transfer_contract.to_address[..]); + to_address = TRX::expand_address_with_checksum(&to_u8_addr); + } + + let value = U256::from_i64(transfer_contract.amount).to_f64(self.get_decimals()); + return Ok(TxInfo { + sender: owner_address, + receiver: to_address, + value, + tx_type: TxType::Transfer, + }); + } + + ContractType::TriggerSmartContract => { + let trigger_contract = + protocol::TriggerSmartContract::decode(parameter.value.as_slice())?; + let mut owner_u8_addr: [u8; 21] = [0; 21]; + let mut owner_address = String::from(""); + + if trigger_contract.owner_address.len() >= TRX_ADD_RAW_LEN { + owner_u8_addr.copy_from_slice(&trigger_contract.owner_address[..]); + owner_address = TRX::expand_address_with_checksum(&owner_u8_addr); + } + + let mut to_u8_addr: [u8; 21] = [0; 21]; + let mut to_address = String::from(""); + + if trigger_contract.contract_address.len() >= TRX_ADD_RAW_LEN { + to_u8_addr.copy_from_slice(&trigger_contract.contract_address[..]); + to_address = TRX::expand_address_with_checksum(&to_u8_addr); + } + + return Ok(TxInfo { + sender: owner_address, + receiver: to_address, + value: 0.0, + tx_type: TxType::TriggerContract, + }); + } + _ => {} + }; + + Ok(TxInfo { + sender: "".to_string(), + receiver: "".to_string(), + value: 0.0, + tx_type: TxType::Unknown, + }) + } +} + +#[cfg(test)] +mod test { + use crate::chains::{Chain, Transaction}; + use alloc::string::{String, ToString}; + use alloc::vec; + + #[test] + fn test_trx_derive() { + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + + let seed = crate::chains::trx::TRX {} + .mnemonic_to_seed(mnemonic, String::from("")) + .unwrap(); + let path = crate::chains::trx::TRX {}.get_path(0, false); + let pvk = crate::chains::trx::TRX {}.derive(seed, path).unwrap(); + assert_eq!(pvk.len(), 32); + let pbk = crate::chains::trx::TRX {}.get_pbk(pvk).unwrap(); + assert_eq!(pbk.len(), 65); + let addr = crate::chains::trx::TRX {}.get_address(pbk).unwrap(); + assert_eq!(addr, "TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH"); + } + + #[test] + fn test_sign_tx() { + let hex_tx = hex::decode( + "0a02487c22080608af18\ + f6ec6c8340d8f8fae2e0315a65080112610a2d747970652e676f6f676c65\ + 617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e74\ + 7261637412300a1541e825d52582eec346c839b4875376117904a76cbc121\ + 54120ab1300cf70c048e4cf5d5b1b33f59653ed6626180a708fb1f7e2e031", + ) + .unwrap(); + + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let path = String::from("m/44'/195'/0'/0/0"); + + let seed = crate::chains::trx::TRX {} + .mnemonic_to_seed(mnemonic, String::from("")) + .unwrap(); + let pvk = crate::chains::trx::TRX {}.derive(seed, path).unwrap(); + assert_eq!(pvk.len(), 32); + + let tx = Transaction { + raw_data: hex_tx, + tx_hash: vec![], + signature: vec![], + options: None, + }; + let _tx = crate::chains::trx::TRX {}.sign_tx(pvk, tx).unwrap(); + } + + #[test] + fn test_decode_tx() { + let hex_tx = hex::decode("0a022986220894d3a7d6c869ebc840c0e1b2b5e1315a65080112610a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412300a1541e825d52582eec346c839b4875376117904a76cbc12154120ab1300cf70c048e4cf5d5b1b33f59653ed6626180a70ae9cafb5e131").unwrap(); + + let info = crate::chains::trx::TRX {}.get_tx_info(hex_tx).unwrap(); + let res = tiny_json_rs::encode(info); + assert_eq!( + res, + r#"{"receiver":"TCwwZeH6so1X4R5kcdbKqa4GWuzF53xPqG","sender":"TX8h6Df74VpJsXF6sTDz1QJsq3Ec8dABc3","tx_type":"Transfer","value":0.00001}"# + ); + } +} diff --git a/packages/kos/src/chains/util.rs b/packages/kos/src/chains/util.rs new file mode 100644 index 0000000..2ed63ab --- /dev/null +++ b/packages/kos/src/chains/util.rs @@ -0,0 +1,15 @@ +use crate::chains::ChainError; + +pub fn slice_from_vec(vec: &[u8]) -> Result<[u8; N], ChainError> { + if vec.len() < N { + return Err(ChainError::InvalidMessageSize); + } + + let mut arr: [u8; N] = [0; N]; + arr.copy_from_slice(vec); + Ok(arr) +} + +pub fn private_key_from_vec(vec: &[u8]) -> Result<[u8; N], ChainError> { + slice_from_vec::(vec).map_err(|_| ChainError::InvalidPrivateKey) +} diff --git a/packages/kos/src/chains/xrp/mod.rs b/packages/kos/src/chains/xrp/mod.rs new file mode 100644 index 0000000..472a24f --- /dev/null +++ b/packages/kos/src/chains/xrp/mod.rs @@ -0,0 +1,128 @@ +use crate::chains::util::{private_key_from_vec, slice_from_vec}; +use crate::chains::{Chain, ChainError, Transaction, TxInfo}; +use crate::crypto::b58::custom_b58enc; +use crate::crypto::bip32; +use crate::crypto::hash::{ripemd160_digest, sha256_digest}; +use crate::crypto::secp256k1::{Secp256K1, Secp256k1Trait}; +use alloc::string::String; +use alloc::vec::Vec; +use alloc::{format, vec}; + +const XRP_ALPHA: &[u8; 58] = b"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"; + +#[allow(clippy::upper_case_acronyms)] +pub(crate) struct XRP {} + +impl XRP { + pub fn new() -> Self { + XRP {} + } +} + +impl Chain for XRP { + fn get_id(&self) -> u32 { + 4 + } + + fn get_name(&self) -> &str { + "Ripple" + } + + fn get_symbol(&self) -> &str { + "XRP" + } + + fn get_decimals(&self) -> u32 { + todo!() + } + + fn mnemonic_to_seed(&self, mnemonic: String, password: String) -> Result, ChainError> { + Ok(bip32::mnemonic_to_seed(mnemonic, password)?) + } + + fn derive(&self, seed: Vec, path: String) -> Result, ChainError> { + let pvk = bip32::derive(&seed, path)?; + Ok(pvk.to_vec()) + } + + fn get_path(&self, index: u32, _is_legacy: bool) -> String { + format!("m/44'/144'/0'/0/{}", index) + } + + fn get_pbk(&self, private_key: Vec) -> Result, ChainError> { + if private_key.len() != 32 { + return Err(ChainError::InvalidPrivateKey); + } + + let mut pk_bytes: [u8; 32] = [0; 32]; + pk_bytes.copy_from_slice(&private_key[..32]); + + let pbk = Secp256K1::private_to_public_compressed(&pk_bytes)?; + Ok(pbk.to_vec()) + } + + fn get_address(&self, public_key: Vec) -> Result { + if public_key.len() != 33 { + return Err(ChainError::InvalidPublicKey); + } + + let mut pubkey_bytes = [0; 33]; + pubkey_bytes.copy_from_slice(&public_key[..33]); + + let hash = ripemd160_digest(&pubkey_bytes); + + let to_base_58 = [vec![0], hash[..].to_vec()].concat(); + let checksum = sha256_digest(&sha256_digest(&to_base_58)); + let checksum_bytes = checksum[..4].to_vec(); + let to_base_58 = [&to_base_58[..], &checksum_bytes[..]].concat(); + + let res = custom_b58enc(&to_base_58, XRP_ALPHA); + let addr = String::from_utf8(res)?; + Ok(addr) + } + + fn sign_tx(&self, _private_key: Vec, _tx: Transaction) -> Result { + Err(ChainError::NotSupported) + } + + fn sign_message( + &self, + _private_key: Vec, + _message: Vec, + ) -> Result, ChainError> { + Err(ChainError::NotSupported) + } + + fn sign_raw(&self, private_key: Vec, payload: Vec) -> Result, ChainError> { + let pvk_bytes = private_key_from_vec(&private_key)?; + let payload_bytes = slice_from_vec(&payload)?; + + let sig = Secp256K1::sign(&payload_bytes, &pvk_bytes)?; + Ok(sig.to_vec()) + } + + fn get_tx_info(&self, _raw_tx: Vec) -> Result { + Err(ChainError::NotSupported) + } +} + +#[cfg(test)] +mod test { + use crate::chains::Chain; + use alloc::string::{String, ToString}; + + #[test] + fn test_get_addr() { + let xrp = super::XRP::new(); + + let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string(); + let path = xrp.get_path(0, false); + + let seed = xrp.mnemonic_to_seed(mnemonic, String::from("")).unwrap(); + let pvk = xrp.derive(seed, path).unwrap(); + let pbk = xrp.get_pbk(pvk).unwrap(); + let addr = xrp.get_address(pbk).unwrap(); + + assert_eq!(addr, "rHsMGQEkVNJmpGWs8XUBoTBiAAbwxZN5v3"); + } +} diff --git a/packages/kos/src/crypto/b58.rs b/packages/kos/src/crypto/b58.rs new file mode 100644 index 0000000..a39c2a5 --- /dev/null +++ b/packages/kos/src/crypto/b58.rs @@ -0,0 +1,51 @@ +use alloc::vec; +use alloc::vec::Vec; + +const B58DIGITS_ORDERED: &[u8; 58] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +pub fn custom_b58enc(data: &[u8], alpha: &[u8; 58]) -> Vec { + let mut zcount = 0; + + while zcount < data.len() && data[zcount] == 0 { + zcount += 1; + } + + let size = (data.len() - zcount) * 138 / 100 + 1; + let mut buf = vec![0u8; size]; + + let mut high = size - 1; + for (_, &val) in data.iter().enumerate().skip(zcount) { + let mut carry = val as usize; + let mut j = size - 1; + + while j > high || carry != 0 { + carry += 256 * buf[j] as usize; + buf[j] = (carry % 58) as u8; + carry /= 58; + + if j == 0 { + break; + } + j -= 1; + } + high = j; + } + + let mut j = 0; + while j < size && buf[j] == 0 { + j += 1; + } + + let mut result = Vec::with_capacity(zcount + size - j); + result.extend(vec![alpha[0]; zcount]); + + for &val in &buf[j..] { + result.push(alpha[val as usize]); + } + + result +} + +pub fn b58enc(data: &[u8]) -> Vec { + custom_b58enc(data, B58DIGITS_ORDERED) +} diff --git a/packages/kos/src/crypto/base64.rs b/packages/kos/src/crypto/base64.rs new file mode 100644 index 0000000..27edd2f --- /dev/null +++ b/packages/kos/src/crypto/base64.rs @@ -0,0 +1,63 @@ +// A very simple base64 encoder for demonstration purposes +pub fn simple_base64_encode(input: &[u8]) -> alloc::string::String { + const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + let mut output = alloc::string::String::new(); + + let mut temp = 0u32; + let mut temp_bits = 0; + + for byte in input { + temp <<= 8; + temp |= *byte as u32; + temp_bits += 8; + + while temp_bits >= 6 { + temp_bits -= 6; + output.push(CHARSET[((temp >> temp_bits) & 0x3F) as usize] as char); + } + } + + if temp_bits > 0 { + temp <<= 6 - temp_bits; + output.push(CHARSET[(temp & 0x3F) as usize] as char); + } + + while output.len() % 4 != 0 { + output.push('='); + } + + output +} + +// A very simple base64 decoder for demonstration purposes +pub fn simple_base64_decode(input: &str) -> Result, &'static str> { + let input = input.trim_end_matches('='); + let mut buffer = alloc::vec::Vec::new(); + + let mut temp = 0u32; + let mut temp_bits = 0; + for c in input.chars() { + let value = match c as u8 { + b'A'..=b'Z' => c as u8 - b'A', + b'a'..=b'z' => c as u8 - b'a' + 26, + b'0'..=b'9' => c as u8 - b'0' + 52, + b'+' => 62, + b'/' => 63, + _ => return Err("Invalid base64 character"), + }; + temp <<= 6; + temp |= value as u32; + temp_bits += 6; + + if temp_bits >= 8 { + temp_bits -= 8; + buffer.push((temp >> temp_bits) as u8); + } + } + + if temp_bits >= 6 || (temp & ((1 << temp_bits) - 1)) != 0 { + return Err("Invalid base64 padding"); + } + + Ok(buffer) +} diff --git a/packages/kos/src/crypto/bignum.rs b/packages/kos/src/crypto/bignum.rs new file mode 100644 index 0000000..ea4aa87 --- /dev/null +++ b/packages/kos/src/crypto/bignum.rs @@ -0,0 +1,226 @@ +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::f64; +use core::fmt::Write; +use rlp::{DecoderError, Rlp, RlpStream}; + +pub struct U256(pub [u8; 32]); + +impl Clone for U256 { + fn clone(&self) -> Self { + let mut bytes: [u8; 32] = [0; 32]; + bytes.copy_from_slice(&self.0[..]); + U256(bytes) + } +} + +impl rlp::Decodable for U256 { + fn decode(rlp: &Rlp) -> Result { + let data: Vec = rlp.as_val()?; + + let val = U256::read_data_as_le(data.clone()); + + Ok(val) + } +} + +impl rlp::Encodable for U256 { + fn rlp_append(&self, s: &mut RlpStream) { + //Copy data as the reverse of the bytes removing the leading zeroes + let to_put = self.put_data_as_le(); + + s.encoder().encode_value(&to_put); + } +} + +impl U256 { + #[allow(dead_code)] + pub fn from_f64(value: f64, precision: u32) -> Result { + if value.is_nan() || value.is_infinite() || value < 0.0 { + return Err("Invalid input: NaN, infinity, or negative value."); + } + + let scaled_value = value * powi(10.0, precision as i32); + if scaled_value > (u64::MAX as f64) { + return Err("Input too large to fit in U256."); + } + + let integer_value = scaled_value as u64; + Ok(U256::from_u64(integer_value)) + } + + #[allow(clippy::needless_range_loop)] + pub fn to_f64(&self, precision: u32) -> f64 { + let bytes = self.0; + let mut value: f64 = 0.0; + + for i in 0..32 { + value *= 256.0; + value += bytes[i] as f64; + } + + value /= powi(10.0, precision as i32); + value + } + + pub fn from_i64(value: i64) -> U256 { + let value = value as u64; + U256::from_u64(value) + } + + pub fn from_u64(value: u64) -> U256 { + let mut bytes: [u8; 32] = [0; 32]; + bytes[24..32].copy_from_slice(&value.to_be_bytes()); + U256(bytes) + } + + #[allow(dead_code)] + pub fn from_string(value: &str) -> Result { + if value.len() > 64 { + return Err("String length exceeds U256 size."); + } + + let mut padded = String::new(); + for _ in 0..(64 - value.len()) { + // Zero-padding to ensure 64 characters. + padded.push('0'); + } + padded.push_str(value); + + let bytes = hex::decode(&padded).map_err(|_| "Invalid hex string")?; + let mut data = [0u8; 32]; + data.copy_from_slice(&bytes); + Ok(U256(data)) + } + + #[allow(clippy::inherent_to_string)] + #[allow(dead_code)] + pub fn to_string(&self) -> String { + let mut hex_string = String::new(); + for byte in self.0.iter() { + write!(&mut hex_string, "{:02x}", byte).unwrap(); // Safely format bytes as hex. + } + + let trimmed = hex_string.trim_start_matches('0'); + if trimmed.is_empty() { + "0".to_string() + } else { + trimmed.to_string() + } + } + + #[allow(dead_code)] + pub fn to_u64_be(&self) -> u64 { + let bytes = self.0; + let mut significant_part = [0u8; 8]; + significant_part.copy_from_slice(&bytes[24..32]); + u64::from_be_bytes(significant_part) + } + + #[allow(clippy::manual_memcpy)] + #[allow(dead_code)] + pub fn read_data_as_be(data: Vec) -> Self { + let mut bytes: [u8; 32] = [0; 32]; + let len = data.len().min(32); + for i in 0..len { + bytes[i] = data[i]; + } + + U256(bytes) + } + + pub fn read_data_as_le(data: Vec) -> Self { + let mut bytes: [u8; 32] = [0; 32]; + let len = data.len(); + for i in 0..len { + bytes[i] = data[len - 1 - i]; + } + + U256(bytes) + } + + #[allow(dead_code)] + pub fn put_data_as_be(&self) -> Vec { + let mut data: Vec = Vec::new(); + for i in 0..32 { + data.push(self.0[i]); + } + + data + } + + #[allow(clippy::needless_range_loop)] + pub fn put_data_as_le(&self) -> Vec { + let mut data: Vec = Vec::new(); + //find the end of the data + for i in 0..32 { + data.push(self.0[31 - i]); + } + + //Trim the leading zeroes + let mut start = data.len(); + for i in 0..32 { + if data[i] != 0 { + start = i; + break; + } + } + + data = data[start..].to_vec(); + data + } +} + +fn powi(f: f64, exp: i32) -> f64 { + let mut result = 1.0; + let mut base = f; + let mut exponent = exp; + + if exponent < 0 { + base = 1.0 / base; + exponent = -exponent; + } + + while exponent > 0 { + if exponent % 2 == 1 { + result *= base; + } + base *= base; + exponent /= 2; + } + + result +} + +#[cfg(test)] +mod test { + use crate::crypto::bignum::U256; + + #[test] + fn test_f64_conversion() { + let mut value = U256([0x0; 32]); + value.0[31] = 0x01; + let precision = 6; + let float_value = value.to_f64(precision); + assert_eq!(float_value, 1e-6); + + let reconstructed = U256::from_f64(float_value, precision).unwrap(); + assert_eq!(reconstructed.0, value.0); + + let large_value = 12345.6789; + let large_precision = 4; + let u256_value = U256::from_f64(large_value, large_precision).unwrap(); + let back_to_f64 = u256_value.to_f64(large_precision); + assert_eq!(back_to_f64, large_value); + } + + #[test] + fn test_string_conversion() { + let value = U256::from_u64(123456789); + let hex_string = value.to_string(); + assert_eq!(hex_string, "75bcd15"); + + let parsed = U256::from_string(&hex_string).unwrap(); + assert_eq!(parsed.0, value.0); + } +} diff --git a/packages/kos/src/crypto/bip32.rs b/packages/kos/src/crypto/bip32.rs new file mode 100644 index 0000000..419f0c3 --- /dev/null +++ b/packages/kos/src/crypto/bip32.rs @@ -0,0 +1,360 @@ +use crate::crypto::ed25519::{Ed25519Err, Ed25519Trait}; +use crate::crypto::ed25519_bip32::{add28_mul8, add_mod256}; +use crate::crypto::pbkdf2::{Pbkdf2, Pbkdf2Trait}; +use crate::crypto::secp256k1::{Secp256Err, Secp256K1, Secp256k1Trait}; +use crate::crypto::sr25519::{Sr25519, Sr25519Error, Sr25519Trait}; +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; +use bip39_dict::Entropy; +use core::fmt::{Display, Formatter}; +use core::num::ParseIntError; +use core::str::FromStr; +use hmac::digest::InvalidLength; +use hmac::{Hmac, Mac}; +use pbkdf2::pbkdf2_hmac; +use sha2::Sha512; + +const BTC_SEED_NAME: &[u8] = b"Bitcoin seed"; +const ED25519_NAME: &[u8] = b"ed25519 seed"; + +const BIP39_PBKDF2_ROUNDS: u32 = 2048; + +#[derive(Debug)] +pub enum Bip32Err { + ConversionErr, + Secp256Err(Secp256Err), + Sr25519Error(Sr25519Error), + HmacError, + DeriveError, + PathError, + InvalidMnemonic, +} + +impl Display for Bip32Err { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Bip32Err::ConversionErr => write!(f, "Conversion error"), + Bip32Err::Secp256Err(v) => write!(f, "Curve error: {}", v), + Bip32Err::PathError => write!(f, "Path error"), + Bip32Err::DeriveError => write!(f, "Derive error"), + Bip32Err::InvalidMnemonic => write!(f, "Invalid mnemonic"), + Bip32Err::Sr25519Error(v) => write!(f, "Sr25519 error: {}", v), + Bip32Err::HmacError => write!(f, "Hmac error"), + } + } +} + +impl From for Bip32Err { + fn from(_: ParseIntError) -> Self { + Bip32Err::ConversionErr + } +} + +impl From for Bip32Err { + fn from(value: Secp256Err) -> Self { + Bip32Err::Secp256Err(value) + } +} + +impl From for Bip32Err { + fn from(_: Ed25519Err) -> Self { + Bip32Err::DeriveError + } +} + +impl From for Bip32Err { + fn from(value: Sr25519Error) -> Self { + Bip32Err::Sr25519Error(value) + } +} + +impl From for Bip32Err { + fn from(_: InvalidLength) -> Self { + Bip32Err::HmacError + } +} + +type Hmac512 = Hmac; +pub fn compute_hmac(message: &[u8], key: &[u8]) -> Result<(Vec, Vec), Bip32Err> { + let mut mac = Hmac512::new_from_slice(key)?; + mac.update(message); + let result = mac.finalize().into_bytes(); + let vec_result = result.to_vec(); + let mut left = vec![0u8; 32]; + let mut right = vec![0u8; 32]; + left.copy_from_slice(&vec_result[..32]); + right.copy_from_slice(&vec_result[32..]); + + Ok((left, right)) +} + +pub fn compute_master_from_seed(seed: &[u8]) -> Result<(Vec, Vec), Bip32Err> { + let (left, right) = compute_hmac(seed, BTC_SEED_NAME)?; + Ok((left, right)) +} + +pub struct PathComponent { + pub is_hardened: bool, + pub index: u32, +} + +pub fn handle_path(path: String) -> Result, Bip32Err> { + let mut components: Vec<&str> = path.split('/').collect(); + Vec::remove(&mut components, 0); + let mut path_components: Vec = Vec::new(); + + for component in components { + let mut is_hardened = false; + let index: u32; + if component.contains("'") { + is_hardened = true; + index = component.replace("'", "").parse::().unwrap(); + } else { + index = component.parse::()? + } + + path_components.push(PathComponent { is_hardened, index }); + } + + Ok(path_components) +} + +pub fn derive_private_key( + pvk: &[u8; 32], + ch: &[u8; 32], + mut pc: PathComponent, +) -> Result<([u8; 32], [u8; 32]), Bip32Err> { + let mut data: Vec = Vec::new(); + if pc.is_hardened { + pc.index |= 0x80000000; + data.push(0); + data.extend_from_slice(pvk); + } else { + let pbk = Secp256K1::private_to_public_compressed(pvk)?; + data = Vec::from(pbk); + } + + data.extend_from_slice(&pc.index.to_be_bytes()); + let (hmac_master, hmac_ch) = compute_hmac(&data, ch)?; + let mut new_data: [u8; 32] = [0; 32]; + let mut new_ch: [u8; 32] = [0; 32]; + new_data.copy_from_slice(&hmac_master[..32]); + new_ch.copy_from_slice(&hmac_ch[..32]); + + let x = Secp256K1::add_scalars(pvk, &new_data)?; + + Ok((x, new_ch)) +} + +pub fn derive_pk_from_path( + pvk: &[u8], + ch: &[u8], + path_c: Vec, +) -> Result<[u8; 32], Bip32Err> { + let mut data: [u8; 32] = [0; 32]; + data.copy_from_slice(pvk); + + let mut chaincode: [u8; 32] = [0; 32]; + chaincode.copy_from_slice(ch); + + for component in path_c { + let (new_data, new_ch) = derive_private_key(&data, &chaincode, component)?; + data.copy_from_slice(&new_data); + chaincode.copy_from_slice(&new_ch); + } + + Ok(data) +} +pub fn derive(input_key: &[u8], path: String) -> Result<[u8; 32], Bip32Err> { + let path_components = handle_path(path)?; + let (master_key, chain_code) = compute_master_from_seed(input_key)?; + let pk = derive_pk_from_path(&master_key, &chain_code, path_components)?; + Ok(pk) +} + +pub fn derive_ed25519(input_key: &[u8], path: String) -> Result<[u8; 32], Bip32Err> { + let path_components = handle_path(path)?; + + let (mut key, mut chaincode) = compute_hmac(input_key, ED25519_NAME)?; + + for mut component in path_components { + let mut data = Vec::new(); + data.push(0); + data.extend_from_slice(&key[..32]); + if component.is_hardened { + component.index |= 0x80000000; + } + + data.extend_from_slice(&component.index.to_be_bytes()); + let (hmac_master, hmac_ch) = compute_hmac(&data, &chaincode)?; + key = hmac_master; + chaincode = hmac_ch; + } + + let mut result_key: [u8; 32] = [0; 32]; + result_key.copy_from_slice(&key[..32]); + Ok(result_key) +} + +pub fn derive_sr25519(input_key: &[u8], mut path: String) -> Result<[u8; 64], Bip32Err> { + let mut chaincode = [0u8; 32]; + let mut is_hardned = false; + if !path.is_empty() { + path = path + .strip_prefix("//") + .ok_or(Bip32Err::PathError)? + .to_string(); + path = path + .strip_suffix("///") + .ok_or(Bip32Err::PathError)? + .to_string(); + + let chaincode_value = u8::from_str(path.as_str()).map_err(|_| Bip32Err::PathError)?; + + chaincode[0] = chaincode_value; + is_hardned = true; + } + + let mut mini_sk = [0; 32]; + mini_sk.copy_from_slice(&input_key[..32]); + + let (mut sk, mut nonce) = Sr25519::expand_secret_key(&mini_sk)?; + if is_hardned { + let mini_sk = Sr25519::hard_derive_mini_sk(&mini_sk, &chaincode)?; + (sk, nonce) = Sr25519::expand_secret_key(&mini_sk)? + } + + let mut result_key: [u8; 64] = [0; 64]; + result_key[..32].copy_from_slice(&sk); + result_key[32..].copy_from_slice(&nonce); + + Ok(result_key) +} + +pub fn mnemonic_to_seed(mnemonic: String, password: String) -> Result, Bip32Err> { + let mut salt = String::from("mnemonic"); + salt.push_str(password.as_str()); + let mut seed = [0u8; 64]; + pbkdf2_hmac::( + mnemonic.as_bytes(), + salt.as_bytes(), + BIP39_PBKDF2_ROUNDS, + &mut seed, + ); + Ok(Vec::from(seed)) +} + +pub fn mnemonic_to_seed_substrate(mnemonic: String, password: String) -> Result, Bip32Err> { + let entropy = mnemonic_to_entropy(mnemonic)?; + let mut salt = String::from("mnemonic"); + salt.push_str(password.as_str()); + let mut seed = [0u8; 64]; + pbkdf2_hmac::( + entropy.as_slice(), + salt.as_bytes(), + BIP39_PBKDF2_ROUNDS, + &mut seed, + ); + Ok(Vec::from(seed)) +} + +pub fn mnemonic_to_entropy(mnemonic: String) -> Result, Bip32Err> { + let words_count = mnemonic.split(" ").count(); + match words_count { + 12 => mnemonic_to_entropy_12(mnemonic), + 24 => mnemonic_to_entropy_24(mnemonic), + _ => Err(Bip32Err::InvalidMnemonic), + } +} + +pub fn mnemonic_to_entropy_12(mnemonic: String) -> Result, Bip32Err> { + let mnemonic = + bip39_dict::Mnemonics::<12>::from_string(&bip39_dict::ENGLISH, mnemonic.as_str()) + .map_err(|_| Bip32Err::InvalidMnemonic)?; + + let entropy = + Entropy::<16>::from_mnemonics::<12, 4>(&mnemonic).map_err(|_| Bip32Err::InvalidMnemonic)?; + + Ok(entropy.0.to_vec()) +} + +pub fn mnemonic_to_entropy_24(mnemonic: String) -> Result, Bip32Err> { + let mnemonic = + bip39_dict::Mnemonics::<24>::from_string(&bip39_dict::ENGLISH, mnemonic.as_str()) + .map_err(|_| Bip32Err::InvalidMnemonic)?; + + let entropy = + Entropy::<32>::from_mnemonics::<24, 8>(&mnemonic).map_err(|_| Bip32Err::InvalidMnemonic)?; + + Ok(entropy.0.to_vec()) +} + +pub fn mnemonic_to_seed_ed25519_bip32(mnemonic: String) -> Result, Bip32Err> { + let entropy = mnemonic_to_entropy(mnemonic)?; + let icarus_key = Pbkdf2::pbkdf2_hmac_512::<96>(&[], &entropy, 4096); + + let mut pvk = icarus_key.to_vec(); + pvk[0] &= 0xf8; + pvk[31] &= 0x1f; + pvk[31] |= 0x40; + + Ok(pvk) +} + +pub fn derive_ed25519_bip32(input_key: [u8; 96], path: String) -> Result<[u8; 96], Bip32Err> { + let path_components = handle_path(path)?; + let mut pvk = [0u8; 64]; + let mut chaincode = [0u8; 32]; + + pvk.copy_from_slice(&input_key[..64]); + chaincode.copy_from_slice(&input_key[64..]); + + for mut component in path_components { + if component.is_hardened { + component.index |= 0x80000000; + } + + let mut zmac = Hmac512::new_from_slice(&chaincode)?; + let mut ccmac = Hmac512::new_from_slice(&chaincode)?; + + let sindex = component.index.to_le_bytes().to_vec(); + + if component.is_hardened { + zmac.update(&[0]); + zmac.update(&pvk); + zmac.update(&sindex); + ccmac.update(&[0x01]); + ccmac.update(&pvk); + ccmac.update(&sindex); + } else { + let pub_key = crate::crypto::ed25519::Ed25519::public_from_extended(&pvk)?; + zmac.update(&[0x02]); + zmac.update(&pub_key); + zmac.update(&sindex); + ccmac.update(&[0x03]); + ccmac.update(&pub_key); + ccmac.update(&sindex); + } + + let z = zmac.finalize().into_bytes(); + let zl = z[..32].to_vec(); + let zr = z[32..].to_vec(); + + let kl = add28_mul8(&pvk[0..32], &zl); + let kr = add_mod256(&pvk[32..64], &zr); + + let cc = ccmac.finalize().into_bytes(); + let cc = cc[32..].to_vec(); + + pvk[0..32].copy_from_slice(&kl); + pvk[32..64].copy_from_slice(&kr); + chaincode.copy_from_slice(&cc); + } + + let mut result_key: [u8; 96] = [0; 96]; + result_key[..64].copy_from_slice(&pvk); + result_key[64..].copy_from_slice(&chaincode); + + Ok(result_key) +} diff --git a/packages/kos-crypto/src/cipher.rs b/packages/kos/src/crypto/cipher.rs similarity index 76% rename from packages/kos-crypto/src/cipher.rs rename to packages/kos/src/crypto/cipher.rs index b40f44e..2b0fb66 100644 --- a/packages/kos-crypto/src/cipher.rs +++ b/packages/kos/src/crypto/cipher.rs @@ -1,6 +1,3 @@ -use base64::{engine::general_purpose as b64_engine, Engine}; -use kos_types::error::Error; - use aes::cipher::{ block_padding::Pkcs7, generic_array::GenericArray, AsyncStreamCipher, BlockDecryptMut, BlockEncryptMut, KeyIvInit, @@ -12,6 +9,7 @@ use aes_gcm::{ Nonce, }; +use crate::chains::ChainError; use hmac::Hmac; use pbkdf2::pbkdf2; use pbkdf2::{ @@ -22,8 +20,6 @@ use pem::{parse as parse_pem, Pem}; use rand::Rng; use sha2::Sha256; -use wasm_bindgen::prelude::*; - const KEY_SIZE: usize = 32; // SALTSIZE const NONCE_SIZE: usize = 12; // NONCESIZE const IV_SIZE: usize = 16; // IVSIZE @@ -32,13 +28,12 @@ const BLOCK_SIZE: usize = 16; // BLOCKSIZE const ITERATIONS: u32 = 10000; #[derive(Debug, Clone)] -#[wasm_bindgen] pub enum CipherAlgo { GMC = 0, CBC = 1, CFB = 2, } -// todo!("build with macro") + impl CipherAlgo { pub fn to_vec(&self) -> Vec { match self { @@ -48,16 +43,18 @@ impl CipherAlgo { } } - pub fn from_u8(value: u8) -> Result { + pub fn from_u8(value: u8) -> Result { match value { 0 => Ok(CipherAlgo::GMC), 1 => Ok(CipherAlgo::CBC), 2 => Ok(CipherAlgo::CFB), - _ => Err(Error::CipherError("Invalid cipher algorithm".to_owned())), + _ => Err(ChainError::CipherError( + "Invalid cipher algorithm".to_owned(), + )), } } - pub fn encrypt(&self, data: &[u8], password: &str) -> Result, Error> { + pub fn encrypt(&self, data: &[u8], password: &str) -> Result, ChainError> { match self { CipherAlgo::GMC => gcm_encrypt(data, password), CipherAlgo::CBC => cbc_encrypt(data, password), @@ -65,7 +62,7 @@ impl CipherAlgo { } } - pub fn decrypt(&self, data: &[u8], password: &str) -> Result, Error> { + pub fn decrypt(&self, data: &[u8], password: &str) -> Result, ChainError> { match self { CipherAlgo::GMC => gcm_decrypt(data, password), CipherAlgo::CBC => cbc_decrypt(data, password), @@ -74,7 +71,7 @@ impl CipherAlgo { } } -pub fn to_pem(tag: String, data: &[u8]) -> Result { +pub fn to_pem(tag: String, data: &[u8]) -> Result { Ok(Pem::new(tag, data)) } @@ -82,8 +79,8 @@ pub fn from_pem(pem: Pem) -> Vec { pem.contents().to_vec() } -pub fn string_to_pem(data: &str) -> Result { - parse_pem(data.as_bytes()).map_err(|e| Error::CipherError(format!("{}", e))) +pub fn string_to_pem(data: &str) -> Result { + parse_pem(data.as_bytes()).map_err(|e| ChainError::CipherError(format!("{}", e))) } pub fn create_checksum(password: &str) -> String { @@ -106,17 +103,17 @@ pub fn check_checksum(password: &str, checksum: String) -> bool { pub fn derive_key(salt: &[u8], password: &str) -> Vec { let mut key = vec![0u8; KEY_SIZE]; pbkdf2::>(password.as_bytes(), salt, ITERATIONS, &mut key) - .expect("Error deriving key"); + .expect("ChainError deriving key"); key } -pub fn encrypt(algo: CipherAlgo, data: &[u8], password: &str) -> Result, Error> { +pub fn encrypt(algo: CipherAlgo, data: &[u8], password: &str) -> Result, ChainError> { algo.encrypt(data, password) } -pub fn decrypt(data: &[u8], password: &str) -> Result, Error> { +pub fn decrypt(data: &[u8], password: &str) -> Result, ChainError> { if data.is_empty() { - return Err(Error::CipherError("Invalid PEM data".to_owned())); + return Err(ChainError::CipherError("Invalid PEM data".to_owned())); } // get algo @@ -124,7 +121,7 @@ pub fn decrypt(data: &[u8], password: &str) -> Result, Error> { algo.decrypt(&data[1..], password) } -pub fn gcm_encrypt(data: &[u8], password: &str) -> Result, Error> { +pub fn gcm_encrypt(data: &[u8], password: &str) -> Result, ChainError> { // Derive key from password let salt: [u8; 32] = rand::thread_rng().gen(); let derived_key = derive_key(&salt, password); @@ -137,7 +134,7 @@ pub fn gcm_encrypt(data: &[u8], password: &str) -> Result, Error> { // Encrypt data let ciphertext = cipher .encrypt(&nonce, data) - .map_err(|e| Error::CipherError(format!("encryption failed: {}", e)))?; + .map_err(|e| ChainError::CipherError(format!("encryption failed: {}", e)))?; // Create PEM let mut result = CipherAlgo::GMC.to_vec(); @@ -148,9 +145,9 @@ pub fn gcm_encrypt(data: &[u8], password: &str) -> Result, Error> { Ok(result) } -pub fn gcm_decrypt(encrypted: &[u8], password: &str) -> Result, Error> { +pub fn gcm_decrypt(encrypted: &[u8], password: &str) -> Result, ChainError> { if encrypted.len() < KEY_SIZE + NONCE_SIZE { - return Err(Error::CipherError("Invalid PEM data".to_owned())); + return Err(ChainError::CipherError("Invalid PEM data".to_owned())); } let salt = &encrypted[..KEY_SIZE]; let nonce = &encrypted[KEY_SIZE..KEY_SIZE + NONCE_SIZE]; @@ -160,12 +157,12 @@ pub fn gcm_decrypt(encrypted: &[u8], password: &str) -> Result, Error> { let cipher = Aes256Gcm::new(key); cipher .decrypt(Nonce::from_slice(nonce), encrypted_data) - .map_err(|e| Error::CipherError(format!("decryption failed: {}", e))) + .map_err(|e| ChainError::CipherError(format!("decryption failed: {}", e))) } type Aes256CbcEnc = cbc::Encryptor; type Aes256CbcDec = cbc::Decryptor; -pub fn cbc_encrypt(data: &[u8], password: &str) -> Result, Error> { +pub fn cbc_encrypt(data: &[u8], password: &str) -> Result, ChainError> { let iv: [u8; IV_SIZE] = rand::thread_rng().gen(); let derived_key = derive_key(&iv, password); // KeySize [u8; 32] let key = GenericArray::from_slice(&derived_key); @@ -179,7 +176,7 @@ pub fn cbc_encrypt(data: &[u8], password: &str) -> Result, Error> { let ct = Aes256CbcEnc::new(key, &iv.into()) .encrypt_padded_mut::(&mut buf, data.len()) - .map_err(|e| Error::CipherError(format!("encryption failed: {}", e)))?; + .map_err(|e| ChainError::CipherError(format!("encryption failed: {}", e)))?; // Create PEM let mut result = CipherAlgo::CBC.to_vec(); @@ -189,9 +186,9 @@ pub fn cbc_encrypt(data: &[u8], password: &str) -> Result, Error> { Ok(result) } -pub fn cbc_decrypt(encrypted: &[u8], password: &str) -> Result, Error> { +pub fn cbc_decrypt(encrypted: &[u8], password: &str) -> Result, ChainError> { if encrypted.len() < IV_SIZE { - return Err(Error::CipherError("Invalid PEM data".to_owned())); + return Err(ChainError::CipherError("Invalid PEM data".to_owned())); } let iv = GenericArray::from_slice(&encrypted[..IV_SIZE]); @@ -202,14 +199,14 @@ pub fn cbc_decrypt(encrypted: &[u8], password: &str) -> Result, Error> { let mut buf = encrypted_data.to_vec(); let pt: &[u8] = Aes256CbcDec::new(key, iv) .decrypt_padded_mut::(&mut buf) - .map_err(|e| Error::CipherError(format!("decryption failed: {}", e)))?; + .map_err(|e| ChainError::CipherError(format!("decryption failed: {}", e)))?; Ok(pt.to_vec()) } type Aes256CfbEnc = cfb_mode::Encryptor; type Aes256CfbDec = cfb_mode::Decryptor; -pub fn cfb_encrypt(data: &[u8], password: &str) -> Result, Error> { +pub fn cfb_encrypt(data: &[u8], password: &str) -> Result, ChainError> { let iv: [u8; IV_SIZE] = rand::thread_rng().gen(); let derived_key = derive_key(&iv, password); // KeySize [u8; 32] let key = GenericArray::from_slice(&derived_key); @@ -225,9 +222,9 @@ pub fn cfb_encrypt(data: &[u8], password: &str) -> Result, Error> { Ok(result) } -pub fn cfb_decrypt(encrypted: &[u8], password: &str) -> Result, Error> { +pub fn cfb_decrypt(encrypted: &[u8], password: &str) -> Result, ChainError> { if encrypted.len() < IV_SIZE { - return Err(Error::CipherError("Invalid PEM data".to_owned())); + return Err(ChainError::CipherError("Invalid PEM data".to_owned())); } let iv = GenericArray::from_slice(&encrypted[..IV_SIZE]); @@ -316,40 +313,3 @@ mod tests { assert!(check_checksum(password, checksum)); } } - -/* - LEGACY GCM DECRYPT SHA1 KEY no SALT -*/ -use sha1::Sha1; -#[wasm_bindgen(js_name = gcmDecryptByKey)] -pub fn gcm_decrypt_by_key(encrypted: &str, key: &str) -> Result, Error> { - if encrypted.len() < NONCE_SIZE { - return Err(Error::CipherError("invalid data len".to_owned())); - } - - // convert hex to bytes - let encrypted = hex::decode(encrypted) - .map_err(|e| Error::CipherError(format!("decryption failed: {}", e)))?; - - let b64 = b64_engine::STANDARD; - // convert base64 to bytes - let key = b64 - .decode(key) - .map_err(|e| Error::CipherError(format!("decryption failed: {}", e)))?; - - // compute encrypt key - let salt = crate::hash::sha256("".as_bytes()); - let key_hash = crate::hash::sha256(&key[..]); - let mut key = vec![0u8; KEY_SIZE]; - pbkdf2::>(&key_hash, &salt, 4096, &mut key).expect("Error deriving key"); - - // retrieve nonce and encrypted data - let nonce = &encrypted[..NONCE_SIZE]; - let encrypted_data = &encrypted[NONCE_SIZE..]; - - let key = Key::::from_slice(&key); - let cipher = Aes256Gcm::new(key); - cipher - .decrypt(Nonce::from_slice(nonce), encrypted_data) - .map_err(|e| Error::CipherError(format!("decryption failed: {}", e))) -} diff --git a/packages/kos/src/crypto/ed25519.rs b/packages/kos/src/crypto/ed25519.rs new file mode 100644 index 0000000..3633ad8 --- /dev/null +++ b/packages/kos/src/crypto/ed25519.rs @@ -0,0 +1,64 @@ +use alloc::vec::Vec; +use core::fmt::Display; +use ed25519_dalek::hazmat::ExpandedSecretKey; +use ed25519_dalek::Signer; +use sha2::Sha512; + +#[derive(Debug)] +#[allow(dead_code)] +pub enum Ed25519Err { + ErrDerive, +} + +impl Display for Ed25519Err { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Ed25519Err::ErrDerive => write!(f, "Derive error"), + } + } +} + +pub struct Ed25519 {} + +pub trait Ed25519Trait { + fn public_from_private(pvk: &[u8; 32]) -> Result, Ed25519Err>; + fn sign(pvk: &[u8; 32], msg: &[u8]) -> Result, Ed25519Err>; + fn public_from_extended(pvk: &[u8; 64]) -> Result, Ed25519Err>; + fn sign_extended(pvk: &[u8; 64], msg: &[u8]) -> Result, Ed25519Err>; +} + +impl Ed25519Trait for Ed25519 { + fn public_from_private(pvk: &[u8; 32]) -> Result, Ed25519Err> { + let mut sk = ed25519_dalek::SecretKey::default(); + sk.copy_from_slice(pvk); + let pb = ed25519_dalek::SigningKey::from(&sk) + .verifying_key() + .to_bytes() + .to_vec(); + if pb.len() != 32 { + return Err(Ed25519Err::ErrDerive); + } + + Ok(pb) + } + + fn public_from_extended(pvk: &[u8; 64]) -> Result, Ed25519Err> { + let vk = ed25519_dalek::VerifyingKey::from(&ExpandedSecretKey::from_bytes(pvk)); + Ok(vk.to_bytes().to_vec()) + } + + fn sign_extended(pvk: &[u8; 64], msg: &[u8]) -> Result, Ed25519Err> { + let expanded = ExpandedSecretKey::from_bytes(pvk); + let vk = ed25519_dalek::VerifyingKey::from(&ExpandedSecretKey::from_bytes(pvk)); + let sig = ed25519_dalek::hazmat::raw_sign::(&expanded, msg, &vk); + Ok(sig.to_bytes().as_slice().to_vec()) + } + + fn sign(pvk: &[u8; 32], msg: &[u8]) -> Result, Ed25519Err> { + let mut sk = ed25519_dalek::SecretKey::default(); + sk.copy_from_slice(pvk); + let sig_key = ed25519_dalek::SigningKey::from(&sk); + let sig = sig_key.sign(msg); + Ok(sig.to_bytes().as_slice().to_vec()) + } +} diff --git a/packages/kos/src/crypto/ed25519_bip32.rs b/packages/kos/src/crypto/ed25519_bip32.rs new file mode 100644 index 0000000..7bf59ae --- /dev/null +++ b/packages/kos/src/crypto/ed25519_bip32.rs @@ -0,0 +1,37 @@ +use alloc::vec; +use alloc::vec::Vec; + +pub fn add28_mul8(x: &[u8], y: &[u8]) -> Vec { + let mut out = vec![0u8; 32]; + let mut carry = 0u16; + + // Process the first 28 bytes with multiplication by 8 + for i in 0..28 { + let r = x[i] as u16 + ((y[i] as u16) << 3) + carry; + out[i] = (r & 0xff) as u8; + carry = r >> 8; + } + + // Process the remaining bytes with only carry + for i in 28..32 { + let r = x[i] as u16 + carry; + out[i] = (r & 0xff) as u8; + carry = r >> 8; + } + + out +} + +pub fn add_mod256(x: &[u8], y: &[u8]) -> Vec { + let mut out = vec![0u8; 32]; // Initialize output vector with 32 zeros + let mut carry = 0u16; // Initialize carry as a 16-bit integer to hold overflow + + for i in 0..32 { + // Perform the addition with carry for each byte + let r = x[i] as u16 + y[i] as u16 + carry; + out[i] = (r & 0xFF) as u8; // Assign the lower 8 bits to the output + carry = r >> 8; // Update carry with the overflow bit + } + + out +} diff --git a/packages/kos/src/crypto/hash.rs b/packages/kos/src/crypto/hash.rs new file mode 100644 index 0000000..000b12b --- /dev/null +++ b/packages/kos/src/crypto/hash.rs @@ -0,0 +1,87 @@ +use blake2b_ref::Blake2bBuilder; +use sha2::digest::FixedOutput; +use sha2::Digest; + +pub fn sha256_digest(data: &[u8]) -> [u8; 32] { + let mut hasher = sha2::Sha256::default(); + hasher.update(data); + let mut hash: [u8; 32] = [0; 32]; + let out = hasher.finalize_fixed(); + hash.copy_from_slice(&out[..]); + hash +} + +#[allow(dead_code)] +pub fn sha512_digest(data: &[u8]) -> [u8; 64] { + let mut hasher = sha2::Sha512::default(); + hasher.update(data); + let mut hash: [u8; 64] = [0; 64]; + let out = hasher.finalize_fixed(); + hash.copy_from_slice(&out[..]); + hash +} + +#[allow(dead_code)] +pub fn sha3_digest(data: &[u8]) -> [u8; 32] { + let mut hasher = sha3::Sha3_256::default(); + hasher.update(data); + let mut hash: [u8; 32] = [0; 32]; + let out = hasher.finalize_fixed(); + hash.copy_from_slice(&out[..]); + hash +} + +pub fn keccak256_digest(data: &[u8]) -> [u8; 32] { + let mut hasher = sha3::Keccak256::default(); + hasher.update(data); + let mut hash: [u8; 32] = [0; 32]; + let out = hasher.finalize_fixed(); + hash.copy_from_slice(&out[..]); + hash +} + +pub fn blake2b_digest(data: &[u8]) -> [u8; 32] { + let mut hasher = Blake2bBuilder::new(32).build(); + hasher.update(data); + + let mut result_buffer: [u8; 32] = [0; 32]; + hasher.finalize(result_buffer.as_mut_slice()); + result_buffer +} + +pub fn blake244_digest(data: &[u8]) -> [u8; 28] { + let mut hasher = Blake2bBuilder::new(28).build(); + hasher.update(data); + + let mut result_buffer: [u8; 28] = [0; 28]; + hasher.finalize(result_buffer.as_mut_slice()); + result_buffer +} + +pub fn blake2b_64_digest(data: &[u8]) -> [u8; 64] { + let mut hasher = Blake2bBuilder::new(64).build(); + hasher.update(data); + + let mut result_buffer: [u8; 64] = [0; 64]; + hasher.finalize(result_buffer.as_mut_slice()); + result_buffer +} + +pub fn ripemd160_digest(data: &[u8]) -> [u8; 20] { + let input_md = sha256_digest(data); + let mut hasher = ripemd::Ripemd160::default(); + hasher.update(input_md); + let mut hash: [u8; 20] = [0; 20]; + let out = hasher.finalize_fixed(); + hash.copy_from_slice(&out[..]); + hash +} + +pub fn sha224_digest(data: &[u8]) -> [u8; 28] { + let mut hasher = sha2::Sha224::default(); + hasher.update(data); + let mut hash: [u8; 28] = [0; 28]; + let out = hasher.finalize_fixed(); + hash.copy_from_slice(&out[..]); + hash +} diff --git a/packages/kos-crypto/src/mnemonic.rs b/packages/kos/src/crypto/mnemonic.rs similarity index 57% rename from packages/kos-crypto/src/mnemonic.rs rename to packages/kos/src/crypto/mnemonic.rs index 9c1cd2c..e6e7800 100644 --- a/packages/kos-crypto/src/mnemonic.rs +++ b/packages/kos/src/crypto/mnemonic.rs @@ -1,16 +1,21 @@ -use kos_types::error::Error; - +use crate::chains::ChainError; use coins_bip39::{English, Mnemonic}; -pub fn generate_mnemonic(count: usize) -> Result, Error> { +pub fn generate_mnemonic(count: usize) -> Result, ChainError> { // create rng let mut rng = rand::thread_rng(); // generate mnemonic phrase Ok(Mnemonic::::new_with_count(&mut rng, count)?) } -pub fn validate_mnemonic(phrase: &str) -> Result<(), Error> { +pub fn validate_mnemonic(phrase: &str) -> Result<(), ChainError> { // validate mnemonic phrase let _mnemonic: Mnemonic = phrase.parse()?; Ok(()) } + +impl From for ChainError { + fn from(_: coins_bip39::MnemonicError) -> Self { + Self::InvalidMnemonic + } +} diff --git a/packages/kos/src/crypto/mod.rs b/packages/kos/src/crypto/mod.rs new file mode 100644 index 0000000..7f98b97 --- /dev/null +++ b/packages/kos/src/crypto/mod.rs @@ -0,0 +1,48 @@ +pub mod b58; +pub mod base64; +pub mod bignum; +pub mod bip32; +pub mod ed25519; +mod ed25519_bip32; +pub mod hash; +mod pbkdf2; +mod rng; +pub mod secp256k1; +pub mod sr25519; + +#[cfg(not(feature = "ksafe"))] +pub mod cipher; +#[cfg(not(feature = "ksafe"))] +pub mod mnemonic; + +#[allow(clippy::module_inception)] +#[cfg(not(feature = "ksafe"))] +// pub mod mnemonic; +mod crypto {} + +#[cfg(test)] +mod tests { + use crate::crypto; + use alloc::string::String; + + #[test] + fn test_path() { + let path = String::from("m/44'/0'/0'/0/0"); + let path_components = crypto::bip32::handle_path(path).unwrap(); + assert_eq!(path_components.len(), 5); + } + + #[test] + fn test_derive() { + let seed = hex::decode( + "5eb00bbddc\ + f069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19\ + a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2\ + ce9e38e4", + ) + .unwrap(); + let path = String::from("m/44'/195'/0'/0/0"); + let pvk = crypto::bip32::derive(&seed, path).unwrap(); + assert_eq!(pvk.len(), 32); + } +} diff --git a/packages/kos/src/crypto/pbkdf2.rs b/packages/kos/src/crypto/pbkdf2.rs new file mode 100644 index 0000000..1b25c52 --- /dev/null +++ b/packages/kos/src/crypto/pbkdf2.rs @@ -0,0 +1,44 @@ +#[cfg(feature = "ksafe")] +extern "C" { + fn c_pbkdf2_hmac_sha512( + pass: *const u8, + pass_len: u32, + salt: *const u8, + salt_len: u32, + rounds: u32, + out: *mut u8, + out_len: u32, + ); +} + +pub trait Pbkdf2Trait { + fn pbkdf2_hmac_512(password: &[u8], salt: &[u8], rounds: u32) -> [u8; N]; +} + +pub struct Pbkdf2 {} + +#[cfg(not(feature = "ksafe"))] +impl Pbkdf2Trait for Pbkdf2 { + fn pbkdf2_hmac_512(password: &[u8], salt: &[u8], rounds: u32) -> [u8; N] { + pbkdf2::pbkdf2_hmac_array::(password, salt, rounds) + } +} + +#[cfg(feature = "ksafe")] +impl Pbkdf2Trait for Pbkdf2 { + fn pbkdf2_hmac_512(password: &[u8], salt: &[u8], rounds: u32) -> [u8; N] { + let mut out = [0u8; N]; + unsafe { + c_pbkdf2_hmac_sha512( + password.as_ptr(), + password.len() as u32, + salt.as_ptr(), + salt.len() as u32, + rounds, + out.as_mut_ptr(), + N as u32, + ); + } + out + } +} diff --git a/packages/kos/src/crypto/rng.rs b/packages/kos/src/crypto/rng.rs new file mode 100644 index 0000000..384f1e0 --- /dev/null +++ b/packages/kos/src/crypto/rng.rs @@ -0,0 +1,55 @@ +#[allow(unused_imports)] +use rand_core::{CryptoRng, Error, RngCore}; + +#[cfg(feature = "ksafe")] +extern "C" { + #[allow(dead_code)] + fn random_buffer(p_buffer: *mut u8, size: u32); +} + +#[cfg(feature = "ksafe")] +#[allow(dead_code)] +struct MyRng; + +#[cfg(feature = "ksafe")] +impl RngCore for MyRng { + fn next_u32(&mut self) -> u32 { + let mut buf = [0u8; 4]; + unsafe { + random_buffer(buf.as_mut_ptr(), buf.len() as u32); + } + u32::from_ne_bytes(buf) + } + + fn next_u64(&mut self) -> u64 { + let mut buf = [0u8; 8]; + unsafe { + random_buffer(buf.as_mut_ptr(), buf.len() as u32); + } + u64::from_ne_bytes(buf) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + unsafe { + random_buffer(dest.as_mut_ptr(), dest.len() as u32); + } + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.fill_bytes(dest); + Ok(()) + } +} + +#[cfg(feature = "ksafe")] +impl CryptoRng for MyRng {} + +#[cfg(not(feature = "ksafe"))] +pub fn getrandom_or_panic() -> impl RngCore + CryptoRng { + rand_core::OsRng +} + +#[cfg(feature = "ksafe")] +pub fn getrandom_or_panic() -> impl RngCore + CryptoRng { + MyRng +} diff --git a/packages/kos/src/crypto/secp256k1.rs b/packages/kos/src/crypto/secp256k1.rs new file mode 100644 index 0000000..2dd6e6e --- /dev/null +++ b/packages/kos/src/crypto/secp256k1.rs @@ -0,0 +1,119 @@ +use core::fmt::{Display, Formatter}; + +#[derive(Debug)] +#[allow(dead_code)] +pub enum Secp256Err { + ErrDerive, +} + +impl Display for Secp256Err { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Secp256Err::ErrDerive => write!(f, "Derive error"), + } + } +} + +#[cfg(not(feature = "ksafe"))] +impl From for Secp256Err { + fn from(_: libsecp256k1::Error) -> Self { + Secp256Err::ErrDerive + } +} + +pub struct Secp256K1 {} + +#[cfg(feature = "ksafe")] +extern "C" { + fn c_ecdsa_secp256k1_sign(msg: *const u8, msg_len: u32, pvk: *const u8, sig: *mut u8); + + fn c_ecdsa_scalar_add(a: *const u8, b: *const u8, c: *mut u8) -> u8; // Assuming that C's `bool` is equivalent to Rust's `u8` here + + fn c_ecdsa_get_pub_key65(pvk: *const u8, pbk: *mut u8); + + fn c_ecdsa_get_pub_key33(pvk: *const u8, pbk: *mut u8); +} + +pub trait Secp256k1Trait { + fn add_scalars(a: &[u8; 32], b: &[u8; 32]) -> Result<[u8; 32], Secp256Err>; + fn private_to_public_compressed(pvk: &[u8; 32]) -> Result<[u8; 33], Secp256Err>; + fn private_to_public_uncompressed(pvk: &[u8; 32]) -> Result<[u8; 65], Secp256Err>; + fn sign(msg: &[u8; 32], pvk: &[u8; 32]) -> Result<[u8; 65], Secp256Err>; +} + +#[cfg(feature = "ksafe")] +impl Secp256k1Trait for Secp256K1 { + fn add_scalars(a: &[u8; 32], b: &[u8; 32]) -> Result<[u8; 32], Secp256Err> { + let mut c = [0; 32]; + unsafe { + c_ecdsa_scalar_add(a.as_ptr(), b.as_ptr(), c.as_mut_ptr()); + } + + Ok(c) + } + + fn private_to_public_compressed(pvk: &[u8; 32]) -> Result<[u8; 33], Secp256Err> { + let mut pbk = [0; 33]; + unsafe { + c_ecdsa_get_pub_key33(pvk.as_ptr(), pbk.as_mut_ptr()); + } + + Ok(pbk) + } + + fn private_to_public_uncompressed(pvk: &[u8; 32]) -> Result<[u8; 65], Secp256Err> { + let mut pbk = [0; 65]; + unsafe { + c_ecdsa_get_pub_key65(pvk.as_ptr(), pbk.as_mut_ptr()); + } + + Ok(pbk) + } + + fn sign(msg: &[u8; 32], pvk: &[u8; 32]) -> Result<[u8; 65], Secp256Err> { + let mut sig = [0; 65]; + unsafe { + c_ecdsa_secp256k1_sign( + msg.as_ptr(), + msg.len() as u32, + pvk.as_ptr(), + sig.as_mut_ptr(), + ); + } + + Ok(sig) + } +} + +#[cfg(not(feature = "ksafe"))] +impl Secp256k1Trait for Secp256K1 { + fn add_scalars(a: &[u8; 32], b: &[u8; 32]) -> Result<[u8; 32], Secp256Err> { + let mut a = libsecp256k1::SecretKey::parse(a)?; + let b = libsecp256k1::SecretKey::parse(b)?; + a.tweak_add_assign(&b)?; + Ok(a.serialize()) + } + + fn private_to_public_compressed(pvk: &[u8; 32]) -> Result<[u8; 33], Secp256Err> { + let sk = libsecp256k1::SecretKey::parse(pvk)?; + let pbk = libsecp256k1::PublicKey::from_secret_key(&sk); + Ok(pbk.serialize_compressed()) + } + + fn private_to_public_uncompressed(pvk: &[u8; 32]) -> Result<[u8; 65], Secp256Err> { + let sk = libsecp256k1::SecretKey::parse(pvk)?; + let pbk = libsecp256k1::PublicKey::from_secret_key(&sk); + Ok(pbk.serialize()) + } + + fn sign(msg: &[u8; 32], pvk: &[u8; 32]) -> Result<[u8; 65], Secp256Err> { + let sk = libsecp256k1::SecretKey::parse(pvk)?; + let msg = libsecp256k1::Message::parse(msg); + let (sig, rec) = libsecp256k1::sign(&msg, &sk); + + let mut sig_vec: [u8; 65] = [0; 65]; + sig_vec[..64].copy_from_slice(&sig.serialize()[..]); + sig_vec[64] = rec.serialize(); + Ok(sig_vec) + } +} diff --git a/packages/kos/src/crypto/sr25519.rs b/packages/kos/src/crypto/sr25519.rs new file mode 100644 index 0000000..d16bccf --- /dev/null +++ b/packages/kos/src/crypto/sr25519.rs @@ -0,0 +1,80 @@ +use alloc::vec::Vec; +use core::fmt::{Display, Formatter}; +use schnorrkel::context::SigningContext; +use schnorrkel::derive::ChainCode; + +const SUBSTRATE_CTX: &[u8; 9] = b"substrate"; + +#[derive(Debug)] +pub enum Sr25519Error { + ErrDerive, +} + +impl Display for Sr25519Error { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Sr25519Error::ErrDerive => write!(f, "Derive error"), + } + } +} + +pub trait Sr25519Trait { + fn public_from_private(pvk: &[u8; 64]) -> Result, Sr25519Error>; + fn sign(msg: &[u8], pvk: &[u8; 64]) -> Result, Sr25519Error>; + + fn hard_derive_mini_sk(seed: &[u8; 32], chaincode: &[u8; 32]) + -> Result<[u8; 32], Sr25519Error>; + + fn expand_secret_key(mini_secret: &[u8; 32]) -> Result<([u8; 32], [u8; 32]), Sr25519Error>; +} + +pub struct Sr25519 {} + +impl Sr25519Trait for Sr25519 { + fn public_from_private(pvk: &[u8; 64]) -> Result, Sr25519Error> { + let pvk = schnorrkel::SecretKey::from_bytes(pvk).unwrap(); + + let pbk = pvk.to_public().to_bytes().to_vec(); + Ok(pbk) + } + + fn sign(msg: &[u8], pvk: &[u8; 64]) -> Result, Sr25519Error> { + let pvk = schnorrkel::SecretKey::from_bytes(pvk).map_err(|_| Sr25519Error::ErrDerive)?; + + let pbk = pvk.to_public(); + let ctx = SigningContext::new(SUBSTRATE_CTX).bytes(msg); + let ctx = schnorrkel::context::attach_rng(ctx, crate::crypto::rng::getrandom_or_panic()); + let sig = pvk.sign(ctx, &pbk); + Ok(sig.to_bytes().as_slice().to_vec()) + } + + #[allow(clippy::clone_on_copy)] + fn hard_derive_mini_sk( + seed: &[u8; 32], + chaincode: &[u8; 32], + ) -> Result<[u8; 32], Sr25519Error> { + let seed = + schnorrkel::MiniSecretKey::from_bytes(seed).map_err(|_| Sr25519Error::ErrDerive)?; + let chaincode = ChainCode(chaincode.clone()); + let (new_seed, _) = seed.hard_derive_mini_secret_key( + Some(chaincode), + [], + schnorrkel::ExpansionMode::Ed25519, + ); + Ok(new_seed.to_bytes()) + } + + fn expand_secret_key(mini_secret: &[u8; 32]) -> Result<([u8; 32], [u8; 32]), Sr25519Error> { + let mini_secret = schnorrkel::MiniSecretKey::from_bytes(mini_secret) + .map_err(|_| Sr25519Error::ErrDerive)?; + let secret = mini_secret.expand(schnorrkel::ExpansionMode::Ed25519); + let mut sk = [0; 32]; + let mut nonce = [0; 32]; + + let secret = secret.to_bytes(); + sk.copy_from_slice(&secret[0..32]); + nonce.copy_from_slice(&secret[32..64]); + + Ok((sk, nonce)) + } +} diff --git a/packages/kos/src/lib.rs b/packages/kos/src/lib.rs index 5cfb913..fd34da9 100644 --- a/packages/kos/src/lib.rs +++ b/packages/kos/src/lib.rs @@ -1,77 +1,43 @@ -pub mod api; - -use kos_crypto::cipher; -pub use kos_crypto::cipher::*; -use kos_crypto::mnemonic::generate_mnemonic; -pub use kos_sdk::*; -use kos_types::error::Error; -use kos_utils::logger; - -use qrcode_generator::QrCodeEcc; - -use wasm_bindgen::prelude::*; - -// Called when the wasm module is instantiated -#[wasm_bindgen(start)] -pub fn main() -> Result<(), JsValue> { - console_error_panic_hook::set_once(); - - logger::init(logger::Config::default()); - - log::info!("KOS Module initialized"); - - Ok(()) -} - -/// Generates a random mnemonic phrase given the number of words to generate, `count`. -#[wasm_bindgen(js_name = "generateMnemonicPhrase")] -pub fn generate_mnemonic_phrase(count: usize) -> Result { - Ok(generate_mnemonic(count)?.to_phrase()) -} - -/// Converts the given string to bytes. -#[wasm_bindgen(js_name = "toBytes")] -pub fn to_bytes(data: &str) -> Result, Error> { - Ok(data.as_bytes().to_vec()) -} - -/// Converts the given bytes to a string. -#[wasm_bindgen(js_name = "toString")] -pub fn to_string(data: &[u8]) -> Result { - String::from_utf8(data.to_vec()) - .map_err(|e| Error::InvalidString(format!("Invalid UTF-8 string: {}", e))) -} - -/// Encrypts the given data with the given password. -#[wasm_bindgen(js_name = "encrypt")] -pub fn encrypt(algo: CipherAlgo, data: &[u8], password: &str) -> Result, Error> { - algo.encrypt(data, password) -} - -/// Decrypts the given data with the given password. -/// Data will have the algorithm tag prepended to it (1 byte). -#[wasm_bindgen(js_name = "decrypt")] -pub fn decrypt(data: &[u8], password: &str) -> Result, Error> { - cipher::decrypt(data, password) -} - -/// Create pem file from tag and data -#[wasm_bindgen(js_name = "toPem")] -pub fn to_pem(tag: String, data: &[u8]) -> Result { - let result = cipher::to_pem(tag, data)?; - Ok(result.to_string()) -} - -/// Decrypt pem file to bytes -#[wasm_bindgen(js_name = "fromPem")] -pub fn from_pem(data: &str, password: &str) -> Result, Error> { - let pem = cipher::string_to_pem(data)?; - decrypt(pem.contents(), password) -} - -/// Create QRCode based on data -#[wasm_bindgen(js_name = "generateQR")] -pub fn generate_qr(data: &str) -> Result, Error> { - qrcode_generator::to_png_to_vec(data, QrCodeEcc::Low, 1024) - .map_err(|e| Error::InvalidString(format!("Invalid QRCode data: {}", e))) +#![cfg_attr(feature = "ksafe", no_std)] +pub mod chains; +pub mod crypto; +pub mod protos; + +extern crate alloc; + +#[cfg(feature = "ksafe")] +use core::alloc::{GlobalAlloc, Layout}; + +#[cfg(feature = "ksafe")] +#[allow(dead_code)] +struct FreeRtosAllocator; + +#[cfg(feature = "ksafe")] +#[allow(dead_code)] +extern "C" { + fn pvPortMalloc(size: u32) -> *mut u8; // Using u32 instead of libc::size_t + fn vPortFree(ptr: *mut u8); + fn HardFault_Handler(); + fn DebugErrorHandler(log: *const u8); +} + +#[cfg(feature = "ksafe")] +unsafe impl GlobalAlloc for FreeRtosAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + pvPortMalloc(layout.size() as u32) // Cast to u32 + } + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + vPortFree(ptr); + } +} + +#[cfg(all(feature = "ksafe", not(test)))] +#[global_allocator] +static ALLOCATOR: FreeRtosAllocator = FreeRtosAllocator; + +#[cfg(all(feature = "ksafe", not(test)))] +#[panic_handler] +unsafe fn my_panic(_info: &core::panic::PanicInfo) -> ! { + HardFault_Handler(); + loop {} } diff --git a/packages/kos/src/protos/generated/klv/google.protobuf.rs b/packages/kos/src/protos/generated/klv/google.protobuf.rs new file mode 100644 index 0000000..e69de29 diff --git a/packages/kos/src/protos/generated/klv/mod.rs b/packages/kos/src/protos/generated/klv/mod.rs new file mode 100644 index 0000000..89987f2 --- /dev/null +++ b/packages/kos/src/protos/generated/klv/mod.rs @@ -0,0 +1,2 @@ +#[rustfmt::skip] +pub mod proto; diff --git a/packages/kos/src/protos/generated/klv/proto.rs b/packages/kos/src/protos/generated/klv/proto.rs new file mode 100644 index 0000000..d480efe --- /dev/null +++ b/packages/kos/src/protos/generated/klv/proto.rs @@ -0,0 +1,1645 @@ +// This file is @generated by prost-build. +/// TXContract available +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransferContract { + #[prost(bytes = "vec", tag = "1")] + pub to_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub asset_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub amount: i64, + #[prost(int64, tag = "4")] + pub kda_royalties: i64, +} +/// CreateAssetContract holds the data for a Klever digital asset +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateAssetContract { + #[prost(enumeration = "create_asset_contract::EnumAssetType", tag = "1")] + pub r#type: i32, + #[prost(bytes = "vec", tag = "2")] + pub name: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub ticker: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "4")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(string, tag = "5")] + pub logo: ::prost::alloc::string::String, + #[prost(btree_map = "string, string", tag = "6")] + pub ur_is: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + #[prost(uint32, tag = "7")] + pub precision: u32, + #[prost(int64, tag = "8")] + pub initial_supply: i64, + #[prost(int64, tag = "9")] + pub max_supply: i64, + #[prost(message, optional, tag = "10")] + pub royalties: ::core::option::Option, + #[prost(message, optional, tag = "11")] + pub properties: ::core::option::Option, + #[prost(message, optional, tag = "12")] + pub attributes: ::core::option::Option, + #[prost(message, optional, tag = "13")] + pub staking: ::core::option::Option, + #[prost(message, repeated, tag = "14")] + pub roles: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `CreateAssetContract`. +pub mod create_asset_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumAssetType { + Fungible = 0, + NonFungible = 1, + } + impl EnumAssetType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumAssetType::Fungible => "Fungible", + EnumAssetType::NonFungible => "NonFungible", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Fungible" => Some(Self::Fungible), + "NonFungible" => Some(Self::NonFungible), + _ => None, + } + } + } +} +/// PropertiesInfo hold the properties structure for the KDA asset +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PropertiesInfo { + #[prost(bool, tag = "1")] + pub can_freeze: bool, + #[prost(bool, tag = "2")] + pub can_wipe: bool, + #[prost(bool, tag = "3")] + pub can_pause: bool, + #[prost(bool, tag = "4")] + pub can_mint: bool, + #[prost(bool, tag = "5")] + pub can_burn: bool, + #[prost(bool, tag = "6")] + pub can_change_owner: bool, + #[prost(bool, tag = "7")] + pub can_add_roles: bool, +} +/// AttributesInfo hold the attributes structure for the KDA asset +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AttributesInfo { + #[prost(bool, tag = "1")] + pub is_paused: bool, + #[prost(bool, tag = "2")] + pub is_nft_mint_stopped: bool, + #[prost(bool, tag = "3")] + pub is_royalties_change_stopped: bool, + #[prost(bool, tag = "4")] + pub is_nft_metadata_change_stopped: bool, +} +/// StakingInfo hold the staking structure for the KDA asset +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StakingInfo { + #[prost(enumeration = "staking_info::InterestType", tag = "1")] + pub r#type: i32, + #[prost(uint32, tag = "2")] + pub apr: u32, + #[prost(uint32, tag = "3")] + pub min_epochs_to_claim: u32, + #[prost(uint32, tag = "4")] + pub min_epochs_to_unstake: u32, + #[prost(uint32, tag = "5")] + pub min_epochs_to_withdraw: u32, +} +/// Nested message and enum types in `StakingInfo`. +pub mod staking_info { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum InterestType { + Apri = 0, + Fpri = 1, + } + impl InterestType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + InterestType::Apri => "APRI", + InterestType::Fpri => "FPRI", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "APRI" => Some(Self::Apri), + "FPRI" => Some(Self::Fpri), + _ => None, + } + } + } +} +/// RolesInfo holds the roles for a given asset and the given address +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RolesInfo { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "2")] + pub has_role_mint: bool, + #[prost(bool, tag = "3")] + pub has_role_set_ito_prices: bool, + #[prost(bool, tag = "4")] + pub has_role_deposit: bool, +} +/// RoyaltiesInfo holds the royalties for a given asset +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RoyaltiesInfo { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub transfer_percentage: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub transfer_fixed: i64, + #[prost(uint32, tag = "4")] + pub market_percentage: u32, + #[prost(int64, tag = "5")] + pub market_fixed: i64, + #[prost(btree_map = "string, message", tag = "6")] + pub split_royalties: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + RoyaltySplitInfo, + >, + #[prost(int64, tag = "7")] + pub ito_fixed: i64, + #[prost(uint32, tag = "8")] + pub ito_percentage: u32, +} +/// RoyaltySplitInfo holds the royalty split +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RoyaltySplitInfo { + #[prost(uint32, tag = "1")] + pub percent_transfer_percentage: u32, + #[prost(uint32, tag = "2")] + pub percent_transfer_fixed: u32, + #[prost(uint32, tag = "3")] + pub percent_market_percentage: u32, + #[prost(uint32, tag = "4")] + pub percent_market_fixed: u32, + #[prost(uint32, tag = "5")] + pub percent_ito_percentage: u32, + #[prost(uint32, tag = "6")] + pub percent_ito_fixed: u32, +} +/// RoyaltyInfo holds the royalty threshold +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RoyaltyInfo { + #[prost(int64, tag = "1")] + pub amount: i64, + #[prost(uint32, tag = "2")] + pub percentage: u32, +} +/// KDAPoolInfo holds the KDA Fee pool info +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct KdaPoolInfo { + #[prost(bool, tag = "1")] + pub active: bool, + #[prost(bytes = "vec", tag = "2")] + pub admin_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub f_ratio_kda: i64, + #[prost(int64, tag = "4")] + pub f_ratio_klv: i64, +} +/// AssetTriggerContract triggers assets functions +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AssetTriggerContract { + #[prost(enumeration = "asset_trigger_contract::EnumTriggerType", tag = "1")] + pub trigger_type: i32, + #[prost(bytes = "vec", tag = "2")] + pub asset_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub to_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "4")] + pub amount: i64, + #[prost(bytes = "vec", tag = "5")] + pub mime: ::prost::alloc::vec::Vec, + #[prost(string, tag = "6")] + pub logo: ::prost::alloc::string::String, + #[prost(btree_map = "string, string", tag = "7")] + pub ur_is: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + #[prost(message, optional, tag = "8")] + pub role: ::core::option::Option, + #[prost(message, optional, tag = "9")] + pub staking: ::core::option::Option, + #[prost(message, optional, tag = "10")] + pub royalties: ::core::option::Option, + #[prost(message, optional, tag = "11")] + pub kda_pool: ::core::option::Option, +} +/// Nested message and enum types in `AssetTriggerContract`. +pub mod asset_trigger_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumTriggerType { + Mint = 0, + Burn = 1, + Wipe = 2, + Pause = 3, + Resume = 4, + ChangeOwner = 5, + AddRole = 6, + RemoveRole = 7, + UpdateMetadata = 8, + StopNftMint = 9, + UpdateLogo = 10, + UpdateUrIs = 11, + ChangeRoyaltiesReceiver = 12, + UpdateStaking = 13, + UpdateRoyalties = 14, + UpdateKdaFeePool = 15, + StopRoyaltiesChange = 16, + StopNftMetadataChange = 17, + } + impl EnumTriggerType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumTriggerType::Mint => "Mint", + EnumTriggerType::Burn => "Burn", + EnumTriggerType::Wipe => "Wipe", + EnumTriggerType::Pause => "Pause", + EnumTriggerType::Resume => "Resume", + EnumTriggerType::ChangeOwner => "ChangeOwner", + EnumTriggerType::AddRole => "AddRole", + EnumTriggerType::RemoveRole => "RemoveRole", + EnumTriggerType::UpdateMetadata => "UpdateMetadata", + EnumTriggerType::StopNftMint => "StopNFTMint", + EnumTriggerType::UpdateLogo => "UpdateLogo", + EnumTriggerType::UpdateUrIs => "UpdateURIs", + EnumTriggerType::ChangeRoyaltiesReceiver => "ChangeRoyaltiesReceiver", + EnumTriggerType::UpdateStaking => "UpdateStaking", + EnumTriggerType::UpdateRoyalties => "UpdateRoyalties", + EnumTriggerType::UpdateKdaFeePool => "UpdateKDAFeePool", + EnumTriggerType::StopRoyaltiesChange => "StopRoyaltiesChange", + EnumTriggerType::StopNftMetadataChange => "StopNFTMetadataChange", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Mint" => Some(Self::Mint), + "Burn" => Some(Self::Burn), + "Wipe" => Some(Self::Wipe), + "Pause" => Some(Self::Pause), + "Resume" => Some(Self::Resume), + "ChangeOwner" => Some(Self::ChangeOwner), + "AddRole" => Some(Self::AddRole), + "RemoveRole" => Some(Self::RemoveRole), + "UpdateMetadata" => Some(Self::UpdateMetadata), + "StopNFTMint" => Some(Self::StopNftMint), + "UpdateLogo" => Some(Self::UpdateLogo), + "UpdateURIs" => Some(Self::UpdateUrIs), + "ChangeRoyaltiesReceiver" => Some(Self::ChangeRoyaltiesReceiver), + "UpdateStaking" => Some(Self::UpdateStaking), + "UpdateRoyalties" => Some(Self::UpdateRoyalties), + "UpdateKDAFeePool" => Some(Self::UpdateKdaFeePool), + "StopRoyaltiesChange" => Some(Self::StopRoyaltiesChange), + "StopNFTMetadataChange" => Some(Self::StopNftMetadataChange), + _ => None, + } + } + } +} +/// ValidatorConfig holds the data for a validator configuration +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ValidatorConfig { + #[prost(bytes = "vec", tag = "1")] + pub bls_public_key: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub reward_address: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "3")] + pub can_delegate: bool, + #[prost(uint32, tag = "4")] + pub commission: u32, + #[prost(int64, tag = "5")] + pub max_delegation_amount: i64, + #[prost(string, tag = "6")] + pub logo: ::prost::alloc::string::String, + #[prost(btree_map = "string, string", tag = "7")] + pub ur_is: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + #[prost(string, tag = "8")] + pub name: ::prost::alloc::string::String, +} +/// CreateValidatorContract holds the data for create a validator +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateValidatorContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub config: ::core::option::Option, +} +/// ValidatorConfigContract holds the data for a validator configuration transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ValidatorConfigContract { + #[prost(message, optional, tag = "1")] + pub config: ::core::option::Option, +} +/// FreezeContract holds the data for a freeze transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FreezeContract { + #[prost(bytes = "vec", tag = "1")] + pub asset_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub amount: i64, +} +/// UnfreezeContract holds the data for a unfreeze transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UnfreezeContract { + #[prost(bytes = "vec", tag = "1")] + pub asset_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub bucket_id: ::prost::alloc::vec::Vec, +} +/// DelegateContract holds the data for a delegate transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DelegateContract { + #[prost(bytes = "vec", tag = "1")] + pub to_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub bucket_id: ::prost::alloc::vec::Vec, +} +/// UndelegateContract holds the data for a undelegate transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UndelegateContract { + #[prost(bytes = "vec", tag = "1")] + pub bucket_id: ::prost::alloc::vec::Vec, +} +/// WithdrawContract holds the data for a withdraw transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WithdrawContract { + #[prost(bytes = "vec", tag = "1")] + pub asset_id: ::prost::alloc::vec::Vec, + #[prost(enumeration = "withdraw_contract::EnumWithdrawType", tag = "2")] + pub withdraw_type: i32, + #[prost(int64, tag = "3")] + pub amount: i64, + #[prost(bytes = "vec", tag = "4")] + pub currency_id: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `WithdrawContract`. +pub mod withdraw_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumWithdrawType { + Staking = 0, + KdaPool = 1, + } + impl EnumWithdrawType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumWithdrawType::Staking => "Staking", + EnumWithdrawType::KdaPool => "KDAPool", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Staking" => Some(Self::Staking), + "KDAPool" => Some(Self::KdaPool), + _ => None, + } + } + } +} +/// ClaimContract holds the data for a claim transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ClaimContract { + #[prost(enumeration = "claim_contract::EnumClaimType", tag = "1")] + pub claim_type: i32, + #[prost(bytes = "vec", tag = "2")] + pub id: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `ClaimContract`. +pub mod claim_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumClaimType { + StakingClaim = 0, + AllowanceClaim = 1, + MarketClaim = 2, + } + impl EnumClaimType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumClaimType::StakingClaim => "StakingClaim", + EnumClaimType::AllowanceClaim => "AllowanceClaim", + EnumClaimType::MarketClaim => "MarketClaim", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "StakingClaim" => Some(Self::StakingClaim), + "AllowanceClaim" => Some(Self::AllowanceClaim), + "MarketClaim" => Some(Self::MarketClaim), + _ => None, + } + } + } +} +/// UnjailContract holds the data for a unjail transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UnjailContract {} +/// SetAccountNameContract holds the data for a setAccountName transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SetAccountNameContract { + #[prost(bytes = "vec", tag = "1")] + pub name: ::prost::alloc::vec::Vec, +} +/// ProposalContract holds the data for a proposal transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProposalContract { + #[prost(btree_map = "int32, bytes", tag = "1")] + pub parameters: ::prost::alloc::collections::BTreeMap< + i32, + ::prost::alloc::vec::Vec, + >, + #[prost(bytes = "vec", tag = "2")] + pub description: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "3")] + pub epochs_duration: u32, +} +/// VoteContract holds the data for a vote transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VoteContract { + #[prost(uint64, tag = "1")] + pub proposal_id: u64, + #[prost(int64, tag = "2")] + pub amount: i64, + #[prost(enumeration = "vote_contract::EnumVoteType", tag = "3")] + pub r#type: i32, +} +/// Nested message and enum types in `VoteContract`. +pub mod vote_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumVoteType { + Yes = 0, + No = 1, + } + impl EnumVoteType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumVoteType::Yes => "Yes", + EnumVoteType::No => "No", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Yes" => Some(Self::Yes), + "No" => Some(Self::No), + _ => None, + } + } + } +} +/// ConfigITOContract holds the data for a ConfigITO transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConfigItoContract { + #[prost(bytes = "vec", tag = "1")] + pub asset_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub receiver_address: ::prost::alloc::vec::Vec, + #[prost(enumeration = "config_ito_contract::EnumItoStatus", tag = "3")] + pub status: i32, + #[prost(int64, tag = "4")] + pub max_amount: i64, + #[prost(btree_map = "string, message", tag = "5")] + pub pack_info: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + PackInfo, + >, + #[prost(int64, tag = "6")] + pub default_limit_per_address: i64, + #[prost(enumeration = "config_ito_contract::EnumItoStatus", tag = "7")] + pub whitelist_status: i32, + #[prost(btree_map = "string, message", tag = "8")] + pub whitelist_info: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + WhitelistInfo, + >, + #[prost(int64, tag = "9")] + pub whitelist_start_time: i64, + #[prost(int64, tag = "10")] + pub whitelist_end_time: i64, + #[prost(int64, tag = "11")] + pub start_time: i64, + #[prost(int64, tag = "12")] + pub end_time: i64, +} +/// Nested message and enum types in `ConfigITOContract`. +pub mod config_ito_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumItoStatus { + DefaultIto = 0, + ActiveIto = 1, + PausedIto = 2, + } + impl EnumItoStatus { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumItoStatus::DefaultIto => "DefaultITO", + EnumItoStatus::ActiveIto => "ActiveITO", + EnumItoStatus::PausedIto => "PausedITO", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DefaultITO" => Some(Self::DefaultIto), + "ActiveITO" => Some(Self::ActiveIto), + "PausedITO" => Some(Self::PausedIto), + _ => None, + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WhitelistInfo { + #[prost(int64, tag = "1")] + pub limit: i64, +} +/// SetITOPrices holds the data for a ConfigITO transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SetItoPricesContract { + #[prost(bytes = "vec", tag = "1")] + pub asset_id: ::prost::alloc::vec::Vec, + #[prost(btree_map = "string, message", tag = "2")] + pub pack_info: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + PackInfo, + >, +} +/// ITOTriggerContract triggers assets functions +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ItoTriggerContract { + #[prost(enumeration = "ito_trigger_contract::EnumItoTriggerType", tag = "1")] + pub trigger_type: i32, + #[prost(bytes = "vec", tag = "2")] + pub asset_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub receiver_address: ::prost::alloc::vec::Vec, + #[prost(enumeration = "ito_trigger_contract::EnumItoStatus", tag = "4")] + pub status: i32, + #[prost(int64, tag = "5")] + pub max_amount: i64, + #[prost(btree_map = "string, message", tag = "6")] + pub pack_info: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + PackInfo, + >, + #[prost(int64, tag = "7")] + pub default_limit_per_address: i64, + #[prost(enumeration = "ito_trigger_contract::EnumItoStatus", tag = "8")] + pub whitelist_status: i32, + #[prost(btree_map = "string, message", tag = "9")] + pub whitelist_info: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + WhitelistInfo, + >, + #[prost(int64, tag = "10")] + pub whitelist_start_time: i64, + #[prost(int64, tag = "11")] + pub whitelist_end_time: i64, + #[prost(int64, tag = "12")] + pub start_time: i64, + #[prost(int64, tag = "13")] + pub end_time: i64, +} +/// Nested message and enum types in `ITOTriggerContract`. +pub mod ito_trigger_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumItoTriggerType { + SetItoPrices = 0, + UpdateStatus = 1, + UpdateReceiverAddress = 2, + UpdateMaxAmount = 3, + UpdateDefaultLimitPerAddress = 4, + UpdateTimes = 5, + UpdateWhitelistStatus = 6, + AddToWhitelist = 7, + RemoveFromWhitelist = 8, + UpdateWhitelistTimes = 9, + } + impl EnumItoTriggerType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumItoTriggerType::SetItoPrices => "SetITOPrices", + EnumItoTriggerType::UpdateStatus => "UpdateStatus", + EnumItoTriggerType::UpdateReceiverAddress => "UpdateReceiverAddress", + EnumItoTriggerType::UpdateMaxAmount => "UpdateMaxAmount", + EnumItoTriggerType::UpdateDefaultLimitPerAddress => { + "UpdateDefaultLimitPerAddress" + } + EnumItoTriggerType::UpdateTimes => "UpdateTimes", + EnumItoTriggerType::UpdateWhitelistStatus => "UpdateWhitelistStatus", + EnumItoTriggerType::AddToWhitelist => "AddToWhitelist", + EnumItoTriggerType::RemoveFromWhitelist => "RemoveFromWhitelist", + EnumItoTriggerType::UpdateWhitelistTimes => "UpdateWhitelistTimes", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SetITOPrices" => Some(Self::SetItoPrices), + "UpdateStatus" => Some(Self::UpdateStatus), + "UpdateReceiverAddress" => Some(Self::UpdateReceiverAddress), + "UpdateMaxAmount" => Some(Self::UpdateMaxAmount), + "UpdateDefaultLimitPerAddress" => { + Some(Self::UpdateDefaultLimitPerAddress) + } + "UpdateTimes" => Some(Self::UpdateTimes), + "UpdateWhitelistStatus" => Some(Self::UpdateWhitelistStatus), + "AddToWhitelist" => Some(Self::AddToWhitelist), + "RemoveFromWhitelist" => Some(Self::RemoveFromWhitelist), + "UpdateWhitelistTimes" => Some(Self::UpdateWhitelistTimes), + _ => None, + } + } + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumItoStatus { + DefaultIto = 0, + ActiveIto = 1, + PausedIto = 2, + } + impl EnumItoStatus { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumItoStatus::DefaultIto => "DefaultITO", + EnumItoStatus::ActiveIto => "ActiveITO", + EnumItoStatus::PausedIto => "PausedITO", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DefaultITO" => Some(Self::DefaultIto), + "ActiveITO" => Some(Self::ActiveIto), + "PausedITO" => Some(Self::PausedIto), + _ => None, + } + } + } +} +/// PackInfo holds the pack list structure for the ITO contract +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PackInfo { + #[prost(message, repeated, tag = "1")] + pub packs: ::prost::alloc::vec::Vec, +} +/// PackItem hold the pack structure for the ITO contract +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PackItem { + #[prost(int64, tag = "1")] + pub amount: i64, + #[prost(int64, tag = "2")] + pub price: i64, +} +/// BuyContract holds the data for a buy transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BuyContract { + #[prost(enumeration = "buy_contract::EnumBuyType", tag = "1")] + pub buy_type: i32, + #[prost(bytes = "vec", tag = "2")] + pub id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub currency_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "4")] + pub amount: i64, +} +/// Nested message and enum types in `BuyContract`. +pub mod buy_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumBuyType { + ItoBuy = 0, + MarketBuy = 1, + } + impl EnumBuyType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumBuyType::ItoBuy => "ITOBuy", + EnumBuyType::MarketBuy => "MarketBuy", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ITOBuy" => Some(Self::ItoBuy), + "MarketBuy" => Some(Self::MarketBuy), + _ => None, + } + } + } +} +/// SellContract holds the data for a sell transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SellContract { + #[prost(enumeration = "sell_contract::EnumMarketType", tag = "1")] + pub market_type: i32, + #[prost(bytes = "vec", tag = "2")] + pub marketplace_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub asset_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "4")] + pub currency_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "5")] + pub price: i64, + #[prost(int64, tag = "6")] + pub reserve_price: i64, + #[prost(int64, tag = "7")] + pub end_time: i64, +} +/// Nested message and enum types in `SellContract`. +pub mod sell_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumMarketType { + BuyItNowMarket = 0, + AuctionMarket = 1, + } + impl EnumMarketType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumMarketType::BuyItNowMarket => "BuyItNowMarket", + EnumMarketType::AuctionMarket => "AuctionMarket", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "BuyItNowMarket" => Some(Self::BuyItNowMarket), + "AuctionMarket" => Some(Self::AuctionMarket), + _ => None, + } + } + } +} +/// CancelMarketOrderContract holds the data for a cancel market transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CancelMarketOrderContract { + #[prost(bytes = "vec", tag = "1")] + pub order_id: ::prost::alloc::vec::Vec, +} +/// CreateMarketplaceContract holds the data for a create marketplace transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateMarketplaceContract { + #[prost(bytes = "vec", tag = "1")] + pub name: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub referral_address: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "3")] + pub referral_percentage: u32, +} +/// ConfigMarketplaceContract holds the data for a config marketplace transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConfigMarketplaceContract { + #[prost(bytes = "vec", tag = "1")] + pub marketplace_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub name: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub referral_address: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "4")] + pub referral_percentage: u32, +} +/// TODO: Reuse from account +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccKey { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub weight: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccPermission { + #[prost(enumeration = "acc_permission::AccPermissionType", tag = "2")] + pub r#type: i32, + #[prost(string, tag = "3")] + pub permission_name: ::prost::alloc::string::String, + #[prost(int64, tag = "4")] + pub threshold: i64, + /// 1 bit 1 contract + #[prost(bytes = "vec", tag = "5")] + pub operations: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub signers: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `AccPermission`. +pub mod acc_permission { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum AccPermissionType { + Owner = 0, + User = 1, + } + impl AccPermissionType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + AccPermissionType::Owner => "Owner", + AccPermissionType::User => "User", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Owner" => Some(Self::Owner), + "User" => Some(Self::User), + _ => None, + } + } + } +} +/// UpdateAccountPermissionContract holds the data for update account permission transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UpdateAccountPermissionContract { + #[prost(message, repeated, tag = "8")] + pub permissions: ::prost::alloc::vec::Vec, +} +/// DepositContract holds the data for a deposit transaction +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DepositContract { + #[prost(enumeration = "deposit_contract::EnumDepositType", tag = "1")] + pub deposit_type: i32, + #[prost(bytes = "vec", tag = "2")] + pub id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub currency_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "4")] + pub amount: i64, +} +/// Nested message and enum types in `DepositContract`. +pub mod deposit_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EnumDepositType { + FprDeposit = 0, + KdaPool = 1, + } + impl EnumDepositType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EnumDepositType::FprDeposit => "FPRDeposit", + EnumDepositType::KdaPool => "KDAPool", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "FPRDeposit" => Some(Self::FprDeposit), + "KDAPool" => Some(Self::KdaPool), + _ => None, + } + } + } +} +/// TXContract available +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TxContract { + #[prost(enumeration = "tx_contract::ContractType", tag = "1")] + pub r#type: i32, + #[prost(message, optional, tag = "2")] + pub parameter: ::core::option::Option, +} +/// Nested message and enum types in `TXContract`. +pub mod tx_contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum ContractType { + TransferContractType = 0, + CreateAssetContractType = 1, + CreateValidatorContractType = 2, + ValidatorConfigContractType = 3, + FreezeContractType = 4, + UnfreezeContractType = 5, + DelegateContractType = 6, + UndelegateContractType = 7, + WithdrawContractType = 8, + ClaimContractType = 9, + UnjailContractType = 10, + AssetTriggerContractType = 11, + SetAccountNameContractType = 12, + ProposalContractType = 13, + VoteContractType = 14, + ConfigItoContractType = 15, + SetItoPricesContractType = 16, + BuyContractType = 17, + SellContractType = 18, + CancelMarketOrderContractType = 19, + CreateMarketplaceContractType = 20, + ConfigMarketplaceContractType = 21, + UpdateAccountPermissionContractType = 22, + DepositContractType = 23, + ItoTriggerContractType = 24, + } + impl ContractType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ContractType::TransferContractType => "TransferContractType", + ContractType::CreateAssetContractType => "CreateAssetContractType", + ContractType::CreateValidatorContractType => { + "CreateValidatorContractType" + } + ContractType::ValidatorConfigContractType => { + "ValidatorConfigContractType" + } + ContractType::FreezeContractType => "FreezeContractType", + ContractType::UnfreezeContractType => "UnfreezeContractType", + ContractType::DelegateContractType => "DelegateContractType", + ContractType::UndelegateContractType => "UndelegateContractType", + ContractType::WithdrawContractType => "WithdrawContractType", + ContractType::ClaimContractType => "ClaimContractType", + ContractType::UnjailContractType => "UnjailContractType", + ContractType::AssetTriggerContractType => "AssetTriggerContractType", + ContractType::SetAccountNameContractType => "SetAccountNameContractType", + ContractType::ProposalContractType => "ProposalContractType", + ContractType::VoteContractType => "VoteContractType", + ContractType::ConfigItoContractType => "ConfigITOContractType", + ContractType::SetItoPricesContractType => "SetITOPricesContractType", + ContractType::BuyContractType => "BuyContractType", + ContractType::SellContractType => "SellContractType", + ContractType::CancelMarketOrderContractType => { + "CancelMarketOrderContractType" + } + ContractType::CreateMarketplaceContractType => { + "CreateMarketplaceContractType" + } + ContractType::ConfigMarketplaceContractType => { + "ConfigMarketplaceContractType" + } + ContractType::UpdateAccountPermissionContractType => { + "UpdateAccountPermissionContractType" + } + ContractType::DepositContractType => "DepositContractType", + ContractType::ItoTriggerContractType => "ITOTriggerContractType", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TransferContractType" => Some(Self::TransferContractType), + "CreateAssetContractType" => Some(Self::CreateAssetContractType), + "CreateValidatorContractType" => Some(Self::CreateValidatorContractType), + "ValidatorConfigContractType" => Some(Self::ValidatorConfigContractType), + "FreezeContractType" => Some(Self::FreezeContractType), + "UnfreezeContractType" => Some(Self::UnfreezeContractType), + "DelegateContractType" => Some(Self::DelegateContractType), + "UndelegateContractType" => Some(Self::UndelegateContractType), + "WithdrawContractType" => Some(Self::WithdrawContractType), + "ClaimContractType" => Some(Self::ClaimContractType), + "UnjailContractType" => Some(Self::UnjailContractType), + "AssetTriggerContractType" => Some(Self::AssetTriggerContractType), + "SetAccountNameContractType" => Some(Self::SetAccountNameContractType), + "ProposalContractType" => Some(Self::ProposalContractType), + "VoteContractType" => Some(Self::VoteContractType), + "ConfigITOContractType" => Some(Self::ConfigItoContractType), + "SetITOPricesContractType" => Some(Self::SetItoPricesContractType), + "BuyContractType" => Some(Self::BuyContractType), + "SellContractType" => Some(Self::SellContractType), + "CancelMarketOrderContractType" => { + Some(Self::CancelMarketOrderContractType) + } + "CreateMarketplaceContractType" => { + Some(Self::CreateMarketplaceContractType) + } + "ConfigMarketplaceContractType" => { + Some(Self::ConfigMarketplaceContractType) + } + "UpdateAccountPermissionContractType" => { + Some(Self::UpdateAccountPermissionContractType) + } + "DepositContractType" => Some(Self::DepositContractType), + "ITOTriggerContractType" => Some(Self::ItoTriggerContractType), + _ => None, + } + } + } +} +/// Transaction holds all the data needed for a value transfer +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Transaction { + #[prost(message, optional, tag = "1")] + pub raw_data: ::core::option::Option, + #[prost(bytes = "vec", repeated, tag = "2")] + pub signature: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(enumeration = "transaction::TxResult", tag = "3")] + pub result: i32, + #[prost(enumeration = "transaction::TxResultCode", tag = "4")] + pub result_code: i32, + #[prost(message, repeated, tag = "5")] + pub receipts: ::prost::alloc::vec::Vec, + #[prost(uint64, tag = "6")] + pub block: u64, +} +/// Nested message and enum types in `Transaction`. +pub mod transaction { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct KdaFee { + #[prost(bytes = "vec", tag = "1")] + pub kda: ::prost::alloc::vec::Vec, + /// TODO: allow spread + #[prost(int64, tag = "2")] + pub amount: i64, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Raw { + #[prost(uint64, tag = "1")] + pub nonce: u64, + #[prost(bytes = "vec", tag = "2")] + pub sender: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub contract: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "7")] + pub permission_id: i32, + #[prost(bytes = "vec", repeated, tag = "10")] + pub data: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(int64, tag = "13")] + pub k_app_fee: i64, + #[prost(int64, tag = "14")] + pub bandwidth_fee: i64, + #[prost(uint32, tag = "15")] + pub version: u32, + #[prost(bytes = "vec", tag = "16")] + pub chain_id: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "17")] + pub kda_fee: ::core::option::Option, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Receipt { + #[prost(bytes = "vec", repeated, tag = "1")] + pub data: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum TxResult { + Success = 0, + Failed = 1, + } + impl TxResult { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + TxResult::Success => "SUCCESS", + TxResult::Failed => "FAILED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SUCCESS" => Some(Self::Success), + "FAILED" => Some(Self::Failed), + _ => None, + } + } + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum TxResultCode { + Ok = 0, + OutOfFunds = 1, + AccountError = 2, + AssetError = 3, + ContractInvalid = 4, + ContractNotFound = 5, + FeeInvalid = 6, + ParameterInvalid = 7, + AprInvalid = 8, + AssetIdInvalid = 9, + AssetTypeInvalid = 10, + AssetCantBeMinted = 11, + AssetCantBeBurned = 12, + AssetCantBePaused = 13, + AssetCantBeDelegated = 14, + AssetOwnerCantBeChanged = 15, + AccountNotOwner = 16, + CommissionTooHigh = 17, + DelegationAmountInvalid = 18, + ProposalNotActive = 19, + ValueInvalid = 20, + AmountInvalid = 21, + BucketIdInvalid = 22, + KeyConflict = 23, + MaxDelegationAmount = 24, + InvalidPeerKey = 25, + MinKfiStakedUnreached = 26, + MaxSupplyExeeced = 27, + SaveAccountError = 28, + LoadAccountError = 29, + SameAccountError = 30, + AssetPaused = 31, + DeletegateError = 32, + WithdrawNotAvailable = 33, + ErrOverflow = 34, + SetStakingErr = 35, + SetMarketOrderErr = 36, + BalanceError = 37, + KappError = 38, + UnfreezeError = 39, + UndeletegateError = 40, + WithdrawError = 41, + ClaimError = 42, + BucketsExceded = 43, + AssetCantBeWiped = 44, + AssetCantAddRoles = 45, + FreezeError = 46, + ItoNotActive = 47, + NftMintStopped = 48, + RoyaltiesChangeStopped = 49, + ItokappError = 50, + ItoWhiteListError = 51, + NftMetadataChangeStopped = 52, + AlreadyExists = 53, + Fail = 99, + } + impl TxResultCode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + TxResultCode::Ok => "Ok", + TxResultCode::OutOfFunds => "OutOfFunds", + TxResultCode::AccountError => "AccountError", + TxResultCode::AssetError => "AssetError", + TxResultCode::ContractInvalid => "ContractInvalid", + TxResultCode::ContractNotFound => "ContractNotFound", + TxResultCode::FeeInvalid => "FeeInvalid", + TxResultCode::ParameterInvalid => "ParameterInvalid", + TxResultCode::AprInvalid => "APRInvalid", + TxResultCode::AssetIdInvalid => "AssetIDInvalid", + TxResultCode::AssetTypeInvalid => "AssetTypeInvalid", + TxResultCode::AssetCantBeMinted => "AssetCantBeMinted", + TxResultCode::AssetCantBeBurned => "AssetCantBeBurned", + TxResultCode::AssetCantBePaused => "AssetCantBePaused", + TxResultCode::AssetCantBeDelegated => "AssetCantBeDelegated", + TxResultCode::AssetOwnerCantBeChanged => "AssetOwnerCantBeChanged", + TxResultCode::AccountNotOwner => "AccountNotOwner", + TxResultCode::CommissionTooHigh => "CommissionTooHigh", + TxResultCode::DelegationAmountInvalid => "DelegationAmountInvalid", + TxResultCode::ProposalNotActive => "ProposalNotActive", + TxResultCode::ValueInvalid => "ValueInvalid", + TxResultCode::AmountInvalid => "AmountInvalid", + TxResultCode::BucketIdInvalid => "BucketIDInvalid", + TxResultCode::KeyConflict => "KeyConflict", + TxResultCode::MaxDelegationAmount => "MaxDelegationAmount", + TxResultCode::InvalidPeerKey => "InvalidPeerKey", + TxResultCode::MinKfiStakedUnreached => "MinKFIStakedUnreached", + TxResultCode::MaxSupplyExeeced => "MaxSupplyExeeced", + TxResultCode::SaveAccountError => "SaveAccountError", + TxResultCode::LoadAccountError => "LoadAccountError", + TxResultCode::SameAccountError => "SameAccountError", + TxResultCode::AssetPaused => "AssetPaused", + TxResultCode::DeletegateError => "DeletegateError", + TxResultCode::WithdrawNotAvailable => "WithdrawNotAvailable", + TxResultCode::ErrOverflow => "ErrOverflow", + TxResultCode::SetStakingErr => "SetStakingErr", + TxResultCode::SetMarketOrderErr => "SetMarketOrderErr", + TxResultCode::BalanceError => "BalanceError", + TxResultCode::KappError => "KAPPError", + TxResultCode::UnfreezeError => "UnfreezeError", + TxResultCode::UndeletegateError => "UndeletegateError", + TxResultCode::WithdrawError => "WithdrawError", + TxResultCode::ClaimError => "ClaimError", + TxResultCode::BucketsExceded => "BucketsExceded", + TxResultCode::AssetCantBeWiped => "AssetCantBeWiped", + TxResultCode::AssetCantAddRoles => "AssetCantAddRoles", + TxResultCode::FreezeError => "FreezeError", + TxResultCode::ItoNotActive => "ITONotActive", + TxResultCode::NftMintStopped => "NFTMintStopped", + TxResultCode::RoyaltiesChangeStopped => "RoyaltiesChangeStopped", + TxResultCode::ItokappError => "ITOKAPPError", + TxResultCode::ItoWhiteListError => "ITOWhiteListError", + TxResultCode::NftMetadataChangeStopped => "NFTMetadataChangeStopped", + TxResultCode::AlreadyExists => "AlreadyExists", + TxResultCode::Fail => "Fail", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Ok" => Some(Self::Ok), + "OutOfFunds" => Some(Self::OutOfFunds), + "AccountError" => Some(Self::AccountError), + "AssetError" => Some(Self::AssetError), + "ContractInvalid" => Some(Self::ContractInvalid), + "ContractNotFound" => Some(Self::ContractNotFound), + "FeeInvalid" => Some(Self::FeeInvalid), + "ParameterInvalid" => Some(Self::ParameterInvalid), + "APRInvalid" => Some(Self::AprInvalid), + "AssetIDInvalid" => Some(Self::AssetIdInvalid), + "AssetTypeInvalid" => Some(Self::AssetTypeInvalid), + "AssetCantBeMinted" => Some(Self::AssetCantBeMinted), + "AssetCantBeBurned" => Some(Self::AssetCantBeBurned), + "AssetCantBePaused" => Some(Self::AssetCantBePaused), + "AssetCantBeDelegated" => Some(Self::AssetCantBeDelegated), + "AssetOwnerCantBeChanged" => Some(Self::AssetOwnerCantBeChanged), + "AccountNotOwner" => Some(Self::AccountNotOwner), + "CommissionTooHigh" => Some(Self::CommissionTooHigh), + "DelegationAmountInvalid" => Some(Self::DelegationAmountInvalid), + "ProposalNotActive" => Some(Self::ProposalNotActive), + "ValueInvalid" => Some(Self::ValueInvalid), + "AmountInvalid" => Some(Self::AmountInvalid), + "BucketIDInvalid" => Some(Self::BucketIdInvalid), + "KeyConflict" => Some(Self::KeyConflict), + "MaxDelegationAmount" => Some(Self::MaxDelegationAmount), + "InvalidPeerKey" => Some(Self::InvalidPeerKey), + "MinKFIStakedUnreached" => Some(Self::MinKfiStakedUnreached), + "MaxSupplyExeeced" => Some(Self::MaxSupplyExeeced), + "SaveAccountError" => Some(Self::SaveAccountError), + "LoadAccountError" => Some(Self::LoadAccountError), + "SameAccountError" => Some(Self::SameAccountError), + "AssetPaused" => Some(Self::AssetPaused), + "DeletegateError" => Some(Self::DeletegateError), + "WithdrawNotAvailable" => Some(Self::WithdrawNotAvailable), + "ErrOverflow" => Some(Self::ErrOverflow), + "SetStakingErr" => Some(Self::SetStakingErr), + "SetMarketOrderErr" => Some(Self::SetMarketOrderErr), + "BalanceError" => Some(Self::BalanceError), + "KAPPError" => Some(Self::KappError), + "UnfreezeError" => Some(Self::UnfreezeError), + "UndeletegateError" => Some(Self::UndeletegateError), + "WithdrawError" => Some(Self::WithdrawError), + "ClaimError" => Some(Self::ClaimError), + "BucketsExceded" => Some(Self::BucketsExceded), + "AssetCantBeWiped" => Some(Self::AssetCantBeWiped), + "AssetCantAddRoles" => Some(Self::AssetCantAddRoles), + "FreezeError" => Some(Self::FreezeError), + "ITONotActive" => Some(Self::ItoNotActive), + "NFTMintStopped" => Some(Self::NftMintStopped), + "RoyaltiesChangeStopped" => Some(Self::RoyaltiesChangeStopped), + "ITOKAPPError" => Some(Self::ItokappError), + "ITOWhiteListError" => Some(Self::ItoWhiteListError), + "NFTMetadataChangeStopped" => Some(Self::NftMetadataChangeStopped), + "AlreadyExists" => Some(Self::AlreadyExists), + "Fail" => Some(Self::Fail), + _ => None, + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Key { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub weight: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Permission { + #[prost(int32, tag = "1")] + pub id: i32, + #[prost(enumeration = "permission::PermissionType", tag = "2")] + pub r#type: i32, + #[prost(string, tag = "3")] + pub permission_name: ::prost::alloc::string::String, + #[prost(int64, tag = "4")] + pub threshold: i64, + #[prost(bytes = "vec", tag = "5")] + pub operations: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub signers: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `Permission`. +pub mod permission { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum PermissionType { + Owner = 0, + User = 1, + } + impl PermissionType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + PermissionType::Owner => "Owner", + PermissionType::User => "User", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Owner" => Some(Self::Owner), + "User" => Some(Self::User), + _ => None, + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UserAccountData { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub name: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub root_hash: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "5")] + pub balance: i64, + #[prost(int64, tag = "6")] + pub allowance: i64, + #[prost(uint64, tag = "7")] + pub nonce: u64, + #[prost(message, repeated, tag = "8")] + pub permissions: ::prost::alloc::vec::Vec, +} diff --git a/packages/kos/src/protos/generated/mod.rs b/packages/kos/src/protos/generated/mod.rs new file mode 100644 index 0000000..f4905d5 --- /dev/null +++ b/packages/kos/src/protos/generated/mod.rs @@ -0,0 +1,3 @@ +#![allow(clippy::all)] +pub(crate) mod klv; +pub(crate) mod trx; diff --git a/packages/kos/src/protos/generated/trx/google.protobuf.rs b/packages/kos/src/protos/generated/trx/google.protobuf.rs new file mode 100644 index 0000000..e69de29 diff --git a/packages/kos/src/protos/generated/trx/mod.rs b/packages/kos/src/protos/generated/trx/mod.rs new file mode 100644 index 0000000..8a1738d --- /dev/null +++ b/packages/kos/src/protos/generated/trx/mod.rs @@ -0,0 +1,2 @@ +#[rustfmt::skip] +pub mod protocol; diff --git a/packages/kos/src/protos/generated/trx/protocol.rs b/packages/kos/src/protos/generated/trx/protocol.rs new file mode 100644 index 0000000..9f8192c --- /dev/null +++ b/packages/kos/src/protos/generated/trx/protocol.rs @@ -0,0 +1,3111 @@ +// This file is @generated by prost-build. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Endpoint { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "2")] + pub port: i32, + #[prost(bytes = "vec", tag = "3")] + pub node_id: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PingMessage { + #[prost(message, optional, tag = "1")] + pub from: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub to: ::core::option::Option, + #[prost(int32, tag = "3")] + pub version: i32, + #[prost(int64, tag = "4")] + pub timestamp: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PongMessage { + #[prost(message, optional, tag = "1")] + pub from: ::core::option::Option, + #[prost(int32, tag = "2")] + pub echo: i32, + #[prost(int64, tag = "3")] + pub timestamp: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FindNeighbours { + #[prost(message, optional, tag = "1")] + pub from: ::core::option::Option, + #[prost(bytes = "vec", tag = "2")] + pub target_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub timestamp: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Neighbours { + #[prost(message, optional, tag = "1")] + pub from: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub neighbours: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub timestamp: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BackupMessage { + #[prost(bool, tag = "1")] + pub flag: bool, + #[prost(int32, tag = "2")] + pub priority: i32, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ResourceCode { + Bandwidth = 0, + Energy = 1, + TronPower = 2, +} +impl ResourceCode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ResourceCode::Bandwidth => "BANDWIDTH", + ResourceCode::Energy => "ENERGY", + ResourceCode::TronPower => "TRON_POWER", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "BANDWIDTH" => Some(Self::Bandwidth), + "ENERGY" => Some(Self::Energy), + "TRON_POWER" => Some(Self::TronPower), + _ => None, + } + } +} +/// AccountId, (name, address) use name, (null, address) use address, (name, null) use name, +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountId { + #[prost(bytes = "vec", tag = "1")] + pub name: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub address: ::prost::alloc::vec::Vec, +} +/// vote message +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Vote { + /// the super rep address + #[prost(bytes = "vec", tag = "1")] + pub vote_address: ::prost::alloc::vec::Vec, + /// the vote num to this super rep. + #[prost(int64, tag = "2")] + pub vote_count: i64, +} +/// Proposal +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Proposal { + #[prost(int64, tag = "1")] + pub proposal_id: i64, + #[prost(bytes = "vec", tag = "2")] + pub proposer_address: ::prost::alloc::vec::Vec, + #[prost(btree_map = "int64, int64", tag = "3")] + pub parameters: ::prost::alloc::collections::BTreeMap, + #[prost(int64, tag = "4")] + pub expiration_time: i64, + #[prost(int64, tag = "5")] + pub create_time: i64, + #[prost(bytes = "vec", repeated, tag = "6")] + pub approvals: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(enumeration = "proposal::State", tag = "7")] + pub state: i32, +} +/// Nested message and enum types in `Proposal`. +pub mod proposal { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum State { + Pending = 0, + Disapproved = 1, + Approved = 2, + Canceled = 3, + } + impl State { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + State::Pending => "PENDING", + State::Disapproved => "DISAPPROVED", + State::Approved => "APPROVED", + State::Canceled => "CANCELED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PENDING" => Some(Self::Pending), + "DISAPPROVED" => Some(Self::Disapproved), + "APPROVED" => Some(Self::Approved), + "CANCELED" => Some(Self::Canceled), + _ => None, + } + } + } +} +/// Exchange +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Exchange { + #[prost(int64, tag = "1")] + pub exchange_id: i64, + #[prost(bytes = "vec", tag = "2")] + pub creator_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub create_time: i64, + #[prost(bytes = "vec", tag = "6")] + pub first_token_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "7")] + pub first_token_balance: i64, + #[prost(bytes = "vec", tag = "8")] + pub second_token_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "9")] + pub second_token_balance: i64, +} +/// market +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketOrder { + #[prost(bytes = "vec", tag = "1")] + pub order_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub create_time: i64, + #[prost(bytes = "vec", tag = "4")] + pub sell_token_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "5")] + pub sell_token_quantity: i64, + #[prost(bytes = "vec", tag = "6")] + pub buy_token_id: ::prost::alloc::vec::Vec, + /// min to receive + #[prost(int64, tag = "7")] + pub buy_token_quantity: i64, + #[prost(int64, tag = "9")] + pub sell_token_quantity_remain: i64, + /// When state != ACTIVE and sell_token_quantity_return !=0, + /// it means that some sell tokens are returned to the account due to insufficient remaining amount + #[prost(int64, tag = "10")] + pub sell_token_quantity_return: i64, + #[prost(enumeration = "market_order::State", tag = "11")] + pub state: i32, + #[prost(bytes = "vec", tag = "12")] + pub prev: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "13")] + pub next: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `MarketOrder`. +pub mod market_order { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum State { + Active = 0, + Inactive = 1, + Canceled = 2, + } + impl State { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + State::Active => "ACTIVE", + State::Inactive => "INACTIVE", + State::Canceled => "CANCELED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ACTIVE" => Some(Self::Active), + "INACTIVE" => Some(Self::Inactive), + "CANCELED" => Some(Self::Canceled), + _ => None, + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketOrderList { + #[prost(message, repeated, tag = "1")] + pub orders: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketOrderPairList { + #[prost(message, repeated, tag = "1")] + pub order_pair: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketOrderPair { + #[prost(bytes = "vec", tag = "1")] + pub sell_token_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub buy_token_id: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketAccountOrder { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + /// order_id list + #[prost(bytes = "vec", repeated, tag = "2")] + pub orders: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// active count + #[prost(int64, tag = "3")] + pub count: i64, + #[prost(int64, tag = "4")] + pub total_count: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketPrice { + #[prost(int64, tag = "1")] + pub sell_token_quantity: i64, + #[prost(int64, tag = "2")] + pub buy_token_quantity: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketPriceList { + #[prost(bytes = "vec", tag = "1")] + pub sell_token_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub buy_token_id: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub prices: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketOrderIdList { + #[prost(bytes = "vec", tag = "1")] + pub head: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub tail: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ChainParameters { + #[prost(message, repeated, tag = "1")] + pub chain_parameter: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `ChainParameters`. +pub mod chain_parameters { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ChainParameter { + #[prost(string, tag = "1")] + pub key: ::prost::alloc::string::String, + #[prost(int64, tag = "2")] + pub value: i64, + } +} +/// Account +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Account { + /// account nick name + #[prost(bytes = "vec", tag = "1")] + pub account_name: ::prost::alloc::vec::Vec, + #[prost(enumeration = "AccountType", tag = "2")] + pub r#type: i32, + /// the create address + #[prost(bytes = "vec", tag = "3")] + pub address: ::prost::alloc::vec::Vec, + /// the trx balance + #[prost(int64, tag = "4")] + pub balance: i64, + /// the votes + #[prost(message, repeated, tag = "5")] + pub votes: ::prost::alloc::vec::Vec, + /// the other asset owned by this account + #[prost(btree_map = "string, int64", tag = "6")] + pub asset: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + i64, + >, + /// the other asset owned by this account,key is assetId + #[prost(btree_map = "string, int64", tag = "56")] + pub asset_v2: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + i64, + >, + /// the frozen balance for bandwidth + #[prost(message, repeated, tag = "7")] + pub frozen: ::prost::alloc::vec::Vec, + /// bandwidth, get from frozen + #[prost(int64, tag = "8")] + pub net_usage: i64, + /// Frozen balance provided by other accounts to this account + #[prost(int64, tag = "41")] + pub acquired_delegated_frozen_balance_for_bandwidth: i64, + /// Freeze and provide balances to other accounts + #[prost(int64, tag = "42")] + pub delegated_frozen_balance_for_bandwidth: i64, + #[prost(int64, tag = "46")] + pub old_tron_power: i64, + #[prost(message, optional, tag = "47")] + pub tron_power: ::core::option::Option, + #[prost(bool, tag = "60")] + pub asset_optimized: bool, + /// this account create time + #[prost(int64, tag = "9")] + pub create_time: i64, + /// this last operation time, including transfer, voting and so on. //FIXME fix grammar + #[prost(int64, tag = "10")] + pub latest_opration_time: i64, + /// witness block producing allowance + #[prost(int64, tag = "11")] + pub allowance: i64, + /// last withdraw time + #[prost(int64, tag = "12")] + pub latest_withdraw_time: i64, + /// not used so far + #[prost(bytes = "vec", tag = "13")] + pub code: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "14")] + pub is_witness: bool, + #[prost(bool, tag = "15")] + pub is_committee: bool, + /// frozen asset(for asset issuer) + #[prost(message, repeated, tag = "16")] + pub frozen_supply: ::prost::alloc::vec::Vec, + /// asset_issued_name + #[prost(bytes = "vec", tag = "17")] + pub asset_issued_name: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "57")] + pub asset_issued_id: ::prost::alloc::vec::Vec, + #[prost(btree_map = "string, int64", tag = "18")] + pub latest_asset_operation_time: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + i64, + >, + #[prost(btree_map = "string, int64", tag = "58")] + pub latest_asset_operation_time_v2: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + i64, + >, + #[prost(int64, tag = "19")] + pub free_net_usage: i64, + #[prost(btree_map = "string, int64", tag = "20")] + pub free_asset_net_usage: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + i64, + >, + #[prost(btree_map = "string, int64", tag = "59")] + pub free_asset_net_usage_v2: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + i64, + >, + #[prost(int64, tag = "21")] + pub latest_consume_time: i64, + #[prost(int64, tag = "22")] + pub latest_consume_free_time: i64, + /// the identity of this account, case insensitive + #[prost(bytes = "vec", tag = "23")] + pub account_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "24")] + pub net_window_size: i64, + #[prost(bool, tag = "25")] + pub net_window_optimized: bool, + #[prost(message, optional, tag = "26")] + pub account_resource: ::core::option::Option, + #[prost(bytes = "vec", tag = "30")] + pub code_hash: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "31")] + pub owner_permission: ::core::option::Option, + #[prost(message, optional, tag = "32")] + pub witness_permission: ::core::option::Option, + #[prost(message, repeated, tag = "33")] + pub active_permission: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "34")] + pub frozen_v2: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "35")] + pub unfrozen_v2: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "36")] + pub delegated_frozen_v2_balance_for_bandwidth: i64, + #[prost(int64, tag = "37")] + pub acquired_delegated_frozen_v2_balance_for_bandwidth: i64, +} +/// Nested message and enum types in `Account`. +pub mod account { + /// frozen balance + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Frozen { + /// the frozen trx balance + #[prost(int64, tag = "1")] + pub frozen_balance: i64, + /// the expire time + #[prost(int64, tag = "2")] + pub expire_time: i64, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct AccountResource { + /// energy resource, get from frozen + #[prost(int64, tag = "1")] + pub energy_usage: i64, + /// the frozen balance for energy + #[prost(message, optional, tag = "2")] + pub frozen_balance_for_energy: ::core::option::Option, + #[prost(int64, tag = "3")] + pub latest_consume_time_for_energy: i64, + /// Frozen balance provided by other accounts to this account + #[prost(int64, tag = "4")] + pub acquired_delegated_frozen_balance_for_energy: i64, + /// Frozen balances provided to other accounts + #[prost(int64, tag = "5")] + pub delegated_frozen_balance_for_energy: i64, + /// storage resource, get from market + #[prost(int64, tag = "6")] + pub storage_limit: i64, + #[prost(int64, tag = "7")] + pub storage_usage: i64, + #[prost(int64, tag = "8")] + pub latest_exchange_storage_time: i64, + #[prost(int64, tag = "9")] + pub energy_window_size: i64, + #[prost(int64, tag = "10")] + pub delegated_frozen_v2_balance_for_energy: i64, + #[prost(int64, tag = "11")] + pub acquired_delegated_frozen_v2_balance_for_energy: i64, + #[prost(bool, tag = "12")] + pub energy_window_optimized: bool, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct FreezeV2 { + #[prost(enumeration = "super::ResourceCode", tag = "1")] + pub r#type: i32, + #[prost(int64, tag = "2")] + pub amount: i64, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct UnFreezeV2 { + #[prost(enumeration = "super::ResourceCode", tag = "1")] + pub r#type: i32, + #[prost(int64, tag = "3")] + pub unfreeze_amount: i64, + #[prost(int64, tag = "4")] + pub unfreeze_expire_time: i64, + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Key { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub weight: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DelegatedResource { + #[prost(bytes = "vec", tag = "1")] + pub from: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub to: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub frozen_balance_for_bandwidth: i64, + #[prost(int64, tag = "4")] + pub frozen_balance_for_energy: i64, + #[prost(int64, tag = "5")] + pub expire_time_for_bandwidth: i64, + #[prost(int64, tag = "6")] + pub expire_time_for_energy: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Authority { + #[prost(message, optional, tag = "1")] + pub account: ::core::option::Option, + #[prost(bytes = "vec", tag = "2")] + pub permission_name: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Permission { + #[prost(enumeration = "permission::PermissionType", tag = "1")] + pub r#type: i32, + /// Owner id=0, Witness id=1, Active id start by 2 + #[prost(int32, tag = "2")] + pub id: i32, + #[prost(string, tag = "3")] + pub permission_name: ::prost::alloc::string::String, + #[prost(int64, tag = "4")] + pub threshold: i64, + #[prost(int32, tag = "5")] + pub parent_id: i32, + /// 1 bit 1 contract + #[prost(bytes = "vec", tag = "6")] + pub operations: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "7")] + pub keys: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `Permission`. +pub mod permission { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum PermissionType { + Owner = 0, + Witness = 1, + Active = 2, + } + impl PermissionType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + PermissionType::Owner => "Owner", + PermissionType::Witness => "Witness", + PermissionType::Active => "Active", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Owner" => Some(Self::Owner), + "Witness" => Some(Self::Witness), + "Active" => Some(Self::Active), + _ => None, + } + } + } +} +/// Witness +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Witness { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub vote_count: i64, + #[prost(bytes = "vec", tag = "3")] + pub pub_key: ::prost::alloc::vec::Vec, + #[prost(string, tag = "4")] + pub url: ::prost::alloc::string::String, + #[prost(int64, tag = "5")] + pub total_produced: i64, + #[prost(int64, tag = "6")] + pub total_missed: i64, + #[prost(int64, tag = "7")] + pub latest_block_num: i64, + #[prost(int64, tag = "8")] + pub latest_slot_num: i64, + #[prost(bool, tag = "9")] + pub is_jobs: bool, +} +/// Vote Change +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Votes { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub old_votes: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub new_votes: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TxOutput { + #[prost(int64, tag = "1")] + pub value: i64, + #[prost(bytes = "vec", tag = "2")] + pub pub_key_hash: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TxInput { + #[prost(message, optional, tag = "1")] + pub raw_data: ::core::option::Option, + #[prost(bytes = "vec", tag = "4")] + pub signature: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `TXInput`. +pub mod tx_input { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Raw { + #[prost(bytes = "vec", tag = "1")] + pub tx_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub vout: i64, + #[prost(bytes = "vec", tag = "3")] + pub pub_key: ::prost::alloc::vec::Vec, + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TxOutputs { + #[prost(message, repeated, tag = "1")] + pub outputs: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ResourceReceipt { + #[prost(int64, tag = "1")] + pub energy_usage: i64, + #[prost(int64, tag = "2")] + pub energy_fee: i64, + #[prost(int64, tag = "3")] + pub origin_energy_usage: i64, + #[prost(int64, tag = "4")] + pub energy_usage_total: i64, + #[prost(int64, tag = "5")] + pub net_usage: i64, + #[prost(int64, tag = "6")] + pub net_fee: i64, + #[prost(enumeration = "transaction::result::ContractResult", tag = "7")] + pub result: i32, + #[prost(int64, tag = "8")] + pub energy_penalty_total: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketOrderDetail { + #[prost(bytes = "vec", tag = "1")] + pub maker_order_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub taker_order_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub fill_sell_quantity: i64, + #[prost(int64, tag = "4")] + pub fill_buy_quantity: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Transaction { + #[prost(message, optional, tag = "1")] + pub raw_data: ::core::option::Option, + /// only support size = 1, repeated list here for muti-sig extension + #[prost(bytes = "vec", repeated, tag = "2")] + pub signature: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(message, repeated, tag = "5")] + pub ret: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `Transaction`. +pub mod transaction { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Contract { + #[prost(enumeration = "contract::ContractType", tag = "1")] + pub r#type: i32, + #[prost(message, optional, tag = "2")] + pub parameter: ::core::option::Option<::prost_types::Any>, + #[prost(bytes = "vec", tag = "3")] + pub provider: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "4")] + pub contract_name: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "5")] + pub permission_id: i32, + } + /// Nested message and enum types in `Contract`. + pub mod contract { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum ContractType { + AccountCreateContract = 0, + TransferContract = 1, + TransferAssetContract = 2, + VoteAssetContract = 3, + VoteWitnessContract = 4, + WitnessCreateContract = 5, + AssetIssueContract = 6, + WitnessUpdateContract = 8, + ParticipateAssetIssueContract = 9, + AccountUpdateContract = 10, + FreezeBalanceContract = 11, + UnfreezeBalanceContract = 12, + WithdrawBalanceContract = 13, + UnfreezeAssetContract = 14, + UpdateAssetContract = 15, + ProposalCreateContract = 16, + ProposalApproveContract = 17, + ProposalDeleteContract = 18, + SetAccountIdContract = 19, + CustomContract = 20, + CreateSmartContract = 30, + TriggerSmartContract = 31, + GetContract = 32, + UpdateSettingContract = 33, + ExchangeCreateContract = 41, + ExchangeInjectContract = 42, + ExchangeWithdrawContract = 43, + ExchangeTransactionContract = 44, + UpdateEnergyLimitContract = 45, + AccountPermissionUpdateContract = 46, + ClearAbiContract = 48, + UpdateBrokerageContract = 49, + ShieldedTransferContract = 51, + MarketSellAssetContract = 52, + MarketCancelOrderContract = 53, + FreezeBalanceV2Contract = 54, + UnfreezeBalanceV2Contract = 55, + WithdrawExpireUnfreezeContract = 56, + DelegateResourceContract = 57, + UnDelegateResourceContract = 58, + CancelAllUnfreezeV2Contract = 59, + } + impl ContractType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ContractType::AccountCreateContract => "AccountCreateContract", + ContractType::TransferContract => "TransferContract", + ContractType::TransferAssetContract => "TransferAssetContract", + ContractType::VoteAssetContract => "VoteAssetContract", + ContractType::VoteWitnessContract => "VoteWitnessContract", + ContractType::WitnessCreateContract => "WitnessCreateContract", + ContractType::AssetIssueContract => "AssetIssueContract", + ContractType::WitnessUpdateContract => "WitnessUpdateContract", + ContractType::ParticipateAssetIssueContract => { + "ParticipateAssetIssueContract" + } + ContractType::AccountUpdateContract => "AccountUpdateContract", + ContractType::FreezeBalanceContract => "FreezeBalanceContract", + ContractType::UnfreezeBalanceContract => "UnfreezeBalanceContract", + ContractType::WithdrawBalanceContract => "WithdrawBalanceContract", + ContractType::UnfreezeAssetContract => "UnfreezeAssetContract", + ContractType::UpdateAssetContract => "UpdateAssetContract", + ContractType::ProposalCreateContract => "ProposalCreateContract", + ContractType::ProposalApproveContract => "ProposalApproveContract", + ContractType::ProposalDeleteContract => "ProposalDeleteContract", + ContractType::SetAccountIdContract => "SetAccountIdContract", + ContractType::CustomContract => "CustomContract", + ContractType::CreateSmartContract => "CreateSmartContract", + ContractType::TriggerSmartContract => "TriggerSmartContract", + ContractType::GetContract => "GetContract", + ContractType::UpdateSettingContract => "UpdateSettingContract", + ContractType::ExchangeCreateContract => "ExchangeCreateContract", + ContractType::ExchangeInjectContract => "ExchangeInjectContract", + ContractType::ExchangeWithdrawContract => "ExchangeWithdrawContract", + ContractType::ExchangeTransactionContract => { + "ExchangeTransactionContract" + } + ContractType::UpdateEnergyLimitContract => { + "UpdateEnergyLimitContract" + } + ContractType::AccountPermissionUpdateContract => { + "AccountPermissionUpdateContract" + } + ContractType::ClearAbiContract => "ClearABIContract", + ContractType::UpdateBrokerageContract => "UpdateBrokerageContract", + ContractType::ShieldedTransferContract => "ShieldedTransferContract", + ContractType::MarketSellAssetContract => "MarketSellAssetContract", + ContractType::MarketCancelOrderContract => { + "MarketCancelOrderContract" + } + ContractType::FreezeBalanceV2Contract => "FreezeBalanceV2Contract", + ContractType::UnfreezeBalanceV2Contract => { + "UnfreezeBalanceV2Contract" + } + ContractType::WithdrawExpireUnfreezeContract => { + "WithdrawExpireUnfreezeContract" + } + ContractType::DelegateResourceContract => "DelegateResourceContract", + ContractType::UnDelegateResourceContract => { + "UnDelegateResourceContract" + } + ContractType::CancelAllUnfreezeV2Contract => { + "CancelAllUnfreezeV2Contract" + } + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "AccountCreateContract" => Some(Self::AccountCreateContract), + "TransferContract" => Some(Self::TransferContract), + "TransferAssetContract" => Some(Self::TransferAssetContract), + "VoteAssetContract" => Some(Self::VoteAssetContract), + "VoteWitnessContract" => Some(Self::VoteWitnessContract), + "WitnessCreateContract" => Some(Self::WitnessCreateContract), + "AssetIssueContract" => Some(Self::AssetIssueContract), + "WitnessUpdateContract" => Some(Self::WitnessUpdateContract), + "ParticipateAssetIssueContract" => { + Some(Self::ParticipateAssetIssueContract) + } + "AccountUpdateContract" => Some(Self::AccountUpdateContract), + "FreezeBalanceContract" => Some(Self::FreezeBalanceContract), + "UnfreezeBalanceContract" => Some(Self::UnfreezeBalanceContract), + "WithdrawBalanceContract" => Some(Self::WithdrawBalanceContract), + "UnfreezeAssetContract" => Some(Self::UnfreezeAssetContract), + "UpdateAssetContract" => Some(Self::UpdateAssetContract), + "ProposalCreateContract" => Some(Self::ProposalCreateContract), + "ProposalApproveContract" => Some(Self::ProposalApproveContract), + "ProposalDeleteContract" => Some(Self::ProposalDeleteContract), + "SetAccountIdContract" => Some(Self::SetAccountIdContract), + "CustomContract" => Some(Self::CustomContract), + "CreateSmartContract" => Some(Self::CreateSmartContract), + "TriggerSmartContract" => Some(Self::TriggerSmartContract), + "GetContract" => Some(Self::GetContract), + "UpdateSettingContract" => Some(Self::UpdateSettingContract), + "ExchangeCreateContract" => Some(Self::ExchangeCreateContract), + "ExchangeInjectContract" => Some(Self::ExchangeInjectContract), + "ExchangeWithdrawContract" => Some(Self::ExchangeWithdrawContract), + "ExchangeTransactionContract" => { + Some(Self::ExchangeTransactionContract) + } + "UpdateEnergyLimitContract" => Some(Self::UpdateEnergyLimitContract), + "AccountPermissionUpdateContract" => { + Some(Self::AccountPermissionUpdateContract) + } + "ClearABIContract" => Some(Self::ClearAbiContract), + "UpdateBrokerageContract" => Some(Self::UpdateBrokerageContract), + "ShieldedTransferContract" => Some(Self::ShieldedTransferContract), + "MarketSellAssetContract" => Some(Self::MarketSellAssetContract), + "MarketCancelOrderContract" => Some(Self::MarketCancelOrderContract), + "FreezeBalanceV2Contract" => Some(Self::FreezeBalanceV2Contract), + "UnfreezeBalanceV2Contract" => Some(Self::UnfreezeBalanceV2Contract), + "WithdrawExpireUnfreezeContract" => { + Some(Self::WithdrawExpireUnfreezeContract) + } + "DelegateResourceContract" => Some(Self::DelegateResourceContract), + "UnDelegateResourceContract" => { + Some(Self::UnDelegateResourceContract) + } + "CancelAllUnfreezeV2Contract" => { + Some(Self::CancelAllUnfreezeV2Contract) + } + _ => None, + } + } + } + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Result { + #[prost(int64, tag = "1")] + pub fee: i64, + #[prost(enumeration = "result::Code", tag = "2")] + pub ret: i32, + #[prost(enumeration = "result::ContractResult", tag = "3")] + pub contract_ret: i32, + #[prost(string, tag = "14")] + pub asset_issue_id: ::prost::alloc::string::String, + #[prost(int64, tag = "15")] + pub withdraw_amount: i64, + #[prost(int64, tag = "16")] + pub unfreeze_amount: i64, + #[prost(int64, tag = "18")] + pub exchange_received_amount: i64, + #[prost(int64, tag = "19")] + pub exchange_inject_another_amount: i64, + #[prost(int64, tag = "20")] + pub exchange_withdraw_another_amount: i64, + #[prost(int64, tag = "21")] + pub exchange_id: i64, + #[prost(int64, tag = "22")] + pub shielded_transaction_fee: i64, + #[prost(bytes = "vec", tag = "25")] + pub order_id: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "26")] + pub order_details: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "27")] + pub withdraw_expire_amount: i64, + #[prost(btree_map = "string, int64", tag = "28")] + pub cancel_unfreeze_v2_amount: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + i64, + >, + } + /// Nested message and enum types in `Result`. + pub mod result { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Code { + Sucess = 0, + Failed = 1, + } + impl Code { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Code::Sucess => "SUCESS", + Code::Failed => "FAILED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SUCESS" => Some(Self::Sucess), + "FAILED" => Some(Self::Failed), + _ => None, + } + } + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum ContractResult { + Default = 0, + Success = 1, + Revert = 2, + BadJumpDestination = 3, + OutOfMemory = 4, + PrecompiledContract = 5, + StackTooSmall = 6, + StackTooLarge = 7, + IllegalOperation = 8, + StackOverflow = 9, + OutOfEnergy = 10, + OutOfTime = 11, + JvmStackOverFlow = 12, + Unknown = 13, + TransferFailed = 14, + InvalidCode = 15, + } + impl ContractResult { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ContractResult::Default => "DEFAULT", + ContractResult::Success => "SUCCESS", + ContractResult::Revert => "REVERT", + ContractResult::BadJumpDestination => "BAD_JUMP_DESTINATION", + ContractResult::OutOfMemory => "OUT_OF_MEMORY", + ContractResult::PrecompiledContract => "PRECOMPILED_CONTRACT", + ContractResult::StackTooSmall => "STACK_TOO_SMALL", + ContractResult::StackTooLarge => "STACK_TOO_LARGE", + ContractResult::IllegalOperation => "ILLEGAL_OPERATION", + ContractResult::StackOverflow => "STACK_OVERFLOW", + ContractResult::OutOfEnergy => "OUT_OF_ENERGY", + ContractResult::OutOfTime => "OUT_OF_TIME", + ContractResult::JvmStackOverFlow => "JVM_STACK_OVER_FLOW", + ContractResult::Unknown => "UNKNOWN", + ContractResult::TransferFailed => "TRANSFER_FAILED", + ContractResult::InvalidCode => "INVALID_CODE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DEFAULT" => Some(Self::Default), + "SUCCESS" => Some(Self::Success), + "REVERT" => Some(Self::Revert), + "BAD_JUMP_DESTINATION" => Some(Self::BadJumpDestination), + "OUT_OF_MEMORY" => Some(Self::OutOfMemory), + "PRECOMPILED_CONTRACT" => Some(Self::PrecompiledContract), + "STACK_TOO_SMALL" => Some(Self::StackTooSmall), + "STACK_TOO_LARGE" => Some(Self::StackTooLarge), + "ILLEGAL_OPERATION" => Some(Self::IllegalOperation), + "STACK_OVERFLOW" => Some(Self::StackOverflow), + "OUT_OF_ENERGY" => Some(Self::OutOfEnergy), + "OUT_OF_TIME" => Some(Self::OutOfTime), + "JVM_STACK_OVER_FLOW" => Some(Self::JvmStackOverFlow), + "UNKNOWN" => Some(Self::Unknown), + "TRANSFER_FAILED" => Some(Self::TransferFailed), + "INVALID_CODE" => Some(Self::InvalidCode), + _ => None, + } + } + } + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Raw { + #[prost(bytes = "vec", tag = "1")] + pub ref_block_bytes: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub ref_block_num: i64, + #[prost(bytes = "vec", tag = "4")] + pub ref_block_hash: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "8")] + pub expiration: i64, + #[prost(message, repeated, tag = "9")] + pub auths: ::prost::alloc::vec::Vec, + /// data not used + #[prost(bytes = "vec", tag = "10")] + pub data: ::prost::alloc::vec::Vec, + /// only support size = 1, repeated list here for extension + #[prost(message, repeated, tag = "11")] + pub contract: ::prost::alloc::vec::Vec, + /// scripts not used + #[prost(bytes = "vec", tag = "12")] + pub scripts: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "14")] + pub timestamp: i64, + #[prost(int64, tag = "18")] + pub fee_limit: i64, + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionInfo { + #[prost(bytes = "vec", tag = "1")] + pub id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub fee: i64, + #[prost(int64, tag = "3")] + pub block_number: i64, + #[prost(int64, tag = "4")] + pub block_time_stamp: i64, + #[prost(bytes = "vec", repeated, tag = "5")] + pub contract_result: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(bytes = "vec", tag = "6")] + pub contract_address: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "7")] + pub receipt: ::core::option::Option, + #[prost(message, repeated, tag = "8")] + pub log: ::prost::alloc::vec::Vec, + #[prost(enumeration = "transaction_info::Code", tag = "9")] + pub result: i32, + #[prost(bytes = "vec", tag = "10")] + pub res_message: ::prost::alloc::vec::Vec, + #[prost(string, tag = "14")] + pub asset_issue_id: ::prost::alloc::string::String, + #[prost(int64, tag = "15")] + pub withdraw_amount: i64, + #[prost(int64, tag = "16")] + pub unfreeze_amount: i64, + #[prost(message, repeated, tag = "17")] + pub internal_transactions: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "18")] + pub exchange_received_amount: i64, + #[prost(int64, tag = "19")] + pub exchange_inject_another_amount: i64, + #[prost(int64, tag = "20")] + pub exchange_withdraw_another_amount: i64, + #[prost(int64, tag = "21")] + pub exchange_id: i64, + #[prost(int64, tag = "22")] + pub shielded_transaction_fee: i64, + #[prost(bytes = "vec", tag = "25")] + pub order_id: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "26")] + pub order_details: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "27")] + pub packing_fee: i64, + #[prost(int64, tag = "28")] + pub withdraw_expire_amount: i64, + #[prost(btree_map = "string, int64", tag = "29")] + pub cancel_unfreeze_v2_amount: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + i64, + >, +} +/// Nested message and enum types in `TransactionInfo`. +pub mod transaction_info { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Log { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", repeated, tag = "2")] + pub topics: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(bytes = "vec", tag = "3")] + pub data: ::prost::alloc::vec::Vec, + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Code { + Sucess = 0, + Failed = 1, + } + impl Code { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Code::Sucess => "SUCESS", + Code::Failed => "FAILED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SUCESS" => Some(Self::Sucess), + "FAILED" => Some(Self::Failed), + _ => None, + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionRet { + #[prost(int64, tag = "1")] + pub block_number: i64, + #[prost(int64, tag = "2")] + pub block_time_stamp: i64, + #[prost(message, repeated, tag = "3")] + pub transactioninfo: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Transactions { + #[prost(message, repeated, tag = "1")] + pub transactions: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockHeader { + #[prost(message, optional, tag = "1")] + pub raw_data: ::core::option::Option, + #[prost(bytes = "vec", tag = "2")] + pub witness_signature: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `BlockHeader`. +pub mod block_header { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Raw { + #[prost(int64, tag = "1")] + pub timestamp: i64, + #[prost(bytes = "vec", tag = "2")] + pub tx_trie_root: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub parent_hash: ::prost::alloc::vec::Vec, + /// bytes nonce = 5; + /// bytes difficulty = 6; + #[prost(int64, tag = "7")] + pub number: i64, + #[prost(int64, tag = "8")] + pub witness_id: i64, + #[prost(bytes = "vec", tag = "9")] + pub witness_address: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "10")] + pub version: i32, + #[prost(bytes = "vec", tag = "11")] + pub account_state_root: ::prost::alloc::vec::Vec, + } +} +/// block +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Block { + #[prost(message, repeated, tag = "1")] + pub transactions: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub block_header: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ChainInventory { + #[prost(message, repeated, tag = "1")] + pub ids: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub remain_num: i64, +} +/// Nested message and enum types in `ChainInventory`. +pub mod chain_inventory { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct BlockId { + #[prost(bytes = "vec", tag = "1")] + pub hash: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub number: i64, + } +} +/// Inventory +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockInventory { + #[prost(message, repeated, tag = "1")] + pub ids: ::prost::alloc::vec::Vec, + #[prost(enumeration = "block_inventory::Type", tag = "2")] + pub r#type: i32, +} +/// Nested message and enum types in `BlockInventory`. +pub mod block_inventory { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct BlockId { + #[prost(bytes = "vec", tag = "1")] + pub hash: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub number: i64, + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Type { + Sync = 0, + Advtise = 1, + Fetch = 2, + } + impl Type { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Type::Sync => "SYNC", + Type::Advtise => "ADVTISE", + Type::Fetch => "FETCH", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SYNC" => Some(Self::Sync), + "ADVTISE" => Some(Self::Advtise), + "FETCH" => Some(Self::Fetch), + _ => None, + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Inventory { + #[prost(enumeration = "inventory::InventoryType", tag = "1")] + pub r#type: i32, + #[prost(bytes = "vec", repeated, tag = "2")] + pub ids: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +/// Nested message and enum types in `Inventory`. +pub mod inventory { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum InventoryType { + Trx = 0, + Block = 1, + } + impl InventoryType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + InventoryType::Trx => "TRX", + InventoryType::Block => "BLOCK", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TRX" => Some(Self::Trx), + "BLOCK" => Some(Self::Block), + _ => None, + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Items { + #[prost(enumeration = "items::ItemType", tag = "1")] + pub r#type: i32, + #[prost(message, repeated, tag = "2")] + pub blocks: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub block_headers: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub transactions: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `Items`. +pub mod items { + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum ItemType { + Err = 0, + Trx = 1, + Block = 2, + Blockheader = 3, + } + impl ItemType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ItemType::Err => "ERR", + ItemType::Trx => "TRX", + ItemType::Block => "BLOCK", + ItemType::Blockheader => "BLOCKHEADER", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ERR" => Some(Self::Err), + "TRX" => Some(Self::Trx), + "BLOCK" => Some(Self::Block), + "BLOCKHEADER" => Some(Self::Blockheader), + _ => None, + } + } + } +} +/// DynamicProperties +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DynamicProperties { + #[prost(int64, tag = "1")] + pub last_solidity_block_num: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DisconnectMessage { + #[prost(enumeration = "ReasonCode", tag = "1")] + pub reason: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct HelloMessage { + #[prost(message, optional, tag = "1")] + pub from: ::core::option::Option, + #[prost(int32, tag = "2")] + pub version: i32, + #[prost(int64, tag = "3")] + pub timestamp: i64, + #[prost(message, optional, tag = "4")] + pub genesis_block_id: ::core::option::Option, + #[prost(message, optional, tag = "5")] + pub solid_block_id: ::core::option::Option, + #[prost(message, optional, tag = "6")] + pub head_block_id: ::core::option::Option, + #[prost(bytes = "vec", tag = "7")] + pub address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "8")] + pub signature: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "9")] + pub node_type: i32, + #[prost(int64, tag = "10")] + pub lowest_block_num: i64, + #[prost(bytes = "vec", tag = "11")] + pub code_version: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `HelloMessage`. +pub mod hello_message { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct BlockId { + #[prost(bytes = "vec", tag = "1")] + pub hash: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub number: i64, + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InternalTransaction { + /// internalTransaction identity, the root InternalTransaction hash + /// should equals to root transaction id. + #[prost(bytes = "vec", tag = "1")] + pub hash: ::prost::alloc::vec::Vec, + /// the one send trx (TBD: or token) via function + #[prost(bytes = "vec", tag = "2")] + pub caller_address: ::prost::alloc::vec::Vec, + /// the one recieve trx (TBD: or token) via function + #[prost(bytes = "vec", tag = "3")] + pub transfer_to_address: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub call_value_info: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "5")] + pub note: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "6")] + pub rejected: bool, + #[prost(string, tag = "7")] + pub extra: ::prost::alloc::string::String, +} +/// Nested message and enum types in `InternalTransaction`. +pub mod internal_transaction { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct CallValueInfo { + /// trx (TBD: or token) value + #[prost(int64, tag = "1")] + pub call_value: i64, + /// TBD: tokenName, trx should be empty + #[prost(string, tag = "2")] + pub token_id: ::prost::alloc::string::String, + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DelegatedResourceAccountIndex { + #[prost(bytes = "vec", tag = "1")] + pub account: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", repeated, tag = "2")] + pub from_accounts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(bytes = "vec", repeated, tag = "3")] + pub to_accounts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(int64, tag = "4")] + pub timestamp: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NodeInfo { + #[prost(int64, tag = "1")] + pub begin_sync_num: i64, + #[prost(string, tag = "2")] + pub block: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub solidity_block: ::prost::alloc::string::String, + /// connect information + #[prost(int32, tag = "4")] + pub current_connect_count: i32, + #[prost(int32, tag = "5")] + pub active_connect_count: i32, + #[prost(int32, tag = "6")] + pub passive_connect_count: i32, + #[prost(int64, tag = "7")] + pub total_flow: i64, + #[prost(message, repeated, tag = "8")] + pub peer_info_list: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "9")] + pub config_node_info: ::core::option::Option, + #[prost(message, optional, tag = "10")] + pub machine_info: ::core::option::Option, + #[prost(btree_map = "string, string", tag = "11")] + pub cheat_witness_info_map: ::prost::alloc::collections::BTreeMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, +} +/// Nested message and enum types in `NodeInfo`. +pub mod node_info { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct PeerInfo { + #[prost(string, tag = "1")] + pub last_sync_block: ::prost::alloc::string::String, + #[prost(int64, tag = "2")] + pub remain_num: i64, + #[prost(int64, tag = "3")] + pub last_block_update_time: i64, + #[prost(bool, tag = "4")] + pub sync_flag: bool, + #[prost(int64, tag = "5")] + pub head_block_time_we_both_have: i64, + #[prost(bool, tag = "6")] + pub need_sync_from_peer: bool, + #[prost(bool, tag = "7")] + pub need_sync_from_us: bool, + #[prost(string, tag = "8")] + pub host: ::prost::alloc::string::String, + #[prost(int32, tag = "9")] + pub port: i32, + #[prost(string, tag = "10")] + pub node_id: ::prost::alloc::string::String, + #[prost(int64, tag = "11")] + pub connect_time: i64, + #[prost(double, tag = "12")] + pub avg_latency: f64, + #[prost(int32, tag = "13")] + pub sync_to_fetch_size: i32, + #[prost(int64, tag = "14")] + pub sync_to_fetch_size_peek_num: i64, + #[prost(int32, tag = "15")] + pub sync_block_requested_size: i32, + #[prost(int64, tag = "16")] + pub un_fetch_syn_num: i64, + #[prost(int32, tag = "17")] + pub block_in_porc_size: i32, + #[prost(string, tag = "18")] + pub head_block_we_both_have: ::prost::alloc::string::String, + #[prost(bool, tag = "19")] + pub is_active: bool, + #[prost(int32, tag = "20")] + pub score: i32, + #[prost(int32, tag = "21")] + pub node_count: i32, + #[prost(int64, tag = "22")] + pub in_flow: i64, + #[prost(int32, tag = "23")] + pub disconnect_times: i32, + #[prost(string, tag = "24")] + pub local_disconnect_reason: ::prost::alloc::string::String, + #[prost(string, tag = "25")] + pub remote_disconnect_reason: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ConfigNodeInfo { + #[prost(string, tag = "1")] + pub code_version: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub p2p_version: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub listen_port: i32, + #[prost(bool, tag = "4")] + pub discover_enable: bool, + #[prost(int32, tag = "5")] + pub active_node_size: i32, + #[prost(int32, tag = "6")] + pub passive_node_size: i32, + #[prost(int32, tag = "7")] + pub send_node_size: i32, + #[prost(int32, tag = "8")] + pub max_connect_count: i32, + #[prost(int32, tag = "9")] + pub same_ip_max_connect_count: i32, + #[prost(int32, tag = "10")] + pub backup_listen_port: i32, + #[prost(int32, tag = "11")] + pub backup_member_size: i32, + #[prost(int32, tag = "12")] + pub backup_priority: i32, + #[prost(int32, tag = "13")] + pub db_version: i32, + #[prost(int32, tag = "14")] + pub min_participation_rate: i32, + #[prost(bool, tag = "15")] + pub support_constant: bool, + #[prost(double, tag = "16")] + pub min_time_ratio: f64, + #[prost(double, tag = "17")] + pub max_time_ratio: f64, + #[prost(int64, tag = "18")] + pub allow_creation_of_contracts: i64, + #[prost(int64, tag = "19")] + pub allow_adaptive_energy: i64, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct MachineInfo { + #[prost(int32, tag = "1")] + pub thread_count: i32, + #[prost(int32, tag = "2")] + pub dead_lock_thread_count: i32, + #[prost(int32, tag = "3")] + pub cpu_count: i32, + #[prost(int64, tag = "4")] + pub total_memory: i64, + #[prost(int64, tag = "5")] + pub free_memory: i64, + #[prost(double, tag = "6")] + pub cpu_rate: f64, + #[prost(string, tag = "7")] + pub java_version: ::prost::alloc::string::String, + #[prost(string, tag = "8")] + pub os_name: ::prost::alloc::string::String, + #[prost(int64, tag = "9")] + pub jvm_total_memory: i64, + #[prost(int64, tag = "10")] + pub jvm_free_memory: i64, + #[prost(double, tag = "11")] + pub process_cpu_rate: f64, + #[prost(message, repeated, tag = "12")] + pub memory_desc_info_list: ::prost::alloc::vec::Vec< + machine_info::MemoryDescInfo, + >, + #[prost(message, repeated, tag = "13")] + pub dead_lock_thread_info_list: ::prost::alloc::vec::Vec< + machine_info::DeadLockThreadInfo, + >, + } + /// Nested message and enum types in `MachineInfo`. + pub mod machine_info { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct MemoryDescInfo { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(int64, tag = "2")] + pub init_size: i64, + #[prost(int64, tag = "3")] + pub use_size: i64, + #[prost(int64, tag = "4")] + pub max_size: i64, + #[prost(double, tag = "5")] + pub use_rate: f64, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct DeadLockThreadInfo { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub lock_name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub lock_owner: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub state: ::prost::alloc::string::String, + #[prost(int64, tag = "5")] + pub block_time: i64, + #[prost(int64, tag = "6")] + pub wait_time: i64, + #[prost(string, tag = "7")] + pub stack_trace: ::prost::alloc::string::String, + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MetricsInfo { + #[prost(int64, tag = "1")] + pub interval: i64, + #[prost(message, optional, tag = "2")] + pub node: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub blockchain: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub net: ::core::option::Option, +} +/// Nested message and enum types in `MetricsInfo`. +pub mod metrics_info { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct NodeInfo { + #[prost(string, tag = "1")] + pub ip: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub node_type: i32, + #[prost(string, tag = "3")] + pub version: ::prost::alloc::string::String, + #[prost(int32, tag = "4")] + pub backup_status: i32, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct BlockChainInfo { + #[prost(int64, tag = "1")] + pub head_block_num: i64, + #[prost(int64, tag = "2")] + pub head_block_timestamp: i64, + #[prost(string, tag = "3")] + pub head_block_hash: ::prost::alloc::string::String, + #[prost(int32, tag = "4")] + pub fork_count: i32, + #[prost(int32, tag = "5")] + pub fail_fork_count: i32, + #[prost(message, optional, tag = "6")] + pub block_process_time: ::core::option::Option, + #[prost(message, optional, tag = "7")] + pub tps: ::core::option::Option, + #[prost(int32, tag = "8")] + pub transaction_cache_size: i32, + #[prost(message, optional, tag = "9")] + pub missed_transaction: ::core::option::Option, + #[prost(message, repeated, tag = "10")] + pub witnesses: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "11")] + pub fail_process_block_num: i64, + #[prost(string, tag = "12")] + pub fail_process_block_reason: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "13")] + pub dup_witness: ::prost::alloc::vec::Vec, + } + /// Nested message and enum types in `BlockChainInfo`. + pub mod block_chain_info { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Witness { + #[prost(string, tag = "1")] + pub address: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub version: i32, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct DupWitness { + #[prost(string, tag = "1")] + pub address: ::prost::alloc::string::String, + #[prost(int64, tag = "2")] + pub block_num: i64, + #[prost(int32, tag = "3")] + pub count: i32, + } + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct RateInfo { + #[prost(int64, tag = "1")] + pub count: i64, + #[prost(double, tag = "2")] + pub mean_rate: f64, + #[prost(double, tag = "3")] + pub one_minute_rate: f64, + #[prost(double, tag = "4")] + pub five_minute_rate: f64, + #[prost(double, tag = "5")] + pub fifteen_minute_rate: f64, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct NetInfo { + #[prost(int32, tag = "1")] + pub error_proto_count: i32, + #[prost(message, optional, tag = "2")] + pub api: ::core::option::Option, + #[prost(int32, tag = "3")] + pub connection_count: i32, + #[prost(int32, tag = "4")] + pub valid_connection_count: i32, + #[prost(message, optional, tag = "5")] + pub tcp_in_traffic: ::core::option::Option, + #[prost(message, optional, tag = "6")] + pub tcp_out_traffic: ::core::option::Option, + #[prost(int32, tag = "7")] + pub disconnection_count: i32, + #[prost(message, repeated, tag = "8")] + pub disconnection_detail: ::prost::alloc::vec::Vec< + net_info::DisconnectionDetailInfo, + >, + #[prost(message, optional, tag = "9")] + pub udp_in_traffic: ::core::option::Option, + #[prost(message, optional, tag = "10")] + pub udp_out_traffic: ::core::option::Option, + #[prost(message, optional, tag = "11")] + pub latency: ::core::option::Option, + } + /// Nested message and enum types in `NetInfo`. + pub mod net_info { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ApiInfo { + #[prost(message, optional, tag = "1")] + pub qps: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub fail_qps: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub out_traffic: ::core::option::Option, + #[prost(message, repeated, tag = "4")] + pub detail: ::prost::alloc::vec::Vec, + } + /// Nested message and enum types in `ApiInfo`. + pub mod api_info { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct ApiDetailInfo { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub qps: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub fail_qps: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub out_traffic: ::core::option::Option, + } + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct DisconnectionDetailInfo { + #[prost(string, tag = "1")] + pub reason: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub count: i32, + } + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct LatencyInfo { + #[prost(int32, tag = "1")] + pub top99: i32, + #[prost(int32, tag = "2")] + pub top95: i32, + #[prost(int32, tag = "3")] + pub top75: i32, + #[prost(int32, tag = "4")] + pub total_count: i32, + #[prost(int32, tag = "5")] + pub delay1_s: i32, + #[prost(int32, tag = "6")] + pub delay2_s: i32, + #[prost(int32, tag = "7")] + pub delay3_s: i32, + #[prost(message, repeated, tag = "8")] + pub detail: ::prost::alloc::vec::Vec, + } + /// Nested message and enum types in `LatencyInfo`. + pub mod latency_info { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct LatencyDetailInfo { + #[prost(string, tag = "1")] + pub witness: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub top99: i32, + #[prost(int32, tag = "3")] + pub top95: i32, + #[prost(int32, tag = "4")] + pub top75: i32, + #[prost(int32, tag = "5")] + pub count: i32, + #[prost(int32, tag = "6")] + pub delay1_s: i32, + #[prost(int32, tag = "7")] + pub delay2_s: i32, + #[prost(int32, tag = "8")] + pub delay3_s: i32, + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PbftMessage { + #[prost(message, optional, tag = "1")] + pub raw_data: ::core::option::Option, + #[prost(bytes = "vec", tag = "2")] + pub signature: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `PBFTMessage`. +pub mod pbft_message { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Raw { + #[prost(enumeration = "MsgType", tag = "1")] + pub msg_type: i32, + #[prost(enumeration = "DataType", tag = "2")] + pub data_type: i32, + #[prost(int64, tag = "3")] + pub view_n: i64, + #[prost(int64, tag = "4")] + pub epoch: i64, + #[prost(bytes = "vec", tag = "5")] + pub data: ::prost::alloc::vec::Vec, + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum MsgType { + ViewChange = 0, + Request = 1, + Preprepare = 2, + Prepare = 3, + Commit = 4, + } + impl MsgType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + MsgType::ViewChange => "VIEW_CHANGE", + MsgType::Request => "REQUEST", + MsgType::Preprepare => "PREPREPARE", + MsgType::Prepare => "PREPARE", + MsgType::Commit => "COMMIT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "VIEW_CHANGE" => Some(Self::ViewChange), + "REQUEST" => Some(Self::Request), + "PREPREPARE" => Some(Self::Preprepare), + "PREPARE" => Some(Self::Prepare), + "COMMIT" => Some(Self::Commit), + _ => None, + } + } + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum DataType { + Block = 0, + Srl = 1, + } + impl DataType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + DataType::Block => "BLOCK", + DataType::Srl => "SRL", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "BLOCK" => Some(Self::Block), + "SRL" => Some(Self::Srl), + _ => None, + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PbftCommitResult { + #[prost(bytes = "vec", tag = "1")] + pub data: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", repeated, tag = "2")] + pub signature: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Srl { + #[prost(bytes = "vec", repeated, tag = "1")] + pub sr_address: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AccountType { + Normal = 0, + AssetIssue = 1, + Contract = 2, +} +impl AccountType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + AccountType::Normal => "Normal", + AccountType::AssetIssue => "AssetIssue", + AccountType::Contract => "Contract", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Normal" => Some(Self::Normal), + "AssetIssue" => Some(Self::AssetIssue), + "Contract" => Some(Self::Contract), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ReasonCode { + Requested = 0, + BadProtocol = 2, + TooManyPeers = 4, + DuplicatePeer = 5, + IncompatibleProtocol = 6, + RandomElimination = 7, + PeerQuiting = 8, + UnexpectedIdentity = 9, + LocalIdentity = 10, + PingTimeout = 11, + UserReason = 16, + Reset = 17, + SyncFail = 18, + FetchFail = 19, + BadTx = 20, + BadBlock = 21, + Forked = 22, + Unlinkable = 23, + IncompatibleVersion = 24, + IncompatibleChain = 25, + TimeOut = 32, + ConnectFail = 33, + TooManyPeersWithSameIp = 34, + LightNodeSyncFail = 35, + BelowThanMe = 36, + NotWitness = 37, + NoSuchMessage = 38, + Unknown = 255, +} +impl ReasonCode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ReasonCode::Requested => "REQUESTED", + ReasonCode::BadProtocol => "BAD_PROTOCOL", + ReasonCode::TooManyPeers => "TOO_MANY_PEERS", + ReasonCode::DuplicatePeer => "DUPLICATE_PEER", + ReasonCode::IncompatibleProtocol => "INCOMPATIBLE_PROTOCOL", + ReasonCode::RandomElimination => "RANDOM_ELIMINATION", + ReasonCode::PeerQuiting => "PEER_QUITING", + ReasonCode::UnexpectedIdentity => "UNEXPECTED_IDENTITY", + ReasonCode::LocalIdentity => "LOCAL_IDENTITY", + ReasonCode::PingTimeout => "PING_TIMEOUT", + ReasonCode::UserReason => "USER_REASON", + ReasonCode::Reset => "RESET", + ReasonCode::SyncFail => "SYNC_FAIL", + ReasonCode::FetchFail => "FETCH_FAIL", + ReasonCode::BadTx => "BAD_TX", + ReasonCode::BadBlock => "BAD_BLOCK", + ReasonCode::Forked => "FORKED", + ReasonCode::Unlinkable => "UNLINKABLE", + ReasonCode::IncompatibleVersion => "INCOMPATIBLE_VERSION", + ReasonCode::IncompatibleChain => "INCOMPATIBLE_CHAIN", + ReasonCode::TimeOut => "TIME_OUT", + ReasonCode::ConnectFail => "CONNECT_FAIL", + ReasonCode::TooManyPeersWithSameIp => "TOO_MANY_PEERS_WITH_SAME_IP", + ReasonCode::LightNodeSyncFail => "LIGHT_NODE_SYNC_FAIL", + ReasonCode::BelowThanMe => "BELOW_THAN_ME", + ReasonCode::NotWitness => "NOT_WITNESS", + ReasonCode::NoSuchMessage => "NO_SUCH_MESSAGE", + ReasonCode::Unknown => "UNKNOWN", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "REQUESTED" => Some(Self::Requested), + "BAD_PROTOCOL" => Some(Self::BadProtocol), + "TOO_MANY_PEERS" => Some(Self::TooManyPeers), + "DUPLICATE_PEER" => Some(Self::DuplicatePeer), + "INCOMPATIBLE_PROTOCOL" => Some(Self::IncompatibleProtocol), + "RANDOM_ELIMINATION" => Some(Self::RandomElimination), + "PEER_QUITING" => Some(Self::PeerQuiting), + "UNEXPECTED_IDENTITY" => Some(Self::UnexpectedIdentity), + "LOCAL_IDENTITY" => Some(Self::LocalIdentity), + "PING_TIMEOUT" => Some(Self::PingTimeout), + "USER_REASON" => Some(Self::UserReason), + "RESET" => Some(Self::Reset), + "SYNC_FAIL" => Some(Self::SyncFail), + "FETCH_FAIL" => Some(Self::FetchFail), + "BAD_TX" => Some(Self::BadTx), + "BAD_BLOCK" => Some(Self::BadBlock), + "FORKED" => Some(Self::Forked), + "UNLINKABLE" => Some(Self::Unlinkable), + "INCOMPATIBLE_VERSION" => Some(Self::IncompatibleVersion), + "INCOMPATIBLE_CHAIN" => Some(Self::IncompatibleChain), + "TIME_OUT" => Some(Self::TimeOut), + "CONNECT_FAIL" => Some(Self::ConnectFail), + "TOO_MANY_PEERS_WITH_SAME_IP" => Some(Self::TooManyPeersWithSameIp), + "LIGHT_NODE_SYNC_FAIL" => Some(Self::LightNodeSyncFail), + "BELOW_THAN_ME" => Some(Self::BelowThanMe), + "NOT_WITNESS" => Some(Self::NotWitness), + "NO_SUCH_MESSAGE" => Some(Self::NoSuchMessage), + "UNKNOWN" => Some(Self::Unknown), + _ => None, + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InventoryItems { + #[prost(int32, tag = "1")] + pub r#type: i32, + #[prost(bytes = "vec", repeated, tag = "2")] + pub items: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountCreateContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub account_address: ::prost::alloc::vec::Vec, + #[prost(enumeration = "AccountType", tag = "3")] + pub r#type: i32, +} +/// Update account name. Account name is not unique now. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountUpdateContract { + #[prost(bytes = "vec", tag = "1")] + pub account_name: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub owner_address: ::prost::alloc::vec::Vec, +} +/// Set account id if the account has no id. Account id is unique and case insensitive. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SetAccountIdContract { + #[prost(bytes = "vec", tag = "1")] + pub account_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub owner_address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountPermissionUpdateContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + /// Empty is invalidate + #[prost(message, optional, tag = "2")] + pub owner: ::core::option::Option, + /// Can be empty + #[prost(message, optional, tag = "3")] + pub witness: ::core::option::Option, + /// Empty is invalidate + #[prost(message, repeated, tag = "4")] + pub actives: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AssetIssueContract { + #[prost(string, tag = "41")] + pub id: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub name: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub abbr: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "4")] + pub total_supply: i64, + #[prost(message, repeated, tag = "5")] + pub frozen_supply: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "6")] + pub trx_num: i32, + #[prost(int32, tag = "7")] + pub precision: i32, + #[prost(int32, tag = "8")] + pub num: i32, + #[prost(int64, tag = "9")] + pub start_time: i64, + #[prost(int64, tag = "10")] + pub end_time: i64, + /// useless + #[prost(int64, tag = "11")] + pub order: i64, + #[prost(int32, tag = "16")] + pub vote_score: i32, + #[prost(bytes = "vec", tag = "20")] + pub description: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "21")] + pub url: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "22")] + pub free_asset_net_limit: i64, + #[prost(int64, tag = "23")] + pub public_free_asset_net_limit: i64, + #[prost(int64, tag = "24")] + pub public_free_asset_net_usage: i64, + #[prost(int64, tag = "25")] + pub public_latest_free_net_time: i64, +} +/// Nested message and enum types in `AssetIssueContract`. +pub mod asset_issue_contract { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct FrozenSupply { + #[prost(int64, tag = "1")] + pub frozen_amount: i64, + #[prost(int64, tag = "2")] + pub frozen_days: i64, + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransferAssetContract { + /// this field is token name before the proposal ALLOW_SAME_TOKEN_NAME is active, otherwise it is token id and token is should be in string format. + #[prost(bytes = "vec", tag = "1")] + pub asset_name: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub to_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "4")] + pub amount: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UnfreezeAssetContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UpdateAssetContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub description: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub url: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "4")] + pub new_limit: i64, + #[prost(int64, tag = "5")] + pub new_public_limit: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ParticipateAssetIssueContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub to_address: ::prost::alloc::vec::Vec, + /// this field is token name before the proposal ALLOW_SAME_TOKEN_NAME is active, otherwise it is token id and token is should be in string format. + #[prost(bytes = "vec", tag = "3")] + pub asset_name: ::prost::alloc::vec::Vec, + /// the amount of drops + #[prost(int64, tag = "4")] + pub amount: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FreezeBalanceContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub frozen_balance: i64, + #[prost(int64, tag = "3")] + pub frozen_duration: i64, + #[prost(enumeration = "ResourceCode", tag = "10")] + pub resource: i32, + #[prost(bytes = "vec", tag = "15")] + pub receiver_address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UnfreezeBalanceContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(enumeration = "ResourceCode", tag = "10")] + pub resource: i32, + #[prost(bytes = "vec", tag = "15")] + pub receiver_address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WithdrawBalanceContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransferContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub to_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub amount: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionBalanceTrace { + #[prost(bytes = "vec", tag = "1")] + pub transaction_identifier: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub operation: ::prost::alloc::vec::Vec, + #[prost(string, tag = "3")] + pub r#type: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub status: ::prost::alloc::string::String, +} +/// Nested message and enum types in `TransactionBalanceTrace`. +pub mod transaction_balance_trace { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Operation { + #[prost(int64, tag = "1")] + pub operation_identifier: i64, + #[prost(bytes = "vec", tag = "2")] + pub address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub amount: i64, + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockBalanceTrace { + #[prost(message, optional, tag = "1")] + pub block_identifier: ::core::option::Option, + #[prost(int64, tag = "2")] + pub timestamp: i64, + /// BlockIdentifier parent_block_identifier = 4; + #[prost(message, repeated, tag = "3")] + pub transaction_balance_trace: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `BlockBalanceTrace`. +pub mod block_balance_trace { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct BlockIdentifier { + #[prost(bytes = "vec", tag = "1")] + pub hash: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub number: i64, + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountTrace { + #[prost(int64, tag = "1")] + pub balance: i64, + #[prost(int64, tag = "99")] + pub placeholder: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountIdentifier { + #[prost(bytes = "vec", tag = "1")] + pub address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountBalanceRequest { + #[prost(message, optional, tag = "1")] + pub account_identifier: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub block_identifier: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountBalanceResponse { + #[prost(int64, tag = "1")] + pub balance: i64, + #[prost(message, optional, tag = "2")] + pub block_identifier: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FreezeBalanceV2Contract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub frozen_balance: i64, + #[prost(enumeration = "ResourceCode", tag = "3")] + pub resource: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UnfreezeBalanceV2Contract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub unfreeze_balance: i64, + #[prost(enumeration = "ResourceCode", tag = "3")] + pub resource: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WithdrawExpireUnfreezeContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DelegateResourceContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(enumeration = "ResourceCode", tag = "2")] + pub resource: i32, + #[prost(int64, tag = "3")] + pub balance: i64, + #[prost(bytes = "vec", tag = "4")] + pub receiver_address: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "5")] + pub lock: bool, + #[prost(int64, tag = "6")] + pub lock_period: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UnDelegateResourceContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(enumeration = "ResourceCode", tag = "2")] + pub resource: i32, + #[prost(int64, tag = "3")] + pub balance: i64, + #[prost(bytes = "vec", tag = "4")] + pub receiver_address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CancelAllUnfreezeV2Contract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExchangeCreateContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub first_token_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub first_token_balance: i64, + #[prost(bytes = "vec", tag = "4")] + pub second_token_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "5")] + pub second_token_balance: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExchangeInjectContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub exchange_id: i64, + #[prost(bytes = "vec", tag = "3")] + pub token_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "4")] + pub quant: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExchangeWithdrawContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub exchange_id: i64, + #[prost(bytes = "vec", tag = "3")] + pub token_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "4")] + pub quant: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExchangeTransactionContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub exchange_id: i64, + #[prost(bytes = "vec", tag = "3")] + pub token_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "4")] + pub quant: i64, + #[prost(int64, tag = "5")] + pub expected: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketSellAssetContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub sell_token_id: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub sell_token_quantity: i64, + #[prost(bytes = "vec", tag = "4")] + pub buy_token_id: ::prost::alloc::vec::Vec, + /// min to receive + #[prost(int64, tag = "5")] + pub buy_token_quantity: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MarketCancelOrderContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub order_id: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProposalApproveContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub proposal_id: i64, + /// add or remove approval + #[prost(bool, tag = "3")] + pub is_add_approval: bool, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProposalCreateContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(btree_map = "int64, int64", tag = "2")] + pub parameters: ::prost::alloc::collections::BTreeMap, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProposalDeleteContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub proposal_id: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuthenticationPath { + #[prost(bool, repeated, tag = "1")] + pub value: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MerklePath { + #[prost(message, repeated, tag = "1")] + pub authentication_paths: ::prost::alloc::vec::Vec, + #[prost(bool, repeated, tag = "2")] + pub index: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub rt: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OutputPoint { + #[prost(bytes = "vec", tag = "1")] + pub hash: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "2")] + pub index: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OutputPointInfo { + #[prost(message, repeated, tag = "1")] + pub out_points: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "2")] + pub block_num: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PedersenHash { + #[prost(bytes = "vec", tag = "1")] + pub content: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IncrementalMerkleTree { + #[prost(message, optional, tag = "1")] + pub left: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub right: ::core::option::Option, + #[prost(message, repeated, tag = "3")] + pub parents: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IncrementalMerkleVoucher { + #[prost(message, optional, tag = "1")] + pub tree: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub filled: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "3")] + pub cursor: ::core::option::Option, + #[prost(int64, tag = "4")] + pub cursor_depth: i64, + #[prost(bytes = "vec", tag = "5")] + pub rt: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "10")] + pub output_point: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IncrementalMerkleVoucherInfo { + #[prost(message, repeated, tag = "1")] + pub vouchers: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", repeated, tag = "2")] + pub paths: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SpendDescription { + #[prost(bytes = "vec", tag = "1")] + pub value_commitment: ::prost::alloc::vec::Vec, + /// merkle root + #[prost(bytes = "vec", tag = "2")] + pub anchor: ::prost::alloc::vec::Vec, + /// used for check double spend + #[prost(bytes = "vec", tag = "3")] + pub nullifier: ::prost::alloc::vec::Vec, + /// used for check spend authority signature + #[prost(bytes = "vec", tag = "4")] + pub rk: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "5")] + pub zkproof: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "6")] + pub spend_authority_signature: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReceiveDescription { + #[prost(bytes = "vec", tag = "1")] + pub value_commitment: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub note_commitment: ::prost::alloc::vec::Vec, + /// for Encryption + #[prost(bytes = "vec", tag = "3")] + pub epk: ::prost::alloc::vec::Vec, + /// Encryption for incoming, decrypt it with ivk + #[prost(bytes = "vec", tag = "4")] + pub c_enc: ::prost::alloc::vec::Vec, + /// Encryption for audit, decrypt it with ovk + #[prost(bytes = "vec", tag = "5")] + pub c_out: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "6")] + pub zkproof: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ShieldedTransferContract { + /// transparent address + #[prost(bytes = "vec", tag = "1")] + pub transparent_from_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub from_amount: i64, + #[prost(message, repeated, tag = "3")] + pub spend_description: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub receive_description: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "5")] + pub binding_signature: ::prost::alloc::vec::Vec, + /// transparent address + #[prost(bytes = "vec", tag = "6")] + pub transparent_to_address: ::prost::alloc::vec::Vec, + /// the amount to transparent to_address + #[prost(int64, tag = "7")] + pub to_amount: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SmartContract { + #[prost(bytes = "vec", tag = "1")] + pub origin_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub contract_address: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "3")] + pub abi: ::core::option::Option, + #[prost(bytes = "vec", tag = "4")] + pub bytecode: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "5")] + pub call_value: i64, + #[prost(int64, tag = "6")] + pub consume_user_resource_percent: i64, + #[prost(string, tag = "7")] + pub name: ::prost::alloc::string::String, + #[prost(int64, tag = "8")] + pub origin_energy_limit: i64, + #[prost(bytes = "vec", tag = "9")] + pub code_hash: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "10")] + pub trx_hash: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "11")] + pub version: i32, +} +/// Nested message and enum types in `SmartContract`. +pub mod smart_contract { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Abi { + #[prost(message, repeated, tag = "1")] + pub entrys: ::prost::alloc::vec::Vec, + } + /// Nested message and enum types in `ABI`. + pub mod abi { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Entry { + #[prost(bool, tag = "1")] + pub anonymous: bool, + #[prost(bool, tag = "2")] + pub constant: bool, + #[prost(string, tag = "3")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub inputs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub outputs: ::prost::alloc::vec::Vec, + #[prost(enumeration = "entry::EntryType", tag = "6")] + pub r#type: i32, + #[prost(bool, tag = "7")] + pub payable: bool, + #[prost(enumeration = "entry::StateMutabilityType", tag = "8")] + pub state_mutability: i32, + } + /// Nested message and enum types in `Entry`. + pub mod entry { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Param { + #[prost(bool, tag = "1")] + pub indexed: bool, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + /// SolidityType type = 3; + #[prost(string, tag = "3")] + pub r#type: ::prost::alloc::string::String, + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum EntryType { + UnknownEntryType = 0, + Constructor = 1, + Function = 2, + Event = 3, + Fallback = 4, + Receive = 5, + Error = 6, + } + impl EntryType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EntryType::UnknownEntryType => "UnknownEntryType", + EntryType::Constructor => "Constructor", + EntryType::Function => "Function", + EntryType::Event => "Event", + EntryType::Fallback => "Fallback", + EntryType::Receive => "Receive", + EntryType::Error => "Error", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "UnknownEntryType" => Some(Self::UnknownEntryType), + "Constructor" => Some(Self::Constructor), + "Function" => Some(Self::Function), + "Event" => Some(Self::Event), + "Fallback" => Some(Self::Fallback), + "Receive" => Some(Self::Receive), + "Error" => Some(Self::Error), + _ => None, + } + } + } + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum StateMutabilityType { + UnknownMutabilityType = 0, + Pure = 1, + View = 2, + Nonpayable = 3, + Payable = 4, + } + impl StateMutabilityType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + StateMutabilityType::UnknownMutabilityType => { + "UnknownMutabilityType" + } + StateMutabilityType::Pure => "Pure", + StateMutabilityType::View => "View", + StateMutabilityType::Nonpayable => "Nonpayable", + StateMutabilityType::Payable => "Payable", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "UnknownMutabilityType" => Some(Self::UnknownMutabilityType), + "Pure" => Some(Self::Pure), + "View" => Some(Self::View), + "Nonpayable" => Some(Self::Nonpayable), + "Payable" => Some(Self::Payable), + _ => None, + } + } + } + } + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContractState { + #[prost(int64, tag = "1")] + pub energy_usage: i64, + #[prost(int64, tag = "2")] + pub energy_factor: i64, + #[prost(int64, tag = "3")] + pub update_cycle: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateSmartContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub new_contract: ::core::option::Option, + #[prost(int64, tag = "3")] + pub call_token_value: i64, + #[prost(int64, tag = "4")] + pub token_id: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TriggerSmartContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub contract_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub call_value: i64, + #[prost(bytes = "vec", tag = "4")] + pub data: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "5")] + pub call_token_value: i64, + #[prost(int64, tag = "6")] + pub token_id: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ClearAbiContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub contract_address: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UpdateSettingContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub contract_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub consume_user_resource_percent: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UpdateEnergyLimitContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub contract_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "3")] + pub origin_energy_limit: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SmartContractDataWrapper { + #[prost(message, optional, tag = "1")] + pub smart_contract: ::core::option::Option, + #[prost(bytes = "vec", tag = "2")] + pub runtimecode: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "3")] + pub contract_state: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BuyStorageBytesContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + /// storage bytes for buy + #[prost(int64, tag = "2")] + pub bytes: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BuyStorageContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + /// trx quantity for buy storage (sun) + #[prost(int64, tag = "2")] + pub quant: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SellStorageContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub storage_bytes: i64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UpdateBrokerageContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + /// 1 mean 1% + #[prost(int32, tag = "2")] + pub brokerage: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VoteAssetContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", repeated, tag = "2")] + pub vote_address: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(bool, tag = "3")] + pub support: bool, + #[prost(int32, tag = "5")] + pub count: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WitnessCreateContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub url: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WitnessUpdateContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "12")] + pub update_url: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VoteWitnessContract { + #[prost(bytes = "vec", tag = "1")] + pub owner_address: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub votes: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "3")] + pub support: bool, +} +/// Nested message and enum types in `VoteWitnessContract`. +pub mod vote_witness_contract { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Vote { + #[prost(bytes = "vec", tag = "1")] + pub vote_address: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "2")] + pub vote_count: i64, + } +} diff --git a/packages/kos-proto/proto/klever/contracts.proto b/packages/kos/src/protos/klever/contracts.proto similarity index 97% rename from packages/kos-proto/proto/klever/contracts.proto rename to packages/kos/src/protos/klever/contracts.proto index 72ae07a..595dd83 100644 --- a/packages/kos-proto/proto/klever/contracts.proto +++ b/packages/kos/src/protos/klever/contracts.proto @@ -55,9 +55,9 @@ message AttributesInfo { // StakingInfo hold the staking structure for the KDA asset message StakingInfo { - enum InterestType { - APRI = 0; - FPRI = 1; + enum InterestType { + APRI = 0; + FPRI = 1; } InterestType Type = 1 [json_name = "Type"]; uint32 APR = 2 [json_name = "APR"]; @@ -396,16 +396,4 @@ message DepositContract { bytes ID = 2 [json_name = "ID"]; bytes CurrencyID = 3 [json_name = "CurrencyID"]; int64 Amount = 4 [json_name = "Amount"]; -} - -message SmartContract { - enum SCType { - SCInvoke = 0; - SCDeploy = 1; - } - SCType Type = 1 [json_name = "Type"]; - bytes Address = 2 [json_name = "Address"]; - // SmartContract CallValue is represented by a map of currencyID and amount - // should be limited to 50 currencies - map CallValue = 3 [json_name = "CallValue"]; -} +} \ No newline at end of file diff --git a/packages/kos/src/protos/klever/transaction.proto b/packages/kos/src/protos/klever/transaction.proto new file mode 100644 index 0000000..cb3267d --- /dev/null +++ b/packages/kos/src/protos/klever/transaction.proto @@ -0,0 +1,137 @@ +syntax = "proto3"; + +package proto; + +option go_package = "./;transaction"; + +import "google/protobuf/any.proto"; + +// TXContract available +message TXContract { + enum ContractType { + TransferContractType = 0; + CreateAssetContractType = 1; + CreateValidatorContractType = 2; + ValidatorConfigContractType = 3; + FreezeContractType = 4; + UnfreezeContractType = 5; + DelegateContractType = 6; + UndelegateContractType = 7; + WithdrawContractType = 8; + ClaimContractType = 9; + UnjailContractType = 10; + AssetTriggerContractType = 11; + SetAccountNameContractType = 12; + ProposalContractType = 13; + VoteContractType = 14; + ConfigITOContractType = 15; + SetITOPricesContractType = 16; + BuyContractType = 17; + SellContractType = 18; + CancelMarketOrderContractType = 19; + CreateMarketplaceContractType = 20; + ConfigMarketplaceContractType = 21; + UpdateAccountPermissionContractType = 22; + DepositContractType = 23; + ITOTriggerContractType = 24; + } + ContractType Type = 1 [json_name = "Type"]; + google.protobuf.Any Parameter = 2 [json_name = "Parameter"]; +} + +// Transaction holds all the data needed for a value transfer +message Transaction { + enum TXResult { + SUCCESS = 0; + FAILED = 1; + } + + enum TXResultCode { + Ok = 0; + OutOfFunds = 1; + AccountError = 2; + AssetError = 3; + ContractInvalid = 4; + ContractNotFound = 5; + FeeInvalid = 6; + ParameterInvalid = 7; + APRInvalid = 8; + AssetIDInvalid = 9; + AssetTypeInvalid = 10; + AssetCantBeMinted = 11; + AssetCantBeBurned = 12; + AssetCantBePaused = 13; + AssetCantBeDelegated = 14; + AssetOwnerCantBeChanged = 15; + AccountNotOwner = 16; + CommissionTooHigh = 17; + DelegationAmountInvalid = 18; + ProposalNotActive = 19; + ValueInvalid = 20; + AmountInvalid = 21; + BucketIDInvalid = 22; + KeyConflict = 23; + MaxDelegationAmount = 24; + InvalidPeerKey = 25; + MinKFIStakedUnreached = 26; + MaxSupplyExeeced = 27; + SaveAccountError = 28; + LoadAccountError = 29; + SameAccountError = 30; + AssetPaused = 31; + DeletegateError = 32; + WithdrawNotAvailable = 33; + ErrOverflow = 34; + SetStakingErr = 35; + SetMarketOrderErr = 36; + BalanceError = 37; + KAPPError = 38; + UnfreezeError = 39; + UndeletegateError = 40; + WithdrawError = 41; + ClaimError = 42; + BucketsExceded = 43; + AssetCantBeWiped = 44; + AssetCantAddRoles = 45; + FreezeError = 46; + ITONotActive = 47; + NFTMintStopped = 48; + RoyaltiesChangeStopped = 49; + ITOKAPPError = 50; + ITOWhiteListError = 51; + NFTMetadataChangeStopped = 52; + AlreadyExists = 53; + + Fail = 99; + } + + message KDAFee { + bytes KDA = 1 [json_name = "KDA"]; + int64 Amount = 2 [json_name = "Amount"]; + // TODO: allow spread + } + + message Raw { + uint64 Nonce = 1 [json_name = "Nonce"]; + bytes Sender = 2 [json_name = "Sender"]; + repeated TXContract Contract = 6 [json_name = "Contract"]; + int32 PermissionID = 7 [json_name = "PermissionID"]; + repeated bytes Data = 10 [json_name = "Data"]; + int64 KAppFee = 13 [json_name = "KAppFee"]; + int64 BandwidthFee = 14 [json_name = "BandwidthFee"]; + uint32 Version = 15 [json_name = "Version"]; + bytes ChainID = 16 [json_name = "ChainID"]; + KDAFee KDAFee = 17 [json_name = "KDAFee"]; + } + + message Receipt { + repeated bytes Data = 1 [json_name = "data"]; + } + + Raw RawData = 1 [json_name = "RawData"]; + repeated bytes Signature = 2 [json_name = "Signature"]; + TXResult Result = 3 [json_name = "Result"]; + TXResultCode ResultCode = 4 [json_name = "ResultCode"]; + repeated Receipt Receipts = 5 [json_name = "Receipts"]; + uint64 Block = 6 [json_name = "Block"]; +} \ No newline at end of file diff --git a/packages/kos/src/protos/klever/userAccountData.proto b/packages/kos/src/protos/klever/userAccountData.proto new file mode 100644 index 0000000..5e23dbc --- /dev/null +++ b/packages/kos/src/protos/klever/userAccountData.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; + +package proto; + +option go_package = "./;state"; + +message Key { + bytes address = 1; + int64 weight = 2; +} + +message Permission { + enum PermissionType { + Owner = 0; + User = 1; + } + int32 ID = 1; + PermissionType Type = 2; + string PermissionName = 3; + int64 Threshold = 4; + bytes Operations = 5; + repeated Key Signers = 6; +} + +message UserAccountData { + bytes Address = 1; + bytes Name = 2; + bytes RootHash = 3; + int64 Balance = 5; + int64 Allowance = 6; + uint64 Nonce = 7; + + repeated Permission Permissions = 8; +} \ No newline at end of file diff --git a/packages/kos/src/protos/mod.rs b/packages/kos/src/protos/mod.rs new file mode 100644 index 0000000..6e4a9cc --- /dev/null +++ b/packages/kos/src/protos/mod.rs @@ -0,0 +1,62 @@ +pub mod generated; +use crate::crypto::base64::{simple_base64_decode, simple_base64_encode}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use tiny_json_rs::mapper; +use tiny_json_rs::mapper::Value; +use tiny_json_rs::serializer::DecodeError; +use tiny_json_rs::serializer::{Deserialize, Serialize}; + +#[allow(dead_code)] +pub trait TypeUrl { + fn type_url() -> &'static str; +} + +// Implement serialization for `Any` +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Any { + #[prost(string, tag = "1")] + pub type_url: String, + // This field will hold the actual byte array + #[prost(bytes, tag = "2")] + pub value: Vec, +} + +impl Any { + // Encodes the value field to a base64 string + pub fn encode_to_base64(&self) -> String { + simple_base64_encode(&self.value) + } + + // Decodes a base64 string to the value field +} + +// Implement serde's Serialize for Any +// This is a simplified example and you'll likely need a more comprehensive implementation. +impl Serialize for Any { + fn serialize(&self) -> Value { + let mut object = mapper::Object::new(); + object.insert("type_url".to_string(), self.type_url.serialize()); + object.insert("value".to_string(), self.encode_to_base64().serialize()); + Value::Object(object) + } +} + +impl Deserialize for Any { + fn deserialize(value: Option<&Value>) -> Result { + let value = match value { + None => return Err(DecodeError::UnexpectedType), + Some(v) => v, + }; + + let key = value.get_value::("type_url")?; + let value = value.get_value::("value")?; + + let value = simple_base64_decode(&value) + .map_err(|_| DecodeError::ParseError("Error encoding Any".to_string()))?; + Ok(Any { + type_url: key, + value, + }) + } +} diff --git a/packages/kos/src/protos/tron b/packages/kos/src/protos/tron new file mode 160000 index 0000000..2a67893 --- /dev/null +++ b/packages/kos/src/protos/tron @@ -0,0 +1 @@ +Subproject commit 2a678934da3992b1a67f975769bbb2d31989451f