diff --git a/Cargo.lock b/Cargo.lock index c724bff..7d1420e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,6 +105,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.22.1" @@ -120,6 +126,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "base64ct" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" + [[package]] name = "bech32" version = "0.9.1" @@ -198,7 +210,62 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "blsful" +version = "3.0.0" +source = "git+https://github.com/dashpay/agora-blsful?rev=0c34a7a488a0bd1c9a9a2196e793b303ad35c900#0c34a7a488a0bd1c9a9a2196e793b303ad35c900" +dependencies = [ + "anyhow", + "blstrs_plus", + "hex", + "hkdf", + "merlin", + "pairing", + "rand", + "rand_chacha", + "rand_core", + "serde", + "serde_bare", + "sha2", + "sha3", + "subtle", + "thiserror", + "uint-zigzag", + "vsss-rs", + "zeroize", +] + +[[package]] +name = "blst" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "blstrs_plus" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a16dd4b0d6b4538e1fa0388843acb186363082713a8fc8416d802a04d013818" +dependencies = [ + "arrayref", + "blst", + "elliptic-curve", + "ff", + "group", + "pairing", + "rand_core", + "serde", + "subtle", + "zeroize", ] [[package]] @@ -257,6 +324,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "constant_time_eq" version = "0.3.1" @@ -265,9 +338,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "convert_case" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db05ffb6856bf0ecdf6367558a76a0e8a77b1713044eb92845c692100ed50190" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -278,6 +351,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -287,13 +369,26 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core", + "serdect", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] @@ -332,6 +427,7 @@ dependencies = [ "bech32", "bitvec", "blake3", + "blsful", "dash-network", "dashcore-private", "dashcore_hashes", @@ -359,6 +455,16 @@ dependencies = [ "serde", ] +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derive_more" version = "1.0.0" @@ -388,6 +494,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -473,6 +580,41 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array 0.14.7", + "group", + "hkdf", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "tap", + "zeroize", +] + +[[package]] +name = "elliptic-curve-tools" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de2b6fae800f08032a6ea32995b52925b1d451bff9d445c8ab2932323277faf" +dependencies = [ + "elliptic-curve", + "heapless", + "hex", + "multiexp", + "serde", + "zeroize", +] + [[package]] name = "env_filter" version = "0.1.4" @@ -502,12 +644,29 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "bitvec", + "rand_core", + "subtle", +] + [[package]] name = "find-msvc-tools" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "funty" version = "2.0.0" @@ -611,6 +770,18 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +dependencies = [ + "rustversion", + "serde_core", + "typenum", ] [[package]] @@ -626,6 +797,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand", + "rand_core", + "rand_xorshift", + "subtle", +] + [[package]] name = "grovedb-costs" version = "3.1.0" @@ -669,11 +859,33 @@ dependencies = [ "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "foldhash", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] [[package]] name = "heck" @@ -681,6 +893,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" @@ -702,6 +920,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -808,6 +1044,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -842,11 +1087,37 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core", + "zeroize", +] + +[[package]] +name = "multiexp" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ec2ce93a6f06ac6cae04c1da3f2a6a24fcfc1f0eb0b4e0f3d302f0df45326cb" +dependencies = [ + "ff", + "group", + "rand_core", + "rustversion", + "std-shims", + "zeroize", +] + [[package]] name = "napi" -version = "3.5.2" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e917a98ac74187a5d486604a269ed69cd7901dd4824453d5573fb051f69b1b3" +checksum = "000f205daae6646003fdc38517be6232af2b150bad4b67bdaf4c5aadb119d738" dependencies = [ "bitflags", "ctor", @@ -865,9 +1136,9 @@ checksum = "d376940fd5b723c6893cd1ee3f33abbfd86acb1cd1ec079f3ab04a2a3bc4d3b1" [[package]] name = "napi-derive" -version = "3.3.3" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258a6521951715e00568b258b8fb7a44c6087f588c371dc6b84a413f2728fdb" +checksum = "97ef4e39564b008771df9a4983e4ea6c1f7fa6ad5252347b6dbf7f347a8f689a" dependencies = [ "convert_case", "ctor", @@ -879,9 +1150,9 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "3.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c36636292fe04366a1eec028adc25bc72f4fd7cce35bdcc310499ef74fb7de" +checksum = "eb0d9a16e3fbfd6397f97e4eaafe9fbefb888a6120dd289dd12869fdb16af372" dependencies = [ "convert_case", "proc-macro2", @@ -892,9 +1163,9 @@ dependencies = [ [[package]] name = "napi-sys" -version = "3.1.1" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50ef9c1086f16aea2417c3788dbefed7591c3bccd800b827f4dfb271adff1149" +checksum = "8eb602b84d7c1edae45e50bbf1374696548f36ae179dfa667f577e384bb90c2b" dependencies = [ "libloading", ] @@ -905,6 +1176,75 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "rand", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", + "rand", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -914,6 +1254,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.7.5" @@ -948,6 +1298,15 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -960,6 +1319,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "platform-serialization" version = "2.1.3" @@ -994,6 +1363,7 @@ dependencies = [ "platform-version", "rand", "serde", + "serde_json", "thiserror", "treediff", ] @@ -1068,9 +1438,12 @@ version = "2.0.0-dev.1" dependencies = [ "dpp", "drive", + "hex", "napi", "napi-build", "napi-derive", + "serde_json", + "sha2", ] [[package]] @@ -1118,6 +1491,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + [[package]] name = "regex" version = "1.12.2" @@ -1165,6 +1547,20 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "secp256k1" version = "0.30.0" @@ -1202,6 +1598,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bare" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51c55386eed0f1ae957b091dc2ca8122f287b60c79c774cbe3d5f2b69fded660" +dependencies = [ + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -1247,6 +1652,16 @@ dependencies = [ "syn", ] +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha2" version = "0.10.9" @@ -1258,6 +1673,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1270,6 +1695,22 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "sqlparser" version = "0.38.0" @@ -1279,6 +1720,23 @@ dependencies = [ "log", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "std-shims" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "227c4f8561598188d0df96dbe749824576174bba278b5b6bb2eacff1066067d0" +dependencies = [ + "hashbrown", + "rustversion", + "spin", +] + [[package]] name = "strum" version = "0.26.3" @@ -1301,6 +1759,12 @@ dependencies = [ "syn", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.110" @@ -1338,6 +1802,15 @@ dependencies = [ "syn", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tinyvec" version = "1.10.0" @@ -1426,6 +1899,15 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "uint-zigzag" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abbf77aed65cb885a8ba07138c365879be3d9a93dce82bf6cc50feca9138ec15" +dependencies = [ + "core2", +] + [[package]] name = "unicode-ident" version = "1.0.22" @@ -1479,6 +1961,24 @@ version = "0.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7302ac74a033bf17b6e609ceec0f891ca9200d502d31f02dc7908d3d98767c9d" +[[package]] +name = "vsss-rs" +version = "5.1.0" +source = "git+https://github.com/dashpay/vsss-rs?branch=main#668f1406bf25a4b9a95cd97c9069f7a1632897c3" +dependencies = [ + "crypto-bigint", + "elliptic-curve", + "elliptic-curve-tools", + "generic-array 1.3.5", + "hex", + "num", + "rand_core", + "serde", + "sha3", + "subtle", + "zeroize", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1635,3 +2135,23 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 7918727..87a9272 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,20 @@ crate-type = ["cdylib", "lib"] [dependencies] dpp = { git = "https://github.com/owl352/platform", rev = "3f13188", default-features = false, features = [ "identity-value-conversion", - "identity-serialization" + "identity-serialization", + "state-transition-signing", + "platform-value", + "document-json-conversion" ] } drive = { git = "https://github.com/owl352/platform", rev = "3f13188", default-features = false } -napi = {version = "3.4.0", features = ["napi3"], default-features = false} -napi-derive = "3.3.0" +napi = {version = "3.8.1", features = ["napi3"], default-features = false} +napi-derive = "3.5.0" +serde_json = { version = "1.0", features = ["preserve_order"] } +hex = "0.4.3" +sha2 = "0.10.8" [build-dependencies] -napi-build = "2.2.4" +napi-build = "2.3.1" [profile.release] lto = "fat" diff --git a/package.json b/package.json index 1953022..3579ce1 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "babel": "^6.23.0", "babel-jest": "^30.2.0", "core-js": "^3.41.0", - "ferric-cli": "^0.3.9", + "ferric-cli": "0.3.9", "jest": "^30.2.0", "mocha": "^11.1.0", "node-addon-api": "^8.5.0", diff --git a/src/asset_lock_proof/chain_lock/mod.rs b/src/asset_lock_proof/chain_lock/mod.rs new file mode 100644 index 0000000..2c9fa93 --- /dev/null +++ b/src/asset_lock_proof/chain_lock/mod.rs @@ -0,0 +1,83 @@ +use dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof; +use napi::{Status, bindgen_prelude::Uint8Array}; +use napi_derive::napi; + +use crate::{asset_lock_proof::outpoint::OutPointNAPI, identifier::IdentifierNAPI}; + +#[napi(object, js_name = "ChainAssetLockProofParams")] +pub struct ChainAssetLockProofParams { + #[napi(js_name = "coreChainLockedHeight")] + pub core_chain_locked_height: u32, + #[napi(js_name = "outPoint")] + pub out_point: Uint8Array, +} + +#[napi(js_name = "ChainAssetLockProofNAPI")] +#[derive(Clone)] +pub struct ChainAssetLockProofNAPI(ChainAssetLockProof); + +impl From for ChainAssetLockProof { + fn from(chain_lock: ChainAssetLockProofNAPI) -> Self { + chain_lock.0 + } +} + +impl From for ChainAssetLockProofNAPI { + fn from(chain_lock: ChainAssetLockProof) -> Self { + ChainAssetLockProofNAPI(chain_lock) + } +} + +#[napi] +impl ChainAssetLockProofNAPI { + #[napi(constructor)] + pub fn new(core_chain_locked_height: u32, out_point: &OutPointNAPI) -> ChainAssetLockProofNAPI { + ChainAssetLockProofNAPI(ChainAssetLockProof { + core_chain_locked_height, + out_point: out_point.clone().into(), + }) + } + + #[napi(js_name = "fromRawObject")] + pub fn from_raw_value( + raw_asset_lock_proof: ChainAssetLockProofParams, + ) -> Result { + let vec_outpoint = raw_asset_lock_proof.out_point.to_vec(); + + let out_point: [u8; 36] = vec_outpoint.try_into().map_err(|_| { + napi::Error::new(Status::GenericFailure, "outPoint must be a 36 byte array") + })?; + + let rs_proof = + ChainAssetLockProof::new(raw_asset_lock_proof.core_chain_locked_height, out_point); + + Ok(ChainAssetLockProofNAPI(rs_proof)) + } + + #[napi(setter, js_name = "coreChainLockedHeight")] + pub fn set_core_chain_locked_height(&mut self, core_chain_locked_height: u32) { + self.0.core_chain_locked_height = core_chain_locked_height; + } + + #[napi(setter, js_name = "outPoint")] + pub fn set_out_point(&mut self, outpoint: &OutPointNAPI) { + self.0.out_point = outpoint.clone().into(); + } + + #[napi(getter, js_name = "coreChainLockedHeight")] + pub fn get_core_chain_locked_height(&self) -> u32 { + self.0.core_chain_locked_height + } + + #[napi(getter, js_name = "outPoint")] + pub fn get_out_point(&self) -> OutPointNAPI { + self.0.out_point.into() + } + + #[napi(js_name = "createIdentityId")] + pub fn create_identifier(&self) -> IdentifierNAPI { + let identifier = self.0.create_identifier(); + + identifier.into() + } +} diff --git a/src/asset_lock_proof/instant_lock/mod.rs b/src/asset_lock_proof/instant_lock/mod.rs new file mode 100644 index 0000000..1dbc5d4 --- /dev/null +++ b/src/asset_lock_proof/instant_lock/mod.rs @@ -0,0 +1,123 @@ +use dpp::dashcore::consensus::{deserialize, serialize}; +use dpp::dashcore::{InstantLock, Transaction}; +use dpp::identity::state_transition::asset_lock_proof::InstantAssetLockProof; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::asset_lock_proof::outpoint::OutPointNAPI; +use crate::asset_lock_proof::tx_out::TxOutNAPI; +use crate::identifier::IdentifierNAPI; +use crate::instant_lock::InstantLockNAPI; +use crate::utils::WithJsError; + +#[napi(object, js_name = "InstantAssetLockProofRAW")] +pub struct InstantAssetLockProofRAW { + #[napi(js_name = "instantLock")] + pub instant_lock: Uint8Array, + #[napi(js_name = "transaction")] + pub transaction: Uint8Array, + #[napi(js_name = "outputIndex")] + pub output_index: u32, +} + +#[derive(Clone)] +#[napi(js_name = "InstantAssetLockProofNAPI")] +pub struct InstantAssetLockProofNAPI(InstantAssetLockProof); + +impl From for InstantAssetLockProof { + fn from(proof: InstantAssetLockProofNAPI) -> Self { + proof.0 + } +} + +impl From for InstantAssetLockProofNAPI { + fn from(proof: InstantAssetLockProof) -> Self { + InstantAssetLockProofNAPI(proof) + } +} + +#[napi] +impl InstantAssetLockProofNAPI { + #[napi(constructor)] + pub fn new( + instant_lock: Uint8Array, + transaction: Uint8Array, + output_index: u32, + ) -> Result { + let instant_lock_bytes = instant_lock.to_vec(); + let transaction_bytes = transaction.to_vec(); + + let instant_lock: InstantLock = deserialize(instant_lock_bytes.as_slice()) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + let transaction: Transaction = deserialize(transaction_bytes.as_slice()) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + + Ok(InstantAssetLockProofNAPI(InstantAssetLockProof { + instant_lock, + transaction, + output_index, + })) + } + + #[napi(js_name = "fromRawObject ")] + pub fn from_object( + value: InstantAssetLockProofRAW, + ) -> Result { + InstantAssetLockProofNAPI::new(value.instant_lock, value.transaction, value.output_index) + } + + #[napi(js_name = "getOutput")] + pub fn get_output(&self) -> Option { + match self.0.output() { + Some(output) => Some(output.clone().into()), + None => None, + } + } + + #[napi(js_name = "getOutPoint")] + pub fn get_out_point(&self) -> Option { + match self.0.out_point() { + Some(output) => Some(output.clone().into()), + None => None, + } + } + + #[napi(getter, js_name = "outputIndex")] + pub fn get_output_index(&self) -> u32 { + self.0.output_index() + } + + #[napi(getter, js_name = "instantLock")] + pub fn get_instant_lock(&self) -> InstantLockNAPI { + self.0.instant_lock.clone().into() + } + + #[napi(setter, js_name = "outputIndex")] + pub fn set_output_index(&mut self, output_index: u32) { + self.0.output_index = output_index; + } + + #[napi(setter, js_name = "instantLock")] + pub fn set_instant_lock(&mut self, instant_lock: &InstantLockNAPI) { + self.0.instant_lock = instant_lock.clone().into(); + } + + #[napi(js_name=getTransaction)] + pub fn get_transaction(&self) -> Uint8Array { + let transaction = self.0.transaction(); + serialize(transaction).into() + } + + #[napi(js_name=getInstantLockBytes)] + pub fn get_instant_lock_bytes(&self) -> Uint8Array { + let instant_lock = self.0.instant_lock(); + serialize(instant_lock).into() + } + + #[napi(js_name = "createIdentityId")] + pub fn create_identifier(&self) -> Result { + let identifier = self.0.create_identifier().with_js_error()?; + + Ok(identifier.into()) + } +} diff --git a/src/asset_lock_proof/mod.rs b/src/asset_lock_proof/mod.rs new file mode 100644 index 0000000..5532879 --- /dev/null +++ b/src/asset_lock_proof/mod.rs @@ -0,0 +1,162 @@ +pub mod chain_lock; +pub mod instant_lock; +pub mod outpoint; +pub mod transaction; +pub mod tx_in; +pub mod tx_out; +pub mod witness; + +use crate::{ + asset_lock_proof::{ + chain_lock::ChainAssetLockProofNAPI, instant_lock::InstantAssetLockProofNAPI, + outpoint::OutPointNAPI, + }, + enums::lock_types::AssetLockProofTypeNAPI, + identifier::IdentifierNAPI, + utils::WithJsError, +}; +use dpp::prelude::AssetLockProof; +use napi::{Either, bindgen_prelude::Uint8Array}; +use napi_derive::napi; + +#[napi(js_name = "AssetLockProofNAPI")] +#[derive(Clone)] +pub struct AssetLockProofNAPI(AssetLockProof); + +impl From for AssetLockProof { + fn from(proof: AssetLockProofNAPI) -> Self { + proof.0 + } +} + +impl From for AssetLockProofNAPI { + fn from(proof: AssetLockProof) -> Self { + AssetLockProofNAPI(proof) + } +} + +impl From for AssetLockProofNAPI { + fn from(proof: ChainAssetLockProofNAPI) -> Self { + AssetLockProofNAPI(AssetLockProof::Chain(proof.into())) + } +} + +impl From for AssetLockProofNAPI { + fn from(proof: InstantAssetLockProofNAPI) -> Self { + AssetLockProofNAPI(AssetLockProof::Instant(proof.into())) + } +} + +impl From for ChainAssetLockProofNAPI { + fn from(proof: AssetLockProof) -> ChainAssetLockProofNAPI { + match proof { + AssetLockProof::Chain(chain) => ChainAssetLockProofNAPI::from(chain), + _ => panic!("invalid asset lock proof. must contains chain lock"), + } + } +} + +impl From for InstantAssetLockProofNAPI { + fn from(proof: AssetLockProof) -> InstantAssetLockProofNAPI { + match proof { + AssetLockProof::Instant(instant) => InstantAssetLockProofNAPI::from(instant), + _ => panic!("invalid asset lock proof. must contains chain lock"), + } + } +} + +#[napi] +impl AssetLockProofNAPI { + #[napi(constructor)] + pub fn new( + js_asset_lock_proof: Either<&ChainAssetLockProofNAPI, &InstantAssetLockProofNAPI>, + ) -> AssetLockProofNAPI { + match js_asset_lock_proof { + Either::A(chain_lock) => AssetLockProofNAPI::from(chain_lock.clone()), + Either::B(instant_lock) => AssetLockProofNAPI::from(instant_lock.clone()), + } + } + + #[napi(js_name = "createInstantAssetLockProof")] + pub fn new_instant_asset_lock_proof( + instant_lock: Uint8Array, + transaction: Uint8Array, + output_index: u32, + ) -> Result { + Ok(InstantAssetLockProofNAPI::new(instant_lock, transaction, output_index)?.into()) + } + + #[napi(js_name = "createChainAssetLockProof")] + pub fn new_chain_asset_lock_proof( + core_chain_locked_height: u32, + out_point: &OutPointNAPI, + ) -> AssetLockProofNAPI { + ChainAssetLockProofNAPI::new(core_chain_locked_height, out_point).into() + } + + #[napi(js_name = "getLockType")] + pub fn get_lock_type(&self) -> String { + match self.0 { + AssetLockProof::Chain(_) => AssetLockProofTypeNAPI::Chain.into(), + AssetLockProof::Instant(_) => AssetLockProofTypeNAPI::Instant.into(), + } + } + + #[napi(js_name = "getInstantLockProof")] + pub fn get_instant_lock(&self) -> InstantAssetLockProofNAPI { + self.clone().0.into() + } + + #[napi(js_name = "getChainLockProof")] + pub fn get_chain_lock(&self) -> ChainAssetLockProofNAPI { + self.clone().0.into() + } + + #[napi(js_name = "getOutPoint")] + pub fn get_out_point(&self) -> Option { + match self.0.out_point() { + Some(out_point) => Some(OutPointNAPI::from(out_point)), + None => None, + } + } + + #[napi(js_name = "createIdentityId")] + pub fn create_identifier(&self) -> Result { + let identifier = self.0.create_identifier().with_js_error()?; + + Ok(identifier.into()) + } + + #[napi(js_name = "hex")] + pub fn to_string(&self) -> Result { + Ok(hex::encode(serde_json::to_string(&self.0).map_err( + |err| napi::Error::new(napi::Status::GenericFailure, err.to_string()), + )?)) + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(asset_lock_proof: String) -> Result { + let asset_lock_proof_bytes = hex::decode(&asset_lock_proof).map_err(|e| { + napi::Error::new( + napi::Status::GenericFailure, + format!("Invalid asset lock proof hex: {}", e), + ) + })?; + + let json_str = String::from_utf8(asset_lock_proof_bytes).map_err(|e| { + napi::Error::new( + napi::Status::GenericFailure, + format!("Invalid UTF-8 in asset lock proof: {}", e), + ) + })?; + + let asset_lock_proof = serde_json::from_str(&json_str).map_err(|e| { + napi::Error::new( + napi::Status::GenericFailure, + format!("Failed to parse asset lock proof JSON: {}", e), + ) + })?; + + Ok(AssetLockProofNAPI(asset_lock_proof)) + } +} diff --git a/src/asset_lock_proof/outpoint/mod.rs b/src/asset_lock_proof/outpoint/mod.rs new file mode 100644 index 0000000..f5cac02 --- /dev/null +++ b/src/asset_lock_proof/outpoint/mod.rs @@ -0,0 +1,94 @@ +use dpp::dashcore::{OutPoint, Txid}; +use dpp::platform_value::string_encoding::Encoding::{Base64, Hex}; +use dpp::platform_value::string_encoding::{decode, encode}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +#[napi(js_name = "OutPointNAPI")] +#[derive(Clone)] +pub struct OutPointNAPI(OutPoint); + +impl From for OutPointNAPI { + fn from(outpoint: OutPoint) -> Self { + OutPointNAPI(outpoint) + } +} + +impl From for OutPoint { + fn from(outpoint: OutPointNAPI) -> Self { + outpoint.0 + } +} + +#[napi] +impl OutPointNAPI { + #[napi(constructor)] + pub fn new(txid_hex: String, vout: u32) -> Result { + let out_point = Txid::from_hex(&txid_hex) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + + Ok(OutPointNAPI(OutPoint { + txid: out_point, + vout, + })) + } + + #[napi(js_name = "getVOUT")] + pub fn get_vout(&self) -> u32 { + self.0.vout + } + + #[napi(js_name = "getTXID")] + pub fn get_tx_id(&self) -> String { + self.0.txid.to_hex() + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Uint8Array { + let slice: [u8; 36] = self.0.into(); + slice.to_vec().into() + } + + #[napi(js_name = "hex")] + pub fn to_hex(&self) -> String { + let slice: [u8; 36] = self.0.into(); + + encode(slice.as_slice(), Hex) + } + + #[napi(js_name = "base64")] + pub fn to_base64(&self) -> String { + let slice: [u8; 36] = self.0.into(); + + encode(slice.as_slice(), Base64) + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes(js_buffer: Uint8Array) -> OutPointNAPI { + let bytes_vec = js_buffer.to_vec(); + let mut buffer = [0u8; 36]; + let bytes = bytes_vec.as_slice(); + let len = bytes.len(); + buffer[..len].copy_from_slice(bytes); + + OutPointNAPI(OutPoint::from(buffer)) + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(hex: String) -> Result { + Ok(OutPointNAPI::from_bytes( + decode(hex.as_str(), Hex) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))? + .into(), + )) + } + + #[napi(js_name = "fromBase64")] + pub fn from_base64(base64: String) -> Result { + Ok(OutPointNAPI::from_bytes( + decode(base64.as_str(), Base64) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))? + .into(), + )) + } +} diff --git a/src/asset_lock_proof/transaction/mod.rs b/src/asset_lock_proof/transaction/mod.rs new file mode 100644 index 0000000..7c3a76c --- /dev/null +++ b/src/asset_lock_proof/transaction/mod.rs @@ -0,0 +1,96 @@ +use dpp::dashcore::Transaction; +use napi_derive::napi; + +use crate::asset_lock_proof::{tx_in::TxInNAPI, tx_out::TxOutNAPI}; + +#[napi(js_name = "TransactionNAPI")] +pub struct TransactionNAPI(Transaction); + +impl From for TransactionNAPI { + fn from(tx: Transaction) -> Self { + Self(tx) + } +} + +#[napi] +impl TransactionNAPI { + // TODO: Implement special_transaction_payload + #[napi(constructor)] + pub fn new( + version: u16, + lock_time: u32, + js_input: Vec<&TxInNAPI>, + js_output: Vec<&TxOutNAPI>, + ) -> TransactionNAPI { + TransactionNAPI(Transaction { + version, + lock_time, + input: js_input.into_iter().map(|el| el.clone().into()).collect(), + output: js_output.into_iter().map(|el| el.clone().into()).collect(), + special_transaction_payload: None, + }) + } + + #[napi(getter, js_name = "version")] + pub fn version(&self) -> u16 { + self.0.version + } + + #[napi(getter, js_name = "lockTime")] + pub fn lock_time(&self) -> u32 { + self.0.lock_time + } + + #[napi(getter, js_name = "input")] + pub fn input(&self) -> Vec { + self.0 + .input + .iter() + .map(|el| TxInNAPI::from(el.clone())) + .collect() + } + + #[napi(getter, js_name = "output")] + pub fn output(&self) -> Vec { + self.0 + .output + .iter() + .map(|el| TxOutNAPI::from(el.clone())) + .collect() + } + + #[napi(setter, js_name = "version")] + pub fn set_version(&mut self, version: u16) { + self.0.version = version + } + + #[napi(setter, js_name = "lockTime")] + pub fn set_lock_time(&mut self, lock_time: u32) { + self.0.lock_time = lock_time + } + + #[napi(setter, js_name = "input")] + pub fn set_input(&mut self, js_input: Vec<&TxInNAPI>) { + self.0.input = js_input.into_iter().map(|el| el.clone().into()).collect(); + } + + #[napi(setter, js_name = "output")] + pub fn set_output(&mut self, js_output: Vec<&TxOutNAPI>) { + self.0.output = js_output.into_iter().map(|el| el.clone().into()).collect(); + } + + #[napi(js_name = "isCoinBase")] + pub fn is_coin_base(&self) -> bool { + self.0.is_coin_base() + } + + #[napi(js_name = "getTxType")] + pub fn get_tx_type(&self) -> String { + self.0.tx_type().to_string() + } + + #[napi(js_name = "getTxId")] + pub fn get_tx_id(&self) -> String { + self.0.txid().to_hex() + } +} diff --git a/src/asset_lock_proof/tx_in/mod.rs b/src/asset_lock_proof/tx_in/mod.rs new file mode 100644 index 0000000..f985872 --- /dev/null +++ b/src/asset_lock_proof/tx_in/mod.rs @@ -0,0 +1,78 @@ +use crate::asset_lock_proof::{outpoint::OutPointNAPI, witness::WitnessNAPI}; +use dpp::dashcore::{ScriptBuf, TxIn}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +#[derive(Clone)] +#[napi(js_name = "TxInNAPI")] +pub struct TxInNAPI(TxIn); + +impl From for TxInNAPI { + fn from(tx_in: TxIn) -> Self { + Self(tx_in) + } +} + +impl From for TxIn { + fn from(tx_in: TxInNAPI) -> Self { + tx_in.0 + } +} + +#[napi] +impl TxInNAPI { + #[napi(constructor)] + pub fn new( + previous_output: &OutPointNAPI, + script_sig: Uint8Array, + sequence: u32, + witness: &WitnessNAPI, + ) -> Self { + TxInNAPI(TxIn { + previous_output: previous_output.clone().into(), + script_sig: ScriptBuf(script_sig.to_vec()), + sequence, + witness: witness.clone().into(), + }) + } + + #[napi(getter, js_name = "previousOutput")] + pub fn previous_output(&self) -> OutPointNAPI { + self.0.previous_output.clone().into() + } + + #[napi(getter, js_name = "scriptSig")] + pub fn script_sig(&self) -> Uint8Array { + self.0.script_sig.clone().to_bytes().into() + } + + #[napi(getter, js_name = "sequence")] + pub fn sequence(&self) -> u32 { + self.0.sequence + } + + #[napi(getter, js_name = "witnesses")] + pub fn witness(&self) -> WitnessNAPI { + self.0.witness.clone().into() + } + + #[napi(setter, js_name = "previousOutput")] + pub fn set_previous_output(&mut self, previous_output: &OutPointNAPI) { + self.0.previous_output = previous_output.clone().into(); + } + + #[napi(setter, js_name = "scriptSig")] + pub fn set_script_sig(&mut self, script_sig: Uint8Array) { + self.0.script_sig = ScriptBuf(script_sig.to_vec()) + } + + #[napi(setter, js_name = "sequence")] + pub fn set_sequence(&mut self, sequence: u32) { + self.0.sequence = sequence + } + + #[napi(setter, js_name = "witnesses")] + pub fn set_witness(&mut self, witness: &WitnessNAPI) { + self.0.witness = witness.clone().into() + } +} diff --git a/src/asset_lock_proof/tx_out/mod.rs b/src/asset_lock_proof/tx_out/mod.rs new file mode 100644 index 0000000..4e25faa --- /dev/null +++ b/src/asset_lock_proof/tx_out/mod.rs @@ -0,0 +1,90 @@ +use dpp::dashcore::{ScriptBuf, TxOut}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::dynamic_value::DynamicValue; + +#[napi(js_name = "TxOutNAPI")] +#[derive(Clone)] +pub struct TxOutNAPI(TxOut); + +impl From for TxOutNAPI { + fn from(value: TxOut) -> Self { + TxOutNAPI(value) + } +} + +impl From for TxOut { + fn from(value: TxOutNAPI) -> Self { + value.0 + } +} + +#[napi] +impl TxOutNAPI { + #[napi(constructor)] + pub fn new( + js_value: DynamicValue, + script_pubkey: DynamicValue, + ) -> Result { + let value: u64 = js_value.try_into()?; + + let tx_out: TxOut = match script_pubkey { + DynamicValue::Bytes(script_pubkey) => Ok(TxOut { + value, + script_pubkey: ScriptBuf::from_bytes(script_pubkey.to_vec()), + }), + DynamicValue::Text(script_pubkey) => Ok(TxOut { + value, + script_pubkey: ScriptBuf::from_hex(&script_pubkey).map_err(|err| { + napi::Error::new(napi::Status::GenericFailure, err.to_string()) + })?, + }), + _ => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot parse script pub key", + )), + }?; + + Ok(TxOutNAPI(tx_out)) + } + + #[napi(getter, js_name = "value")] + pub fn get_value(&self) -> DynamicValue { + DynamicValue::Uint64(self.0.value.into()) + } + + #[napi(getter, js_name = "scriptPubKeyHex")] + pub fn get_script_pubkey_hex(&self) -> String { + self.0.script_pubkey.to_hex_string() + } + + #[napi(getter, js_name = "scriptPubKeyBytes")] + pub fn get_script_pubkey_bytes(&self) -> Uint8Array { + self.0.script_pubkey.to_bytes().into() + } + + #[napi(setter, js_name = "value")] + pub fn set_value(&mut self, value: DynamicValue) -> Result<(), napi::Error> { + self.0.value = value.try_into()?; + + Ok(()) + } + + #[napi(setter, js_name = "scriptPubKeyHex")] + pub fn set_script_pubkey_hex(&mut self, script_pubkey_hex: String) -> Result<(), napi::Error> { + self.0.script_pubkey = ScriptBuf::from_hex(&script_pubkey_hex) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; + Ok(()) + } + + #[napi(setter, js_name = "scriptPubKeyBytes")] + pub fn set_script_pubkey_bytes(&mut self, script_pubkey_bytes: Uint8Array) { + self.0.script_pubkey = ScriptBuf::from_bytes(script_pubkey_bytes.to_vec()); + } + + #[napi(js_name = "getScriptPubKeyASM")] + pub fn get_script_pubkey_asm(&self) -> String { + self.0.script_pubkey.to_asm_string() + } +} diff --git a/src/asset_lock_proof/witness/mod.rs b/src/asset_lock_proof/witness/mod.rs new file mode 100644 index 0000000..cc10ba5 --- /dev/null +++ b/src/asset_lock_proof/witness/mod.rs @@ -0,0 +1,59 @@ +use dpp::dashcore::Witness; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +#[derive(Clone)] +#[napi(js_name = "WitnessNAPI")] +pub struct WitnessNAPI(Witness); + +impl From for WitnessNAPI { + fn from(witness: Witness) -> Self { + WitnessNAPI(witness) + } +} + +impl From for Witness { + fn from(witness: WitnessNAPI) -> Self { + witness.0 + } +} + +#[napi] +impl WitnessNAPI { + #[napi(constructor)] + pub fn new(optional_bytes: Option>) -> Self { + WitnessNAPI(match optional_bytes { + None => Witness::new(), + Some(bytes) => Witness::from( + bytes + .iter() + .map(|v| v.to_vec().clone()) + .collect::>>(), + ), + }) + } + + #[napi(js_name = "getBytes")] + pub fn get_bytes(&self) -> Vec { + self.0 + .to_vec() + .iter() + .map(|v| Uint8Array::from(v.as_slice())) + .collect() + } + + #[napi(js_name = "isEmpty")] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + #[napi(js_name = "clear")] + pub fn clear(&mut self) { + self.0.clear(); + } + + #[napi(js_name = "push")] + pub fn push(&mut self, new_element: Uint8Array) { + self.0.push(new_element.to_vec()) + } +} diff --git a/src/consensus_error/mod.rs b/src/consensus_error/mod.rs new file mode 100644 index 0000000..0545057 --- /dev/null +++ b/src/consensus_error/mod.rs @@ -0,0 +1,25 @@ +use dpp::{consensus::ConsensusError, serialization::PlatformDeserializable}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::utils::WithJsError; + +#[napi(js_name = "ConsensusErrorNAPI")] +pub struct ConsensusErrorNAPI(ConsensusError); + +#[napi] +impl ConsensusErrorNAPI { + #[napi(js_name = "deserialize")] + pub fn deserialize(error: Uint8Array) -> Result { + let error_bytes = error.to_vec(); + + Ok(ConsensusErrorNAPI( + ConsensusError::deserialize_from_bytes(error_bytes.as_slice()).with_js_error()?, + )) + } + + #[napi(getter, js_name = "message")] + pub fn message(&self) -> String { + self.0.to_string() + } +} diff --git a/src/contract_bounds/mod.rs b/src/contract_bounds/mod.rs new file mode 100644 index 0000000..f25b7c3 --- /dev/null +++ b/src/contract_bounds/mod.rs @@ -0,0 +1,123 @@ +use crate::{dynamic_value::IdentifierLikeNAPI, identifier::IdentifierNAPI}; +use dpp::identity::contract_bounds::ContractBounds; +use napi_derive::napi; + +#[derive(Clone)] +#[napi(js_name = "ContractBoundsNAPI")] +pub struct ContractBoundsNAPI(ContractBounds); + +impl From for ContractBoundsNAPI { + fn from(value: ContractBounds) -> Self { + ContractBoundsNAPI(value) + } +} + +impl From for ContractBounds { + fn from(value: ContractBoundsNAPI) -> Self { + value.0 + } +} + +#[napi] +impl ContractBoundsNAPI { + #[napi(constructor)] + pub fn new( + js_contract_id: IdentifierLikeNAPI, + document_type_name: Option, + ) -> Result { + let contract_id: IdentifierNAPI = js_contract_id.try_into()?; + + let rs_contract_bounds = match document_type_name { + Some(document_type_name) => ContractBounds::SingleContractDocumentType { + id: contract_id.into(), + document_type_name, + }, + None => ContractBounds::SingleContract { + id: contract_id.into(), + }, + }; + + Ok(ContractBoundsNAPI(rs_contract_bounds)) + } + + #[napi(js_name = "SingleContract")] + pub fn single_contract( + js_contract_id: IdentifierLikeNAPI, + ) -> Result { + let contract_id: IdentifierNAPI = js_contract_id.try_into()?; + + Ok(ContractBoundsNAPI(ContractBounds::SingleContract { + id: contract_id.try_into()?, + })) + } + + #[napi(js_name = "SingleContractDocumentType")] + pub fn single_contract_document_type_name( + js_contract_id: IdentifierLikeNAPI, + document_type_name: String, + ) -> Result { + let contract_id: IdentifierNAPI = js_contract_id.try_into()?; + + Ok(ContractBoundsNAPI( + ContractBounds::SingleContractDocumentType { + id: contract_id.into(), + document_type_name, + }, + )) + } + + #[napi(getter, js_name = "identifier")] + pub fn id(&self) -> IdentifierNAPI { + self.0.identifier().into() + } + + #[napi(getter, js_name = "documentTypeName")] + pub fn document_type_name(&self) -> Option { + match self.0.document_type() { + Some(name) => Some(name.clone()), + None => None, + } + } + + #[napi(getter, js_name = "contractBoundsType")] + pub fn contract_bounds_type(&self) -> String { + self.0.contract_bounds_type_string().into() + } + + #[napi(getter, js_name = "contractBoundsTypeNumber")] + pub fn contract_bounds_type_number(&self) -> u8 { + self.0.contract_bounds_type() + } + + #[napi(setter, js_name = "identifier")] + pub fn set_id(&mut self, js_contract_id: IdentifierLikeNAPI) -> Result<(), napi::Error> { + let contract_id: IdentifierNAPI = js_contract_id.try_into()?; + + self.0 = match self.clone().0 { + ContractBounds::SingleContract { .. } => ContractBounds::SingleContract { + id: contract_id.into(), + }, + ContractBounds::SingleContractDocumentType { + document_type_name, .. + } => ContractBounds::SingleContractDocumentType { + id: contract_id.into(), + document_type_name, + }, + }; + + Ok(()) + } + + #[napi(setter, js_name = "documentTypeName")] + pub fn set_document_type_name(&mut self, document_type_name: String) { + self.0 = match self.clone().0 { + ContractBounds::SingleContract { .. } => self.clone().0, + ContractBounds::SingleContractDocumentType { id, .. } => { + ContractBounds::SingleContractDocumentType { + id, + document_type_name, + } + } + } + } +} diff --git a/src/core_script/mod.rs b/src/core_script/mod.rs new file mode 100644 index 0000000..0491c4f --- /dev/null +++ b/src/core_script/mod.rs @@ -0,0 +1,100 @@ +use dpp::dashcore::address::Payload; +use dpp::dashcore::{Address, opcodes}; +use dpp::identity::core_script::CoreScript; +use dpp::platform_value::string_encoding::Encoding::{Base64, Hex}; +use dpp::platform_value::string_encoding::encode; +use napi::Status; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::enums::network::NetworkNAPI; + +#[napi(js_name = "CoreScriptNAPI")] +#[derive(Clone)] +pub struct CoreScriptNAPI(CoreScript); + +impl From for CoreScript { + fn from(value: CoreScriptNAPI) -> Self { + value.0 + } +} + +impl From for CoreScriptNAPI { + fn from(value: CoreScript) -> Self { + CoreScriptNAPI(value) + } +} + +#[napi] +impl CoreScriptNAPI { + #[napi(js_name = "fromBytes")] + pub fn from_bytes(bytes: Uint8Array) -> Self { + CoreScriptNAPI(CoreScript::from_bytes(bytes.to_vec())) + } + + #[napi(js_name = "newP2PKH")] + pub fn new_p2pkh(js_key_hash: Uint8Array) -> Self { + let js_key_hash_vec = js_key_hash.to_vec(); + + let mut key_hash = [0u8; 20]; + let bytes = js_key_hash_vec.as_slice(); + let len = bytes.len().min(32); + key_hash[..len].copy_from_slice(&bytes[..len]); + + CoreScriptNAPI(CoreScript::new_p2pkh(key_hash)) + } + + #[napi(js_name = "newP2SH")] + pub fn new_p2sh(js_script_hash: Uint8Array) -> Self { + let js_script_hash_vec = js_script_hash.to_vec(); + + let mut script_hash = [0u8; 20]; + let bytes = js_script_hash_vec.as_slice(); + let len = bytes.len().min(32); + script_hash[..len].copy_from_slice(&bytes[..len]); + + let mut bytes = vec![ + opcodes::all::OP_HASH160.to_u8(), + opcodes::all::OP_PUSHBYTES_20.to_u8(), + ]; + bytes.extend_from_slice(&script_hash); + bytes.push(opcodes::all::OP_EQUAL.to_u8()); + + Self::from_bytes(bytes.into()) + } + + #[napi(js_name = "toAddress")] + pub fn to_address(&self, network: NetworkNAPI) -> Result { + let payload = Payload::from_script(self.0.as_script()) + .map_err(|e| napi::Error::new(Status::GenericFailure, e.to_string()))?; + + let address = Address::new(network.into(), payload); + + Ok(address.to_string()) + } + + #[napi(js_name = "toString")] + pub fn to_string(&self) -> String { + self.0.to_string(Base64) + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Uint8Array { + self.0.to_bytes().into() + } + + #[napi(js_name = "hex")] + pub fn to_hex(&self) -> String { + encode(self.0.to_bytes().as_slice(), Hex) + } + + #[napi(js_name = "base64")] + pub fn to_base64(&self) -> String { + encode(self.0.to_bytes().as_slice(), Base64) + } + + #[napi(js_name = "ASMString")] + pub fn to_asm_string(&self) -> String { + self.0.to_asm_string() + } +} diff --git a/src/dynamic_value/mod.rs b/src/dynamic_value/mod.rs index 2421287..bff7c36 100644 --- a/src/dynamic_value/mod.rs +++ b/src/dynamic_value/mod.rs @@ -1,9 +1,14 @@ use napi::{ - Status, + Either, Status, bindgen_prelude::{Null, Uint8Array}, }; use napi_derive::napi; +use crate::identifier::IdentifierNAPI; + +#[napi(js_name = "IdentifierLikeNAPI")] +pub type IdentifierLikeNAPI<'a> = Either<&'a IdentifierNAPI, DynamicValue>; + pub trait TypeChecker { fn is_null(&self) -> bool; } @@ -28,6 +33,14 @@ impl TryToU64 for Uint64String { } } +impl TryFrom for u64 { + type Error = napi::Error; + + fn try_from(value: Uint64String) -> Result { + value.try_to_u64() + } +} + impl From for Uint64String { fn from(value: u64) -> Self { Uint64String { @@ -58,3 +71,17 @@ impl TypeChecker for DynamicValue { } } } + +impl TryFrom for u64 { + type Error = napi::Error; + + fn try_from(value: DynamicValue) -> Result { + match value { + DynamicValue::Uint64(val) => val.try_to_u64(), + _ => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot parse script pub key", + )), + } + } +} diff --git a/src/encrypted_note/mod.rs b/src/encrypted_note/mod.rs new file mode 100644 index 0000000..083b5fc --- /dev/null +++ b/src/encrypted_note/mod.rs @@ -0,0 +1,2 @@ +pub mod private_encrypted_note; +pub mod shared_encrypted_note; diff --git a/src/encrypted_note/private_encrypted_note/mod.rs b/src/encrypted_note/private_encrypted_note/mod.rs new file mode 100644 index 0000000..99f1aea --- /dev/null +++ b/src/encrypted_note/private_encrypted_note/mod.rs @@ -0,0 +1,65 @@ +use dpp::tokens::PrivateEncryptedNote; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +#[derive(Debug, Clone, PartialEq)] +#[napi(js_name = "PrivateEncryptedNoteNAPI")] +pub struct PrivateEncryptedNoteNAPI(PrivateEncryptedNote); + +impl From for PrivateEncryptedNoteNAPI { + fn from(value: PrivateEncryptedNote) -> Self { + PrivateEncryptedNoteNAPI(value) + } +} + +impl From for PrivateEncryptedNote { + fn from(value: PrivateEncryptedNoteNAPI) -> Self { + value.0 + } +} + +#[napi] +impl PrivateEncryptedNoteNAPI { + #[napi(constructor)] + pub fn new( + root_encryption_key_index: u32, + derivation_encryption_key_index: u32, + value: Uint8Array, + ) -> PrivateEncryptedNoteNAPI { + PrivateEncryptedNoteNAPI(( + root_encryption_key_index, + derivation_encryption_key_index, + value.to_vec(), + )) + } + + #[napi(getter, js_name = "rootEncryptionKeyIndex")] + pub fn root_encryption_key_index(&self) -> u32 { + self.0.0 + } + + #[napi(getter, js_name = "derivationEncryptionKeyIndex")] + pub fn derivation_encryption_key_index(&self) -> u32 { + self.0.1 + } + + #[napi(getter, js_name = "value")] + pub fn value(&self) -> Uint8Array { + self.0.2.clone().into() + } + + #[napi(setter, js_name = "rootEncryptionKeyIndex")] + pub fn set_root_encryption_key_index(&mut self, index: u32) { + self.0.0 = index; + } + + #[napi(setter, js_name = "derivationEncryptionKeyIndex")] + pub fn set_derivation_encryption_key_index(&mut self, index: u32) { + self.0.1 = index; + } + + #[napi(setter, js_name = "value")] + pub fn set_value(&mut self, value: Uint8Array) { + self.0.2 = value.to_vec(); + } +} diff --git a/src/encrypted_note/shared_encrypted_note/mod.rs b/src/encrypted_note/shared_encrypted_note/mod.rs new file mode 100644 index 0000000..6f7973e --- /dev/null +++ b/src/encrypted_note/shared_encrypted_note/mod.rs @@ -0,0 +1,57 @@ +use dpp::tokens::SharedEncryptedNote; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +#[derive(Debug, Clone, PartialEq)] +#[napi(js_name = "SharedEncryptedNoteNAPI")] +pub struct SharedEncryptedNoteNAPI(SharedEncryptedNote); + +impl From for SharedEncryptedNoteNAPI { + fn from(note: SharedEncryptedNote) -> Self { + Self(note) + } +} + +impl From for SharedEncryptedNote { + fn from(note: SharedEncryptedNoteNAPI) -> Self { + note.0 + } +} + +#[napi] +impl SharedEncryptedNoteNAPI { + #[napi(constructor)] + pub fn new(sender_key_index: u32, recipient_key_index: u32, value: Uint8Array) -> Self { + SharedEncryptedNoteNAPI((sender_key_index, recipient_key_index, value.to_vec())) + } + + #[napi(getter, js_name = "senderKeyIndex")] + pub fn sender_key_index(&self) -> u32 { + self.0.0 + } + + #[napi(getter, js_name = "recipientKeyIndex")] + pub fn recipient_key_index(&self) -> u32 { + self.0.1 + } + + #[napi(getter, js_name = "value")] + pub fn value(&self) -> Uint8Array { + self.0.2.clone().into() + } + + #[napi(setter, js_name = "senderKeyIndex")] + pub fn set_sender_key_index(&mut self, index: u32) { + self.0.0 = index; + } + + #[napi(setter, js_name = "recipientKeyIndex")] + pub fn set_recipient_key_index(&mut self, index: u32) { + self.0.1 = index; + } + + #[napi(setter, js_name = "value")] + pub fn set_value(&mut self, value: Uint8Array) { + self.0.2 = value.to_vec(); + } +} diff --git a/src/enums/lock_types.rs b/src/enums/lock_types.rs new file mode 100644 index 0000000..d9ac375 --- /dev/null +++ b/src/enums/lock_types.rs @@ -0,0 +1,83 @@ +use napi::Status; +use napi_derive::napi; + +use crate::dynamic_value::{DynamicValue, TryToU64}; + +#[napi(js_name = "AssetLockProofTypeNAPI")] +pub enum AssetLockProofTypeNAPI { + Instant = 0, + Chain = 1, +} + +impl From for String { + fn from(value: AssetLockProofTypeNAPI) -> Self { + match value { + AssetLockProofTypeNAPI::Instant => String::from("Instant"), + AssetLockProofTypeNAPI::Chain => String::from("Chain"), + } + } +} + +impl TryFrom for AssetLockProofTypeNAPI { + type Error = napi::Error; + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Self::Instant), + 1 => Ok(Self::Chain), + _ => Err(napi::Error::new( + Status::GenericFailure, + "Unexpected asset lock proof type", + )), + } + } +} + +impl TryFrom for AssetLockProofTypeNAPI { + type Error = napi::Error; + + fn try_from(value: u64) -> Result { + match value { + 0 => Ok(Self::Instant), + 1 => Ok(Self::Chain), + _ => Err(napi::Error::new( + Status::GenericFailure, + "Unexpected asset lock proof type", + )), + } + } +} + +impl TryFrom for AssetLockProofTypeNAPI { + type Error = napi::Error; + + fn try_from(value: String) -> Result { + match value.to_lowercase().as_str() { + "instant" => Ok(Self::Instant), + "chain" => Ok(Self::Chain), + _ => Err(napi::Error::new( + Status::GenericFailure, + "Unexpected asset lock proof type", + )), + } + } +} + +impl TryFrom for AssetLockProofTypeNAPI { + type Error = napi::Error; + + fn try_from(value: DynamicValue) -> Result { + match value { + DynamicValue::Text(str) => AssetLockProofTypeNAPI::try_from(str), + DynamicValue::Uint8(num) => AssetLockProofTypeNAPI::try_from(num), + DynamicValue::Uint16(num) => AssetLockProofTypeNAPI::try_from(num as u8), + DynamicValue::Uint32(num) => AssetLockProofTypeNAPI::try_from(num as u8), + DynamicValue::Uint64(num_str) => { + AssetLockProofTypeNAPI::try_from(num_str.try_to_u64()? as u8) + } + _ => Err(napi::Error::new( + Status::InvalidArg, + "Invalid key type value", + )), + } + } +} diff --git a/src/enums/mod.rs b/src/enums/mod.rs index 590a23b..6254763 100644 --- a/src/enums/mod.rs +++ b/src/enums/mod.rs @@ -1,5 +1,7 @@ pub mod key_type; +pub mod lock_types; pub mod network; pub mod platform_version; +pub mod pooling; pub mod purpose; pub mod security_level; diff --git a/src/enums/network.rs b/src/enums/network.rs index 61c6790..c9468ce 100644 --- a/src/enums/network.rs +++ b/src/enums/network.rs @@ -6,6 +6,7 @@ use crate::dynamic_value::{DynamicValue, TryToU64}; #[napi(js_name = "NetworkNAPI")] #[allow(non_camel_case_types)] +#[derive(Clone)] pub enum NetworkNAPI { Mainnet = 0, Testnet = 1, @@ -13,6 +14,18 @@ pub enum NetworkNAPI { Regtest = 3, } +impl From for NetworkNAPI { + fn from(value: Network) -> Self { + match value { + Network::Dash => NetworkNAPI::Mainnet, + Network::Devnet => NetworkNAPI::Devnet, + Network::Testnet => NetworkNAPI::Testnet, + Network::Regtest => NetworkNAPI::Regtest, + _ => NetworkNAPI::Testnet, + } + } +} + impl From for String { fn from(value: NetworkNAPI) -> Self { match value { diff --git a/src/enums/pooling.rs b/src/enums/pooling.rs new file mode 100644 index 0000000..f1f9739 --- /dev/null +++ b/src/enums/pooling.rs @@ -0,0 +1,92 @@ +use dpp::withdrawal::Pooling; +use napi::Status; +use napi_derive::napi; + +use crate::dynamic_value::{DynamicValue, TryToU64}; + +#[napi] +pub enum PoolingNAPI { + Never = 0, + IfAvailable = 1, + Standard = 2, +} + +impl From for PoolingNAPI { + fn from(value: Pooling) -> Self { + match value { + Pooling::IfAvailable => PoolingNAPI::IfAvailable, + Pooling::Standard => PoolingNAPI::Standard, + Pooling::Never => PoolingNAPI::Never, + } + } +} + +impl From for String { + fn from(value: PoolingNAPI) -> Self { + match value { + PoolingNAPI::Never => "Never".to_string(), + PoolingNAPI::IfAvailable => "IfAvailable".to_string(), + PoolingNAPI::Standard => "Standard".to_string(), + } + } +} + +impl From for Pooling { + fn from(network: PoolingNAPI) -> Self { + match network { + PoolingNAPI::Never => Pooling::Never, + PoolingNAPI::IfAvailable => Pooling::IfAvailable, + PoolingNAPI::Standard => Pooling::Standard, + } + } +} + +impl TryFrom for PoolingNAPI { + type Error = napi::Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(PoolingNAPI::Never), + 1 => Ok(PoolingNAPI::IfAvailable), + 2 => Ok(PoolingNAPI::Standard), + _ => Err(napi::Error::new( + Status::InvalidArg, + "Invalid pooling value", + )), + } + } +} + +impl TryFrom for PoolingNAPI { + type Error = napi::Error; + + fn try_from(value: String) -> Result { + match value.to_lowercase().as_str() { + "never" => Ok(PoolingNAPI::Never), + "ifavailable" => Ok(PoolingNAPI::IfAvailable), + "standard" => Ok(PoolingNAPI::Standard), + _ => Err(napi::Error::new( + Status::InvalidArg, + "Invalid pooling value", + )), + } + } +} + +impl TryFrom for PoolingNAPI { + type Error = napi::Error; + + fn try_from(value: DynamicValue) -> Result { + match value { + DynamicValue::Text(str) => PoolingNAPI::try_from(str), + DynamicValue::Uint8(num) => PoolingNAPI::try_from(num), + DynamicValue::Uint16(num) => PoolingNAPI::try_from(num as u8), + DynamicValue::Uint32(num) => PoolingNAPI::try_from(num as u8), + DynamicValue::Uint64(num_str) => PoolingNAPI::try_from(num_str.try_to_u64()? as u8), + _ => Err(napi::Error::new( + Status::InvalidArg, + "Invalid pooling value", + )), + } + } +} diff --git a/src/group_state_transition_info/mod.rs b/src/group_state_transition_info/mod.rs new file mode 100644 index 0000000..c40c8fe --- /dev/null +++ b/src/group_state_transition_info/mod.rs @@ -0,0 +1,67 @@ +use dpp::group::GroupStateTransitionInfo; +use napi_derive::napi; + +use crate::{dynamic_value::IdentifierLikeNAPI, identifier::IdentifierNAPI}; + +#[derive(Debug, Clone, PartialEq)] +#[napi(js_name=GroupStateTransitionInfoNAPI)] +pub struct GroupStateTransitionInfoNAPI(GroupStateTransitionInfo); + +impl From for GroupStateTransitionInfo { + fn from(info: GroupStateTransitionInfoNAPI) -> Self { + info.0 + } +} + +impl From for GroupStateTransitionInfoNAPI { + fn from(info: GroupStateTransitionInfo) -> Self { + GroupStateTransitionInfoNAPI(info) + } +} + +#[napi] +impl GroupStateTransitionInfoNAPI { + #[napi(constructor)] + pub fn new( + group_contract_position: u16, + action_id: IdentifierLikeNAPI, + action_is_proposer: bool, + ) -> Result { + Ok(GroupStateTransitionInfoNAPI(GroupStateTransitionInfo { + group_contract_position, + action_id: IdentifierNAPI::try_from(action_id)?.into(), + action_is_proposer, + })) + } + + #[napi(setter, js_name = "groupContractPosition")] + pub fn set_group_contract_position(&mut self, group_contract_position: u16) { + self.0.group_contract_position = group_contract_position; + } + + #[napi(setter, js_name = "actionId")] + pub fn set_action_id(&mut self, action_id: IdentifierLikeNAPI) -> Result<(), napi::Error> { + self.0.action_id = IdentifierNAPI::try_from(action_id)?.into(); + Ok(()) + } + + #[napi(setter, js_name = "actionIsProposer")] + pub fn set_action_is_proposer(&mut self, action_is_proposer: bool) { + self.0.action_is_proposer = action_is_proposer; + } + + #[napi(getter, js_name = "groupContractPosition")] + pub fn get_group_contract_position(&mut self) -> u16 { + self.0.group_contract_position + } + + #[napi(getter, js_name = "actionId")] + pub fn get_action_id(&self) -> IdentifierNAPI { + self.0.action_id.into() + } + + #[napi(getter, js_name = "actionIsProposer")] + pub fn get_action_is_proposer(&self) -> bool { + self.0.action_is_proposer + } +} diff --git a/src/identifier/mod.rs b/src/identifier/mod.rs index 5dae89d..4220ce5 100644 --- a/src/identifier/mod.rs +++ b/src/identifier/mod.rs @@ -1,24 +1,59 @@ use crate::dynamic_value::DynamicValue; use dpp::identifier::Identifier; use dpp::platform_value::string_encoding::Encoding; +use napi::Either; use napi::bindgen_prelude::Uint8Array; use napi_derive::napi; #[derive(Clone)] #[napi(js_name = "IdentifierNAPI")] -pub struct IdentifierNAPI { - id: Identifier, -} +pub struct IdentifierNAPI(Identifier); impl From for IdentifierNAPI { fn from(id: Identifier) -> Self { - IdentifierNAPI { id } + IdentifierNAPI(id) + } +} + +impl From<&Identifier> for IdentifierNAPI { + fn from(id: &Identifier) -> Self { + IdentifierNAPI(id.clone()) } } impl From for Identifier { fn from(value: IdentifierNAPI) -> Self { - Identifier::from(value.id) + Identifier::from(value.0) + } +} + +impl From<&IdentifierNAPI> for Identifier { + fn from(value: &IdentifierNAPI) -> Self { + Identifier::from(value.0.clone()) + } +} + +impl TryFrom> for IdentifierNAPI { + type Error = napi::Error; + + fn try_from(value: Either<&IdentifierNAPI, DynamicValue>) -> Result { + match value { + Either::A(id) => Ok(id.clone()), + Either::B(dyn_val) => match dyn_val { + DynamicValue::Text(txt) => { + if txt.len() == 64 { + return IdentifierNAPI::from_hex(txt); + } else { + return IdentifierNAPI::from_base58(txt); + } + } + DynamicValue::Bytes(uint8_array) => IdentifierNAPI::from_bytes(uint8_array), + _ => Err(napi::Error::new( + napi::Status::InvalidArg, + "Invalid Identifier value", + )), + }, + } } } @@ -27,41 +62,44 @@ impl IdentifierNAPI { #[napi(constructor)] pub fn new(js_id: DynamicValue) -> Result { match js_id { - DynamicValue::Text(str) => Ok(IdentifierNAPI { - id: Identifier::from_string(str.as_str(), Encoding::Base58).map_err(|err| { - napi::Error::new(napi::Status::GenericFailure, err.to_string()) - })?, - }), - DynamicValue::Bytes(bytes) => Ok(IdentifierNAPI { - id: Identifier::from_vec(bytes.to_vec()).map_err(|err| { + DynamicValue::Text(str) => Ok(IdentifierNAPI( + Identifier::from_string(str.as_str(), Encoding::Base58) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))? + .clone(), + )), + DynamicValue::Bytes(bytes) => Ok(IdentifierNAPI( + Identifier::from_vec(bytes.to_vec()).map_err(|err| { napi::Error::new(napi::Status::GenericFailure, err.to_string()) })?, - }), + )), _ => Err(napi::Error::new( napi::Status::GenericFailure, "Bad identifier type", ))?, } } +} - #[napi(js_name = base58)] +#[napi] +impl IdentifierNAPI { + #[napi(js_name = "base58")] pub fn base58(&self) -> String { - self.id.to_string(Encoding::Base58) + self.0.to_string(Encoding::Base58) } - #[napi(js_name = hex)] + #[napi(js_name = "hex")] pub fn hex(&self) -> String { - self.id.to_string(Encoding::Hex) + self.0.to_string(Encoding::Hex) } - #[napi(js_name = base64)] + #[napi(js_name = "base64")] pub fn base64(&self) -> String { - self.id.to_string(Encoding::Base64) + self.0.to_string(Encoding::Base64) } - #[napi(js_name = bytes)] + #[napi(js_name = "bytes")] pub fn bytes(&self) -> Uint8Array { - self.id.to_vec().into() + self.0.to_vec().into() } #[napi(js_name = "fromBase58")] @@ -69,7 +107,7 @@ impl IdentifierNAPI { let identitfier = Identifier::from_string(base58.as_str(), Encoding::Base58) .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; - Ok(IdentifierNAPI { id: identitfier }) + Ok(IdentifierNAPI(identitfier)) } #[napi(js_name = "fromBase64")] @@ -77,7 +115,7 @@ impl IdentifierNAPI { let identitfier = Identifier::from_string(base64.as_str(), Encoding::Base64) .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; - Ok(IdentifierNAPI { id: identitfier }) + Ok(IdentifierNAPI(identitfier)) } #[napi(js_name = "fromHex")] @@ -85,7 +123,7 @@ impl IdentifierNAPI { let identitfier = Identifier::from_string(hex.as_str(), Encoding::Hex) .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; - Ok(IdentifierNAPI { id: identitfier }) + Ok(IdentifierNAPI(identitfier)) } #[napi(js_name = "fromBytes")] @@ -93,12 +131,12 @@ impl IdentifierNAPI { let identifier = Identifier::from_vec(bytes.to_vec()) .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; - Ok(IdentifierNAPI { id: identifier }) + Ok(IdentifierNAPI(identifier)) } } impl IdentifierNAPI { pub fn to_slice(&self) -> [u8; 32] { - self.id.as_bytes().clone() + self.0.as_bytes().clone() } } diff --git a/src/identity/mod.rs b/src/identity/mod.rs index c6f2126..b59f7e0 100644 --- a/src/identity/mod.rs +++ b/src/identity/mod.rs @@ -1,5 +1,5 @@ use crate::{ - dynamic_value::{DynamicValue, TryToU64, TypeChecker, Uint64String}, + dynamic_value::{DynamicValue, IdentifierLikeNAPI, TypeChecker, Uint64String}, enums::platform_version::PlatformVersionNAPI, identifier::IdentifierNAPI, identity_public_key::IdentityPublicKeyNAPI, @@ -19,19 +19,17 @@ use napi_derive::napi; #[derive(Clone)] #[napi(js_name = IdentityNAPI)] -pub struct IdentityNAPI { - identity: Identity, -} +pub struct IdentityNAPI(Identity); impl From for IdentityNAPI { fn from(identity: Identity) -> Self { - IdentityNAPI { identity } + IdentityNAPI(identity) } } impl From for Identity { fn from(identity: IdentityNAPI) -> Self { - identity.identity + identity.0 } } @@ -39,67 +37,73 @@ impl From for Identity { impl IdentityNAPI { #[napi(constructor)] pub fn new( - id: &IdentifierNAPI, + js_id: IdentifierLikeNAPI, js_platform_version: DynamicValue, ) -> Result { + let id: IdentifierNAPI = js_id.try_into()?; + let platform_version: PlatformVersionNAPI = match js_platform_version.is_null() { true => PlatformVersionNAPI::default(), false => js_platform_version.try_into()?, }; - Ok(IdentityNAPI { - identity: Identity::create_basic_identity(id.clone().into(), &platform_version.into()) + Ok(IdentityNAPI( + Identity::create_basic_identity(id.clone().into(), &platform_version.into()) .with_js_error()?, - }) + )) } #[napi(setter, js_name = "id")] - pub fn set_id(&mut self, id: &IdentifierNAPI) { - self.identity.set_id(id.clone().into()); + pub fn set_id(&mut self, js_id: IdentifierLikeNAPI) -> Result<(), napi::Error> { + let id: IdentifierNAPI = js_id.try_into()?; + + self.0.set_id(id.clone().into()); + + Ok(()) } #[napi(setter, js_name = "balance")] pub fn set_balance(&mut self, balance: Uint64String) -> Result<(), napi::Error> { - self.identity.set_balance(balance.try_to_u64()?); + self.0.set_balance(balance.try_into()?); Ok(()) } #[napi(setter, js_name = "revision")] pub fn set_revision(&mut self, revision: Uint64String) -> Result<(), napi::Error> { - self.identity.set_revision(revision.try_to_u64()?); + self.0.set_revision(revision.try_into()?); Ok(()) } #[napi(getter, js_name = "id")] pub fn get_id(&self) -> IdentifierNAPI { - self.identity.id().into() + self.0.id().into() } #[napi(getter, js_name = "balance")] pub fn get_balance(&self) -> Uint64String { - self.identity.balance().into() + self.0.balance().into() } #[napi(getter, js_name = "revision")] pub fn get_revision(&self) -> Uint64String { - self.identity.revision().into() + self.0.revision().into() } #[napi(js_name = "addPublicKey")] pub fn add_public_key(&mut self, public_key: &IdentityPublicKeyNAPI) { - self.identity.add_public_key(public_key.clone().into()); + self.0.add_public_key(public_key.clone().into()); } #[napi(js_name = "getPublicKeyById")] pub fn get_public_key_by_id(&self, key_id: u32) -> Option { - let identity_public_key = self.identity.get_public_key_by_id(key_id); + let identity_public_key = self.0.get_public_key_by_id(key_id); identity_public_key.map(|key| IdentityPublicKeyNAPI::from(key.clone())) } #[napi(js_name = "getPublicKeys")] pub fn get_public_keys(&self) -> Vec { let keys = self - .identity + .0 .public_keys() .iter() .map(|(_index, key)| IdentityPublicKeyNAPI::from(key.clone())) @@ -133,16 +137,13 @@ impl IdentityNAPI { #[napi(js_name = "bytes")] pub fn to_bytes(&self) -> Result { - Ok(self.identity.serialize_to_bytes().with_js_error()?.into()) + Ok(self.0.serialize_to_bytes().with_js_error()?.into()) } #[napi(js_name = "hex")] pub fn to_hex(&self) -> Result { Ok(encode( - self.identity - .serialize_to_bytes() - .with_js_error()? - .as_slice(), + self.0.serialize_to_bytes().with_js_error()?.as_slice(), Encoding::Hex, )) } @@ -150,10 +151,7 @@ impl IdentityNAPI { #[napi(js_name = "base64")] pub fn to_base64(&self) -> Result { Ok(encode( - self.identity - .serialize_to_bytes() - .with_js_error()? - .as_slice(), + self.0.serialize_to_bytes().with_js_error()?.as_slice(), Encoding::Base64, )) } diff --git a/src/identity_public_key/mod.rs b/src/identity_public_key/mod.rs index 1dd32ad..ceec75e 100644 --- a/src/identity_public_key/mod.rs +++ b/src/identity_public_key/mod.rs @@ -2,7 +2,6 @@ use dpp::dashcore::secp256k1::hashes::hex::{Case, DisplayHex}; use dpp::platform_value::string_encoding::{decode, encode}; use dpp::serialization::{PlatformDeserializable, PlatformSerializable}; use dpp::{ - dashcore::Network, identity::{ KeyType, Purpose, SecurityLevel, hash::IdentityPublicKeyHashMethodsV0, @@ -14,12 +13,14 @@ use dpp::{ platform_value::{BinaryData, string_encoding::Encoding}, prelude::IdentityPublicKey, }; -use napi::Status; use napi::bindgen_prelude::Uint8Array; +use napi::{Either, Status}; use napi_derive::napi; +use crate::contract_bounds::ContractBoundsNAPI; +use crate::private_key::PrivateKeyNAPI; use crate::{ - dynamic_value::{DynamicValue, TryToU64, Uint64String}, + dynamic_value::{DynamicValue, Uint64String}, enums::{ key_type::KeyTypeNAPI, network::NetworkNAPI, purpose::PurposeNAPI, security_level::SecurityLevelNAPI, @@ -29,19 +30,17 @@ use crate::{ #[derive(Clone)] #[napi(js_name = "IdentityPublicKeyNAPI")] -pub struct IdentityPublicKeyNAPI { - public_key: IdentityPublicKey, -} +pub struct IdentityPublicKeyNAPI(IdentityPublicKey); impl From for IdentityPublicKeyNAPI { fn from(public_key: IdentityPublicKey) -> Self { - IdentityPublicKeyNAPI { public_key } + IdentityPublicKeyNAPI(public_key) } } impl From for IdentityPublicKey { fn from(public_key: IdentityPublicKeyNAPI) -> Self { - public_key.public_key + public_key.0 } } @@ -56,104 +55,108 @@ impl IdentityPublicKeyNAPI { read_only: bool, binary_data: String, js_disabled_at: Option, - // TODO: Implement js_contract_bounds + contract_bounds: Option<&ContractBoundsNAPI>, ) -> Result { let purpose: PurposeNAPI = js_purpose.try_into()?; let security_level: SecurityLevelNAPI = js_security_level.try_into()?; let key_type: KeyTypeNAPI = js_key_type.try_into()?; - let disabled_at = js_disabled_at.map(|val| val.try_to_u64()).transpose()?; + let disabled_at = js_disabled_at.map(|val| val.try_into()).transpose()?; - Ok(IdentityPublicKeyNAPI { - public_key: IdentityPublicKey::from(IdentityPublicKeyV0 { + Ok(IdentityPublicKeyNAPI(IdentityPublicKey::from( + IdentityPublicKeyV0 { id, purpose: purpose.into(), security_level: security_level.into(), - contract_bounds: None, + contract_bounds: contract_bounds.map(|bounds| bounds.clone().into()), key_type: key_type.into(), read_only, data: BinaryData::from_string(binary_data.as_str(), Encoding::Hex) .map_err(|err| napi::Error::new(Status::GenericFailure, err.to_string()))?, disabled_at, - }), - }) + }, + ))) } #[napi(js_name = "validatePrivateKey")] pub fn validate_private_key( &self, - js_private_key_bytes: Uint8Array, + js_private_key: Either, js_network: DynamicValue, ) -> Result { + let network = NetworkNAPI::try_from(js_network)?; + + let private_key = PrivateKeyNAPI::from_js_value(js_private_key, network.clone().into())?; + let mut private_key_bytes = [0u8; 32]; - let len = js_private_key_bytes.to_vec().len().min(32); - private_key_bytes[..len].copy_from_slice(&js_private_key_bytes[..len]); + let len = private_key.get_bytes().len().min(32); + private_key_bytes[..len].copy_from_slice(&private_key.get_bytes()[..len]); - let network = Network::from(NetworkNAPI::try_from(js_network)?); + let network = network.into(); - self.public_key + self.0 .validate_private_key_bytes(&private_key_bytes, network) .with_js_error() } #[napi(getter, js_name = "keyId")] pub fn get_key_id(&self) -> u32 { - self.public_key.id() + self.0.id() } #[napi(getter, js_name = purpose)] pub fn get_purpose(&self) -> String { - PurposeNAPI::from(self.public_key.purpose()).into() + PurposeNAPI::from(self.0.purpose()).into() } #[napi(getter, js_name = purposeNumber)] pub fn get_purpose_number(&self) -> PurposeNAPI { - PurposeNAPI::from(self.public_key.purpose()) + PurposeNAPI::from(self.0.purpose()) } #[napi(getter, js_name = securityLevel)] pub fn get_security_level(&self) -> String { - SecurityLevelNAPI::from(self.public_key.security_level()).into() + SecurityLevelNAPI::from(self.0.security_level()).into() } #[napi(getter, js_name = securityLevelNumber)] pub fn get_security_level_number(&self) -> SecurityLevelNAPI { - SecurityLevelNAPI::from(self.public_key.security_level()) + SecurityLevelNAPI::from(self.0.security_level()) } #[napi(getter, js_name = keyType)] pub fn get_key_type(&self) -> String { - KeyTypeNAPI::from(self.public_key.key_type()).into() + KeyTypeNAPI::from(self.0.key_type()).into() } #[napi(getter, js_name = keyTypeNumber)] pub fn get_key_type_number(&self) -> KeyTypeNAPI { - KeyTypeNAPI::from(self.public_key.key_type()) + KeyTypeNAPI::from(self.0.key_type()) } #[napi(getter, js_name = readOnly)] pub fn get_read_only(&self) -> bool { - self.public_key.read_only() + self.0.read_only() } #[napi(getter, js_name = data)] pub fn get_data(&self) -> String { - self.public_key.data().to_string(Encoding::Hex) + self.0.data().to_string(Encoding::Hex) } #[napi(getter, js_name = disabledAt)] pub fn get_disabled_at(&self) -> Option { - self.public_key.disabled_at().map(|num| num.into()) + self.0.disabled_at().map(|num| num.into()) } #[napi(setter, js_name = keyId)] pub fn set_key_id(&mut self, key_id: u32) { - self.public_key.set_id(key_id) + self.0.set_id(key_id) } #[napi(setter, js_name = purpose)] pub fn set_purpose(&mut self, purpose: DynamicValue) -> Result<(), napi::Error> { Ok(self - .public_key + .0 .set_purpose(Purpose::from(PurposeNAPI::try_from(purpose)?))) } @@ -165,7 +168,7 @@ impl IdentityPublicKeyNAPI { #[napi(setter, js_name = securityLevel)] pub fn set_security_level(&mut self, security_level: DynamicValue) -> Result<(), napi::Error> { Ok(self - .public_key + .0 .set_security_level(SecurityLevel::from(SecurityLevelNAPI::try_from( security_level, )?))) @@ -182,7 +185,7 @@ impl IdentityPublicKeyNAPI { #[napi(setter, js_name = keyType)] pub fn set_key_type(&mut self, key_type: DynamicValue) -> Result<(), napi::Error> { Ok(self - .public_key + .0 .set_key_type(KeyType::from(KeyTypeNAPI::try_from(key_type)?))) } @@ -193,7 +196,7 @@ impl IdentityPublicKeyNAPI { #[napi(setter, js_name = readOnly)] pub fn set_read_only(&mut self, read_only: bool) { - self.public_key.set_read_only(read_only) + self.0.set_read_only(read_only) } #[napi(setter, js_name = data)] @@ -201,24 +204,24 @@ impl IdentityPublicKeyNAPI { let data = BinaryData::from_string(binary_data.as_str(), Encoding::Hex) .map_err(|err| napi::Error::new(Status::GenericFailure, err.to_string()))?; - Ok(self.public_key.set_data(data)) + Ok(self.0.set_data(data)) } #[napi(setter, js_name = disabledAt)] pub fn set_disabled_at(&mut self, disabled_at: Uint64String) -> Result<(), napi::Error> { - self.public_key.set_disabled_at(disabled_at.try_to_u64()?); + self.0.set_disabled_at(disabled_at.try_into()?); Ok(()) } #[napi(js_name = removeDisabledAt)] pub fn remove_disabled_at(&mut self) { - self.public_key.remove_disabled_at() + self.0.remove_disabled_at() } #[napi(js_name = "getPublicKeyHash")] pub fn public_key_hash(&self) -> Result { let hash = self - .public_key + .0 .public_key_hash() .with_js_error() .map(|slice| slice.to_vec())? @@ -229,21 +232,18 @@ impl IdentityPublicKeyNAPI { #[napi(js_name = "isMaster")] pub fn is_master(&self) -> bool { - self.public_key.is_master() + self.0.is_master() } #[napi(js_name = bytes)] pub fn to_byes(&self) -> Result { - Ok(self.public_key.serialize_to_bytes().with_js_error()?.into()) + Ok(self.0.serialize_to_bytes().with_js_error()?.into()) } #[napi(js_name = hex)] pub fn to_hex(&self) -> Result { Ok(encode( - self.public_key - .serialize_to_bytes() - .with_js_error()? - .as_slice(), + self.0.serialize_to_bytes().with_js_error()?.as_slice(), Encoding::Hex, )) } @@ -251,20 +251,16 @@ impl IdentityPublicKeyNAPI { #[napi(js_name = base64)] pub fn to_base64(&self) -> Result { Ok(encode( - self.public_key - .serialize_to_bytes() - .with_js_error()? - .as_slice(), + self.0.serialize_to_bytes().with_js_error()?.as_slice(), Encoding::Base64, )) } #[napi(js_name = fromBytes)] pub fn from_bytes(bytes: Uint8Array) -> Result { - let public_key = - IdentityPublicKey::deserialize_from_bytes(bytes.to_vec().as_slice()).with_js_error()?; - - Ok(IdentityPublicKeyNAPI { public_key }) + Ok(IdentityPublicKeyNAPI( + IdentityPublicKey::deserialize_from_bytes(bytes.to_vec().as_slice()).with_js_error()?, + )) } #[napi(js_name = fromHex)] @@ -275,7 +271,7 @@ impl IdentityPublicKeyNAPI { let public_key = IdentityPublicKey::deserialize_from_bytes(bytes.as_slice()).with_js_error()?; - Ok(IdentityPublicKeyNAPI { public_key }) + Ok(IdentityPublicKeyNAPI(public_key)) } #[napi(js_name = fromBase64)] @@ -286,6 +282,6 @@ impl IdentityPublicKeyNAPI { let public_key = IdentityPublicKey::deserialize_from_bytes(bytes.as_slice()).with_js_error()?; - Ok(IdentityPublicKeyNAPI { public_key }) + Ok(IdentityPublicKeyNAPI(public_key)) } } diff --git a/src/identity_public_key_in_creation/mod.rs b/src/identity_public_key_in_creation/mod.rs new file mode 100644 index 0000000..c238a24 --- /dev/null +++ b/src/identity_public_key_in_creation/mod.rs @@ -0,0 +1,203 @@ +use dpp::identity::contract_bounds::ContractBounds; +use dpp::identity::identity_public_key::v0::IdentityPublicKeyV0; +use dpp::identity::{IdentityPublicKey, KeyType, Purpose, SecurityLevel}; +use dpp::platform_value::BinaryData; +use dpp::platform_value::string_encoding::Encoding::Hex; +use dpp::state_transition::public_key_in_creation::IdentityPublicKeyInCreation; +use dpp::state_transition::public_key_in_creation::accessors::{ + IdentityPublicKeyInCreationV0Getters, IdentityPublicKeyInCreationV0Setters, +}; +use dpp::state_transition::public_key_in_creation::v0::IdentityPublicKeyInCreationV0; +use napi::Either; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::contract_bounds::ContractBoundsNAPI; +use crate::dynamic_value::DynamicValue; +use crate::enums::key_type::KeyTypeNAPI; +use crate::enums::purpose::PurposeNAPI; +use crate::enums::security_level::SecurityLevelNAPI; +use crate::identity_public_key::IdentityPublicKeyNAPI; +use crate::private_key::PrivateKeyNAPI; +use crate::utils::WithJsError; + +#[derive(Clone, Debug)] +#[napi(js_name = "IdentityPublicKeyInCreationNAPI")] +pub struct IdentityPublicKeyInCreationNAPI(IdentityPublicKeyInCreation); + +impl From for IdentityPublicKeyInCreationNAPI { + fn from(value: IdentityPublicKeyInCreation) -> Self { + IdentityPublicKeyInCreationNAPI(value) + } +} + +impl From for IdentityPublicKeyInCreation { + fn from(value: IdentityPublicKeyInCreationNAPI) -> Self { + value.0 + } +} + +impl From for IdentityPublicKey { + fn from(value: IdentityPublicKeyInCreationNAPI) -> Self { + let contract_bounds = match value.0.contract_bounds() { + None => None, + Some(bounds) => Some(bounds.clone()), + }; + + IdentityPublicKey::V0(IdentityPublicKeyV0 { + id: value.0.id(), + purpose: value.0.purpose(), + security_level: value.0.security_level(), + contract_bounds, + key_type: value.0.key_type(), + read_only: value.0.read_only(), + data: value.0.data().clone(), + disabled_at: None, + }) + } +} + +#[napi] +impl IdentityPublicKeyInCreationNAPI { + #[napi(constructor)] + pub fn new( + id: u32, + js_purpose: PurposeNAPI, + js_security_level: SecurityLevelNAPI, + js_key_type: KeyTypeNAPI, + read_only: bool, + binary_data: Uint8Array, + signature: Option, + js_contract_bounds: Option<&ContractBoundsNAPI>, + ) -> IdentityPublicKeyInCreationNAPI { + IdentityPublicKeyInCreationNAPI(IdentityPublicKeyInCreation::V0( + IdentityPublicKeyInCreationV0 { + id, + key_type: KeyType::from(js_key_type), + purpose: Purpose::from(js_purpose), + security_level: SecurityLevel::from(js_security_level), + contract_bounds: js_contract_bounds + .map(|bounds| ContractBounds::from(bounds.clone())), + read_only, + data: BinaryData::from(binary_data.to_vec()), + signature: BinaryData::from( + signature.map(|sig| sig.to_vec()).unwrap_or(Vec::new()), + ), + }, + )) + } + + #[napi(js_name = toIdentityPublicKey)] + pub fn to_identity_public_key(&self) -> Result { + IdentityPublicKeyNAPI::new( + self.0.id(), + DynamicValue::Text(PurposeNAPI::from(self.0.purpose()).into()), + DynamicValue::Text(SecurityLevelNAPI::from(self.0.security_level()).into()), + DynamicValue::Text(KeyTypeNAPI::from(self.0.key_type()).into()), + self.0.read_only(), + self.0.data().to_string(Hex), + None, + self.get_contract_bounds().clone().as_ref(), + ) + } + + #[napi(js_name = "validatePrivateKey")] + pub fn validate_private_key( + &self, + js_private_key: Either, + js_network: DynamicValue, + ) -> Result { + let public_key: IdentityPublicKeyNAPI = IdentityPublicKey::from(self.clone()).into(); + + public_key.validate_private_key(js_private_key, js_network) + } + + #[napi(js_name = "getHash")] + pub fn get_hash(&self) -> Result { + Ok(self.0.hash().with_js_error()?.to_vec().into()) + } + + #[napi(getter, js_name = "contractBounds")] + pub fn get_contract_bounds(&self) -> Option { + self.0.contract_bounds().map(|bounds| bounds.clone().into()) + } + + #[napi(getter, js_name = "keyId")] + pub fn get_key_id(&self) -> u32 { + self.0.id() + } + + #[napi(getter, js_name = "purpose")] + pub fn get_purpose(&self) -> String { + PurposeNAPI::from(self.0.purpose()).into() + } + + #[napi(getter, js_name = "securityLevel")] + pub fn get_security_level(&self) -> String { + SecurityLevelNAPI::from(self.0.security_level()).into() + } + + #[napi(getter, js_name = "keyType")] + pub fn get_key_type(&self) -> String { + KeyTypeNAPI::from(self.0.key_type()).into() + } + + #[napi(getter, js_name = "readOnly")] + pub fn get_read_only(&self) -> bool { + self.0.read_only() + } + + #[napi(getter, js_name = "data")] + pub fn get_data(&self) -> Uint8Array { + self.0.data().to_vec().into() + } + + #[napi(getter, js_name = "signature")] + pub fn get_signature(&self) -> Uint8Array { + self.0.signature().to_vec().into() + } + + #[napi(setter, js_name = "keyId")] + pub fn set_key_id(&mut self, key_id: u32) { + self.0.set_id(key_id) + } + + #[napi(setter, js_name = "purpose")] + pub fn set_purpose(&mut self, js_purpose: PurposeNAPI) { + self.0.set_purpose(Purpose::from(js_purpose)) + } + + #[napi(setter, js_name = "securityLevel")] + pub fn set_security_level(&mut self, js_security_level: SecurityLevelNAPI) { + self.0 + .set_security_level(SecurityLevel::from(js_security_level)) + } + + #[napi(setter, js_name = "keyType")] + pub fn set_key_type(&mut self, key_type: KeyTypeNAPI) { + self.0.set_type(key_type.into()); + } + + #[napi(setter, js_name = "readOnly")] + pub fn set_read_only(&mut self, read_only: bool) { + self.0.set_read_only(read_only) + } + + #[napi(setter, js_name = "data")] + pub fn set_data(&mut self, binary_data: Uint8Array) { + let data = BinaryData::from(binary_data.to_vec()); + self.0.set_data(data) + } + + #[napi(setter, js_name = "signature")] + pub fn set_signature(&mut self, binary_data: Uint8Array) { + let signature = BinaryData::from(binary_data.to_vec()); + self.0.set_signature(signature) + } + + #[napi(setter, js_name = "contractBounds")] + pub fn set_contract_bounds(&mut self, js_bounds: Option<&ContractBoundsNAPI>) { + self.0 + .set_contract_bounds(js_bounds.map(|bounds| bounds.clone().into())); + } +} diff --git a/src/identity_transitions/credit_withdrawal_transition/mod.rs b/src/identity_transitions/credit_withdrawal_transition/mod.rs new file mode 100644 index 0000000..7f87fbe --- /dev/null +++ b/src/identity_transitions/credit_withdrawal_transition/mod.rs @@ -0,0 +1,263 @@ +use dpp::identity::core_script::CoreScript; +use dpp::identity::state_transition::OptionallyAssetLockProved; +use dpp::platform_value::Identifier; +use dpp::platform_value::string_encoding::Encoding::{Base64, Hex}; +use dpp::platform_value::string_encoding::{decode, encode}; +use dpp::serialization::{PlatformDeserializable, PlatformSerializable, Signable}; +use dpp::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition; +use dpp::state_transition::identity_credit_withdrawal_transition::accessors::IdentityCreditWithdrawalTransitionAccessorsV0; +use dpp::state_transition::identity_credit_withdrawal_transition::v1::IdentityCreditWithdrawalTransitionV1; +use dpp::state_transition::{StateTransition, StateTransitionIdentitySigned, StateTransitionLike}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::asset_lock_proof::AssetLockProofNAPI; +use crate::core_script::CoreScriptNAPI; +use crate::dynamic_value::{DynamicValue, IdentifierLikeNAPI, Uint64String}; +use crate::enums::pooling::PoolingNAPI; +use crate::enums::purpose::PurposeNAPI; +use crate::identifier::IdentifierNAPI; +use crate::state_transition::StateTransitionNAPI; +use crate::utils::WithJsError; + +#[napi(js_name = "IdentityCreditWithdrawalTransitionNAPI")] +pub struct IdentityCreditWithdrawalTransitionNAPI(IdentityCreditWithdrawalTransition); + +#[napi] +impl IdentityCreditWithdrawalTransitionNAPI { + #[napi(constructor)] + pub fn new( + js_identity_id: IdentifierLikeNAPI, + amount: Uint64String, + core_fee_per_byte: u32, + js_pooling: DynamicValue, + nonce: Uint64String, + js_output_script: Option<&CoreScriptNAPI>, + user_fee_increase: Option, + ) -> Result { + let pooling = PoolingNAPI::try_from(js_pooling)?; + let identity_id: Identifier = IdentifierNAPI::try_from(js_identity_id)?.into(); + + let output_script: Option = + js_output_script.map(|script| script.clone().into()); + + Ok(IdentityCreditWithdrawalTransitionNAPI( + IdentityCreditWithdrawalTransition::V1(IdentityCreditWithdrawalTransitionV1 { + amount: amount.try_into()?, + identity_id, + output_script, + core_fee_per_byte, + pooling: pooling.into(), + nonce: nonce.try_into()?, + user_fee_increase: user_fee_increase.unwrap_or(0), + signature_public_key_id: 0, + signature: Default::default(), + }), + )) + } + + #[napi(getter, js_name = "outputScript")] + pub fn get_output_script(&self) -> Option { + self.0.output_script().map(|script| script.into()) + } + + #[napi(getter, js_name = "pooling")] + pub fn get_pooling(&self) -> String { + PoolingNAPI::from(self.0.pooling()).into() + } + + #[napi(getter, js_name = "identityId")] + pub fn get_identity_id(&self) -> IdentifierNAPI { + IdentifierNAPI::from(self.0.identity_id()) + } + + #[napi(getter, js_name = "userFeeIncrease")] + pub fn get_user_fee_increase(&self) -> u16 { + self.0.user_fee_increase() + } + + #[napi(getter, js_name = "nonce")] + pub fn get_nonce(&self) -> Uint64String { + self.0.nonce().into() + } + + #[napi(getter, js_name = "amount")] + pub fn get_amount(&self) -> Uint64String { + self.0.amount().into() + } + + #[napi(js_name = "getPurposeRequirement")] + pub fn get_purpose_requirement(&self) -> Vec { + self.0 + .purpose_requirement() + .iter() + .map(|purpose| PurposeNAPI::from(purpose.clone()).into()) + .collect() + } + + #[napi(js_name = "getModifiedDataIds")] + pub fn get_modified_data_ids(&self) -> Vec { + self.0 + .modified_data_ids() + .iter() + .map(|id| id.clone().into()) + .collect() + } + + #[napi(js_name = "getOptionalAssetLockProof")] + pub fn get_optional_asset_lock_proof(&self) -> Option { + self.0 + .optional_asset_lock_proof() + .map(|proof| AssetLockProofNAPI::from(proof.clone())) + } + + #[napi(setter, js_name = "outputScript")] + pub fn set_output_script(&mut self, js_script: Option<&CoreScriptNAPI>) { + self.0 + .set_output_script(js_script.map(|script| script.clone().into())) + } + + #[napi(setter, js_name = "pooling")] + pub fn set_pooling(&mut self, js_pooling: DynamicValue) -> Result<(), napi::Error> { + let pooling: PoolingNAPI = PoolingNAPI::try_from(js_pooling)?; + Ok(self.0.set_pooling(pooling.into())) + } + + #[napi(setter, js_name = "identityId")] + pub fn set_identity_id( + &mut self, + js_identity_id: IdentifierLikeNAPI, + ) -> Result<(), napi::Error> { + let identity_id = IdentifierNAPI::try_from(js_identity_id)?; + + Ok(self.0.set_identity_id(identity_id.into())) + } + + #[napi(setter, js_name = "userFeeIncrease")] + pub fn set_user_fee_increase(&mut self, user_fee_increase: u16) { + self.0.set_user_fee_increase(user_fee_increase); + } + + #[napi(setter, js_name = "nonce")] + pub fn set_nonce(&mut self, nonce: Uint64String) -> Result<(), napi::Error> { + self.0.set_nonce(nonce.try_into()?); + + Ok(()) + } + + #[napi(setter, js_name = "amount")] + pub fn set_amount(&mut self, amount: Uint64String) -> Result<(), napi::Error> { + self.0.set_amount(amount.try_into()?); + + Ok(()) + } + + #[napi(setter, js_name = "coreFeePerByte")] + pub fn set_core_fee_per_byte(&mut self, fee_per_byte: u32) { + self.0.set_core_fee_per_byte(fee_per_byte) + } + + #[napi(getter, js_name = "signature")] + pub fn get_signature(&self) -> Uint8Array { + self.0.signature().to_vec().into() + } + + #[napi(getter, js_name = "coreFeePerByte")] + pub fn get_core_fee_per_byte(&self) -> u32 { + self.0.core_fee_per_byte() + } + + #[napi(js_name = "getSignableBytes")] + pub fn get_signable_bytes(&self) -> Result { + Ok(self.0.signable_bytes().with_js_error()?.into()) + } + + #[napi(getter, js_name = "signaturePublicKeyId")] + pub fn get_signature_public_key_id(&self) -> u32 { + self.0.signature_public_key_id() + } + + #[napi(setter, js_name = "signature")] + pub fn set_signature(&mut self, signature: Uint8Array) { + self.0.set_signature_bytes(signature.to_vec()) + } + + #[napi(setter, js_name = "signaturePublicKeyId")] + pub fn set_signature_public_key_id(&mut self, signature_public_key_id: u32) { + self.0.set_signature_public_key_id(signature_public_key_id) + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(hex: String) -> Result { + let bytes = decode(hex.as_str(), Hex) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + + IdentityCreditWithdrawalTransitionNAPI::from_bytes(bytes.into()) + } + + #[napi(js_name = "fromBase64")] + pub fn from_base64( + base64: String, + ) -> Result { + let bytes = decode(base64.as_str(), Base64) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + + IdentityCreditWithdrawalTransitionNAPI::from_bytes(bytes.into()) + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Result { + Ok(self.0.serialize_to_bytes().with_js_error()?.into()) + } + + #[napi(js_name = "hex")] + pub fn to_hex(&self) -> Result { + Ok(encode( + self.0.serialize_to_bytes().with_js_error()?.as_slice(), + Hex, + )) + } + + #[napi(js_name = "base64")] + pub fn to_base64(&self) -> Result { + Ok(encode( + self.0.serialize_to_bytes().with_js_error()?.as_slice(), + Base64, + )) + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes( + js_bytes: Uint8Array, + ) -> Result { + let bytes = js_bytes.to_vec(); + + let rs_transition = + IdentityCreditWithdrawalTransition::deserialize_from_bytes(bytes.as_slice()) + .with_js_error()?; + + Ok(IdentityCreditWithdrawalTransitionNAPI(rs_transition)) + } + + #[napi(js_name = "toStateTransition")] + pub fn to_state_transition(&self) -> StateTransitionNAPI { + StateTransitionNAPI::from(StateTransition::from(self.0.clone())) + } + + #[napi(js_name = "fromStateTransition")] + pub fn from_state_transition( + st: &StateTransitionNAPI, + ) -> Result { + let rs_st: StateTransition = st.clone().into(); + + match rs_st { + StateTransition::IdentityCreditWithdrawal(st) => { + Ok(IdentityCreditWithdrawalTransitionNAPI(st)) + } + _ => Err(napi::Error::new( + napi::Status::InvalidArg, + "Invalid state transition type", + )), + } + } +} diff --git a/src/identity_transitions/identitiy_credit_transfer_transition/mod.rs b/src/identity_transitions/identitiy_credit_transfer_transition/mod.rs new file mode 100644 index 0000000..c90ec41 --- /dev/null +++ b/src/identity_transitions/identitiy_credit_transfer_transition/mod.rs @@ -0,0 +1,210 @@ +use dpp::platform_value::BinaryData; +use dpp::platform_value::string_encoding::Encoding::{Base64, Hex}; +use dpp::platform_value::string_encoding::{decode, encode}; +use dpp::prelude::Identifier; +use dpp::serialization::{PlatformDeserializable, PlatformSerializable, Signable}; +use dpp::state_transition::identity_credit_transfer_transition::IdentityCreditTransferTransition; +use dpp::state_transition::identity_credit_transfer_transition::accessors::IdentityCreditTransferTransitionAccessorsV0; +use dpp::state_transition::identity_credit_transfer_transition::v0::IdentityCreditTransferTransitionV0; +use dpp::state_transition::{StateTransition, StateTransitionIdentitySigned, StateTransitionLike}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::dynamic_value::{IdentifierLikeNAPI, Uint64String}; +use crate::identifier::IdentifierNAPI; +use crate::state_transition::StateTransitionNAPI; +use crate::utils::WithJsError; + +#[napi(js_name = "IdentityCreditTransferNAPI")] +#[derive(Clone)] +pub struct IdentityCreditTransferNAPI(IdentityCreditTransferTransition); + +#[napi] +impl IdentityCreditTransferNAPI { + #[napi(constructor)] + pub fn new( + js_sender: IdentifierLikeNAPI, + amount: Uint64String, + js_recipient: IdentifierLikeNAPI, + nonce: Uint64String, + user_fee_increase: Option, + ) -> Result { + let sender: Identifier = IdentifierNAPI::try_from(js_sender)?.into(); + + let recipient: Identifier = IdentifierNAPI::try_from(js_recipient)?.into(); + + Ok(IdentityCreditTransferNAPI( + IdentityCreditTransferTransition::V0(IdentityCreditTransferTransitionV0 { + identity_id: sender, + recipient_id: recipient, + amount: amount.try_into()?, + nonce: nonce.try_into()?, + user_fee_increase: user_fee_increase.unwrap_or(0), + signature_public_key_id: 0, + signature: Default::default(), + }), + )) + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Result { + Ok(self.0.serialize_to_bytes().with_js_error()?.into()) + } + + #[napi(js_name = "hex")] + pub fn to_hex(&self) -> Result { + Ok(encode( + self.0.serialize_to_bytes().with_js_error()?.as_slice(), + Hex, + )) + } + + #[napi(js_name = "base64")] + pub fn to_base64(&self) -> Result { + Ok(encode( + self.0.serialize_to_bytes().with_js_error()?.as_slice(), + Base64, + )) + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes(js_bytes: Uint8Array) -> Result { + let bytes = js_bytes.to_vec(); + + let rs_transition = + IdentityCreditTransferTransition::deserialize_from_bytes(bytes.as_slice()) + .with_js_error()?; + + Ok(IdentityCreditTransferNAPI(rs_transition)) + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(hex: String) -> Result { + IdentityCreditTransferNAPI::from_bytes( + decode(hex.as_str(), Hex) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))? + .into(), + ) + } + + #[napi(js_name = "fromBase64")] + pub fn from_base64(hex: String) -> Result { + IdentityCreditTransferNAPI::from_bytes( + decode(hex.as_str(), Base64) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))? + .into(), + ) + } + + #[napi(setter, js_name = "recipientId")] + pub fn set_recipient_id( + &mut self, + js_recipient: IdentifierLikeNAPI, + ) -> Result<(), napi::Error> { + Ok(self + .0 + .set_recipient_id(IdentifierNAPI::try_from(js_recipient)?.into())) + } + + #[napi(setter, js_name = "senderId")] + pub fn set_sender_id(&mut self, js_sender: IdentifierLikeNAPI) -> Result<(), napi::Error> { + Ok(self + .0 + .set_identity_id(IdentifierNAPI::try_from(js_sender)?.into())) + } + + #[napi(setter, js_name = "amount")] + pub fn set_amount(&mut self, amount: Uint64String) -> Result<(), napi::Error> { + self.0.set_amount(amount.try_into()?); + + Ok(()) + } + + #[napi(setter, js_name = "nonce")] + pub fn set_nonce(&mut self, nonce: Uint64String) -> Result<(), napi::Error> { + self.0.set_nonce(nonce.try_into()?); + + Ok(()) + } + + #[napi(setter, js_name = "signature")] + pub fn set_signature(&mut self, signature: Uint8Array) { + self.0.set_signature_bytes(signature.to_vec()) + } + + #[napi(setter, js_name = "signaturePublicKeyId")] + pub fn set_signature_public_key_id(&mut self, public_key_id: u32) { + self.0.set_signature_public_key_id(public_key_id) + } + + #[napi(setter, js_name = "userFeeIncrease")] + pub fn set_user_fee_increase(&mut self, amount: u16) { + self.0.set_user_fee_increase(amount) + } + + #[napi(getter, js_name = "signature")] + pub fn get_signature(&self) -> Uint8Array { + self.0.signature().to_vec().into() + } + + #[napi(js_name = "getSignableBytes")] + pub fn get_signable_bytes(&self) -> Result { + Ok(self.0.signable_bytes().with_js_error()?.into()) + } + + #[napi(getter, js_name = "signaturePublicKeyId")] + pub fn get_signature_public_key_id(&self) -> u32 { + self.0.signature_public_key_id() + } + + #[napi(getter, js_name = "userFeeIncrease")] + pub fn get_user_fee_increase(&self) -> u16 { + self.0.user_fee_increase() + } + + #[napi(getter, js_name = "recipientId")] + pub fn get_recipient_id(&self) -> IdentifierNAPI { + self.0.recipient_id().into() + } + + #[napi(getter, js_name = "senderId")] + pub fn get_identity_id(&self) -> IdentifierNAPI { + self.0.identity_id().into() + } + + #[napi(getter, js_name = "amount")] + pub fn get_amount(&self) -> Uint64String { + self.0.amount().into() + } + + #[napi(getter, js_name = "nonce")] + pub fn get_nonce(&self) -> Uint64String { + self.0.nonce().into() + } + + #[napi(js_name = "toStateTransition")] + pub fn to_state_transition(&self) -> StateTransitionNAPI { + StateTransitionNAPI::from(StateTransition::from(self.0.clone())) + } + + #[napi(js_name = "fromStateTransition")] + pub fn from_state_transition( + st: &StateTransitionNAPI, + ) -> Result { + let rs_st: StateTransition = st.clone().into(); + + match rs_st { + StateTransition::IdentityCreditTransfer(st) => Ok(IdentityCreditTransferNAPI(st)), + _ => Err(napi::Error::new( + napi::Status::InvalidArg, + "Invalid state transition type", + )), + } + } +} + +impl IdentityCreditTransferNAPI { + pub fn set_signature_binary_data(&mut self, data: BinaryData) { + self.0.set_signature(data) + } +} diff --git a/src/identity_transitions/identity_create_transition/mod.rs b/src/identity_transitions/identity_create_transition/mod.rs new file mode 100644 index 0000000..b8388c9 --- /dev/null +++ b/src/identity_transitions/identity_create_transition/mod.rs @@ -0,0 +1,183 @@ +use dpp::identity::state_transition::AssetLockProved; +use dpp::platform_value::BinaryData; +use dpp::platform_value::string_encoding::Encoding::{Base64, Hex}; +use dpp::platform_value::string_encoding::decode; +use dpp::serialization::{PlatformDeserializable, PlatformSerializable, Signable}; +use dpp::state_transition::identity_create_transition::IdentityCreateTransition; +use dpp::state_transition::identity_create_transition::accessors::IdentityCreateTransitionAccessorsV0; +use dpp::state_transition::identity_create_transition::v0::IdentityCreateTransitionV0; +use dpp::state_transition::public_key_in_creation::IdentityPublicKeyInCreation; +use dpp::state_transition::{StateTransition, StateTransitionLike}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::asset_lock_proof::AssetLockProofNAPI; +use crate::dynamic_value::DynamicValue; +use crate::enums::platform_version::PlatformVersionNAPI; +use crate::identifier::IdentifierNAPI; +use crate::identity_public_key_in_creation::IdentityPublicKeyInCreationNAPI; +use crate::state_transition::StateTransitionNAPI; +use crate::utils::WithJsError; + +#[napi(js_name = "IdentityCreateTransitionNAPI")] +#[derive(Clone)] +pub struct IdentityCreateTransitionNAPI(IdentityCreateTransition); + +impl From for IdentityCreateTransitionNAPI { + fn from(val: IdentityCreateTransition) -> Self { + IdentityCreateTransitionNAPI(val) + } +} + +impl From for IdentityCreateTransition { + fn from(val: IdentityCreateTransitionNAPI) -> Self { + val.0 + } +} + +#[napi] +impl IdentityCreateTransitionNAPI { + #[napi(constructor)] + pub fn new( + js_public_keys: Vec<&IdentityPublicKeyInCreationNAPI>, + asset_lock: &AssetLockProofNAPI, + signature: Option, + user_fee_increase: Option, + ) -> Result { + Ok(IdentityCreateTransitionNAPI(IdentityCreateTransition::V0( + IdentityCreateTransitionV0 { + public_keys: js_public_keys + .into_iter() + .map(|key| key.clone().into()) + .collect(), + asset_lock_proof: asset_lock.clone().into(), + user_fee_increase: user_fee_increase.unwrap_or(0), + signature: BinaryData::from(signature.map(|sig| sig.to_vec()).unwrap_or(vec![])), + identity_id: asset_lock.create_identifier()?.into(), + }, + ))) + } + + #[napi(js_name = "default")] + pub fn default( + js_platform_version: DynamicValue, + ) -> Result { + let platform_version = PlatformVersionNAPI::try_from(js_platform_version)?; + + IdentityCreateTransition::default_versioned(&platform_version.into()) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string())) + .map(Into::into) + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(hex: String) -> Result { + let bytes = decode(hex.as_str(), Hex) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; + + IdentityCreateTransitionNAPI::from_bytes(bytes.into()) + } + + #[napi(js_name = "fromBase64")] + pub fn from_base64(base64: String) -> Result { + let bytes = decode(base64.as_str(), Base64) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; + + IdentityCreateTransitionNAPI::from_bytes(bytes.into()) + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Result { + Ok(self.0.serialize_to_bytes().with_js_error()?.into()) + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes(bytes: Uint8Array) -> Result { + let bytes_vec = bytes.to_vec(); + + let rs_transition = IdentityCreateTransition::deserialize_from_bytes(bytes_vec.as_slice()) + .with_js_error()?; + + Ok(IdentityCreateTransitionNAPI(rs_transition)) + } + + #[napi(getter, js_name = "publicKeys")] + pub fn get_public_keys(&self) -> Vec { + self.0 + .public_keys() + .iter() + .map(|key| IdentityPublicKeyInCreationNAPI::from(key.clone())) + .collect() + } + + #[napi(js_name = "getIdentifier")] + pub fn get_identity_id(&self) -> IdentifierNAPI { + self.0.identity_id().into() + } + + #[napi(getter, js_name = "userFeeIncrease")] + pub fn get_user_fee_increase(&self) -> u16 { + self.0.user_fee_increase() + } + + #[napi(getter, js_name = "signature")] + pub fn get_signature(&self) -> Uint8Array { + self.0.signature().to_vec().into() + } + + #[napi(js_name = "getSignableBytes")] + pub fn get_signable_bytes(&self) -> Result { + Ok(self.0.signable_bytes().with_js_error()?.into()) + } + + #[napi(getter, js_name = "assetLock")] + pub fn get_asset_lock_proof(&self) -> AssetLockProofNAPI { + AssetLockProofNAPI::from(self.0.asset_lock_proof().clone()) + } + + #[napi(setter, js_name = "publicKeys")] + pub fn set_public_keys(&mut self, js_public_keys: Vec<&IdentityPublicKeyInCreationNAPI>) { + self.0.set_public_keys( + js_public_keys + .into_iter() + .map(|key| IdentityPublicKeyInCreation::from(key.clone())) + .collect(), + ); + } + + #[napi(setter, js_name = "userFeeIncrease")] + pub fn set_user_fee_increase(&mut self, amount: u16) { + self.0.set_user_fee_increase(amount) + } + + #[napi(setter, js_name = "signature")] + pub fn set_signature(&mut self, signature: Uint8Array) { + self.0.set_signature_bytes(signature.to_vec()) + } + + #[napi(setter, js_name = "assetLock")] + pub fn set_asset_lock_proof(&mut self, proof: &AssetLockProofNAPI) -> Result<(), napi::Error> { + self.0 + .set_asset_lock_proof(proof.clone().into()) + .with_js_error() + } + + #[napi(js_name = "toStateTransition")] + pub fn to_state_transition(&self) -> StateTransitionNAPI { + StateTransitionNAPI::from(StateTransition::IdentityCreate(self.clone().0)) + } + + #[napi(js_name = "fromStateTransition")] + pub fn from_state_transition( + st: &StateTransitionNAPI, + ) -> Result { + let rs_st: StateTransition = st.clone().into(); + + match rs_st { + StateTransition::IdentityCreate(st) => Ok(IdentityCreateTransitionNAPI(st)), + _ => Err(napi::Error::new( + napi::Status::InvalidArg, + "Invalid state document_transition type", + )), + } + } +} diff --git a/src/identity_transitions/identity_top_up_transition/mod.rs b/src/identity_transitions/identity_top_up_transition/mod.rs new file mode 100644 index 0000000..b662e43 --- /dev/null +++ b/src/identity_transitions/identity_top_up_transition/mod.rs @@ -0,0 +1,182 @@ +use dpp::identifier::Identifier; +use dpp::identity::state_transition::{AssetLockProved, OptionallyAssetLockProved}; +use dpp::platform_value::string_encoding::Encoding::{Base64, Hex}; +use dpp::platform_value::string_encoding::{decode, encode}; +use dpp::serialization::{PlatformDeserializable, PlatformSerializable, Signable}; +use dpp::state_transition::identity_topup_transition::IdentityTopUpTransition; +use dpp::state_transition::identity_topup_transition::accessors::IdentityTopUpTransitionAccessorsV0; +use dpp::state_transition::identity_topup_transition::v0::IdentityTopUpTransitionV0; +use dpp::state_transition::{StateTransition, StateTransitionLike}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::asset_lock_proof::AssetLockProofNAPI; +use crate::dynamic_value::IdentifierLikeNAPI; +use crate::identifier::IdentifierNAPI; +use crate::state_transition::StateTransitionNAPI; +use crate::utils::WithJsError; + +#[napi(js_name = "IdentityTopUpTransitionNAPI")] +#[derive(Clone)] +pub struct IdentityTopUpTransitionNAPI(IdentityTopUpTransition); + +#[napi] +impl IdentityTopUpTransitionNAPI { + #[napi(constructor)] + pub fn new( + asset_lock_proof: &AssetLockProofNAPI, + js_identity_id: IdentifierLikeNAPI, + user_fee_increase: Option, + ) -> Result { + let identity_id: Identifier = IdentifierNAPI::try_from(js_identity_id)?.into(); + + Ok(IdentityTopUpTransitionNAPI(IdentityTopUpTransition::V0( + IdentityTopUpTransitionV0 { + asset_lock_proof: asset_lock_proof.clone().into(), + identity_id, + user_fee_increase: user_fee_increase.unwrap_or(0), + signature: Default::default(), + }, + ))) + } + + #[napi(js_name = "getModifiedDataIds")] + pub fn get_modified_data_ids(&self) -> Vec { + self.0 + .modified_data_ids() + .iter() + .map(|id| id.clone().into()) + .collect() + } + + #[napi(js_name = "getOptionalAssetLockProof")] + pub fn get_optional_asset_lock_proof(&self) -> Option { + self.0 + .optional_asset_lock_proof() + .map(|lock| lock.clone().into()) + } + + #[napi(getter, js_name = "userFeeIncrease")] + pub fn get_user_fee_increase(&self) -> u16 { + self.0.user_fee_increase() + } + + #[napi(getter, js_name = "identityIdentifier")] + pub fn get_identity_identifier(&self) -> IdentifierNAPI { + self.0.identity_id().clone().into() + } + + #[napi(getter, js_name = "assetLockProof")] + pub fn get_asset_lock_proof(&self) -> AssetLockProofNAPI { + self.0.asset_lock_proof().clone().into() + } + + #[napi(setter, js_name = "userFeeIncrease")] + pub fn set_user_fee_increase(&mut self, user_fee_increase: u16) { + self.0.set_user_fee_increase(user_fee_increase); + } + + #[napi(setter, js_name = "identityIdentifier")] + pub fn set_identity_identifier( + &mut self, + js_identity_identifier: IdentifierLikeNAPI, + ) -> Result<(), napi::Error> { + Ok(self + .0 + .set_identity_id(IdentifierNAPI::try_from(js_identity_identifier)?.into())) + } + + #[napi(setter, js_name = "assetLockProof")] + pub fn set_asset_lock_proof( + &mut self, + asset_lock_proof: &AssetLockProofNAPI, + ) -> Result<(), napi::Error> { + self.0 + .set_asset_lock_proof(asset_lock_proof.clone().into()) + .with_js_error() + } + + #[napi(getter, js_name = "signature")] + pub fn get_signature(&self) -> Uint8Array { + self.0.signature().to_vec().into() + } + + #[napi(js_name = "getSignableBytes")] + pub fn get_signable_bytes(&self) -> Result { + Ok(self.0.signable_bytes().with_js_error()?.into()) + } + + #[napi(setter, js_name = "signature")] + pub fn set_signature(&mut self, signature: Uint8Array) { + self.0.set_signature_bytes(signature.to_vec()) + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Result { + Ok(self.0.serialize_to_bytes().with_js_error()?.into()) + } + + #[napi(js_name = "hex")] + pub fn to_hex(&self) -> Result { + Ok(encode( + self.0.serialize_to_bytes().with_js_error()?.as_slice(), + Hex, + )) + } + + #[napi(js_name = "base64")] + pub fn to_base64(&self) -> Result { + Ok(encode( + self.0.serialize_to_bytes().with_js_error()?.as_slice(), + Base64, + )) + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes(js_bytes: Uint8Array) -> Result { + let bytes = js_bytes.to_vec(); + + let rs_transition = + IdentityTopUpTransition::deserialize_from_bytes(bytes.as_slice()).with_js_error()?; + + Ok(IdentityTopUpTransitionNAPI(rs_transition)) + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(hex: String) -> Result { + IdentityTopUpTransitionNAPI::from_bytes( + decode(hex.as_str(), Hex) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))? + .into(), + ) + } + + #[napi(js_name = "fromBase64")] + pub fn from_base64(base64: String) -> Result { + IdentityTopUpTransitionNAPI::from_bytes( + decode(base64.as_str(), Base64) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))? + .into(), + ) + } + + #[napi(js_name = "toStateTransition")] + pub fn to_state_transition(&self) -> StateTransitionNAPI { + StateTransitionNAPI::from(StateTransition::from(self.0.clone())) + } + + #[napi(js_name = "fromStateTransition")] + pub fn from_state_transition( + st: &StateTransitionNAPI, + ) -> Result { + let rs_st: StateTransition = st.clone().into(); + + match rs_st { + StateTransition::IdentityTopUp(st) => Ok(IdentityTopUpTransitionNAPI(st)), + _ => Err(napi::Error::new( + napi::Status::InvalidArg, + "Invalid state transition type", + )), + } + } +} diff --git a/src/identity_transitions/identity_update_transition/mod.rs b/src/identity_transitions/identity_update_transition/mod.rs new file mode 100644 index 0000000..b39670d --- /dev/null +++ b/src/identity_transitions/identity_update_transition/mod.rs @@ -0,0 +1,251 @@ +use dpp::identity::state_transition::OptionallyAssetLockProved; +use dpp::platform_value::string_encoding::Encoding::{Base64, Hex}; +use dpp::platform_value::string_encoding::{decode, encode}; +use dpp::serialization::{PlatformDeserializable, PlatformSerializable, Signable}; +use dpp::state_transition::identity_update_transition::IdentityUpdateTransition; +use dpp::state_transition::identity_update_transition::accessors::IdentityUpdateTransitionAccessorsV0; +use dpp::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0; +use dpp::state_transition::public_key_in_creation::IdentityPublicKeyInCreation; +use dpp::state_transition::{StateTransition, StateTransitionIdentitySigned, StateTransitionLike}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::asset_lock_proof::AssetLockProofNAPI; +use crate::dynamic_value::{IdentifierLikeNAPI, Uint64String}; +use crate::enums::purpose::PurposeNAPI; +use crate::identifier::IdentifierNAPI; +use crate::identity_public_key_in_creation::IdentityPublicKeyInCreationNAPI; +use crate::state_transition::StateTransitionNAPI; +use crate::utils::WithJsError; + +#[napi(js_name = "IdentityUpdateTransitionNAPI")] +#[derive(Clone)] +pub struct IdentityUpdateTransitionNAPI(IdentityUpdateTransition); + +#[napi] +impl IdentityUpdateTransitionNAPI { + #[napi(constructor)] + pub fn new( + js_identity_id: IdentifierLikeNAPI, + revision: Uint64String, + nonce: Uint64String, + js_add_public_keys: Vec<&IdentityPublicKeyInCreationNAPI>, + disable_public_keys: Vec, + user_fee_increase: Option, + ) -> Result { + let identity_id = IdentifierNAPI::try_from(js_identity_id)?; + + Ok(IdentityUpdateTransitionNAPI(IdentityUpdateTransition::V0( + IdentityUpdateTransitionV0 { + identity_id: identity_id.into(), + revision: revision.try_into()?, + nonce: nonce.try_into()?, + add_public_keys: js_add_public_keys + .into_iter() + .map(|key| key.clone().into()) + .collect(), + disable_public_keys, + user_fee_increase: user_fee_increase.unwrap_or(0), + signature_public_key_id: 0, + signature: Default::default(), + }, + ))) + } + + #[napi(getter, js_name = "revision")] + pub fn get_revision(&self) -> Uint64String { + self.0.revision().into() + } + + #[napi(getter, js_name = "nonce")] + pub fn get_nonce(&self) -> Uint64String { + self.0.nonce().into() + } + + #[napi(getter, js_name = "identityIdentifier")] + pub fn get_identity_identifier(&self) -> IdentifierNAPI { + self.0.identity_id().into() + } + + #[napi(js_name = "getPurposeRequirement")] + pub fn get_purpose_requirement(&self) -> Vec { + self.0 + .purpose_requirement() + .iter() + .map(|purpose| PurposeNAPI::from(purpose.clone()).into()) + .collect() + } + + #[napi(js_name = "getModifiedDataIds")] + pub fn get_modified_data_ids(&self) -> Vec { + self.0 + .modified_data_ids() + .iter() + .map(|id| id.clone().into()) + .collect() + } + + #[napi(js_name = "getOptionalAssetLockProof")] + pub fn get_optional_asset_lock_proof(&self) -> Option { + self.0 + .optional_asset_lock_proof() + .map(|proof| AssetLockProofNAPI::from(proof.clone())) + } + + #[napi(getter, js_name = "publicKeyIdsToDisable")] + pub fn get_public_key_ids_to_disable(&self) -> Vec { + self.0.public_key_ids_to_disable().to_vec() + } + + #[napi(getter, js_name = "publicKeyIdsToAdd")] + pub fn get_public_key_ids_to_add(&self) -> Vec { + self.0 + .public_keys_to_add() + .to_vec() + .iter() + .map(|id| id.clone().into()) + .collect() + } + + #[napi(getter, js_name = "userFeeIncrease")] + pub fn get_user_fee_increase(&self) -> u16 { + self.0.user_fee_increase() + } + + #[napi(setter, js_name = "revision")] + pub fn set_revision(&mut self, revision: Uint64String) -> Result<(), napi::Error> { + self.0.set_revision(revision.try_into()?); + Ok(()) + } + + #[napi(setter, js_name = "nonce")] + pub fn set_nonce(&mut self, nonce: Uint64String) -> Result<(), napi::Error> { + self.0.set_nonce(nonce.try_into()?); + Ok(()) + } + + #[napi(setter, js_name = "identityIdentifier")] + pub fn set_identity_identifier( + &mut self, + js_identity_id: IdentifierLikeNAPI, + ) -> Result<(), napi::Error> { + let identity_id = IdentifierNAPI::try_from(js_identity_id)?; + self.0.set_identity_id(identity_id.clone().into()); + Ok(()) + } + + #[napi(setter, js_name = "publicKeyIdsToAdd")] + pub fn set_public_key_ids_to_add( + &mut self, + js_add_public_keys: Vec<&IdentityPublicKeyInCreationNAPI>, + ) { + let keys: Vec = js_add_public_keys + .into_iter() + .map(|id| id.clone().into()) + .collect(); + + self.0.set_public_keys_to_add(keys) + } + + #[napi(setter, js_name = "publicKeyIdsToDisable")] + pub fn set_public_key_ids_to_disable(&mut self, public_keys: Vec) { + self.0.set_public_key_ids_to_disable(public_keys) + } + + #[napi(setter, js_name = "userFeeIncrease")] + pub fn set_user_fee_increase(&mut self, user_fee_increase: u16) { + self.0.set_user_fee_increase(user_fee_increase) + } + + #[napi(getter, js_name = "signature")] + pub fn get_signature(&self) -> Uint8Array { + self.0.signature().to_vec().into() + } + + #[napi(js_name = "getSignableBytes")] + pub fn get_signable_bytes(&self) -> Result { + Ok(self.0.signable_bytes().with_js_error()?.into()) + } + + #[napi(getter, js_name = "signaturePublicKeyId")] + pub fn get_signature_public_key_id(&self) -> u32 { + self.0.signature_public_key_id() + } + + #[napi(setter, js_name = "signature")] + pub fn set_signature(&mut self, signature: Uint8Array) { + self.0.set_signature_bytes(signature.to_vec()) + } + + #[napi(setter, js_name = "signaturePublicKeyId")] + pub fn set_signature_public_key_id(&mut self, signature_public_key_id: u32) { + self.0.set_signature_public_key_id(signature_public_key_id) + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(hex: String) -> Result { + let bytes = decode(hex.as_str(), Hex) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + + IdentityUpdateTransitionNAPI::from_bytes(bytes.into()) + } + + #[napi(js_name = "fromBase64")] + pub fn from_base64(base64: String) -> Result { + let bytes = decode(base64.as_str(), Base64) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + + IdentityUpdateTransitionNAPI::from_bytes(bytes.into()) + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Result { + Ok(self.0.serialize_to_bytes().with_js_error()?.into()) + } + + #[napi(js_name = "hex")] + pub fn to_hex(&self) -> Result { + Ok(encode( + self.0.serialize_to_bytes().with_js_error()?.as_slice(), + Hex, + )) + } + + #[napi(js_name = "base64")] + pub fn to_base64(&self) -> Result { + Ok(encode( + self.0.serialize_to_bytes().with_js_error()?.as_slice(), + Base64, + )) + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes(js_bytes: Uint8Array) -> Result { + let bytes = js_bytes.to_vec(); + + let rs_transition = + IdentityUpdateTransition::deserialize_from_bytes(bytes.as_slice()).with_js_error()?; + + Ok(IdentityUpdateTransitionNAPI(rs_transition)) + } + + #[napi(js_name = "toStateTransition")] + pub fn to_state_transition(&self) -> StateTransitionNAPI { + StateTransitionNAPI::from(StateTransition::from(self.0.clone())) + } + + #[napi(js_name = "fromStateTransition")] + pub fn from_state_transition( + st: &StateTransitionNAPI, + ) -> Result { + let rs_st: StateTransition = st.clone().into(); + + match rs_st { + StateTransition::IdentityUpdate(st) => Ok(IdentityUpdateTransitionNAPI(st)), + _ => Err(napi::Error::new( + napi::Status::InvalidArg, + "Invalid state transition type", + )), + } + } +} diff --git a/src/identity_transitions/mod.rs b/src/identity_transitions/mod.rs new file mode 100644 index 0000000..45582f7 --- /dev/null +++ b/src/identity_transitions/mod.rs @@ -0,0 +1,5 @@ +pub mod credit_withdrawal_transition; +pub mod identitiy_credit_transfer_transition; +pub mod identity_create_transition; +pub mod identity_top_up_transition; +pub mod identity_update_transition; diff --git a/src/instant_lock/mod.rs b/src/instant_lock/mod.rs new file mode 100644 index 0000000..4ccbfa4 --- /dev/null +++ b/src/instant_lock/mod.rs @@ -0,0 +1,116 @@ +use dpp::dashcore::bls_sig_utils::BLSSignature; +use dpp::dashcore::hash_types::CycleHash; +use dpp::dashcore::hashes::hex::FromHex; +use dpp::dashcore::secp256k1::hashes::hex::Case::Lower; +use dpp::dashcore::secp256k1::hashes::hex::DisplayHex; +use dpp::dashcore::{InstantLock, Txid}; +use napi::Status; +use napi_derive::napi; +use std::str::FromStr; + +use crate::asset_lock_proof::outpoint::OutPointNAPI; + +#[napi(js_name = "InstantLockNAPI")] +#[derive(Clone)] +pub struct InstantLockNAPI(InstantLock); + +impl From for InstantLock { + fn from(value: InstantLockNAPI) -> Self { + value.0 + } +} + +impl From for InstantLockNAPI { + fn from(value: InstantLock) -> Self { + InstantLockNAPI(value) + } +} + +#[napi] +impl InstantLockNAPI { + #[napi(constructor)] + pub fn new( + version: u8, + js_inputs: Vec<&OutPointNAPI>, + txid: String, + cycle_hash: String, + bls_signature: String, + ) -> Result { + Ok(InstantLockNAPI(InstantLock { + version, + inputs: js_inputs + .into_iter() + .map(|input| input.clone().into()) + .collect(), + txid: Txid::from_hex(&txid) + .map_err(|err| napi::Error::new(Status::GenericFailure, err.to_string()))?, + cyclehash: CycleHash::from_str(&cycle_hash) + .map_err(|err| napi::Error::new(Status::GenericFailure, err.to_string()))?, + signature: BLSSignature::from_hex(&bls_signature) + .map_err(|err| napi::Error::new(Status::GenericFailure, err.to_string()))?, + })) + } + + #[napi(getter, js_name = "version")] + pub fn get_version(&self) -> u8 { + self.0.version + } + + #[napi(getter, js_name = "inputs")] + pub fn get_inputs(&self) -> Vec { + self.0 + .inputs + .iter() + .map(|input| input.clone().into()) + .collect() + } + + #[napi(getter, js_name = "txid")] + pub fn get_txid(&self) -> String { + self.0.txid.to_hex() + } + + #[napi(getter, js_name = "cyclehash")] + pub fn get_cycle_hash(&self) -> String { + self.0.cyclehash.to_string() + } + + #[napi(getter, js_name = "blsSignature")] + pub fn get_bls_signature(&self) -> String { + self.0.signature.to_bytes().to_hex_string(Lower) + } + + #[napi(setter, js_name = "version")] + pub fn set_version(&mut self, v: u8) { + self.0.version = v; + } + + #[napi(setter, js_name = "inputs")] + pub fn set_inputs(&mut self, inputs: Vec<&OutPointNAPI>) { + self.0.inputs = inputs + .into_iter() + .map(|input| input.clone().into()) + .collect(); + } + + #[napi(setter, js_name = "txid")] + pub fn set_txid(&mut self, txid: String) -> Result<(), napi::Error> { + self.0.txid = Txid::from_hex(&txid) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + Ok(()) + } + + #[napi(setter, js_name = "cyclehash")] + pub fn set_cycle_hash(&mut self, cycle_hash: String) -> Result<(), napi::Error> { + self.0.cyclehash = CycleHash::from_str(&cycle_hash) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + Ok(()) + } + + #[napi(setter, js_name = "blsSignature")] + pub fn set_bls_signature(&mut self, bls_signature: String) -> Result<(), napi::Error> { + self.0.signature = BLSSignature::from_hex(&bls_signature) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 9009487..cacab7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,23 @@ #![no_main] +pub mod asset_lock_proof; +pub mod consensus_error; +pub mod contract_bounds; +pub mod core_script; pub mod dynamic_value; +pub mod encrypted_note; pub mod enums; +pub mod group_state_transition_info; pub mod identifier; pub mod identity; pub mod identity_public_key; +pub mod identity_public_key_in_creation; +pub mod identity_transitions; +pub mod instant_lock; +pub mod masternode_vote; +pub mod mock_bls; +pub mod partial_identity; +pub mod private_key; +pub mod public_key; +pub mod state_transition; pub mod utils; diff --git a/src/masternode_vote/mod.rs b/src/masternode_vote/mod.rs new file mode 100644 index 0000000..9f5f00b --- /dev/null +++ b/src/masternode_vote/mod.rs @@ -0,0 +1,230 @@ +pub mod resource_vote_choice; +pub mod vote; +pub mod vote_poll; + +use dpp::identity::state_transition::OptionallyAssetLockProved; +use dpp::platform_value::BinaryData; +use dpp::platform_value::string_encoding::Encoding::{Base64, Hex}; +use dpp::platform_value::string_encoding::decode; +use dpp::serialization::{PlatformDeserializable, PlatformSerializable, Signable}; +use dpp::state_transition::masternode_vote_transition::MasternodeVoteTransition; +use dpp::state_transition::masternode_vote_transition::accessors::MasternodeVoteTransitionAccessorsV0; +use dpp::state_transition::masternode_vote_transition::v0::MasternodeVoteTransitionV0; +use dpp::state_transition::{StateTransition, StateTransitionIdentitySigned, StateTransitionLike}; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::asset_lock_proof::AssetLockProofNAPI; +use crate::dynamic_value::{IdentifierLikeNAPI, Uint64String}; +use crate::identifier::IdentifierNAPI; +use crate::masternode_vote::vote::VoteNAPI; +use crate::state_transition::StateTransitionNAPI; +use crate::utils::WithJsError; + +#[napi(js_name = "MasternodeVoteTransitionNAPI")] +#[derive(Clone)] +pub struct MasternodeVoteTransitionNAPI(MasternodeVoteTransition); + +impl From for MasternodeVoteTransitionNAPI { + fn from(val: MasternodeVoteTransition) -> Self { + MasternodeVoteTransitionNAPI(val) + } +} + +impl From for MasternodeVoteTransition { + fn from(val: MasternodeVoteTransitionNAPI) -> Self { + val.0 + } +} + +#[napi] +impl MasternodeVoteTransitionNAPI { + #[napi(constructor)] + pub fn new( + js_pro_tx_hash: IdentifierLikeNAPI, + js_voter_identity_id: IdentifierLikeNAPI, + vote: &VoteNAPI, + nonce: Uint64String, + signature_public_key: Option, + js_signature: Option, + ) -> Result { + let pro_tx_hash = IdentifierNAPI::try_from(js_pro_tx_hash)?; + let voter_identity_id = IdentifierNAPI::try_from(js_voter_identity_id)?; + + Ok(MasternodeVoteTransitionNAPI(MasternodeVoteTransition::V0( + MasternodeVoteTransitionV0 { + pro_tx_hash: pro_tx_hash.into(), + voter_identity_id: voter_identity_id.into(), + vote: vote.clone().into(), + nonce: nonce.try_into()?, + signature_public_key_id: signature_public_key.unwrap_or(0), + signature: BinaryData::from(js_signature.map(|sig| sig.to_vec()).unwrap_or(vec![])), + }, + ))) + } + + #[napi(getter, js_name = "proTxHash")] + pub fn pro_tx_hash(&self) -> IdentifierNAPI { + self.0.pro_tx_hash().into() + } + + #[napi(getter, js_name = "voterIdentityId")] + pub fn voter_identity_id(&self) -> IdentifierNAPI { + self.0.voter_identity_id().into() + } + + #[napi(getter, js_name = "vote")] + pub fn vote(&self) -> VoteNAPI { + self.0.vote().clone().into() + } + + #[napi(getter, js_name = "nonce")] + pub fn nonce(&self) -> Uint64String { + self.0.nonce().into() + } + + #[napi(getter, js_name = "signaturePublicKeyId")] + pub fn signature_public_key_id(&self) -> u32 { + self.0.signature_public_key_id() + } + + #[napi(getter, js_name = "signature")] + pub fn signature(&self) -> Uint8Array { + self.0.signature().clone().to_vec().into() + } + + #[napi(setter, js_name = "proTxHash")] + pub fn set_pro_tx_hash( + &mut self, + js_pro_tx_hash: IdentifierLikeNAPI, + ) -> Result<(), napi::Error> { + let pro_tx_hash = IdentifierNAPI::try_from(js_pro_tx_hash)?; + + self.0.set_pro_tx_hash(pro_tx_hash.into()); + + Ok(()) + } + + #[napi(setter, js_name = "voterIdentityId")] + pub fn set_voter_identity_id( + &mut self, + js_voter_identity_id: IdentifierLikeNAPI, + ) -> Result<(), napi::Error> { + let voter_identity_id = IdentifierNAPI::try_from(js_voter_identity_id)?; + + self.0.set_voter_identity_id(voter_identity_id.into()); + + Ok(()) + } + + #[napi(setter, js_name = "vote")] + pub fn set_vote(&mut self, vote: &VoteNAPI) { + self.0.set_vote(vote.clone().into()) + } + + #[napi(setter, js_name = "nonce")] + pub fn set_nonce(&mut self, nonce: Uint64String) -> Result<(), napi::Error> { + self.0 = match self.0.clone() { + MasternodeVoteTransition::V0(mut vote) => { + vote.nonce = nonce.try_into()?; + + MasternodeVoteTransition::V0(vote) + } + }; + + Ok(()) + } + + #[napi(setter, js_name = "signaturePublicKeyId")] + pub fn set_signature_public_key_id(&mut self, signature_public_key_id: u32) { + self.0.set_signature_public_key_id(signature_public_key_id) + } + + #[napi(setter, js_name = "signature")] + pub fn set_signature(&mut self, signature: Uint8Array) { + self.0.set_signature_bytes(signature.to_vec()); + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(hex: String) -> Result { + let bytes = decode(hex.as_str(), Hex) + .map_err(|_| napi::Error::new(napi::Status::InvalidArg, "Invalid hex string"))?; + + MasternodeVoteTransitionNAPI::from_bytes(bytes.into()) + } + + #[napi(js_name = "fromBase64")] + pub fn from_base64(base64: String) -> Result { + let bytes = decode(base64.as_str(), Base64) + .map_err(|_| napi::Error::new(napi::Status::InvalidArg, "Invalid base64 string"))?; + + MasternodeVoteTransitionNAPI::from_bytes(bytes.into()) + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Result { + Ok(self.0.serialize_to_bytes().with_js_error()?.into()) + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes(js_bytes: Uint8Array) -> Result { + let bytes = js_bytes.to_vec(); + + let rs_transition = + MasternodeVoteTransition::deserialize_from_bytes(bytes.as_slice()).with_js_error()?; + + Ok(MasternodeVoteTransitionNAPI(rs_transition)) + } + + #[napi(getter, js_name = "userFeeIncrease")] + pub fn get_user_fee_increase(&self) -> u16 { + self.0.user_fee_increase() + } + + #[napi(js_name = "getSignableBytes")] + pub fn get_signable_bytes(&self) -> Result { + Ok(self.0.signable_bytes().with_js_error()?.into()) + } + + #[napi(getter, js_name = "assetLock")] + pub fn get_asset_lock_proof(&self) -> Option { + match self.0.optional_asset_lock_proof().clone() { + None => None, + Some(asset_lock_proof) => Some(AssetLockProofNAPI::from(asset_lock_proof.clone())), + } + } + + #[napi(setter, js_name = "userFeeIncrease")] + pub fn set_user_fee_increase(&mut self, amount: u16) { + self.0.set_user_fee_increase(amount) + } + + #[napi(getter, js_name = "modifiedDataIds")] + pub fn get_modified_data_ids(&self) -> Vec { + self.0 + .modified_data_ids() + .iter() + .map(|id| id.clone().into()) + .collect() + } + + #[napi(js_name = "toStateTransition")] + pub fn to_state_transition(&self) -> StateTransitionNAPI { + StateTransitionNAPI::from(StateTransition::MasternodeVote(self.clone().0)) + } + + #[napi(js_name = "fromStateTransition")] + pub fn from_state_transition( + st: &StateTransitionNAPI, + ) -> Result { + let rs_st: StateTransition = st.clone().into(); + + match rs_st { + StateTransition::MasternodeVote(st) => Ok(MasternodeVoteTransitionNAPI(st)), + _ => Err(napi::Error::new( + napi::Status::InvalidArg, + "Invalid state document_transition type", + )), + } + } +} diff --git a/src/masternode_vote/resource_vote_choice/mod.rs b/src/masternode_vote/resource_vote_choice/mod.rs new file mode 100644 index 0000000..5dafcbc --- /dev/null +++ b/src/masternode_vote/resource_vote_choice/mod.rs @@ -0,0 +1,60 @@ +use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; +use napi_derive::napi; + +use crate::{dynamic_value::IdentifierLikeNAPI, identifier::IdentifierNAPI}; + +#[derive(Clone)] +#[napi(js_name = "ResourceVoteChoiceNAPI")] +pub struct ResourceVoteChoiceNAPI(ResourceVoteChoice); + +impl From for ResourceVoteChoiceNAPI { + fn from(choice: ResourceVoteChoice) -> Self { + Self(choice) + } +} + +impl From for ResourceVoteChoice { + fn from(choice: ResourceVoteChoiceNAPI) -> Self { + choice.0 + } +} + +#[napi] +impl ResourceVoteChoiceNAPI { + #[napi(js_name = "TowardsIdentity")] + pub fn towards_identity(js_id: IdentifierLikeNAPI) -> Result { + let id = IdentifierNAPI::try_from(js_id)?; + + Ok(ResourceVoteChoiceNAPI(ResourceVoteChoice::TowardsIdentity( + id.into(), + ))) + } + + #[napi(js_name = "Abstain")] + pub fn abstain() -> Self { + ResourceVoteChoiceNAPI(ResourceVoteChoice::Abstain) + } + + #[napi(js_name = "Lock")] + pub fn lock() -> Self { + ResourceVoteChoiceNAPI(ResourceVoteChoice::Lock) + } + + #[napi(js_name = "getValue")] + pub fn get_value(&self) -> Option { + match self.0.clone() { + ResourceVoteChoice::TowardsIdentity(id) => Some(id.into()), + ResourceVoteChoice::Abstain => None, + ResourceVoteChoice::Lock => None, + } + } + + #[napi(js_name = "getType")] + pub fn get_type(&self) -> String { + match self.0.clone() { + ResourceVoteChoice::TowardsIdentity(_) => "TowardsIdentity".to_string(), + ResourceVoteChoice::Abstain => "Abstain".to_string(), + ResourceVoteChoice::Lock => "Lock".to_string(), + } + } +} diff --git a/src/masternode_vote/vote/mod.rs b/src/masternode_vote/vote/mod.rs new file mode 100644 index 0000000..37b6c7d --- /dev/null +++ b/src/masternode_vote/vote/mod.rs @@ -0,0 +1,69 @@ +use dpp::voting::votes::Vote; +use dpp::voting::votes::resource_vote::ResourceVote; +use dpp::voting::votes::resource_vote::accessors::v0::ResourceVoteGettersV0; +use dpp::voting::votes::resource_vote::v0::ResourceVoteV0; +use napi_derive::napi; + +use crate::masternode_vote::resource_vote_choice::ResourceVoteChoiceNAPI; +use crate::masternode_vote::vote_poll::VotePollNAPI; + +#[derive(Clone)] +#[napi(js_name = "VoteNAPI")] +pub struct VoteNAPI(Vote); + +impl From for VoteNAPI { + fn from(vote: Vote) -> Self { + Self(vote) + } +} + +impl From for Vote { + fn from(vote: VoteNAPI) -> Self { + vote.0 + } +} + +#[napi] +impl VoteNAPI { + #[napi(constructor)] + pub fn new(vote_poll: &VotePollNAPI, resource_vote_choice: &ResourceVoteChoiceNAPI) -> Self { + VoteNAPI(Vote::ResourceVote(ResourceVote::V0(ResourceVoteV0 { + vote_poll: vote_poll.clone().into(), + resource_vote_choice: resource_vote_choice.clone().into(), + }))) + } + + #[napi(getter, js_name = "votePoll")] + pub fn vote_poll(&self) -> VotePollNAPI { + match self.0.clone() { + Vote::ResourceVote(vote) => vote.vote_poll().clone().into(), + } + } + + #[napi(getter, js_name = "resourceVoteChoice")] + pub fn resource_vote_choice(&self) -> ResourceVoteChoiceNAPI { + match self.0.clone() { + Vote::ResourceVote(vote) => vote.resource_vote_choice().clone().into(), + } + } + + #[napi(setter, js_name = "votePoll")] + pub fn set_vote_poll(&mut self, vote_poll: &VotePollNAPI) { + self.0 = match self.0.clone() { + Vote::ResourceVote(vote) => Vote::ResourceVote(ResourceVote::V0(ResourceVoteV0 { + vote_poll: vote_poll.clone().into(), + resource_vote_choice: vote.resource_vote_choice(), + })), + } + } + + #[napi(setter, js_name = "resourceVoteChoice")] + pub fn set_resource_vote_choice(&mut self, resource_vote_choice: &ResourceVoteChoiceNAPI) { + self.0 = match self.0.clone() { + Vote::ResourceVote(vote) => Vote::ResourceVote(ResourceVote::V0(ResourceVoteV0 { + vote_poll: vote.vote_poll().clone(), + resource_vote_choice: resource_vote_choice.clone().into(), + })), + } + } +} diff --git a/src/masternode_vote/vote_poll/mod.rs b/src/masternode_vote/vote_poll/mod.rs new file mode 100644 index 0000000..b22357b --- /dev/null +++ b/src/masternode_vote/vote_poll/mod.rs @@ -0,0 +1,168 @@ +use dpp::bincode; +use dpp::voting::vote_polls::VotePoll; +use dpp::voting::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePoll; +use napi::bindgen_prelude::{Object, Uint8Array}; +use napi_derive::napi; + +use crate::dynamic_value::IdentifierLikeNAPI; +use crate::identifier::IdentifierNAPI; +use crate::utils::with_serde_to_platform_value; + +#[derive(Clone)] +#[napi(js_name = "VotePollNAPI")] +pub struct VotePollNAPI(VotePoll); + +impl From for VotePollNAPI { + fn from(poll: VotePoll) -> Self { + VotePollNAPI(poll) + } +} + +impl From for VotePoll { + fn from(poll: VotePollNAPI) -> Self { + poll.0 + } +} + +#[napi] +impl VotePollNAPI { + #[napi(constructor)] + pub fn new( + js_contract_id: IdentifierLikeNAPI, + document_type_name: String, + index_name: String, + js_index_values: Object, + ) -> Result { + let contract_id = IdentifierNAPI::try_from(js_contract_id)?; + + let index_values = match with_serde_to_platform_value(js_index_values)?.as_array() { + None => Err(napi::Error::new( + napi::Status::InvalidArg, + "index values must be array", + )), + Some(array) => Ok(array.clone()), + }?; + + Ok(VotePollNAPI(VotePoll::ContestedDocumentResourceVotePoll( + ContestedDocumentResourceVotePoll { + contract_id: contract_id.into(), + document_type_name, + index_name, + index_values, + }, + ))) + } + + #[napi(js_name = "toString")] + pub fn to_string(&self) -> String { + self.0.to_string() + } + + #[napi(getter, js_name = "contractId")] + pub fn contract_id(&self) -> IdentifierNAPI { + match self.0.clone() { + VotePoll::ContestedDocumentResourceVotePoll(poll) => poll.contract_id.into(), + } + } + + #[napi(getter, js_name = "documentTypeName")] + pub fn document_type_name(&self) -> String { + match self.0.clone() { + VotePoll::ContestedDocumentResourceVotePoll(poll) => poll.document_type_name.into(), + } + } + + #[napi(getter, js_name = "indexName")] + pub fn index_name(&self) -> String { + match self.0.clone() { + VotePoll::ContestedDocumentResourceVotePoll(poll) => poll.index_name.into(), + } + } + + #[napi(getter, js_name = "indexValues")] + pub fn index_values(&self) -> Result, napi::Error> { + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + match self.0.clone() { + VotePoll::ContestedDocumentResourceVotePoll(poll) => { + let encoded: Result>, napi::Error> = poll + .index_values + .iter() + .map(|value| { + bincode::encode_to_vec(value, config).map_err(|err| { + napi::Error::new(napi::Status::GenericFailure, err.to_string()) + }) + }) + .collect(); + + Ok(encoded? + .into_iter() + .map(|bytes| Uint8Array::from(bytes)) + .collect()) + } + } + } + + #[napi(setter, js_name = "contractId")] + pub fn set_contract_id( + &mut self, + js_contract_id: IdentifierLikeNAPI, + ) -> Result<(), napi::Error> { + let contract_id = IdentifierNAPI::try_from(js_contract_id)?; + + self.0 = match self.0.clone() { + VotePoll::ContestedDocumentResourceVotePoll(mut poll) => { + poll.contract_id = contract_id.into(); + + VotePoll::ContestedDocumentResourceVotePoll(poll) + } + }; + + Ok(()) + } + + #[napi(setter, js_name = "documentTypeName")] + pub fn set_document_type_name(&mut self, document_type_name: String) { + self.0 = match self.0.clone() { + VotePoll::ContestedDocumentResourceVotePoll(mut poll) => { + poll.document_type_name = document_type_name; + + VotePoll::ContestedDocumentResourceVotePoll(poll) + } + } + } + + #[napi(setter, js_name = "indexName")] + pub fn set_index_name(&mut self, index_name: String) { + self.0 = match self.0.clone() { + VotePoll::ContestedDocumentResourceVotePoll(mut poll) => { + poll.index_name = index_name; + + VotePoll::ContestedDocumentResourceVotePoll(poll) + } + }; + } + + #[napi(setter, js_name = "indexValues")] + pub fn set_index_values(&mut self, js_index_values: Object) -> Result<(), napi::Error> { + let index_values = match with_serde_to_platform_value(js_index_values)?.as_array() { + None => Err(napi::Error::new( + napi::Status::InvalidArg, + "index values must be array", + )), + Some(array) => Ok(array.clone()), + }?; + + self.0 = match self.0.clone() { + VotePoll::ContestedDocumentResourceVotePoll(mut poll) => { + poll.index_values = index_values; + + VotePoll::ContestedDocumentResourceVotePoll(poll) + } + }; + + Ok(()) + } +} diff --git a/src/mock_bls/mod.rs b/src/mock_bls/mod.rs new file mode 100644 index 0000000..3954a1d --- /dev/null +++ b/src/mock_bls/mod.rs @@ -0,0 +1,26 @@ +use dpp::{BlsModule, ProtocolError, PublicKeyValidationError}; + +pub struct MockBLS {} + +impl BlsModule for MockBLS { + fn validate_public_key(&self, _pk: &[u8]) -> Result<(), PublicKeyValidationError> { + panic!("BLS signatures are not implemented"); + } + + fn verify_signature( + &self, + _signature: &[u8], + _data: &[u8], + _public_key: &[u8], + ) -> Result { + panic!("BLS signatures are not implemented"); + } + + fn private_key_to_public_key(&self, _private_key: &[u8]) -> Result, ProtocolError> { + panic!("BLS signatures are not implemented"); + } + + fn sign(&self, _data: &[u8], _private_key: &[u8]) -> Result, ProtocolError> { + panic!("BLS signatures are not implemented"); + } +} diff --git a/src/partial_identity/mod.rs b/src/partial_identity/mod.rs new file mode 100644 index 0000000..5a16929 --- /dev/null +++ b/src/partial_identity/mod.rs @@ -0,0 +1,117 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use dpp::identity::{IdentityPublicKey, PartialIdentity}; +use napi_derive::napi; + +use crate::{ + dynamic_value::{IdentifierLikeNAPI, Uint64String}, + identifier::IdentifierNAPI, + identity_public_key::IdentityPublicKeyNAPI, +}; + +#[derive(Clone)] +#[napi(js_name = "PartialIdentityNAPI")] +pub struct PartialIdentityNAPI(PartialIdentity); + +impl From for PartialIdentityNAPI { + fn from(value: PartialIdentity) -> Self { + Self(value) + } +} + +#[napi] +impl PartialIdentityNAPI { + #[napi(constructor)] + pub fn new( + js_id: IdentifierLikeNAPI, + js_loaded_public_keys: BTreeMap, + balance: Option, + revision: Option, + js_not_found_public_keys: Option>, + ) -> Result { + let id = IdentifierNAPI::try_from(js_id)?; + let loaded_public_keys: BTreeMap = js_loaded_public_keys + .into_iter() + .map(|(k, v)| (k.parse().unwrap(), IdentityPublicKey::from(v.clone()))) + .collect(); + + Ok(PartialIdentityNAPI(PartialIdentity { + id: id.into(), + loaded_public_keys, + balance: balance.map(|b| b.try_into()).transpose()?, + revision: revision.map(|r| r.try_into()).transpose()?, + not_found_public_keys: js_not_found_public_keys + .map(|arr| BTreeSet::from_iter(arr.into_iter())) + .unwrap_or(BTreeSet::new()), + })) + } + + #[napi(getter, js_name = "id")] + pub fn id(&self) -> IdentifierNAPI { + self.0.id.into() + } + + #[napi(getter, js_name = "loadedPublicKeys")] + pub fn loaded_public_keys(&self) -> BTreeMap { + self.0 + .loaded_public_keys + .clone() + .iter() + .map(|(k, v)| (k.to_string(), v.clone().into())) + .collect() + } + + #[napi(getter, js_name = "balance")] + pub fn balance(&self) -> Option { + self.0.balance.map(Into::into) + } + + #[napi(getter, js_name = "revision")] + pub fn revision(&self) -> Option { + self.0.revision.map(Into::into) + } + + #[napi(getter, js_name = "notFoundPublicKeys")] + pub fn not_found_public_keys(&self) -> Vec { + Vec::from_iter(self.0.not_found_public_keys.clone().into_iter()) + } + + #[napi(setter, js_name = "id")] + pub fn set_id(&mut self, js_id: IdentifierLikeNAPI) -> Result<(), napi::Error> { + let identifier = IdentifierNAPI::try_from(js_id)?; + + self.0.id = identifier.into(); + + Ok(()) + } + + #[napi(setter, js_name = "loadedPublicKeys")] + pub fn set_loaded_public_keys( + &mut self, + loaded_public_keys: BTreeMap, + ) { + self.0.loaded_public_keys = loaded_public_keys + .into_iter() + .map(|(k, v)| (k.parse().unwrap(), IdentityPublicKey::from(v.clone()))) + .collect(); + } + + #[napi(setter, js_name = "balance")] + pub fn set_balance(&mut self, balance: Option) -> Result<(), napi::Error> { + self.0.balance = balance.map(|bal| bal.try_into()).transpose()?; + Ok(()) + } + + #[napi(setter, js_name = "revision")] + pub fn set_revision(&mut self, revision: Option) -> Result<(), napi::Error> { + self.0.revision = revision.map(|rev| rev.try_into()).transpose()?; + Ok(()) + } + + #[napi(setter, js_name = "notFoundPublicKeys")] + pub fn set_not_found_public_keys(&mut self, keys: Option>) { + self.0.not_found_public_keys = keys + .map(|k| BTreeSet::from_iter(k.into_iter())) + .unwrap_or(BTreeSet::new()); + } +} diff --git a/src/private_key/mod.rs b/src/private_key/mod.rs new file mode 100644 index 0000000..4c84023 --- /dev/null +++ b/src/private_key/mod.rs @@ -0,0 +1,225 @@ +use dpp::dashcore::hashes::hex::FromHex; +use dpp::dashcore::key::Secp256k1; +use dpp::dashcore::secp256k1::Message; +use dpp::dashcore::secp256k1::hashes::hex::{Case, DisplayHex}; +use dpp::dashcore::signer::{CompactSignature, double_sha}; +use dpp::dashcore::{Network, PrivateKey, base58}; +use dpp::platform_value::string_encoding::{Encoding, decode}; +use napi::Either; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::dynamic_value::DynamicValue; +use crate::enums::network::NetworkNAPI; +use crate::public_key::PublicKeyNAPI; + +#[derive(Debug, Clone)] +#[napi(js_name = "PrivateKeyNAPI")] +pub struct PrivateKeyNAPI(PrivateKey); + +#[napi] +impl PrivateKeyNAPI { + #[napi(constructor)] + pub fn new( + value: Either, + js_network: NetworkNAPI, + ) -> Result { + PrivateKeyNAPI::from_js_value(value, js_network) + } + + #[napi(js_name = "fromWIF")] + pub fn from_wif(wif: String) -> Result { + let pk = PrivateKey::from_wif(wif.as_str()) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; + + Ok(PrivateKeyNAPI(pk)) + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes(bytes: Uint8Array, js_network: NetworkNAPI) -> Result { + let bytes_vec = bytes.to_vec(); + + let fixed_bytes: [u8; 32] = bytes_vec.as_slice().try_into().map_err(|_| { + napi::Error::new( + napi::Status::GenericFailure, + "Cannot parse private key from bytes", + ) + })?; + + let pk = PrivateKey::from_byte_array(&fixed_bytes, js_network.into()) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; + + Ok(PrivateKeyNAPI(pk)) + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(hex_key: String, js_network: NetworkNAPI) -> Result { + let bytes = Vec::from_hex(hex_key.as_str()) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; + + PrivateKeyNAPI::from_bytes(bytes.into(), js_network) + } + + #[napi(js_name = "getPublicKey")] + pub fn get_public_key(&self) -> PublicKeyNAPI { + let secp = Secp256k1::new(); + + let public_key = self.0.public_key(&secp); + + public_key.into() + } +} + +#[napi] +impl PrivateKeyNAPI { + #[napi(js_name = "getNetwork")] + pub fn get_network(&self) -> String { + NetworkNAPI::from(self.0.network).into() + } + + #[napi(js_name = "WIF")] + pub fn get_wif(&self) -> String { + self.0.to_wif() + } + + #[napi(js_name = "bytes")] + pub fn get_bytes(&self) -> Uint8Array { + self.0.to_bytes().into() + } + + #[napi(js_name = "hex")] + pub fn get_hex(&self) -> String { + self.0.to_bytes().to_hex_string(Case::Upper) + } + + #[napi(js_name = "getPublicKeyHash")] + pub fn get_public_key_hash(&self) -> String { + let secp = Secp256k1::new(); + + self.0.public_key(&secp).pubkey_hash().to_hex() + } + + #[napi(js_name = "sign")] + pub fn sign(&self, data: Uint8Array) -> Result { + let data_vec = data.to_vec(); + + let data_hash = double_sha(data_vec); + self.sign_hash(data_hash.into()).into() + } + + #[napi(js_name = "signHash")] + pub fn sign_hash(&self, data_hash: Uint8Array) -> Result { + let data_hash_vec = data_hash.to_vec(); + + let secp = Secp256k1::new(); + let msg = Message::from_digest(data_hash_vec.as_slice().try_into().map_err(|_| { + napi::Error::new( + napi::Status::InvalidArg, + "Cannot convert data_hash to [u8; 32]", + ) + })?); + + let signature = secp + .sign_ecdsa_recoverable(&msg, &self.0.inner) + .to_compact_signature(self.0.compressed); + + Ok(signature.to_vec().into()) + } +} + +impl PrivateKeyNAPI { + pub fn network(&self) -> Network { + self.0.network + } + + pub fn from_js_value( + value: Either, + js_network: NetworkNAPI, + ) -> Result { + match value { + Either::A(value) => { + match value { + DynamicValue::Text(str) => { + if str.len() == 64 { + // raw hex + PrivateKeyNAPI::from_hex(str, js_network) + } else { + // base58 check + let key_base58 = base58::decode_check(&str).map_err(|err| { + napi::Error::new( + napi::Status::InvalidArg, + format!("Private Key error read wif ({})", err), + ) + })?; + + if key_base58.clone().len() == 33 || key_base58.clone().len() == 34 { + PrivateKeyNAPI::from_wif(str) + } else { + Err(napi::Error::new( + napi::Status::InvalidArg, + format!( + "Private key decoded wif must be 38 byte length ({})", + key_base58.clone().len() + ), + )) + } + } + } + DynamicValue::Bytes(bytes) => PrivateKeyNAPI::from_bytes(bytes, js_network), + _ => Err(napi::Error::new( + napi::Status::InvalidArg, + "Cannot parse private key", + ))?, + } + } + Either::B(key) => Ok(key.clone()), + } + } + + pub fn bytes_from_js_value( + value: Either, + ) -> Result { + match value { + Either::A(value) => { + match value { + DynamicValue::Text(str) => { + if str.len() == 64 { + // raw hex + Ok(decode(&str, Encoding::Hex) + .map_err(|err| { + napi::Error::new(napi::Status::InvalidArg, err.to_string()) + })? + .into()) + } else { + // base58 check + let key_base58 = base58::decode_check(&str).map_err(|err| { + napi::Error::new( + napi::Status::InvalidArg, + format!("Private Key error read wif ({})", err), + ) + })?; + + if key_base58.clone().len() == 33 || key_base58.clone().len() == 34 { + Ok(PrivateKeyNAPI::from_wif(str)?.get_bytes()) + } else { + Err(napi::Error::new( + napi::Status::InvalidArg, + format!( + "Private key decoded wif must be 38 byte length ({})", + key_base58.clone().len() + ), + )) + } + } + } + DynamicValue::Bytes(bytes) => Ok(bytes), + _ => Err(napi::Error::new( + napi::Status::InvalidArg, + "Cannot create private key", + )), + } + } + Either::B(key) => Ok(key.get_bytes()), + } + } +} diff --git a/src/public_key/mod.rs b/src/public_key/mod.rs new file mode 100644 index 0000000..9824715 --- /dev/null +++ b/src/public_key/mod.rs @@ -0,0 +1,164 @@ +use dpp::dashcore::key::constants; +use dpp::dashcore::{PublicKey, secp256k1}; +use dpp::util::hash::ripemd160_sha256; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +#[napi(js_name = "PublicKeyNAPI")] +pub struct PublicKeyNAPI(PublicKey); + +impl From for PublicKeyNAPI { + fn from(pk: PublicKey) -> Self { + Self(pk) + } +} + +impl From for PublicKey { + fn from(pk: PublicKeyNAPI) -> Self { + pk.0 + } +} + +#[napi] +impl PublicKeyNAPI { + #[napi(constructor)] + pub fn new( + compressed: bool, + public_key_bytes: Uint8Array, + ) -> Result { + let inner = match compressed { + true => { + if public_key_bytes.len() != constants::PUBLIC_KEY_SIZE { + return Err(napi::Error::new( + napi::Status::InvalidArg, + format!( + "compressed public key size must be equal to {}", + constants::PUBLIC_KEY_SIZE + ), + )); + } + + let public_key_bytes_vec = public_key_bytes.to_vec(); + + secp256k1::PublicKey::from_byte_array_compressed( + &public_key_bytes_vec.try_into().map_err(|_| { + napi::Error::new(napi::Status::InvalidArg, "Invalid public key bytes") + })?, + ) + } + false => { + if public_key_bytes.len() != constants::UNCOMPRESSED_PUBLIC_KEY_SIZE { + return Err(napi::Error::new( + napi::Status::InvalidArg, + format!( + "uncompressed public key size must be equal to {}", + constants::UNCOMPRESSED_PUBLIC_KEY_SIZE + ), + )); + } + + let public_key_bytes_vec = public_key_bytes.to_vec(); + + secp256k1::PublicKey::from_byte_array_uncompressed( + &public_key_bytes_vec.try_into().map_err(|_| { + napi::Error::new(napi::Status::InvalidArg, "Invalid public key bytes") + })?, + ) + } + } + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; + + Ok(PublicKeyNAPI(PublicKey { compressed, inner })) + } + + #[napi(getter, js_name = "compressed")] + pub fn compressed(&self) -> bool { + self.0.compressed + } + + #[napi(getter, js_name = "inner")] + pub fn inner(&self) -> Uint8Array { + match self.0.compressed { + true => self.0.inner.serialize().into(), + false => self.0.inner.serialize_uncompressed().into(), + } + } + + #[napi(setter, js_name = "compressed")] + pub fn set_compressed(&mut self, compressed: bool) { + self.0.compressed = compressed; + } + + #[napi(setter, js_name = "inner")] + pub fn set_inner(&mut self, inner: Uint8Array) -> Result<(), napi::Error> { + match inner.len() == constants::PUBLIC_KEY_SIZE { + true => { + let inner_vec = inner.to_vec(); + + self.0.compressed = true; + self.0.inner = secp256k1::PublicKey::from_byte_array_compressed( + &inner_vec.try_into().map_err(|_| { + napi::Error::new( + napi::Status::GenericFailure, + "Cannot convert Uint8Array to byte array", + ) + })?, + ) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))? + } + false => { + let inner_vec = inner.to_vec(); + + if inner_vec.len() != constants::UNCOMPRESSED_PUBLIC_KEY_SIZE { + return Err(napi::Error::new( + napi::Status::GenericFailure, + format!( + "uncompressed public key size must be equal to {}", + constants::UNCOMPRESSED_PUBLIC_KEY_SIZE + ), + )); + } + + self.0.compressed = false; + self.0.inner = secp256k1::PublicKey::from_byte_array_uncompressed( + &inner_vec.try_into().map_err(|_| { + napi::Error::new( + napi::Status::GenericFailure, + "Cannot convert Uint8Array to byte array", + ) + })?, + ) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?; + } + }; + + Ok(()) + } + + #[napi(js_name = getPublicKeyHash)] + pub fn get_public_key_hash(&self) -> String { + self.0.pubkey_hash().to_hex() + } + + #[napi(js_name = hash160)] + pub fn get_public_key_hash_160(&self) -> Uint8Array { + ripemd160_sha256(self.0.to_bytes().as_slice()) + .to_vec() + .into() + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Uint8Array { + self.0.to_bytes().into() + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes(bytes: Uint8Array) -> Result { + let bytes_vec = bytes.to_vec(); + + Ok(PublicKeyNAPI( + PublicKey::from_slice(bytes_vec.as_slice()) + .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))?, + )) + } +} diff --git a/src/state_transition/mod.rs b/src/state_transition/mod.rs new file mode 100644 index 0000000..9003f8e --- /dev/null +++ b/src/state_transition/mod.rs @@ -0,0 +1,642 @@ +use dpp::dashcore::secp256k1::hashes::hex::Case::Lower; +use dpp::dashcore::secp256k1::hashes::hex::DisplayHex; +use dpp::data_contract::serialized_version::DataContractInSerializationFormat; +use dpp::identity::KeyType; +use dpp::platform_value::BinaryData; +use dpp::platform_value::string_encoding::{Encoding, decode, encode}; +use dpp::serialization::{PlatformDeserializable, PlatformSerializable, Signable}; +use dpp::state_transition::StateTransition::{ + Batch, DataContractCreate, DataContractUpdate, IdentityCreditTransfer, + IdentityCreditWithdrawal, IdentityTopUp, IdentityUpdate, MasternodeVote, +}; +use dpp::state_transition::batch_transition::BatchTransition; +use dpp::state_transition::batch_transition::batched_transition::BatchedTransition; +use dpp::state_transition::batch_transition::batched_transition::document_transition::DocumentTransitionV0Methods; +use dpp::state_transition::batch_transition::batched_transition::token_transition::TokenTransitionV0Methods; +use dpp::state_transition::batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0; +use dpp::state_transition::data_contract_create_transition::DataContractCreateTransition; +use dpp::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0; +use dpp::state_transition::data_contract_update_transition::DataContractUpdateTransition; +use dpp::state_transition::data_contract_update_transition::accessors::DataContractUpdateTransitionAccessorsV0; +use dpp::state_transition::identity_credit_transfer_transition::accessors::IdentityCreditTransferTransitionAccessorsV0; +use dpp::state_transition::identity_credit_withdrawal_transition::accessors::IdentityCreditWithdrawalTransitionAccessorsV0; +use dpp::state_transition::identity_topup_transition::accessors::IdentityTopUpTransitionAccessorsV0; +use dpp::state_transition::identity_update_transition::accessors::IdentityUpdateTransitionAccessorsV0; +use dpp::state_transition::masternode_vote_transition::MasternodeVoteTransition; +use dpp::state_transition::masternode_vote_transition::accessors::MasternodeVoteTransitionAccessorsV0; +use dpp::state_transition::{ + StateTransition, StateTransitionIdentitySigned, StateTransitionSigningOptions, +}; +use napi::Either; +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; +use sha2::{Digest, Sha256}; + +use crate::dynamic_value::{DynamicValue, IdentifierLikeNAPI, Uint64String}; +use crate::enums::key_type::KeyTypeNAPI; +use crate::enums::purpose::PurposeNAPI; +use crate::enums::security_level::SecurityLevelNAPI; +use crate::identifier::IdentifierNAPI; +use crate::identity_public_key::IdentityPublicKeyNAPI; +use crate::mock_bls::MockBLS; +use crate::private_key::PrivateKeyNAPI; +use crate::utils::WithJsError; + +#[derive(Clone)] +#[napi(js_name = "StateTransitionNAPI")] +pub struct StateTransitionNAPI(StateTransition); + +impl From for StateTransitionNAPI { + fn from(transition: StateTransition) -> Self { + StateTransitionNAPI(transition) + } +} + +impl From for StateTransition { + fn from(transition: StateTransitionNAPI) -> Self { + transition.0 + } +} + +#[napi] +impl StateTransitionNAPI { + #[napi(js_name = "sign")] + pub fn sign( + &mut self, + js_private_key: Either, + public_key: &IdentityPublicKeyNAPI, + ) -> Result { + let private_key_bytes = PrivateKeyNAPI::bytes_from_js_value(js_private_key)?.to_vec(); + + self.0 + .sign( + &public_key.clone().into(), + private_key_bytes.as_slice(), + &MockBLS {}, + ) + .with_js_error()?; + + self.0 + .serialize_to_bytes() + .with_js_error() + .map(|bytes| bytes.into()) + } + + #[napi(js_name = "signByPrivateKey")] + pub fn sign_by_private_key( + &mut self, + js_private_key: Either, + key_id: Option, + js_key_type: Option, + ) -> Result { + let private_key_bytes = PrivateKeyNAPI::bytes_from_js_value(js_private_key)?.to_vec(); + + let key_type = match js_key_type.is_none() { + true => KeyTypeNAPI::ECDSA_SECP256K1, + false => KeyTypeNAPI::try_from(js_key_type.unwrap())?, + }; + + let _sig = self + .0 + .sign_by_private_key( + &private_key_bytes.as_slice(), + KeyType::from(key_type), + &MockBLS {}, + ) + .with_js_error(); + + match key_id { + Some(key_id) => self.0.set_signature_public_key_id(key_id), + None => {} + } + + self.0 + .serialize_to_bytes() + .with_js_error() + .map(|bytes| bytes.into()) + } + + #[napi(js_name = "verifyPublicKey")] + pub fn verify_public_key( + &self, + public_key: &IdentityPublicKeyNAPI, + js_allow_signing_with_any_security_level: Option, + js_allow_signing_with_any_purpose: Option, + ) -> Result<(), napi::Error> { + let allow_signing_with_any_security_level = + js_allow_signing_with_any_security_level.unwrap_or(false); + let allow_signing_with_any_purpose = js_allow_signing_with_any_purpose.unwrap_or(false); + + match &self.0 { + DataContractCreate(st) => { + st.verify_public_key_level_and_purpose( + &public_key.clone().into(), + StateTransitionSigningOptions { + allow_signing_with_any_security_level, + allow_signing_with_any_purpose, + }, + ) + .with_js_error()?; + + st.verify_public_key_is_enabled(&public_key.clone().into()) + .with_js_error()?; + } + DataContractUpdate(st) => { + st.verify_public_key_level_and_purpose( + &public_key.clone().into(), + StateTransitionSigningOptions { + allow_signing_with_any_security_level, + allow_signing_with_any_purpose, + }, + ) + .with_js_error()?; + + st.verify_public_key_is_enabled(&public_key.clone().into()) + .with_js_error()?; + } + Batch(st) => { + st.verify_public_key_level_and_purpose( + &public_key.clone().into(), + StateTransitionSigningOptions { + allow_signing_with_any_security_level, + allow_signing_with_any_purpose, + }, + ) + .with_js_error()?; + + st.verify_public_key_is_enabled(&public_key.clone().into()) + .with_js_error()?; + } + IdentityCreditWithdrawal(st) => { + st.verify_public_key_level_and_purpose( + &public_key.clone().into(), + StateTransitionSigningOptions { + allow_signing_with_any_security_level, + allow_signing_with_any_purpose, + }, + ) + .with_js_error()?; + + st.verify_public_key_is_enabled(&public_key.clone().into()) + .with_js_error()?; + } + IdentityUpdate(st) => { + st.verify_public_key_level_and_purpose( + &public_key.clone().into(), + StateTransitionSigningOptions { + allow_signing_with_any_security_level, + allow_signing_with_any_purpose, + }, + ) + .with_js_error()?; + + st.verify_public_key_is_enabled(&public_key.clone().into()) + .with_js_error()?; + } + IdentityCreditTransfer(st) => { + st.verify_public_key_level_and_purpose( + &public_key.clone().into(), + StateTransitionSigningOptions { + allow_signing_with_any_security_level, + allow_signing_with_any_purpose, + }, + ) + .with_js_error()?; + + st.verify_public_key_is_enabled(&public_key.clone().into()) + .with_js_error()?; + } + MasternodeVote(st) => { + st.verify_public_key_level_and_purpose( + &public_key.clone().into(), + StateTransitionSigningOptions { + allow_signing_with_any_security_level, + allow_signing_with_any_purpose, + }, + ) + .with_js_error()?; + + st.verify_public_key_is_enabled(&public_key.clone().into()) + .with_js_error()?; + } + _ => {} + } + + Ok(()) + } + + #[napi(js_name = "bytes")] + pub fn to_bytes(&self) -> Result { + let bytes = self.0.serialize_to_bytes().with_js_error()?; + + Ok(bytes.into()) + } + + #[napi(js_name = "hex")] + pub fn to_hex(&self) -> Result { + let bytes = self.0.serialize_to_bytes().with_js_error()?; + + Ok(encode(bytes.as_slice(), Encoding::Hex)) + } + + #[napi(js_name = "base64")] + pub fn to_base64(&self) -> Result { + let bytes = self.0.serialize_to_bytes().with_js_error()?; + + Ok(encode(bytes.as_slice(), Encoding::Base64)) + } + + #[napi(js_name = "fromBytes")] + pub fn from_bytes(js_bytes: Uint8Array) -> Result { + let bytes = js_bytes.to_vec(); + + let st = StateTransition::deserialize_from_bytes(bytes.as_slice()).with_js_error()?; + + Ok(st.into()) + } + + #[napi(js_name = "fromHex")] + pub fn from_hex(hex: String) -> Result { + let bytes = decode(&hex, Encoding::Hex) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + + let st = StateTransition::deserialize_from_bytes(bytes.as_slice()).with_js_error()?; + + Ok(st.into()) + } + + #[napi(js_name = "fromBase64")] + pub fn from_base64(base64: String) -> Result { + let bytes = decode(&base64, Encoding::Base64) + .map_err(|err| napi::Error::new(napi::Status::InvalidArg, err.to_string()))?; + + let st = StateTransition::deserialize_from_bytes(bytes.as_slice()).with_js_error()?; + + Ok(st.into()) + } + + #[napi(js_name = "hash")] + pub fn get_hash(&self, skip_signature: bool) -> Result { + let payload: Vec; + + if skip_signature { + payload = self.0.signable_bytes().with_js_error()?; + } else { + payload = dpp::serialization::PlatformSerializable::serialize_to_bytes(&self.0) + .with_js_error()?; + } + + Ok(Sha256::digest(payload).to_hex_string(Lower)) + } + + #[napi(js_name = "getActionName")] + pub fn get_action_name(&self) -> String { + self.0.name() + } + + #[napi(js_name = "getActionType")] + pub fn get_action_type(&self) -> String { + match self.0 { + DataContractCreate(_) => "DATA_CONTRACT_CREATE", + Batch(_) => "BATCH", + StateTransition::IdentityCreate(_) => "IDENTITY_CREATE", + IdentityTopUp(_) => "IDENTITY_TOP_UP", + DataContractUpdate(_) => "DATA_CONTRACT_UPDATE", + IdentityUpdate(_) => "IDENTITY_UPDATE", + IdentityCreditWithdrawal(_) => "IDENTITY_CREDIT_WITHDRAWAL", + IdentityCreditTransfer(_) => "IDENTITY_CREDIT_TRANSFER", + MasternodeVote(_) => "MASTERNODE_VOTE", + } + .to_string() + } + + #[napi(js_name = "getActionTypeNumber")] + pub fn get_action_type_number(&self) -> u8 { + match self.0 { + DataContractCreate(_) => 0, + Batch(_) => 1, + StateTransition::IdentityCreate(_) => 2, + IdentityTopUp(_) => 3, + DataContractUpdate(_) => 4, + IdentityUpdate(_) => 5, + IdentityCreditWithdrawal(_) => 6, + IdentityCreditTransfer(_) => 7, + MasternodeVote(_) => 8, + } + } + + #[napi(js_name = "getOwnerId")] + pub fn get_owner_id(&self) -> IdentifierNAPI { + self.0.owner_id().into() + } + + #[napi(getter, js_name = "signature")] + pub fn get_signature(&self) -> Uint8Array { + self.0.signature().to_vec().into() + } + + #[napi(getter, js_name = "signaturePublicKeyId")] + pub fn get_signature_public_key_id(&self) -> Option { + self.0.signature_public_key_id() + } + + #[napi(getter, js_name = "userFeeIncrease")] + pub fn get_user_fee_increase(&self) -> u16 { + self.0.user_fee_increase() + } + + #[napi(js_name = "getPurposeRequirement")] + pub fn get_purpose_requirement(&self) -> Option> { + let requirements = self.0.purpose_requirement(); + + match requirements { + None => None, + Some(req) => Some( + req.iter() + .map(|purpose| PurposeNAPI::from(purpose.clone())) + .map(String::from) + .collect(), + ), + } + } + + #[napi(js_name = "getKeyLevelRequirement")] + pub fn get_key_level_requirement( + &self, + js_purpose: DynamicValue, + ) -> Result>, napi::Error> { + let purpose = PurposeNAPI::try_from(js_purpose)?; + + let requirements = self.0.security_level_requirement(purpose.into()); + + match requirements { + None => Ok(None), + Some(req) => Ok(Some( + req.iter() + .map(|security_level| SecurityLevelNAPI::from(security_level.clone())) + .map(String::from) + .collect(), + )), + } + } + + #[napi(js_name = "getIdentityContractNonce")] + pub fn get_identity_contract_nonce(&self) -> Option { + match self.0.clone() { + DataContractCreate(_) => None, + DataContractUpdate(contract_update) => { + Some(contract_update.identity_contract_nonce().into()) + } + Batch(batch) => match batch { + BatchTransition::V0(v0) => { + Some(v0.transitions.first()?.identity_contract_nonce().into()) + } + BatchTransition::V1(v1) => match v1.transitions.first()? { + BatchedTransition::Document(doc_batch) => { + Some(doc_batch.identity_contract_nonce().into()) + } + BatchedTransition::Token(token_batch) => { + Some(token_batch.identity_contract_nonce().into()) + } + }, + }, + StateTransition::IdentityCreate(_) => None, + IdentityTopUp(_) => None, + IdentityCreditWithdrawal(_) => None, + IdentityUpdate(_) => None, + IdentityCreditTransfer(_) => None, + MasternodeVote(_) => None, + } + } + + #[napi(js_name = "getIdentityNonce")] + pub fn get_identity_nonce(&self) -> Option { + match self.0.clone() { + DataContractCreate(contract_create) => Some(contract_create.identity_nonce().into()), + DataContractUpdate(_) => None, + Batch(_) => None, + StateTransition::IdentityCreate(_) => None, + IdentityTopUp(_) => None, + IdentityCreditWithdrawal(withdrawal) => Some(withdrawal.nonce().into()), + IdentityUpdate(identity_update) => Some(identity_update.nonce().into()), + IdentityCreditTransfer(credit_transfer) => Some(credit_transfer.nonce().into()), + MasternodeVote(mn_vote) => Some(mn_vote.nonce().into()), + } + } + + #[napi(setter, js_name = "signature")] + pub fn set_signature(&mut self, signature: Uint8Array) { + self.0.set_signature(BinaryData::from(signature.to_vec())) + } + + #[napi(setter, js_name = "signaturePublicKeyId")] + pub fn set_signature_public_key_id(&mut self, key_id: u32) { + self.0.set_signature_public_key_id(key_id) + } + + #[napi(setter, js_name = "userFeeIncrease")] + pub fn set_user_fee_increase(&mut self, user_fee_increase: u16) { + self.0.set_user_fee_increase(user_fee_increase) + } + + #[napi(js_name = "setOwnerId")] + pub fn set_owner_id(&mut self, js_owner_id: IdentifierLikeNAPI) -> Result<(), napi::Error> { + let owner_id = IdentifierNAPI::try_from(js_owner_id)?; + + match self.0.clone() { + DataContractCreate(mut contract_create) => { + let new_contract = match contract_create.data_contract().clone() { + DataContractInSerializationFormat::V0(mut v0) => { + v0.owner_id = owner_id.into(); + + DataContractInSerializationFormat::V0(v0) + } + DataContractInSerializationFormat::V1(mut v1) => { + v1.owner_id = owner_id.into(); + + DataContractInSerializationFormat::V1(v1) + } + }; + + contract_create.set_data_contract(new_contract); + + self.0 = DataContractCreate(contract_create); + } + DataContractUpdate(mut contract_update) => { + let new_contract = match contract_update.data_contract().clone() { + DataContractInSerializationFormat::V0(mut v0) => { + v0.owner_id = owner_id.into(); + + DataContractInSerializationFormat::V0(v0) + } + DataContractInSerializationFormat::V1(mut v1) => { + v1.owner_id = owner_id.into(); + + DataContractInSerializationFormat::V1(v1) + } + }; + + contract_update.set_data_contract(new_contract); + + self.0 = DataContractUpdate(contract_update); + } + Batch(mut batch) => { + batch = match batch { + BatchTransition::V0(mut v0) => { + v0.owner_id = owner_id.into(); + + BatchTransition::V0(v0) + } + BatchTransition::V1(mut v1) => { + v1.owner_id = owner_id.into(); + + BatchTransition::V1(v1) + } + }; + + self.0 = Batch(batch); + } + StateTransition::IdentityCreate(_) => { + Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set owner for identity create transition", + ))?; + } + IdentityTopUp(mut top_up) => { + top_up.set_identity_id(owner_id.into()); + + self.0 = IdentityTopUp(top_up); + } + IdentityCreditWithdrawal(mut withdrawal) => { + withdrawal.set_identity_id(owner_id.into()); + + self.0 = IdentityCreditWithdrawal(withdrawal); + } + IdentityUpdate(mut identity_update) => { + identity_update.set_identity_id(owner_id.into()); + + self.0 = IdentityUpdate(identity_update); + } + IdentityCreditTransfer(mut credit_transfer) => { + credit_transfer.set_identity_id(owner_id.into()); + + self.0 = IdentityCreditTransfer(credit_transfer); + } + MasternodeVote(mut mn_vote) => { + mn_vote.set_voter_identity_id(owner_id.into()); + + self.0 = MasternodeVote(mn_vote); + } + }; + + Ok(()) + } + + #[napi(js_name = "setIdentityContractNonce")] + pub fn set_identity_contract_nonce(&mut self, nonce: Uint64String) -> Result<(), napi::Error> { + self.0 = match self.0.clone() { + DataContractCreate(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity contract nonce for Data Contract Create", + ))?, + DataContractUpdate(contract_update) => match contract_update { + DataContractUpdateTransition::V0(mut v0) => { + v0.identity_contract_nonce = nonce.try_into()?; + + DataContractUpdateTransition::V0(v0).into() + } + }, + Batch(mut batch) => { + batch.set_identity_contract_nonce(nonce.try_into()?); + + batch.into() + } + StateTransition::IdentityCreate(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity contract nonce for Identity Create", + ))?, + IdentityTopUp(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity contract nonce for Identity Top Up", + ))?, + IdentityCreditWithdrawal(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity contract nonce for Identity Credit Withdrawal", + ))?, + IdentityUpdate(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity contract nonce for Identity Update", + ))?, + IdentityCreditTransfer(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity contract nonce for Identity Credit Transfer", + ))?, + MasternodeVote(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity contract nonce for Masternode Vote", + ))?, + }; + + Ok(()) + } + + #[napi(js_name = "setIdentityNonce")] + pub fn set_identity_nonce(&mut self, nonce: Uint64String) -> Result<(), napi::Error> { + self.0 = match self.0.clone() { + DataContractCreate(mut contract_create) => { + contract_create = match contract_create { + DataContractCreateTransition::V0(mut v0) => { + v0.identity_nonce = nonce.try_into()?; + v0.into() + } + }; + + contract_create.into() + } + DataContractUpdate(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity nonce for Data Contract Update", + ))?, + Batch(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity nonce for Batch", + ))?, + StateTransition::IdentityCreate(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity nonce for Identity Create", + ))?, + IdentityTopUp(_) => Err(napi::Error::new( + napi::Status::GenericFailure, + "Cannot set identity nonce for Identity Top Up", + ))?, + IdentityCreditWithdrawal(mut withdrawal) => { + withdrawal.set_nonce(nonce.try_into()?); + + withdrawal.into() + } + IdentityUpdate(mut identity_update) => { + identity_update.set_nonce(nonce.try_into()?); + + identity_update.into() + } + IdentityCreditTransfer(mut credit_transfer) => { + credit_transfer.set_nonce(nonce.try_into()?); + + credit_transfer.into() + } + MasternodeVote(mut mn_vote) => { + mn_vote = match mn_vote { + MasternodeVoteTransition::V0(mut v0) => { + v0.nonce = nonce.try_into()?; + + v0.into() + } + }; + + mn_vote.into() + } + }; + + Ok(()) + } +} diff --git a/src/utils.rs b/src/utils.rs index 5d8d4e8..73fb461 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,9 @@ -use dpp::ProtocolError; -use napi::Status; +use dpp::{ProtocolError, platform_value::Value}; +use napi::{ + Env, Status, + bindgen_prelude::{Function, JsObjectValue, JsValuesTuple, Object}, +}; +use serde_json::Value as JsonValue; pub trait WithJsError { fn with_js_error(self) -> Result; @@ -22,3 +26,26 @@ impl WithJsError for Result { } } } + +pub fn with_serde_to_json_value(data: Object) -> Result { + let json: Object = Env::from(data.env()) + .get_global()? + .get_named_property("JSON")?; + + let stringify: Option> = json.get("stringify")?; + + if stringify.is_none() { + return Err(napi::Error::new( + Status::GenericFailure, + "JSON.stringify not found", + )); + } + + let value: JsonValue = serde_json::from_str(&stringify.unwrap().call(data)?) + .map_err(|e| napi::Error::new(napi::Status::GenericFailure, format!("{e:#}")))?; + Ok(value) +} + +pub fn with_serde_to_platform_value(data: Object) -> Result { + Ok(with_serde_to_json_value(data.clone())?.into()) +} diff --git a/templates/binaries/wasm.js b/templates/binaries/wasm.js index eaf79b6..63762e0 100644 --- a/templates/binaries/wasm.js +++ b/templates/binaries/wasm.js @@ -19,6 +19,9 @@ const wasi = new WASI({ const emnapiContext = getDefaultContext() +emnapiContext.feature.supportNewFunction = false +emnapiContext.feature.supportBigInt = false + const __sharedMemory = new WebAssembly.Memory({ initial: 1000, maximum: 2000, diff --git a/tests/unit/Identifier.spec.ts b/tests/unit/Identifier.spec.ts index 7360114..cb09a70 100644 --- a/tests/unit/Identifier.spec.ts +++ b/tests/unit/Identifier.spec.ts @@ -1,80 +1,105 @@ -import {IdentifierWASM} from '../../dist/src/wasm.js' +import { IdentifierWASM } from "../../dist/src/wasm.js"; -let identifierBytes: Uint8Array +let identifierBytes: Uint8Array; -describe('Identifier', function () { +describe("Identifier", function () { beforeAll(async function () { - identifierBytes = Uint8Array.from([9, 40, 40, 237, 192, 129, 211, 186, 26, 84, 240, 67, 37, 155, 148, 19, 104, 242, 199, 24, 136, 27, 6, 169, 211, 71, 136, 59, 33, 191, 227, 19]) - }) - - describe('serialization / deserialization', function () { - test('should allows to create Identifier from base58', function () { - const identifier = IdentifierWASM.fromBase58('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U') - - expect(identifier.bytes()).toEqual(identifierBytes) - }) - - test('should allows to create Identifier from base64', function () { - const identifier = IdentifierWASM.fromBase64('CSgo7cCB07oaVPBDJZuUE2jyxxiIGwap00eIOyG/4xM=') - - expect(identifier.bytes()).toEqual(identifierBytes) - }) - - test('should allows to create Identifier from hex', function () { - const identifier = IdentifierWASM.fromHex('092828edc081d3ba1a54f043259b941368f2c718881b06a9d347883b21bfe313') - - expect(identifier.bytes()).toEqual(identifierBytes) - }) - - test('should allows to create Identifier from bytes', function () { - const identifier = IdentifierWASM.fromBytes(identifierBytes) - - expect(identifier.bytes()).toEqual(identifierBytes) - }) - - test('should allows to create Identifier from Identifier', function () { - const identifier = IdentifierWASM.fromBytes(identifierBytes) - const identifier2 = new IdentifierWASM(identifier) - - expect(identifier2.bytes()).toEqual(identifierBytes) - }) - - test('should allows to create Identifier from bytes in constructor', function () { - const identifier = new IdentifierWASM(identifierBytes) - - expect(identifier.bytes()).toEqual(identifierBytes) - }) - - test('should allows to create Identifier from base58 in constructor', function () { - const identifier = new IdentifierWASM('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U') - - expect(identifier.bytes()).toEqual(identifierBytes) - }) - }) - - describe('getters', function () { - test('should allow to get identifier base58', function () { - const identifier = IdentifierWASM.fromBase58('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U') - - expect(identifier.base58()).toEqual('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U') - }) - - test('should allow to get identifier base64', function () { - const identifier = IdentifierWASM.fromBase58('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U') - - expect(identifier.base64()).toEqual('CSgo7cCB07oaVPBDJZuUE2jyxxiIGwap00eIOyG/4xM=') - }) - - test('should allow to get identifier hex', function () { - const identifier = IdentifierWASM.fromBase58('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U') - - expect(identifier.hex()).toEqual('092828edc081d3ba1a54f043259b941368f2c718881b06a9d347883b21bfe313') - }) - - test('should allow to get identifier bytes', function () { - const identifier = IdentifierWASM.fromBase58('ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U') - - expect(identifier.bytes()).toEqual(identifierBytes) - }) - }) -}) + identifierBytes = Uint8Array.from([ + 9, 40, 40, 237, 192, 129, 211, 186, 26, 84, 240, 67, 37, 155, 148, 19, + 104, 242, 199, 24, 136, 27, 6, 169, 211, 71, 136, 59, 33, 191, 227, 19, + ]); + }); + + describe("serialization / deserialization", function () { + test("should allows to create Identifier from base58", function () { + const identifier = IdentifierWASM.fromBase58( + "ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U", + ); + + expect(identifier.bytes()).toEqual(identifierBytes); + }); + + test("should allows to create Identifier from base64", function () { + const identifier = IdentifierWASM.fromBase64( + "CSgo7cCB07oaVPBDJZuUE2jyxxiIGwap00eIOyG/4xM=", + ); + + expect(identifier.bytes()).toEqual(identifierBytes); + }); + + test("should allows to create Identifier from hex", function () { + const identifier = IdentifierWASM.fromHex( + "092828edc081d3ba1a54f043259b941368f2c718881b06a9d347883b21bfe313", + ); + + expect(identifier.bytes()).toEqual(identifierBytes); + }); + + test("should allows to create Identifier from bytes", function () { + const identifier = IdentifierWASM.fromBytes(identifierBytes); + + expect(identifier.bytes()).toEqual(identifierBytes); + }); + + test("should allows to create Identifier from Identifier", function () { + const identifier = IdentifierWASM.fromBytes(identifierBytes); + const identifier2 = new IdentifierWASM(identifier); + + expect(identifier2.bytes()).toEqual(identifierBytes); + }); + + test("should allows to create Identifier from bytes in constructor", function () { + const identifier = new IdentifierWASM(identifierBytes); + + expect(identifier.bytes()).toEqual(identifierBytes); + }); + + test("should allows to create Identifier from base58 in constructor", function () { + const identifier = new IdentifierWASM( + "ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U", + ); + + expect(identifier.bytes()).toEqual(identifierBytes); + }); + }); + + describe("getters", function () { + test("should allow to get identifier base58", function () { + const identifier = IdentifierWASM.fromBase58( + "ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U", + ); + + expect(identifier.base58()).toEqual( + "ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U", + ); + }); + + test("should allow to get identifier base64", function () { + const identifier = IdentifierWASM.fromBase58( + "ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U", + ); + + expect(identifier.base64()).toEqual( + "CSgo7cCB07oaVPBDJZuUE2jyxxiIGwap00eIOyG/4xM=", + ); + }); + + test("should allow to get identifier hex", function () { + const identifier = IdentifierWASM.fromBase58( + "ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U", + ); + + expect(identifier.hex()).toEqual( + "092828edc081d3ba1a54f043259b941368f2c718881b06a9d347883b21bfe313", + ); + }); + + test("should allow to get identifier bytes", function () { + const identifier = IdentifierWASM.fromBase58( + "ckBqfQe7LU7vwrwXopyCB4n5phZShjA16BGhNGpsD5U", + ); + + expect(identifier.bytes()).toEqual(identifierBytes); + }); + }); +});