From a3bafe2b7c6f8290a610b43058b19c7abb85eb48 Mon Sep 17 00:00:00 2001 From: Maksym Zub Date: Tue, 16 Jan 2024 13:11:53 +0100 Subject: [PATCH] New shielder smart contract (#42) --- shielder/contract/Cargo.lock | 1151 +++++++++++++++++++ shielder/contract/Cargo.toml | 23 + shielder/contract/errors.rs | 19 + shielder/contract/lib.rs | 125 ++ shielder/contract/merkle.rs | 81 ++ shielder/contract/mocked_zk/account.rs | 63 + shielder/contract/mocked_zk/mod.rs | 9 + shielder/contract/mocked_zk/note.rs | 42 + shielder/contract/mocked_zk/ops.rs | 34 + shielder/contract/mocked_zk/relations.rs | 143 +++ shielder/contract/mocked_zk/tests/merkle.rs | 61 + shielder/contract/mocked_zk/tests/mod.rs | 163 +++ shielder/contract/mocked_zk/traits.rs | 5 + shielder/contract/rust-toolchain.toml | 5 + shielder/contract/traits/mod.rs | 1 + shielder/contract/traits/psp22.rs | 139 +++ shielder/contract/types.rs | 53 + 17 files changed, 2117 insertions(+) create mode 100644 shielder/contract/Cargo.lock create mode 100755 shielder/contract/Cargo.toml create mode 100644 shielder/contract/errors.rs create mode 100755 shielder/contract/lib.rs create mode 100644 shielder/contract/merkle.rs create mode 100644 shielder/contract/mocked_zk/account.rs create mode 100644 shielder/contract/mocked_zk/mod.rs create mode 100644 shielder/contract/mocked_zk/note.rs create mode 100644 shielder/contract/mocked_zk/ops.rs create mode 100644 shielder/contract/mocked_zk/relations.rs create mode 100644 shielder/contract/mocked_zk/tests/merkle.rs create mode 100644 shielder/contract/mocked_zk/tests/mod.rs create mode 100644 shielder/contract/mocked_zk/traits.rs create mode 100644 shielder/contract/rust-toolchain.toml create mode 100644 shielder/contract/traits/mod.rs create mode 100644 shielder/contract/traits/psp22.rs create mode 100644 shielder/contract/types.rs diff --git a/shielder/contract/Cargo.lock b/shielder/contract/Cargo.lock new file mode 100644 index 0000000..dd55856 --- /dev/null +++ b/shielder/contract/Cargo.lock @@ -0,0 +1,1151 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "array-init" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const_env" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9e4f72c6e3398ca6da372abd9affd8f89781fe728869bbf986206e9af9627e" +dependencies = [ + "const_env_impl", +] + +[[package]] +name = "const_env_impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4f51209740b5e1589e702b3044cdd4562cef41b6da404904192ffffb852d62" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom_or_panic" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" +dependencies = [ + "rand", + "rand_core", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ink" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f0047f1e70ddeae00d45c55bb069d94836ac74d895d2f24c3d6a65ba13b628" +dependencies = [ + "derive_more", + "ink_env", + "ink_macro", + "ink_metadata", + "ink_prelude", + "ink_primitives", + "ink_storage", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "ink_allocator" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c1d2eddb445b3ef076dacaa9968a7ff5b80ad5b9b724966fab66a8f4e48bde4" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ink_codegen" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d7479eab1bb62f52a13fca02414a65cd4d9d116bbcae97eafd0808ee5f7ba4" +dependencies = [ + "blake2", + "derive_more", + "either", + "heck", + "impl-serde", + "ink_ir", + "ink_primitives", + "itertools", + "parity-scale-codec", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.48", +] + +[[package]] +name = "ink_engine" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99aae25c6820d9e9dae12f79df79a10cba3c961fe1204a68fefaf72f9ae7b14" +dependencies = [ + "blake2", + "derive_more", + "ink_primitives", + "parity-scale-codec", + "secp256k1", + "sha2", + "sha3", +] + +[[package]] +name = "ink_env" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8f814ac18100f5ba3d265fbc29e1639325660571883184bedf31dcfecd2428" +dependencies = [ + "blake2", + "cfg-if", + "const_env", + "derive_more", + "ink_allocator", + "ink_engine", + "ink_prelude", + "ink_primitives", + "ink_storage_traits", + "num-traits", + "parity-scale-codec", + "paste", + "rlibc", + "scale-decode", + "scale-encode", + "scale-info", + "schnorrkel", + "secp256k1", + "sha2", + "sha3", + "static_assertions", +] + +[[package]] +name = "ink_ir" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ff5a0f5d1a7cbced5e4f0e828cfebae7c94948b27de7b6075937f85f58f8a9" +dependencies = [ + "blake2", + "either", + "ink_prelude", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "ink_macro" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4347620c4ab436e3aa8b0b1ea7da4ea1d09a7281c8d917e01ba2649cabad541" +dependencies = [ + "ink_codegen", + "ink_ir", + "ink_primitives", + "parity-scale-codec", + "proc-macro2", + "quote", + "syn 2.0.48", + "synstructure", +] + +[[package]] +name = "ink_metadata" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75859c7142101f3ad30a763fd0e5468df164e3d424086df8de40531fb54940f3" +dependencies = [ + "derive_more", + "impl-serde", + "ink_prelude", + "ink_primitives", + "linkme", + "scale-info", + "schemars", + "serde", +] + +[[package]] +name = "ink_prelude" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc3fbd55e0cf78e456ad4a92558d55b577bf65944bb37bf7debb8f03ed66a47b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ink_primitives" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c976554c1bd075c6722d0f30cc3a8337b7ebaf487b95a40c9e81097d995f921" +dependencies = [ + "derive_more", + "ink_prelude", + "parity-scale-codec", + "scale-decode", + "scale-encode", + "scale-info", + "xxhash-rust", +] + +[[package]] +name = "ink_storage" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e3cf99814c37c9cf789a94479cc39fe86e8af0be43e5962080e01fa53ca0e5" +dependencies = [ + "array-init", + "cfg-if", + "derive_more", + "ink_env", + "ink_metadata", + "ink_prelude", + "ink_primitives", + "ink_storage_traits", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "ink_storage_traits" +version = "5.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a469c01b48282a459130ca72a75e57feedbb69ab151639f532156d10d574353" +dependencies = [ + "ink_metadata", + "ink_prelude", + "ink_primitives", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "linkme" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b53ad6a33de58864705954edb5ad5d571a010f9e296865ed43dc72a5621b430" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e542a18c94a9b6fcc7adb090fa3ba6b79ee220a16404f325672729f32a66ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[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 = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parity-scale-codec" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +dependencies = [ + "proc-macro-crate 2.0.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rlibc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "scale-bits" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "036575c29af9b6e4866ffb7fa055dbf623fe7a9cc159b33786de6013a6969d89" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "scale-decode" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7789f5728e4e954aaa20cadcc370b99096fb8645fca3c9333ace44bb18f30095" +dependencies = [ + "derive_more", + "parity-scale-codec", + "scale-bits", + "scale-decode-derive", + "scale-info", + "smallvec", +] + +[[package]] +name = "scale-decode-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27873eb6005868f8cc72dcfe109fae664cf51223d35387bc2f28be4c28d94c47" +dependencies = [ + "darling", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-encode" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d70cb4b29360105483fac1ed567ff95d65224a14dd275b6303ed0a654c78de5" +dependencies = [ + "derive_more", + "parity-scale-codec", + "scale-encode-derive", + "scale-info", + "smallvec", +] + +[[package]] +name = "scale-encode-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995491f110efdc6bea96d6a746140e32bfceb4ea47510750a5467295a4707a25" +dependencies = [ + "darling", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-info" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "schemars", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "schnorrkel" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0" +dependencies = [ + "aead", + "arrayref", + "arrayvec", + "curve25519-dalek", + "getrandom_or_panic", + "merlin", + "rand_core", + "serde_bytes", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f622567e3b4b38154fb8190bcf6b160d7a4301d70595a49195b48c116007a27" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "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 = "shielder-contract" +version = "0.1.0" +dependencies = [ + "ink", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53be06678ed9e83edb1745eb72efc0bbcd7b5c3c35711a860906aed827a13d61" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] diff --git a/shielder/contract/Cargo.toml b/shielder/contract/Cargo.toml new file mode 100755 index 0000000..3bef7bc --- /dev/null +++ b/shielder/contract/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "shielder-contract" +version = "0.1.0" +authors = ["Cardinal"] +homepage = "https://alephzero.org" +repository = "https://github.com/Cardinal-Cryptography/zk-apps" +edition = "2021" + +[dependencies] +ink = { version = "5.0.0-rc", default-features = false } + +[dev-dependencies] + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/shielder/contract/errors.rs b/shielder/contract/errors.rs new file mode 100644 index 0000000..5fbd8aa --- /dev/null +++ b/shielder/contract/errors.rs @@ -0,0 +1,19 @@ +use crate::traits::psp22::PSP22Error; + +#[ink::scale_derive(Encode, Decode, TypeInfo)] +#[derive(PartialEq, Debug)] +pub enum ShielderError { + PSP22(PSP22Error), + NullifierIsInSet, + MerkleTreeVerificationFail, + MerkleTreeLimitExceeded, + MerkleTreeProofGenFail, + ZkpVerificationFail, + ArithmeticError, +} + +impl From for ShielderError { + fn from(inner: PSP22Error) -> Self { + ShielderError::PSP22(inner) + } +} diff --git a/shielder/contract/lib.rs b/shielder/contract/lib.rs new file mode 100755 index 0000000..a64eb62 --- /dev/null +++ b/shielder/contract/lib.rs @@ -0,0 +1,125 @@ +//! Smart contract implementing shielder specification +//! https://docs.alephzero.org/aleph-zero/shielder/introduction-informal + +#![cfg_attr(not(feature = "std"), no_std, no_main)] +#![deny(missing_docs)] + +mod errors; +mod merkle; +mod mocked_zk; +mod traits; +mod types; + +/// Contract module +#[ink::contract] +pub mod contract { + + use crate::{ + errors::ShielderError, + merkle::MerkleTree, + mocked_zk::relations::ZkProof, + traits::psp22::PSP22, + types::{Scalar, Set}, + }; + + /// Enum + #[ink::scale_derive(Encode, Decode, TypeInfo)] + #[derive(Clone, Copy)] + pub enum OpPub { + /// Deposit PSP-22 token + Deposit { + /// amount of deposit + amount: u128, + /// PSP-22 token address + token: AccountId, + /// User address, from whom tokens are transferred + user: AccountId, + }, + /// Withdraw PSP-22 token + Withdraw { + /// amount of withdrawal + amount: u128, + /// PSP-22 token address + token: AccountId, + /// User address, from whom tokens are transferred + user: AccountId, + }, + } + + /// Contract storage + #[ink(storage)] + #[derive(Default)] + pub struct Contract { + nullifier_set: Set, + notes: MerkleTree, + } + + impl Contract { + /// Constructor + #[ink(constructor)] + pub fn new() -> Self { + Self::default() + } + + /// Adds empty note to shielder storage + /// Registers new account with empty balance + #[ink(message)] + pub fn add_note( + &mut self, + h_note_new: Scalar, + proof: ZkProof, + ) -> Result<(), ShielderError> { + proof.verify_creation(h_note_new)?; + self.notes.add_leaf(h_note_new)?; + Ok(()) + } + + /// Updates existing note + /// Applies operation to private account stored in shielder + #[ink(message)] + pub fn update_note( + &mut self, + op_pub: OpPub, + h_note_new: Scalar, + merkle_root: Scalar, + nullifier_old: Scalar, + proof: ZkProof, + ) -> Result<(), ShielderError> { + self.notes.is_historical_root(merkle_root)?; + self.nullify(nullifier_old)?; + proof.verify_update(op_pub, h_note_new, merkle_root, nullifier_old)?; + self.notes.add_leaf(h_note_new)?; + self.process_operation(op_pub)?; + Ok(()) + } + + fn process_operation(&mut self, op_pub: OpPub) -> Result<(), ShielderError> { + match op_pub { + OpPub::Deposit { + amount, + token, + user, + } => { + let mut psp22: ink::contract_ref!(PSP22) = token.into(); + psp22.transfer_from(user, self.env().account_id(), amount, [].to_vec())?; + } + OpPub::Withdraw { + amount, + token, + user, + } => { + let mut psp22: ink::contract_ref!(PSP22) = token.into(); + psp22.transfer(user, amount, [].to_vec())?; + } + }; + Ok(()) + } + + fn nullify(&mut self, nullifier: Scalar) -> Result<(), ShielderError> { + self.nullifier_set + .insert(nullifier, &()) + .map(|_| {}) + .ok_or(ShielderError::NullifierIsInSet) + } + } +} diff --git a/shielder/contract/merkle.rs b/shielder/contract/merkle.rs new file mode 100644 index 0000000..a084e6e --- /dev/null +++ b/shielder/contract/merkle.rs @@ -0,0 +1,81 @@ +use ink::{ + env::hash::{CryptoHash, Sha2x256}, + storage::Mapping, +}; + +use crate::{ + errors::ShielderError, + types::{Scalar, Set}, +}; + +/// depth of the tree +pub const DEPTH: usize = 10; + +#[ink::storage_item] +#[derive(Debug)] +pub struct MerkleTree { + /// mapping of tree indexes to values held in nodes + nodes: Mapping, + /// set of historical roots (nodes[1]) of tree + roots_log: Set, + /// index of next available leaf + next_leaf_idx: u32, + /// number of leaves in the tree, should be equal to 2^DEPTH + size: u32, +} + +pub fn compute_hash(first: Scalar, second: Scalar) -> Scalar { + let mut res = [0x0; 32]; + Sha2x256::hash([first.bytes, second.bytes].concat().as_slice(), &mut res); + Scalar { bytes: res } +} + +impl Default for MerkleTree { + fn default() -> Self { + Self { + nodes: Default::default(), + roots_log: Default::default(), + next_leaf_idx: 0, + size: (1 << DEPTH), + } + } +} + +impl MerkleTree { + pub fn add_leaf(&mut self, leaf_value: Scalar) -> Result<(), ShielderError> { + if self.next_leaf_idx == self.size { + return Err(ShielderError::MerkleTreeLimitExceeded); + } + let mut id = self + .next_leaf_idx + .checked_add(self.size) + .ok_or(ShielderError::ArithmeticError)?; + self.nodes.insert(id, &leaf_value); + + id /= 2; + while id > 0 { + let id_mul_2 = id.checked_mul(2).ok_or(ShielderError::ArithmeticError)?; + let left_n = self.node_value(id_mul_2); + let right_n = self.node_value(id.checked_add(1).ok_or(ShielderError::ArithmeticError)?); + let hash = compute_hash(left_n, right_n); + self.nodes.insert(id, &hash); + id /= 2; + } + self.next_leaf_idx = self + .next_leaf_idx + .checked_add(1) + .ok_or(ShielderError::ArithmeticError)?; + Ok(()) + } + + pub fn is_historical_root(&self, merkle_root_possible: Scalar) -> Result<(), ShielderError> { + self.roots_log + .contains(merkle_root_possible) + .then_some(()) + .ok_or(ShielderError::MerkleTreeVerificationFail) + } + + fn node_value(&self, id: u32) -> Scalar { + self.nodes.get(id).unwrap_or_default() + } +} diff --git a/shielder/contract/mocked_zk/account.rs b/shielder/contract/mocked_zk/account.rs new file mode 100644 index 0000000..394d132 --- /dev/null +++ b/shielder/contract/mocked_zk/account.rs @@ -0,0 +1,63 @@ +use ink::env::hash::{CryptoHash, Sha2x256}; + +use super::{ops::Operation, traits::Hashable, USDT_TOKEN}; +use crate::{contract::OpPub, errors::ShielderError, types::Scalar}; + +#[ink::scale_derive(Encode, Decode, TypeInfo)] +#[derive(Default, Clone, Copy)] +pub struct Account { + balance_aleph: Scalar, + balance_usdt: Scalar, +} + +impl Hashable for Account { + fn hash(&self) -> Scalar { + let mut res = [0x0; 32]; + Sha2x256::hash( + [self.balance_aleph.bytes, self.balance_usdt.bytes] + .concat() + .as_slice(), + &mut res, + ); + Scalar { bytes: res } + } +} + +impl Account { + pub fn new() -> Self { + Self { + balance_aleph: 0_u128.into(), + balance_usdt: 0_u128.into(), + } + } + pub fn update(&self, operation: Operation) -> Result { + match operation.op_pub { + OpPub::Deposit { amount, token, .. } => { + let mut balance_usdt = self.balance_usdt; + if token.as_ref() == USDT_TOKEN { + balance_usdt = (u128::from(balance_usdt) + .checked_add(amount) + .ok_or(ShielderError::ArithmeticError)?) + .into(); + } + Ok(Self { + balance_aleph: self.balance_aleph, + balance_usdt, + }) + } + OpPub::Withdraw { amount, token, .. } => { + let mut balance_usdt = self.balance_usdt; + if token.as_ref() == USDT_TOKEN { + balance_usdt = (u128::from(balance_usdt) + .checked_sub(amount) + .ok_or(ShielderError::ArithmeticError)?) + .into(); + } + Ok(Self { + balance_aleph: self.balance_aleph, + balance_usdt, + }) + } + } + } +} diff --git a/shielder/contract/mocked_zk/mod.rs b/shielder/contract/mocked_zk/mod.rs new file mode 100644 index 0000000..84b505b --- /dev/null +++ b/shielder/contract/mocked_zk/mod.rs @@ -0,0 +1,9 @@ +mod account; +mod note; +mod ops; +pub mod relations; +#[cfg(test)] +mod tests; +mod traits; + +const USDT_TOKEN: [u8; 32] = [0x2_u8; 32]; diff --git a/shielder/contract/mocked_zk/note.rs b/shielder/contract/mocked_zk/note.rs new file mode 100644 index 0000000..362adbf --- /dev/null +++ b/shielder/contract/mocked_zk/note.rs @@ -0,0 +1,42 @@ +use ink::env::hash::{CryptoHash, Sha2x256}; + +use super::traits::Hashable; +use crate::types::Scalar; + +#[ink::scale_derive(Encode, Decode, TypeInfo)] +#[derive(Clone, Copy)] +pub struct Note { + id: Scalar, + trapdoor: Scalar, + nullifier: Scalar, + account_hash: Scalar, +} + +impl Note { + pub fn new(id: Scalar, trapdoor: Scalar, nullifier: Scalar, account_hash: Scalar) -> Self { + Self { + id, + trapdoor, + nullifier, + account_hash, + } + } +} + +impl Hashable for Note { + fn hash(&self) -> Scalar { + let mut res = [0x0; 32]; + Sha2x256::hash( + [ + self.id.bytes, + self.trapdoor.bytes, + self.nullifier.bytes, + self.account_hash.bytes, + ] + .concat() + .as_slice(), + &mut res, + ); + Scalar { bytes: res } + } +} diff --git a/shielder/contract/mocked_zk/ops.rs b/shielder/contract/mocked_zk/ops.rs new file mode 100644 index 0000000..2247db1 --- /dev/null +++ b/shielder/contract/mocked_zk/ops.rs @@ -0,0 +1,34 @@ +use ink::primitives::AccountId; + +use crate::{contract::OpPub, errors::ShielderError}; + +/// empty private operation +#[ink::scale_derive(Encode, Decode, TypeInfo)] +#[derive(Clone, Copy)] +pub struct OpPriv { + pub user: AccountId, +} + +#[ink::scale_derive(Encode, Decode, TypeInfo)] +#[derive(Clone, Copy)] +pub struct Operation { + pub op_pub: OpPub, +} + +impl Operation { + pub fn combine(op_pub: OpPub, _op_priv: OpPriv) -> Result { + match op_pub { + OpPub::Deposit { user, .. } => { + if user != _op_priv.user { + return Err(ShielderError::ZkpVerificationFail); + } + } + OpPub::Withdraw { user, .. } => { + if user != _op_priv.user { + return Err(ShielderError::ZkpVerificationFail); + } + } + } + Ok(Operation { op_pub }) + } +} diff --git a/shielder/contract/mocked_zk/relations.rs b/shielder/contract/mocked_zk/relations.rs new file mode 100644 index 0000000..2ddea9c --- /dev/null +++ b/shielder/contract/mocked_zk/relations.rs @@ -0,0 +1,143 @@ +use super::{ + account::Account, + note::Note, + ops::{OpPriv, Operation}, + traits::Hashable, +}; +use crate::{ + contract::OpPub, + errors::ShielderError, + merkle::{self, DEPTH}, + types::Scalar, +}; + +/// mocked proof of knowledge, not ZK +/// you can imagine ZkProof object as someone's "knowledge" +/// functions starting with verify_ are mocks of relation +#[ink::scale_derive(Encode, Decode, TypeInfo)] +#[derive(Clone, Copy)] +pub struct ZkProof { + id: Scalar, + trapdoor_new: Scalar, + trapdoor_old: Scalar, + nullifier_new: Scalar, + acc_old: Account, + acc_new: Account, + op_priv: OpPriv, + merkle_proof: [Scalar; merkle::DEPTH], + merkle_proof_leaf_id: u32, +} + +pub fn verify_hash(to_hash: T, hash: Scalar) -> Result { + let real_hash = to_hash.hash(); + if real_hash != hash { + return Err(ShielderError::ZkpVerificationFail); + } + Ok(real_hash) +} + +impl ZkProof { + pub fn new( + id: Scalar, + trapdoor: Scalar, + nullifier: Scalar, + op_priv: OpPriv, + acc: Account, + ) -> Self { + Self { + id, + trapdoor_new: trapdoor, + nullifier_new: nullifier, + acc_new: acc, + trapdoor_old: 0_u128.into(), + acc_old: Account::new(), + op_priv, + merkle_proof: [0_u128.into(); DEPTH], + merkle_proof_leaf_id: 0, + } + } + + pub fn transition( + &self, + trapdoor: Scalar, + nullifier: Scalar, + acc: Account, + op_priv: OpPriv, + merkle_proof: [Scalar; DEPTH], + merkle_proof_leaf_id: u32, + ) -> Self { + Self { + id: self.id, + trapdoor_new: trapdoor, + trapdoor_old: self.trapdoor_new, + nullifier_new: nullifier, + acc_new: acc, + acc_old: self.acc_new, + op_priv, + merkle_proof, + merkle_proof_leaf_id, + } + } + + pub fn update_account(&self, operation: Operation) -> Result { + self.acc_new.update(operation) + } + + pub fn verify_acccount_update( + &self, + op: Operation, + h_acc_old: Scalar, + ) -> Result { + let acc_new = self.acc_old.update(op)?; + verify_hash(self.acc_old, h_acc_old)?; + Ok(acc_new) + } + + fn verify_merkle_proof( + &self, + h_note_old: Scalar, + merkle_root: Scalar, + ) -> Result<(), ShielderError> { + let mut id = self.merkle_proof_leaf_id; + let mut scalar = h_note_old; + for node in self.merkle_proof { + if id % 2 == 0 { + scalar = merkle::compute_hash(scalar, node); + } else { + scalar = merkle::compute_hash(node, scalar); + } + id /= 2; + } + if scalar != merkle_root { + return Err(ShielderError::ZkpVerificationFail); + } + Ok(()) + } + + pub fn verify_creation(&self, h_note_new: Scalar) -> Result<(), ShielderError> { + let acc_new = Account::new(); + let h_acc_new = acc_new.hash(); + let note_new = Note::new(self.id, self.trapdoor_new, self.nullifier_new, h_acc_new); + verify_hash(note_new, h_note_new)?; + Ok(()) + } + + pub fn verify_update( + &self, + op_pub: OpPub, + h_note_new: Scalar, + merkle_root: Scalar, + nullifier_old: Scalar, + ) -> Result<(), ShielderError> { + let h_acc_old = self.acc_old.hash(); + let op = Operation::combine(op_pub, self.op_priv)?; + let acc_new = self.verify_acccount_update(op, h_acc_old)?; + let h_acc_new = acc_new.hash(); + let note_new = Note::new(self.id, self.trapdoor_new, self.nullifier_new, h_acc_new); + verify_hash(note_new, h_note_new)?; + let note_old = Note::new(self.id, self.trapdoor_old, nullifier_old, h_acc_old); + let h_note_old = note_old.hash(); + self.verify_merkle_proof(h_note_old, merkle_root)?; + Ok(()) + } +} diff --git a/shielder/contract/mocked_zk/tests/merkle.rs b/shielder/contract/mocked_zk/tests/merkle.rs new file mode 100644 index 0000000..0e556d5 --- /dev/null +++ b/shielder/contract/mocked_zk/tests/merkle.rs @@ -0,0 +1,61 @@ +use ink::env::hash::{CryptoHash, Sha2x256}; + +use crate::{errors::ShielderError, types::Scalar}; + +/// depth of the tree +pub const DEPTH: usize = 10; + +#[derive(Default)] +pub struct MerkleTree { + nodes: Vec, + next_leaf_idx: usize, + size: usize, +} + +pub fn compute_hash(first: Scalar, second: Scalar) -> Scalar { + let mut res = [0x0; 32]; + Sha2x256::hash([first.bytes, second.bytes].concat().as_slice(), &mut res); + Scalar { bytes: res } +} + +impl MerkleTree { + pub fn new() -> Self { + Self { + nodes: vec![Scalar { bytes: [0x0; 32] }; 1 << (DEPTH + 1)], + next_leaf_idx: 0, + size: (1 << DEPTH), + } + } + + pub fn add_leaf(&mut self, leaf_value: Scalar) -> Result { + if self.next_leaf_idx == self.size { + return Err(ShielderError::MerkleTreeLimitExceeded); + } + let mut id = self.next_leaf_idx + self.size; + self.nodes[id] = leaf_value; + + id /= 2; + while id > 0 { + let left_n = self.nodes[id * 2]; + let right_n = self.nodes[id * 2 + 1]; + let hash = compute_hash(left_n, right_n); + self.nodes[id] = hash; + id /= 2; + } + self.next_leaf_idx += 1; + Ok(self.nodes[1]) + } + + pub fn gen_proof(&self, leaf_id: usize) -> Result<[Scalar; DEPTH], ShielderError> { + let mut res = [Scalar { bytes: [0x0; 32] }; DEPTH]; + if self.next_leaf_idx == self.size { + return Err(ShielderError::MerkleTreeProofGenFail); + } + let mut id = leaf_id + self.size; + for i in 0..DEPTH { + res[i] = self.nodes[id ^ 1]; + id /= 2; + } + Ok(res) + } +} diff --git a/shielder/contract/mocked_zk/tests/mod.rs b/shielder/contract/mocked_zk/tests/mod.rs new file mode 100644 index 0000000..448dc4f --- /dev/null +++ b/shielder/contract/mocked_zk/tests/mod.rs @@ -0,0 +1,163 @@ +mod merkle; + +use ink::primitives::AccountId; + +use self::merkle::MerkleTree; +use super::{ + account::Account, + note::Note, + ops::{OpPriv, Operation}, + relations::ZkProof, + traits::Hashable, +}; +use crate::{contract::OpPub, errors::ShielderError, mocked_zk::USDT_TOKEN, types::Scalar}; + +fn create_empty_note_proof(id: Scalar, nullifier: Scalar, trapdoor: Scalar) -> (Scalar, ZkProof) { + let acc_new = Account::new(); + let note = Note::new(id, trapdoor, nullifier, acc_new.hash()); + let proof = ZkProof::new( + id, + trapdoor, + nullifier, + OpPriv { + user: AccountId::from([0x1; 32]), + }, + acc_new, + ); + (note.hash(), proof) +} + +fn update_account( + id: Scalar, + nullifier: Scalar, + trapdoor: Scalar, + op_pub: OpPub, + proof: ZkProof, + merkle_proof: [Scalar; 10], + merkle_proof_leaf_id: u32, +) -> (Scalar, ZkProof) { + let op_priv = OpPriv { + user: AccountId::from([0x1; 32]), + }; + let operation = Operation::combine(op_pub, op_priv).unwrap(); + let acc_updated = proof.update_account(operation).unwrap(); + let note = Note::new(id, trapdoor, nullifier, acc_updated.hash()); + let new_proof = proof.transition( + trapdoor, + nullifier, + acc_updated, + op_priv, + merkle_proof, + merkle_proof_leaf_id, + ); + (note.hash(), new_proof) +} + +#[test] +fn test_create_note() -> Result<(), ShielderError> { + let id = 0_u128.into(); + let nullifier = 0_u128.into(); + let trapdoor = 0_u128.into(); + let (h_new_note, proof) = create_empty_note_proof(id, nullifier, trapdoor); + proof.verify_creation(h_new_note)?; + Ok(()) +} + +#[test] +fn test_create_note_fails() -> Result<(), ShielderError> { + let id = 0_u128.into(); + let nullifier = 0_u128.into(); + let trapdoor = 0_u128.into(); + let (_, proof) = create_empty_note_proof(id, nullifier, trapdoor); + let (h_new_note, _) = create_empty_note_proof(1_u128.into(), nullifier, trapdoor); + assert_eq!( + ShielderError::ZkpVerificationFail, + proof.verify_creation(h_new_note).unwrap_err() + ); + Ok(()) +} + +#[test] +fn test_update_note() -> Result<(), ShielderError> { + let id = 0_u128.into(); + + let mut merkle_tree = MerkleTree::new(); + + let nullifier = 0_u128.into(); + let trapdoor = 0_u128.into(); + + let (h_new_note, proof) = create_empty_note_proof(id, nullifier, trapdoor); + proof.verify_creation(h_new_note)?; + let merkle_root = merkle_tree.add_leaf(h_new_note)?; + let merkle_proof = merkle_tree.gen_proof(0)?; + + let nullifier_new = 1_u128.into(); + let trapdoor_new = 1_u128.into(); + + let op_pub = crate::contract::OpPub::Deposit { + amount: 10, + token: AccountId::from(USDT_TOKEN), + user: AccountId::from([0x1; 32]), + }; + + let (h_new_note, proof) = update_account( + id, + nullifier_new, + trapdoor_new, + op_pub, + proof, + merkle_proof, + 0, + ); + proof.verify_update(op_pub, h_new_note, merkle_root, nullifier)?; + + Ok(()) +} + +#[test] +fn test_update_note_fail_op_priv() -> Result<(), ShielderError> { + let id = 0_u128.into(); + + let mut merkle_tree = MerkleTree::new(); + + let nullifier = 0_u128.into(); + let trapdoor = 0_u128.into(); + + let (h_new_note, proof) = create_empty_note_proof(id, nullifier, trapdoor); + proof.verify_creation(h_new_note)?; + let merkle_root = merkle_tree.add_leaf(h_new_note)?; + let merkle_proof = merkle_tree.gen_proof(0)?; + + let nullifier_new = 1_u128.into(); + let trapdoor_new = 1_u128.into(); + + let op_pub = crate::contract::OpPub::Deposit { + amount: 10, + token: AccountId::from(USDT_TOKEN), + user: AccountId::from([0x1; 32]), + }; + let op_pub_fake = crate::contract::OpPub::Deposit { + amount: 10, + token: AccountId::from(USDT_TOKEN), + user: AccountId::from([0x2; 32]), + }; + + let (h_new_note, proof) = update_account( + id, + nullifier_new, + trapdoor_new, + op_pub, + proof, + merkle_proof, + 0, + ); + + assert_eq!( + ShielderError::ZkpVerificationFail, + proof + .verify_update(op_pub_fake, h_new_note, merkle_root, nullifier) + .unwrap_err() + ); + + Ok(()) +} diff --git a/shielder/contract/mocked_zk/traits.rs b/shielder/contract/mocked_zk/traits.rs new file mode 100644 index 0000000..3e77cae --- /dev/null +++ b/shielder/contract/mocked_zk/traits.rs @@ -0,0 +1,5 @@ +use crate::types::Scalar; + +pub trait Hashable { + fn hash(&self) -> Scalar; +} diff --git a/shielder/contract/rust-toolchain.toml b/shielder/contract/rust-toolchain.toml new file mode 100644 index 0000000..fd29f82 --- /dev/null +++ b/shielder/contract/rust-toolchain.toml @@ -0,0 +1,5 @@ +[toolchain] +channel = "1.75" +components = [ "rustfmt", "clippy", "rust-src" ] +targets = [ "wasm32-unknown-unknown" ] +profile = "minimal" diff --git a/shielder/contract/traits/mod.rs b/shielder/contract/traits/mod.rs new file mode 100644 index 0000000..f0f8b35 --- /dev/null +++ b/shielder/contract/traits/mod.rs @@ -0,0 +1 @@ +pub mod psp22; diff --git a/shielder/contract/traits/psp22.rs b/shielder/contract/traits/psp22.rs new file mode 100644 index 0000000..78db803 --- /dev/null +++ b/shielder/contract/traits/psp22.rs @@ -0,0 +1,139 @@ +use ink::{ + prelude::{string::String, vec::Vec}, + primitives::AccountId, +}; + +#[ink::scale_derive(Encode, Decode, TypeInfo)] +#[derive(Debug, PartialEq, Eq)] +pub enum PSP22Error { + /// Custom error type for implementation-based errors. + Custom(String), + /// Returned when an account does not have enough tokens to complete the operation. + InsufficientBalance, + /// Returned if there is not enough allowance to complete the operation. + InsufficientAllowance, + /// Returned if recipient's address is zero [deprecated]. + ZeroRecipientAddress, + /// Returned if sender's address is zero [deprecated]. + ZeroSenderAddress, + /// Returned if a safe transfer check failed [deprecated]. + SafeTransferCheckFailed(String), +} + +#[ink::trait_definition] +pub trait PSP22 { + /// Returns the total token supply. + #[ink(message)] + fn total_supply(&self) -> u128; + + /// Returns the account balance for the specified `owner`. + /// + /// Returns `0` if the account is non-existent. + #[ink(message)] + fn balance_of(&self, owner: AccountId) -> u128; + + /// Returns the amount which `spender` is still allowed to withdraw from `owner`. + /// + /// Returns `0` if no allowance has been set. + #[ink(message)] + fn allowance(&self, owner: AccountId, spender: AccountId) -> u128; + + /// Transfers `value` amount of tokens from the caller's account to account `to` + /// with additional `data` in unspecified format. + /// + /// # Events + /// + /// On success a `Transfer` event is emitted. + /// + /// No-op if the caller and `to` is the same address or `value` is zero, returns success + /// and no events are emitted. + /// + /// # Errors + /// + /// Reverts with `InsufficientBalance` if the `value` exceeds the caller's balance. + #[ink(message)] + fn transfer(&mut self, to: AccountId, value: u128, data: Vec) -> Result<(), PSP22Error>; + + /// Transfers `value` tokens on the behalf of `from` to the account `to` + /// with additional `data` in unspecified format. + /// + /// If `from` and the caller are different addresses, the caller must be allowed + /// by `from` to spend at least `value` tokens. + /// + /// # Events + /// + /// On success a `Transfer` event is emitted. + /// + /// No-op if `from` and `to` is the same address or `value` is zero, returns success + /// and no events are emitted. + /// + /// If `from` and the caller are different addresses, a successful transfer results + /// in decreased allowance by `from` to the caller and an `Approval` event with + /// the new allowance amount is emitted. + /// + /// # Errors + /// + /// Reverts with `InsufficientBalance` if the `value` exceeds the balance of the account `from`. + /// + /// Reverts with `InsufficientAllowance` if `from` and the caller are different addresses and + /// the `value` exceeds the allowance granted by `from` to the caller. + /// + /// If conditions for both `InsufficientBalance` and `InsufficientAllowance` errors are met, + /// reverts with `InsufficientAllowance`. + #[ink(message)] + fn transfer_from( + &mut self, + from: AccountId, + to: AccountId, + value: u128, + data: Vec, + ) -> Result<(), PSP22Error>; + + /// Allows `spender` to withdraw from the caller's account multiple times, up to + /// the total amount of `value`. + /// + /// Successive calls of this method overwrite previous values. + /// + /// # Events + /// + /// An `Approval` event is emitted. + /// + /// No-op if the caller and `spender` is the same address, returns success and no events are emitted. + #[ink(message)] + fn approve(&mut self, spender: AccountId, value: u128) -> Result<(), PSP22Error>; + + /// Increases by `delta-value` the allowance granted to `spender` by the caller. + /// + /// # Events + /// + /// An `Approval` event with the new allowance amount is emitted. + /// + /// No-op if the caller and `spender` is the same address or `delta-value` is zero, returns success + /// and no events are emitted. + #[ink(message)] + fn increase_allowance( + &mut self, + spender: AccountId, + delta_value: u128, + ) -> Result<(), PSP22Error>; + + /// Decreases by `delta-value` the allowance granted to `spender` by the caller. + /// + /// # Events + /// + /// An `Approval` event with the new allowance amount is emitted. + /// + /// No-op if the caller and `spender` is the same address or `delta-value` is zero, returns success + /// and no events are emitted. + /// + /// # Errors + /// + /// Reverts with `InsufficientAllowance` if `spender` and the caller are different addresses and + /// the `delta-value` exceeds the allowance granted by the caller to `spender`. + #[ink(message)] + fn decrease_allowance( + &mut self, + spender: AccountId, + delta_value: u128, + ) -> Result<(), PSP22Error>; +} diff --git a/shielder/contract/types.rs b/shielder/contract/types.rs new file mode 100644 index 0000000..330ba4a --- /dev/null +++ b/shielder/contract/types.rs @@ -0,0 +1,53 @@ +use ink::storage::Mapping; + +pub type Set = Mapping; + +#[ink::scale_derive(Encode, Decode, TypeInfo)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(ink::storage::traits::StorageLayout))] +pub struct Scalar { + pub bytes: [u8; 32], +} + +impl From for Scalar { + fn from(value: u128) -> Self { + Self { + bytes: [value.to_le_bytes(), [0x0; 16]] + .concat() + .as_slice() + .try_into() + .unwrap(), + } + } +} + +impl From for u128 { + fn from(value: Scalar) -> Self { + u128::from_le_bytes(value.bytes[0..16].try_into().unwrap()) + } +} + +#[cfg(test)] +mod tests { + use crate::types::Scalar; + + #[test] + fn test_scalar_from_u128() { + let mut bytes = [0x0; 32]; + bytes[2] = 0x01; + bytes[1] = 0xE2; + bytes[0] = 0x40; + assert_eq!(Scalar::from(123456_u128), Scalar { bytes }); + } + + #[test] + fn test_u128_from_scalar() { + let expected = 987654321_u128; + let mut bytes = [0x0; 32]; + bytes[3] = 0x3A; + bytes[2] = 0xDE; + bytes[1] = 0x68; + bytes[0] = 0xB1; + assert_eq!(expected, u128::from(Scalar { bytes })); + } +}