diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 838fbd9f06..2ab68e47e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,8 +33,8 @@ on: branches: - dev paths-ignore: - - '**/dependabot.yml' - - '**/README.md' + - "**/dependabot.yml" + - "**/README.md" pull_request: branches: - dev @@ -225,7 +225,7 @@ jobs: - set-condition - sequentialise if: needs.set-condition.outputs.rebuild_tee == 'true' - container: 'litentry/litentry-tee-dev:latest' + container: "litentry/litentry-tee-dev:latest" steps: - uses: actions/checkout@v4 @@ -500,7 +500,7 @@ jobs: - name: Run ts tests for ${{ matrix.chain }} if: needs.set-condition.outputs.run_parachain_test == 'true' - timeout-minutes: 30 + timeout-minutes: 35 run: | make test-ts-docker-${{ matrix.chain }} @@ -620,6 +620,7 @@ jobs: - test_name: lit-ii-identity-test - test_name: lit-di-substrate-identity-test - test_name: lit-di-evm-identity-test + - test_name: lit-di-bitcoin-identity-test - test_name: lit-di-vc-test - test_name: lit-parentchain-nonce - test_name: lit-ii-batch-test diff --git a/.github/workflows/create-release-draft.yml b/.github/workflows/create-release-draft.yml index a9e621f000..fa619fa504 100644 --- a/.github/workflows/create-release-draft.yml +++ b/.github/workflows/create-release-draft.yml @@ -266,7 +266,7 @@ jobs: chain: [rococo, litmus, litentry] include: - chain: rococo - ref_url: wss://rpc.rococo-parachain-sg.litentry.io + ref_url: wss://rpc.rococo-parachain.litentry.io - chain: litmus ref_url: wss://rpc.litmus-parachain.litentry.io - chain: litentry diff --git a/.github/workflows/simulate-runtime-upgrade.yml b/.github/workflows/simulate-runtime-upgrade.yml index 556090c1cb..af9e760d01 100644 --- a/.github/workflows/simulate-runtime-upgrade.yml +++ b/.github/workflows/simulate-runtime-upgrade.yml @@ -17,7 +17,7 @@ env: jobs: simulate-runtime-upgrade: runs-on: ubuntu-latest - timeout-minutes: 20 + timeout-minutes: 30 strategy: fail-fast: false matrix: @@ -36,7 +36,7 @@ jobs: run: corepack enable && corepack enable pnpm - name: Fork ${{ matrix.chain }} and launch parachain - timeout-minutes: 10 + timeout-minutes: 20 run: | ./scripts/fork-parachain-and-launch.sh ${{ matrix.chain }} diff --git a/Cargo.lock b/Cargo.lock index dc3184d833..e9908df97b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,18 +445,18 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -1058,7 +1058,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -1186,11 +1186,15 @@ name = "core-primitives" version = "0.9.12" dependencies = [ "frame-support", + "itp-utils", "litentry-macros", + "pallet-evm", "parity-scale-codec", "ring", "scale-info", + "serde", "sp-core", + "sp-io", "sp-runtime", "sp-std", "strum 0.25.0", @@ -1512,7 +1516,7 @@ dependencies = [ "cumulus-client-consensus-common", "cumulus-client-network", "cumulus-primitives-core", - "futures 0.3.29", + "futures 0.3.30", "parity-scale-codec", "parking_lot 0.12.1", "polkadot-node-primitives", @@ -1535,7 +1539,7 @@ dependencies = [ "async-trait", "cumulus-client-consensus-common", "cumulus-primitives-core", - "futures 0.3.29", + "futures 0.3.30", "parity-scale-codec", "sc-client-api", "sc-consensus", @@ -1566,7 +1570,7 @@ dependencies = [ "cumulus-primitives-core", "cumulus-relay-chain-interface", "dyn-clone", - "futures 0.3.29", + "futures 0.3.30", "log", "parity-scale-codec", "polkadot-primitives", @@ -1587,7 +1591,7 @@ source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603 dependencies = [ "async-trait", "cumulus-relay-chain-interface", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "parity-scale-codec", "parking_lot 0.12.1", @@ -1611,7 +1615,7 @@ dependencies = [ "async-trait", "cumulus-primitives-core", "cumulus-relay-chain-interface", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "parity-scale-codec", "polkadot-node-primitives", @@ -1641,7 +1645,7 @@ dependencies = [ "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", - "futures 0.3.29", + "futures 0.3.30", "polkadot-primitives", "sc-client-api", "sc-consensus", @@ -1732,7 +1736,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -1832,7 +1836,7 @@ version = "0.1.0" source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" dependencies = [ "cumulus-primitives-core", - "futures 0.3.29", + "futures 0.3.30", "parity-scale-codec", "sp-inherents", "sp-std", @@ -1865,7 +1869,7 @@ dependencies = [ "async-trait", "cumulus-primitives-core", "cumulus-relay-chain-interface", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "polkadot-cli", "polkadot-client", @@ -1889,7 +1893,7 @@ source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603 dependencies = [ "async-trait", "cumulus-primitives-core", - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee-core", "parity-scale-codec", "polkadot-overseer", @@ -1910,7 +1914,7 @@ dependencies = [ "cumulus-primitives-core", "cumulus-relay-chain-interface", "cumulus-relay-chain-rpc-interface", - "futures 0.3.29", + "futures 0.3.30", "lru 0.9.0", "polkadot-availability-recovery", "polkadot-collator-protocol", @@ -1946,7 +1950,7 @@ dependencies = [ "async-trait", "cumulus-primitives-core", "cumulus-relay-chain-interface", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "jsonrpsee", "lru 0.9.0", @@ -2045,7 +2049,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -2062,7 +2066,7 @@ checksum = "b8fcfa71f66c8563c4fa9dd2bb68368d50267856f831ac5d85367e0805f9606c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -2340,7 +2344,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -2533,7 +2537,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -2544,7 +2548,7 @@ checksum = "b893c4eb2dc092c811165f84dc7447fae16fb66521717968c34c509b39b1a5c5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -2794,7 +2798,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", ] [[package]] @@ -2844,7 +2848,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -2944,7 +2948,7 @@ dependencies = [ "fc-storage", "fp-consensus", "fp-rpc", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "log", "parking_lot 0.12.1", @@ -2972,7 +2976,7 @@ dependencies = [ "fp-evm", "fp-rpc", "fp-storage", - "futures 0.3.29", + "futures 0.3.30", "hex", "jsonrpsee", "libsecp256k1", @@ -3101,7 +3105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" dependencies = [ "either", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "log", "num-traits", @@ -3361,7 +3365,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -3416,7 +3420,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-recursion", - "futures 0.3.29", + "futures 0.3.30", "indicatif", "jsonrpsee", "log", @@ -3476,7 +3480,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -3488,7 +3492,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -3498,7 +3502,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -3595,9 +3599,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -3610,9 +3614,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -3620,15 +3624,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -3638,9 +3642,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" @@ -3659,13 +3663,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -3681,15 +3685,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" @@ -3699,9 +3703,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures 0.1.31", "futures-channel", @@ -4190,7 +4194,7 @@ dependencies = [ "async-io", "core-foundation", "fnv", - "futures 0.3.29", + "futures 0.3.30", "if-addrs", "ipnet", "log", @@ -4385,6 +4389,14 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "itp-utils" +version = "0.9.0" +dependencies = [ + "hex", + "parity-scale-codec", +] + [[package]] name = "jobserver" version = "0.1.26" @@ -4764,7 +4776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7b0104790be871edcf97db9bd2356604984e623a08d825c3f27852290266b8" dependencies = [ "bytes", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "getrandom 0.2.10", "instant", @@ -4802,7 +4814,7 @@ dependencies = [ "ed25519-dalek", "either", "fnv", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "instant", "log", @@ -4833,7 +4845,7 @@ checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" dependencies = [ "either", "fnv", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "instant", "libp2p-identity", @@ -4859,7 +4871,7 @@ version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "libp2p-core 0.38.0", "log", "parking_lot 0.12.1", @@ -4874,7 +4886,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c052d0026f4817b44869bfb6810f4e1112f43aec8553f2cb38881c524b563abf" dependencies = [ "asynchronous-codec", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "libp2p-core 0.38.0", "libp2p-swarm", @@ -4917,7 +4929,7 @@ dependencies = [ "bytes", "either", "fnv", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "instant", "libp2p-core 0.38.0", @@ -4941,7 +4953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04f378264aade9872d6ccd315c0accc18be3a35d15fc1b9c36e5b6f983b62b5b" dependencies = [ "data-encoding", - "futures 0.3.29", + "futures 0.3.30", "if-watch", "libp2p-core 0.38.0", "libp2p-swarm", @@ -4976,7 +4988,7 @@ checksum = "03805b44107aa013e7cbbfa5627b31c36cbedfdfb00603c0311998882bc4bace" dependencies = [ "asynchronous-codec", "bytes", - "futures 0.3.29", + "futures 0.3.30", "libp2p-core 0.38.0", "log", "nohash-hasher", @@ -4994,7 +5006,7 @@ checksum = "a978cb57efe82e892ec6f348a536bfbd9fee677adbe5689d7a93ad3a9bffbf2e" dependencies = [ "bytes", "curve25519-dalek 3.2.0", - "futures 0.3.29", + "futures 0.3.30", "libp2p-core 0.38.0", "log", "once_cell", @@ -5015,7 +5027,7 @@ version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "929fcace45a112536e22b3dcfd4db538723ef9c3cb79f672b98be2cc8e25f37f" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "instant", "libp2p-core 0.38.0", @@ -5032,7 +5044,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" dependencies = [ "bytes", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "if-watch", "libp2p-core 0.38.0", @@ -5054,7 +5066,7 @@ checksum = "3236168796727bfcf4927f766393415361e2c644b08bedb6a6b13d957c9a4884" dependencies = [ "async-trait", "bytes", - "futures 0.3.29", + "futures 0.3.30", "instant", "libp2p-core 0.38.0", "libp2p-swarm", @@ -5072,7 +5084,7 @@ checksum = "b2a35472fe3276b3855c00f1c032ea8413615e030256429ad5349cdf67c6e1a0" dependencies = [ "either", "fnv", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "instant", "libp2p-core 0.38.0", @@ -5103,7 +5115,7 @@ version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4b257baf6df8f2df39678b86c578961d48cc8b68642a12f0f763f56c8e5858d" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "if-watch", "libc", @@ -5119,7 +5131,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "futures-rustls", "libp2p-core 0.39.2", "libp2p-identity", @@ -5138,7 +5150,7 @@ version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "js-sys", "libp2p-core 0.38.0", "parity-send-wrapper", @@ -5155,7 +5167,7 @@ dependencies = [ "async-trait", "asynchronous-codec", "bytes", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "hex", "if-watch", @@ -5184,7 +5196,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d705506030d5c0aaf2882437c70dab437605f21c5f9811978f694e6917a3b54" dependencies = [ "either", - "futures 0.3.29", + "futures 0.3.30", "futures-rustls", "libp2p-core 0.38.0", "log", @@ -5202,7 +5214,7 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "libp2p-core 0.38.0", "log", "parking_lot 0.12.1", @@ -5361,7 +5373,7 @@ dependencies = [ "fp-rpc", "frame-benchmarking", "frame-benchmarking-cli", - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee", "litentry-parachain-runtime", "litmus-parachain-runtime", @@ -5421,7 +5433,7 @@ version = "0.9.12" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -5784,7 +5796,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69672161530e8aeca1d1400fbf3f1a1747ff60ea604265a4e906c2442df20532" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "rand 0.8.5", "thrift", ] @@ -5820,7 +5832,7 @@ name = "mmr-gadget" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "log", "parity-scale-codec", "sc-client-api", @@ -5931,7 +5943,7 @@ version = "0.1.0" source = "git+https://github.com/litentry/astar-frame?branch=polkadot-v0.9.42#d9a49c58f248f49e274b0730b8f4ef7f1e72c4b5" dependencies = [ "ethereum-types", - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee", "moonbeam-client-evm-tracing", "moonbeam-rpc-core-types", @@ -5946,7 +5958,7 @@ version = "0.6.0" source = "git+https://github.com/litentry/astar-frame?branch=polkadot-v0.9.42#d9a49c58f248f49e274b0730b8f4ef7f1e72c4b5" dependencies = [ "ethereum-types", - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee", "moonbeam-client-evm-tracing", "moonbeam-rpc-core-types", @@ -5989,7 +6001,7 @@ dependencies = [ "fc-rpc", "fc-storage", "fp-rpc", - "futures 0.3.29", + "futures 0.3.30", "hex-literal 0.3.4", "jsonrpsee", "moonbeam-client-evm-tracing", @@ -6052,7 +6064,7 @@ dependencies = [ "fc-rpc-core", "fc-storage", "fp-rpc", - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee", "moonbeam-client-evm-tracing", "moonbeam-rpc-core-trace", @@ -6200,7 +6212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" dependencies = [ "bytes", - "futures 0.3.29", + "futures 0.3.30", "log", "pin-project", "smallvec", @@ -6294,7 +6306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" dependencies = [ "bytes", - "futures 0.3.29", + "futures 0.3.30", "log", "netlink-packet-core", "netlink-sys", @@ -6309,7 +6321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "bytes", - "futures 0.3.29", + "futures 0.3.30", "libc", "log", "tokio", @@ -6464,43 +6476,43 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ - "num_enum_derive 0.5.11", + "num_enum_derive 0.6.1", ] [[package]] name = "num_enum" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" dependencies = [ - "num_enum_derive 0.6.1", + "num_enum_derive 0.7.1", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.46", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ - "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -6580,7 +6592,7 @@ checksum = "227585216d05ba65c7ab0a0450a3cf2cbd81a98862a54c4df8e14d5ac6adb015" dependencies = [ "async-trait", "dyn-clonable", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "orchestra-proc-macro", "pin-project", @@ -7207,7 +7219,7 @@ dependencies = [ "frame-system", "hex-literal 0.4.1", "log", - "num_enum 0.5.11", + "num_enum 0.7.1", "pallet-balances", "pallet-bridge", "pallet-bridge-transfer", @@ -7262,7 +7274,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "num_enum 0.5.11", + "num_enum 0.7.1", "pallet-balances", "pallet-evm", "pallet-parachain-staking", @@ -7851,7 +7863,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -8369,7 +8381,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -8410,7 +8422,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -8474,7 +8486,7 @@ name = "polkadot-approval-distribution" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", @@ -8490,7 +8502,7 @@ name = "polkadot-availability-bitfield-distribution" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "polkadot-node-network-protocol", "polkadot-node-subsystem", "polkadot-node-subsystem-util", @@ -8506,7 +8518,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f99 dependencies = [ "derive_more", "fatality", - "futures 0.3.29", + "futures 0.3.30", "lru 0.9.0", "parity-scale-codec", "polkadot-erasure-coding", @@ -8528,7 +8540,7 @@ version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "fatality", - "futures 0.3.29", + "futures 0.3.30", "lru 0.9.0", "parity-scale-codec", "polkadot-erasure-coding", @@ -8550,7 +8562,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f99 dependencies = [ "clap", "frame-benchmarking-cli", - "futures 0.3.29", + "futures 0.3.30", "log", "polkadot-client", "polkadot-node-core-pvf-worker", @@ -8582,7 +8594,7 @@ dependencies = [ "frame-benchmarking-cli", "frame-system", "frame-system-rpc-runtime-api", - "futures 0.3.29", + "futures 0.3.30", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "polkadot-core-primitives", @@ -8622,7 +8634,7 @@ dependencies = [ "always-assert", "bitvec", "fatality", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -8655,7 +8667,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f99 dependencies = [ "derive_more", "fatality", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "indexmap 1.9.3", "lru 0.9.0", @@ -8692,7 +8704,7 @@ name = "polkadot-gossip-support" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "polkadot-node-network-protocol", "polkadot-node-subsystem", @@ -8716,7 +8728,7 @@ dependencies = [ "async-trait", "bytes", "fatality", - "futures 0.3.29", + "futures 0.3.30", "parity-scale-codec", "parking_lot 0.12.1", "polkadot-node-metrics", @@ -8735,7 +8747,7 @@ name = "polkadot-node-collation-generation" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-node-primitives", @@ -8755,7 +8767,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f99 dependencies = [ "bitvec", "derive_more", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "kvdb", "lru 0.9.0", @@ -8783,7 +8795,7 @@ version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "bitvec", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "kvdb", "parity-scale-codec", @@ -8805,7 +8817,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f99 dependencies = [ "bitvec", "fatality", - "futures 0.3.29", + "futures 0.3.30", "polkadot-erasure-coding", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -8822,7 +8834,7 @@ name = "polkadot-node-core-bitfield-signing" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", @@ -8838,7 +8850,7 @@ version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "parity-scale-codec", "polkadot-node-core-pvf", @@ -8857,7 +8869,7 @@ name = "polkadot-node-core-chain-api" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "polkadot-node-metrics", "polkadot-node-subsystem", "polkadot-primitives", @@ -8872,7 +8884,7 @@ name = "polkadot-node-core-chain-selection" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "kvdb", "parity-scale-codec", @@ -8890,7 +8902,7 @@ version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "fatality", - "futures 0.3.29", + "futures 0.3.30", "kvdb", "lru 0.9.0", "parity-scale-codec", @@ -8909,7 +8921,7 @@ version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "polkadot-node-subsystem", "polkadot-overseer", @@ -8927,7 +8939,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f99 dependencies = [ "bitvec", "fatality", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -8944,7 +8956,7 @@ version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "always-assert", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "libc", "parity-scale-codec", @@ -8970,7 +8982,7 @@ name = "polkadot-node-core-pvf-checker" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-util", @@ -8988,7 +9000,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f99 dependencies = [ "assert_matches", "cpu-time", - "futures 0.3.29", + "futures 0.3.30", "libc", "parity-scale-codec", "polkadot-node-core-pvf", @@ -9015,7 +9027,7 @@ name = "polkadot-node-core-runtime-api" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "lru 0.9.0", "polkadot-node-metrics", "polkadot-node-subsystem", @@ -9049,7 +9061,7 @@ version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "bs58", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "log", "parity-scale-codec", @@ -9070,7 +9082,7 @@ dependencies = [ "async-trait", "derive_more", "fatality", - "futures 0.3.29", + "futures 0.3.30", "hex", "parity-scale-codec", "polkadot-node-jaeger", @@ -9090,7 +9102,7 @@ version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "bounded-vec", - "futures 0.3.29", + "futures 0.3.30", "parity-scale-codec", "polkadot-parachain", "polkadot-primitives", @@ -9123,7 +9135,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f99 dependencies = [ "async-trait", "derive_more", - "futures 0.3.29", + "futures 0.3.30", "orchestra", "polkadot-node-jaeger", "polkadot-node-network-protocol", @@ -9147,7 +9159,7 @@ dependencies = [ "async-trait", "derive_more", "fatality", - "futures 0.3.29", + "futures 0.3.30", "futures-channel", "itertools", "kvdb", @@ -9178,7 +9190,7 @@ version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "lru 0.9.0", "orchestra", @@ -9507,7 +9519,7 @@ dependencies = [ "frame-benchmarking-cli", "frame-support", "frame-system-rpc-runtime-api", - "futures 0.3.29", + "futures 0.3.30", "hex-literal 0.4.1", "kusama-runtime", "kvdb", @@ -9614,7 +9626,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f99 dependencies = [ "arrayvec 0.5.2", "fatality", - "futures 0.3.29", + "futures 0.3.30", "indexmap 1.9.3", "parity-scale-codec", "polkadot-node-network-protocol", @@ -9713,7 +9725,7 @@ dependencies = [ "hex-literal 0.4.1", "impl-trait-for-tuples", "log", - "num_enum 0.5.11", + "num_enum 0.7.1", "pallet-evm", "parity-scale-codec", "precompile-utils-macro", @@ -9730,11 +9742,11 @@ dependencies = [ name = "precompile-utils-macro" version = "0.9.17" dependencies = [ - "num_enum 0.5.11", + "num_enum 0.7.1", "proc-macro2", "quote", "sha3", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -9800,7 +9812,7 @@ dependencies = [ "coarsetime", "crossbeam-queue", "derive_more", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "nanorand", "thiserror", @@ -9849,14 +9861,14 @@ checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" dependencies = [ "unicode-ident", ] @@ -10040,9 +10052,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -10245,7 +10257,7 @@ checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -10660,7 +10672,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "log", "netlink-packet-route", "netlink-proto", @@ -10890,7 +10902,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "pin-project", "static_assertions", ] @@ -10936,7 +10948,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "ip_network", "libp2p", @@ -10963,7 +10975,7 @@ name = "sc-basic-authorship" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "log", "parity-scale-codec", @@ -11023,7 +11035,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -11035,7 +11047,7 @@ dependencies = [ "chrono", "clap", "fdlimit", - "futures 0.3.29", + "futures 0.3.30", "libp2p", "log", "names", @@ -11072,7 +11084,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "fnv", - "futures 0.3.29", + "futures 0.3.30", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -11124,7 +11136,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "libp2p", "log", @@ -11149,7 +11161,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "log", "parity-scale-codec", "sc-block-builder", @@ -11179,7 +11191,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "async-trait", "fork-tree", - "futures 0.3.29", + "futures 0.3.30", "log", "num-bigint", "num-rational", @@ -11213,7 +11225,7 @@ name = "sc-consensus-babe-rpc" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee", "sc-consensus-babe", "sc-consensus-epochs", @@ -11238,7 +11250,7 @@ dependencies = [ "array-bytes 4.2.0", "async-trait", "fnv", - "futures 0.3.29", + "futures 0.3.30", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -11270,7 +11282,7 @@ name = "sc-consensus-beefy-rpc" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee", "log", "parity-scale-codec", @@ -11308,7 +11320,7 @@ dependencies = [ "dyn-clone", "finality-grandpa", "fork-tree", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "log", "parity-scale-codec", @@ -11343,7 +11355,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "finality-grandpa", - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee", "log", "parity-scale-codec", @@ -11363,7 +11375,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "log", "parity-scale-codec", @@ -11454,7 +11466,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "ansi_term", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "log", "sc-client-api", @@ -11491,7 +11503,7 @@ dependencies = [ "bytes", "either", "fnv", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "ip_network", "libp2p", @@ -11530,7 +11542,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "cid", - "futures 0.3.29", + "futures 0.3.30", "libp2p", "log", "prost", @@ -11553,7 +11565,7 @@ dependencies = [ "async-trait", "bitflags 1.3.2", "bytes", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "libp2p", "parity-scale-codec", @@ -11578,7 +11590,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "ahash 0.8.3", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "libp2p", "log", @@ -11597,7 +11609,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes 4.2.0", - "futures 0.3.29", + "futures 0.3.30", "libp2p", "log", "parity-scale-codec", @@ -11621,7 +11633,7 @@ dependencies = [ "array-bytes 4.2.0", "async-trait", "fork-tree", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "libp2p", "log", @@ -11653,7 +11665,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes 4.2.0", - "futures 0.3.29", + "futures 0.3.30", "libp2p", "log", "parity-scale-codec", @@ -11675,7 +11687,7 @@ dependencies = [ "array-bytes 4.2.0", "bytes", "fnv", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "hyper", "hyper-rustls", @@ -11703,7 +11715,7 @@ name = "sc-peerset" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "libp2p", "log", "sc-utils", @@ -11725,7 +11737,7 @@ name = "sc-rpc" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee", "log", "parity-scale-codec", @@ -11790,7 +11802,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes 4.2.0", - "futures 0.3.29", + "futures 0.3.30", "futures-util", "hex", "jsonrpsee", @@ -11818,7 +11830,7 @@ dependencies = [ "async-trait", "directories", "exit-future", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "jsonrpsee", "log", @@ -11894,7 +11906,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "clap", "fs4", - "futures 0.3.29", + "futures 0.3.30", "log", "sc-client-db", "sc-utils", @@ -11927,7 +11939,7 @@ name = "sc-sysinfo" version = "6.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "libc", "log", "rand 0.8.5", @@ -11947,7 +11959,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "chrono", - "futures 0.3.29", + "futures 0.3.30", "libp2p", "log", "parking_lot 0.12.1", @@ -11999,7 +12011,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -12008,7 +12020,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "linked-hash-map", "log", @@ -12035,7 +12047,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "log", "serde", "sp-blockchain", @@ -12049,7 +12061,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-channel", - "futures 0.3.29", + "futures 0.3.30", "futures-timer", "lazy_static", "log", @@ -12285,7 +12297,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -12479,9 +12491,9 @@ dependencies = [ [[package]] name = "similar-asserts" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf644ad016b75129f01a34a355dcb8d66a5bc803e417c7a77cc5d5ee9fa0f18" +checksum = "e041bb827d1bfca18f213411d51b665309f1afb37a04a5d1464530e13779fc0f" dependencies = [ "console", "similar", @@ -12587,7 +12599,7 @@ dependencies = [ "base64 0.13.1", "bytes", "flate2", - "futures 0.3.29", + "futures 0.3.30", "http", "httparse", "log", @@ -12626,7 +12638,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -12686,7 +12698,7 @@ name = "sp-blockchain" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "log", "lru 0.8.1", "parity-scale-codec", @@ -12705,7 +12717,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", - "futures 0.3.29", + "futures 0.3.30", "log", "sp-core", "sp-inherents", @@ -12814,7 +12826,7 @@ dependencies = [ "bs58", "dyn-clonable", "ed25519-zebra", - "futures 0.3.29", + "futures 0.3.30", "hash-db 0.16.0", "hash256-std-hasher", "impl-serde", @@ -12868,7 +12880,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -12887,7 +12899,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -12924,7 +12936,7 @@ dependencies = [ "bytes", "ed25519", "ed25519-dalek", - "futures 0.3.29", + "futures 0.3.30", "libsecp256k1", "log", "parity-scale-codec", @@ -12958,7 +12970,7 @@ name = "sp-keystore" version = "0.13.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "parity-scale-codec", "parking_lot 0.12.1", "serde", @@ -13098,7 +13110,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -13266,7 +13278,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -13459,7 +13471,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -13542,7 +13554,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "frame-system-rpc-runtime-api", - "futures 0.3.29", + "futures 0.3.30", "jsonrpsee", "log", "parity-scale-codec", @@ -13644,9 +13656,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.40" +version = "2.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e" +checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" dependencies = [ "proc-macro2", "quote", @@ -13770,7 +13782,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -13914,9 +13926,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -13939,7 +13951,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -14085,7 +14097,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -14128,7 +14140,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -14310,7 +14322,7 @@ checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" dependencies = [ "async-trait", "base64 0.13.1", - "futures 0.3.29", + "futures 0.3.30", "log", "md-5", "rand 0.8.5", @@ -14575,7 +14587,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", "wasm-bindgen-shared", ] @@ -14609,7 +14621,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -14676,7 +14688,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "js-sys", "parking_lot 0.11.2", "pin-utils", @@ -15683,7 +15695,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] @@ -15757,7 +15769,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" dependencies = [ - "futures 0.3.29", + "futures 0.3.30", "log", "nohash-hasher", "parking_lot 0.12.1", @@ -15791,7 +15803,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.46", ] [[package]] diff --git a/Makefile b/Makefile index b959abca5c..2572c54712 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ build-all: .PHONY: build-node ## Build release node with `tee-dev` feature build-node: - cargo build --locked -p $(call pkgid, $(NODE_BIN)) --release --features=tee-dev + cargo build --locked -p $(call pkgid, $(NODE_BIN)) --release --features=tee-dev,fast-runtime .PHONY: build-runtime-litentry ## Build litentry release runtime build-runtime-litentry: @@ -61,7 +61,7 @@ srtool-build-wasm-rococo: .PHONY: build-docker-release ## Build docker image using cargo profile `release` # with `tee-dev` feature as we use release profile in dev build-docker-release: - @./scripts/build-docker.sh release latest --features=tee-dev + @./scripts/build-docker.sh release latest --features=tee-dev,fast-runtime .PHONY: build-docker-production ## Build docker image using cargo profile `production` build-docker-production: diff --git a/node/Cargo.toml b/node/Cargo.toml index fb75e823d1..de0ebf6ed6 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -16,13 +16,13 @@ name = 'litentry-collator' targets = ['x86_64-unknown-linux-gnu'] [dependencies] -async-trait = "0.1.74" +async-trait = "0.1.76" clap = { version = "4.3", features = ["derive"] } -futures = { version = "0.3.29", features = ["compat"] } +futures = { version = "0.3.30", features = ["compat"] } log = "0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -tokio = { version = "1.35.0", features = ["macros", "sync"] } +tokio = { version = "1.35.1", features = ["macros", "sync"] } # Substrate dependencies sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } diff --git a/pallets/identity-management/src/benchmarking.rs b/pallets/identity-management/src/benchmarking.rs index d57bc5ed1a..0e9bbea396 100644 --- a/pallets/identity-management/src/benchmarking.rs +++ b/pallets/identity-management/src/benchmarking.rs @@ -20,7 +20,7 @@ use super::*; use crate::Pallet as IdentityManagement; #[allow(unused)] -use core_primitives::{ErrorDetail, IMPError}; +use core_primitives::{AccountId, ErrorDetail, IMPError, Identity}; use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, BenchmarkError}; use frame_support::traits::EnsureOrigin; use frame_system::RawOrigin; @@ -93,75 +93,65 @@ benchmarks! { assert_last_event::(Event::ActivateIdentityRequested{ shard }.into()); } - update_id_graph_hash { - let call_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - let new_hash = H256::default(); - let req_ext_hash = H256::default(); - }: _(call_origin, account.clone(), new_hash, req_ext_hash) - verify { - assert_last_event::(Event::IDGraphHashUpdated { account, new_hash, req_ext_hash }.into()) - } - // Benchmark `identity_linked`. There are no worst conditions. The benchmark showed that // execution time is constant irrespective of encrypted_data size. identity_linked { - let req_ext_hash = H256::default(); let id_graph_hash = H256::default(); + let req_ext_hash = H256::default(); let call_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - }: _(call_origin, account.clone(), id_graph_hash, req_ext_hash) + let prime_identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); + }: _(call_origin, prime_identity.clone(), id_graph_hash, req_ext_hash) verify { - assert_last_event::(Event::IdentityLinked { account, id_graph_hash, req_ext_hash }.into()); + assert_last_event::(Event::IdentityLinked { prime_identity, id_graph_hash, req_ext_hash }.into()); } // Benchmark `identity_deactivated`. There are no worst conditions. The benchmark showed that // execution time is constant irrespective of encrypted_data size. identity_deactivated { - let req_ext_hash = H256::default(); let id_graph_hash = H256::default(); + let req_ext_hash = H256::default(); let call_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - }: _(call_origin, account.clone(), id_graph_hash, req_ext_hash) + let prime_identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); + }: _(call_origin, prime_identity.clone(), id_graph_hash, req_ext_hash) verify { - assert_last_event::(Event::IdentityDeactivated { account, id_graph_hash, req_ext_hash }.into()); + assert_last_event::(Event::IdentityDeactivated { prime_identity, id_graph_hash, req_ext_hash }.into()); } // Benchmark `identity_activated`. There are no worst conditions. The benchmark showed that // execution time is constant irrespective of encrypted_data size. identity_activated { - let req_ext_hash = H256::default(); let id_graph_hash = H256::default(); + let req_ext_hash = H256::default(); let call_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - }: _(call_origin, account.clone(), id_graph_hash, req_ext_hash) + let prime_identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); + }: _(call_origin, prime_identity.clone(), id_graph_hash, req_ext_hash) verify { - assert_last_event::(Event::IdentityActivated { account, id_graph_hash, req_ext_hash }.into()); + assert_last_event::(Event::IdentityActivated { prime_identity, id_graph_hash, req_ext_hash }.into()); } // Benchmark `identity_networks_set`. There are no worst conditions. The benchmark showed that // execution time is constant irrespective of encrypted_data size. identity_networks_set { - let req_ext_hash = H256::default(); let id_graph_hash = H256::default(); + let req_ext_hash = H256::default(); let call_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - }: _(call_origin, account.clone(), id_graph_hash, req_ext_hash) + let prime_identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); + }: _(call_origin, prime_identity.clone(), id_graph_hash, req_ext_hash) verify { - assert_last_event::(Event::IdentityNetworksSet { account, id_graph_hash, req_ext_hash }.into()); + assert_last_event::(Event::IdentityNetworksSet { prime_identity, id_graph_hash, req_ext_hash }.into()); } // Benchmark `some_error`. There are no worst conditions. The benchmark showed that // execution time is constant irrespective of encrypted_data size. some_error { let call_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); + let prime_identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); let detail = ErrorDetail::WrongWeb2Handle; let error = IMPError::LinkIdentityFailed(detail.clone()); let req_ext_hash = H256::default(); - }: _(call_origin, Some(account.clone()), error, req_ext_hash) + }: _(call_origin, Some(prime_identity.clone()), error, req_ext_hash) verify { - assert_last_event::(Event::LinkIdentityFailed { account: Some(account), detail, req_ext_hash }.into()) + assert_last_event::(Event::LinkIdentityFailed { prime_identity: Some(prime_identity), detail, req_ext_hash }.into()) } } diff --git a/pallets/identity-management/src/lib.rs b/pallets/identity-management/src/lib.rs index d136f462bb..b5051b5641 100644 --- a/pallets/identity-management/src/lib.rs +++ b/pallets/identity-management/src/lib.rs @@ -50,7 +50,7 @@ pub use teerex_primitives::ShardIdentifier; #[frame_support::pallet] pub mod pallet { use super::{ShardIdentifier, Vec, WeightInfo, H256}; - use core_primitives::{ErrorDetail, IMPError}; + use core_primitives::{ErrorDetail, IMPError, Identity}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -67,8 +67,6 @@ pub mod pallet { type DelegateeAdminOrigin: EnsureOrigin; // origin that is allowed to call extrinsics type ExtrinsicWhitelistOrigin: EnsureOrigin; - // dedicated origin to update the IDGraph hash - type UpdateIDGraphHashOrigin: EnsureOrigin; } #[pallet::event] @@ -95,22 +93,22 @@ pub mod pallet { // TODO: what if the event is triggered by an extrinsic that is included in a batch call? // Can we retrieve that extrinsic hash in F/E? IdentityLinked { - account: T::AccountId, + prime_identity: Identity, id_graph_hash: H256, req_ext_hash: H256, }, IdentityDeactivated { - account: T::AccountId, + prime_identity: Identity, id_graph_hash: H256, req_ext_hash: H256, }, IdentityActivated { - account: T::AccountId, + prime_identity: Identity, id_graph_hash: H256, req_ext_hash: H256, }, IdentityNetworksSet { - account: T::AccountId, + prime_identity: Identity, id_graph_hash: H256, req_ext_hash: H256, }, @@ -118,34 +116,29 @@ pub mod pallet { // copied from core_primitives::IMPError, we use events instead of pallet::errors, // see https://github.com/litentry/litentry-parachain/issues/1275 // - // why is the `account` in the error event an Option? + // why is the `prime_identity` in the error event an Option? // because in some erroneous cases we can't get the extrinsic sender (e.g. decode error) LinkIdentityFailed { - account: Option, + prime_identity: Option, detail: ErrorDetail, req_ext_hash: H256, }, DeactivateIdentityFailed { - account: Option, + prime_identity: Option, detail: ErrorDetail, req_ext_hash: H256, }, ActivateIdentityFailed { - account: Option, + prime_identity: Option, detail: ErrorDetail, req_ext_hash: H256, }, ImportScheduledEnclaveFailed, UnclassifiedError { - account: Option, + prime_identity: Option, detail: ErrorDetail, req_ext_hash: H256, }, - IDGraphHashUpdated { - account: T::AccountId, - new_hash: H256, - req_ext_hash: H256, - }, } // delegatees who can send extrinsics(currently only `link_identity`) on users' behalf @@ -153,12 +146,6 @@ pub mod pallet { #[pallet::getter(fn delegatee)] pub type Delegatee = StorageMap<_, Blake2_128Concat, T::AccountId, (), OptionQuery>; - // idgraph hashes so that the client can detect out-of-sync local IDGraph - #[pallet::storage] - #[pallet::getter(fn id_graph_hash)] - pub type IDGraphHash = - StorageMap<_, Blake2_128Concat, T::AccountId, H256, OptionQuery>; - #[pallet::error] pub enum Error { /// a delegatee doesn't exist @@ -253,25 +240,6 @@ pub mod pallet { Ok(().into()) } - /// Update the id_graph hash explicitly - /// Each IDGraph mutation event includes the `id_graph_hash` already, however, - /// there might still be situations where we need to call this fn explicitly, - /// e.g. when populating the hashes for the existing IDGraphs for the first time. - #[pallet::call_index(6)] - #[pallet::weight(::WeightInfo::update_id_graph_hash())] - pub fn update_id_graph_hash( - origin: OriginFor, - account: T::AccountId, - new_hash: H256, - req_ext_hash: H256, - ) -> DispatchResultWithPostInfo { - let _ = T::UpdateIDGraphHashOrigin::ensure_origin(origin)?; - // we don't care if `account` already exists - IDGraphHash::::insert(account.clone(), new_hash); - Self::deposit_event(Event::IDGraphHashUpdated { account, new_hash, req_ext_hash }); - Ok(Pays::No.into()) - } - /// --------------------------------------------------- /// The following extrinsics are supposed to be called by TEE only /// --------------------------------------------------- @@ -279,13 +247,16 @@ pub mod pallet { #[pallet::weight(::WeightInfo::identity_linked())] pub fn identity_linked( origin: OriginFor, - account: T::AccountId, + prime_identity: Identity, id_graph_hash: H256, req_ext_hash: H256, ) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; - IDGraphHash::::insert(account.clone(), id_graph_hash); - Self::deposit_event(Event::IdentityLinked { account, id_graph_hash, req_ext_hash }); + Self::deposit_event(Event::IdentityLinked { + prime_identity, + id_graph_hash, + req_ext_hash, + }); Ok(Pays::No.into()) } @@ -293,14 +264,13 @@ pub mod pallet { #[pallet::weight(::WeightInfo::identity_deactivated())] pub fn identity_deactivated( origin: OriginFor, - account: T::AccountId, + prime_identity: Identity, id_graph_hash: H256, req_ext_hash: H256, ) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; - IDGraphHash::::insert(account.clone(), id_graph_hash); Self::deposit_event(Event::IdentityDeactivated { - account, + prime_identity, id_graph_hash, req_ext_hash, }); @@ -311,13 +281,16 @@ pub mod pallet { #[pallet::weight(::WeightInfo::identity_activated())] pub fn identity_activated( origin: OriginFor, - account: T::AccountId, + prime_identity: Identity, id_graph_hash: H256, req_ext_hash: H256, ) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; - IDGraphHash::::insert(account.clone(), id_graph_hash); - Self::deposit_event(Event::IdentityActivated { account, id_graph_hash, req_ext_hash }); + Self::deposit_event(Event::IdentityActivated { + prime_identity, + id_graph_hash, + req_ext_hash, + }); Ok(Pays::No.into()) } @@ -325,14 +298,13 @@ pub mod pallet { #[pallet::weight(::WeightInfo::identity_networks_set())] pub fn identity_networks_set( origin: OriginFor, - account: T::AccountId, + prime_identity: Identity, id_graph_hash: H256, req_ext_hash: H256, ) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; - IDGraphHash::::insert(account.clone(), id_graph_hash); Self::deposit_event(Event::IdentityNetworksSet { - account, + prime_identity, id_graph_hash, req_ext_hash, }); @@ -343,30 +315,38 @@ pub mod pallet { #[pallet::weight(::WeightInfo::some_error())] pub fn some_error( origin: OriginFor, - account: Option, + prime_identity: Option, error: IMPError, req_ext_hash: H256, ) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; match error { IMPError::LinkIdentityFailed(detail) => - Self::deposit_event(Event::LinkIdentityFailed { account, detail, req_ext_hash }), + Self::deposit_event(Event::LinkIdentityFailed { + prime_identity, + detail, + req_ext_hash, + }), IMPError::DeactivateIdentityFailed(detail) => Self::deposit_event(Event::DeactivateIdentityFailed { - account, + prime_identity, detail, req_ext_hash, }), IMPError::ActivateIdentityFailed(detail) => Self::deposit_event(Event::ActivateIdentityFailed { - account, + prime_identity, detail, req_ext_hash, }), IMPError::ImportScheduledEnclaveFailed => Self::deposit_event(Event::ImportScheduledEnclaveFailed), IMPError::UnclassifiedError(detail) => - Self::deposit_event(Event::UnclassifiedError { account, detail, req_ext_hash }), + Self::deposit_event(Event::UnclassifiedError { + prime_identity, + detail, + req_ext_hash, + }), } Ok(Pays::No.into()) } diff --git a/pallets/identity-management/src/mock.rs b/pallets/identity-management/src/mock.rs index 49caeb75ec..ba1f914b68 100644 --- a/pallets/identity-management/src/mock.rs +++ b/pallets/identity-management/src/mock.rs @@ -168,7 +168,6 @@ impl pallet_identity_management::Config for Test { type TEECallOrigin = EnsureEnclaveSigner; type DelegateeAdminOrigin = EnsureRoot; type ExtrinsicWhitelistOrigin = IMPExtrinsicWhitelist; - type UpdateIDGraphHashOrigin = EnsureEnclaveSigner; } impl pallet_group::Config for Test { diff --git a/pallets/identity-management/src/tests.rs b/pallets/identity-management/src/tests.rs index 47bef80961..147d2c1240 100644 --- a/pallets/identity-management/src/tests.rs +++ b/pallets/identity-management/src/tests.rs @@ -143,7 +143,7 @@ fn tee_callback_with_registered_enclave_works() { )); System::assert_last_event(RuntimeEvent::IdentityManagement( crate::Event::LinkIdentityFailed { - account: None, + prime_identity: None, detail: ErrorDetail::WrongWeb2Handle, req_ext_hash: H256::default(), }, diff --git a/pallets/identity-management/src/weights.rs b/pallets/identity-management/src/weights.rs index a6567a164e..706ae3e421 100644 --- a/pallets/identity-management/src/weights.rs +++ b/pallets/identity-management/src/weights.rs @@ -55,7 +55,6 @@ pub trait WeightInfo { fn link_identity() -> Weight; fn deactivate_identity() -> Weight; fn activate_identity() -> Weight; - fn update_id_graph_hash() -> Weight; fn identity_linked() -> Weight; fn identity_deactivated() -> Weight; fn identity_activated() -> Weight; @@ -121,19 +120,6 @@ impl WeightInfo for LitentryWeight { /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) /// Storage: IdentityManagement IDGraphHash (r:0 w:1) /// Proof: IdentityManagement IDGraphHash (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) - fn update_id_graph_hash() -> Weight { - // Proof Size summary in bytes: - // Measured: `255` - // Estimated: `3720` - // Minimum execution time: 22_016_000 picoseconds. - Weight::from_parts(22_404_000, 3720) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: Teerex EnclaveIndex (r:1 w:0) - /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: IdentityManagement IDGraphHash (r:0 w:1) - /// Proof: IdentityManagement IDGraphHash (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn identity_linked() -> Weight { // Proof Size summary in bytes: // Measured: `255` @@ -251,19 +237,6 @@ impl WeightInfo for () { /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) /// Storage: IdentityManagement IDGraphHash (r:0 w:1) /// Proof: IdentityManagement IDGraphHash (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) - fn update_id_graph_hash() -> Weight { - // Proof Size summary in bytes: - // Measured: `255` - // Estimated: `3720` - // Minimum execution time: 22_016_000 picoseconds. - Weight::from_parts(22_404_000, 3720) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: Teerex EnclaveIndex (r:1 w:0) - /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: IdentityManagement IDGraphHash (r:0 w:1) - /// Proof: IdentityManagement IDGraphHash (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) fn identity_linked() -> Weight { // Proof Size summary in bytes: // Measured: `255` diff --git a/pallets/parachain-staking/src/delegation_requests.rs b/pallets/parachain-staking/src/delegation_requests.rs index eb8fddef30..e01b7edf34 100644 --- a/pallets/parachain-staking/src/delegation_requests.rs +++ b/pallets/parachain-staking/src/delegation_requests.rs @@ -214,7 +214,6 @@ impl Pallet { let now = >::get().current; ensure!(request.when_executable <= now, >::PendingDelegationRequestNotDueYet); - match request.action { DelegationAction::Revoke(amount) => { // revoking last delegation => leaving set of delegators diff --git a/pallets/vc-management/Cargo.toml b/pallets/vc-management/Cargo.toml index fa05d00f38..1098aa0dc6 100644 --- a/pallets/vc-management/Cargo.toml +++ b/pallets/vc-management/Cargo.toml @@ -43,6 +43,8 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-group/runtime-benchmarks", "test-utils", ] std = [ @@ -56,5 +58,7 @@ std = [ "frame-benchmarking?/std", "core-primitives/std", "teerex-primitives/std", + "pallet-balances/std", + "pallet-group/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/vc-management/src/benchmarking.rs b/pallets/vc-management/src/benchmarking.rs index 3b50409139..a6bde4b77b 100644 --- a/pallets/vc-management/src/benchmarking.rs +++ b/pallets/vc-management/src/benchmarking.rs @@ -19,7 +19,7 @@ use super::*; use crate::Pallet as VCManagement; -use core_primitives::{ErrorDetail, VCMPError}; +use core_primitives::{AccountId, Assertion, ErrorDetail, Identity, VCMPError}; use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, BenchmarkError}; use frame_support::traits::EnsureOrigin; use frame_system::RawOrigin; @@ -82,10 +82,11 @@ benchmarks! { // execution time is constant irrespective of encrypted_data size. disable_vc { let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); + let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); let assertion = Assertion::A1; let req_ext_hash = H256::default(); let tee_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - VCManagement::::vc_issued(tee_origin, account.clone(), assertion, VC_INDEX, VC_HASH, req_ext_hash)?; + VCManagement::::vc_issued(tee_origin, identity, assertion, VC_INDEX, VC_HASH, req_ext_hash)?; }: _(RawOrigin::Signed(account.clone()), VC_INDEX) verify{ assert_last_event::(Event::VCDisabled{ account, index: VC_HASH }.into()); @@ -95,10 +96,11 @@ benchmarks! { // execution time is constant irrespective of encrypted_data size. revoke_vc { let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); + let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); let assertion = Assertion::A1; let req_ext_hash = H256::default(); let tee_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - VCManagement::::vc_issued(tee_origin, account.clone(), assertion, VC_INDEX, VC_HASH, req_ext_hash)?; + VCManagement::::vc_issued(tee_origin, identity, assertion, VC_INDEX, VC_HASH, req_ext_hash)?; }: _(RawOrigin::Signed(account.clone()), VC_INDEX) verify{ assert_last_event::(Event::VCRevoked{ account, index: VC_HASH }.into()); @@ -108,26 +110,26 @@ benchmarks! { // execution time is constant irrespective of encrypted_data size. vc_issued { let call_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); + let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); let assertion = Assertion::A1; let req_ext_hash = H256::default(); - }: _(call_origin, account.clone(), assertion.clone(), VC_INDEX, VC_HASH, req_ext_hash) + }: _(call_origin, identity.clone(), assertion.clone(), VC_INDEX, VC_HASH, req_ext_hash) verify{ - assert_last_event::(Event::VCIssued{ account, assertion, index: VC_INDEX, req_ext_hash}.into()); + assert_last_event::(Event::VCIssued{ identity, assertion, index: VC_INDEX, req_ext_hash}.into()); } // Benchmark `some_error`. There are no worst conditions. The benchmark showed that // execution time is constant irrespective of encrypted_data size. some_error { let call_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); + let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); let detail = ErrorDetail::WrongWeb2Handle; let assertion = Assertion::A1; let error = VCMPError::RequestVCFailed(assertion.clone(), detail.clone()); let req_ext_hash = H256::default(); - }: _(call_origin, Some(account.clone()), error, req_ext_hash) + }: _(call_origin, Some(identity.clone()), error, req_ext_hash) verify { - assert_last_event::(Event::RequestVCFailed { account: Some(account), assertion, detail, req_ext_hash }.into()) + assert_last_event::(Event::RequestVCFailed { identity: Some(identity), assertion, detail, req_ext_hash }.into()) } // Benchmark `set_admin`. There are no worst conditions. The benchmark showed that @@ -201,20 +203,22 @@ benchmarks! { // execution time is constant irrespective of encrypted_data size. add_vc_registry_item { let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); + let identity: Identity = frame_benchmarking::account::("TEST_B", 0u32, USER_SEED).into(); VCManagement::::set_admin(RawOrigin::Root.into(), account.clone())?; let assertion = Assertion::A1; - }: _(RawOrigin::Signed(account.clone()), VC_INDEX, account.clone(), assertion.clone(), VC_HASH) + }: _(RawOrigin::Signed(account.clone()), VC_INDEX, identity.clone(), assertion.clone(), VC_HASH) verify { - assert_last_event::(Event::VCRegistryItemAdded { account, assertion, index: VC_INDEX }.into()) + assert_last_event::(Event::VCRegistryItemAdded { identity, assertion, index: VC_INDEX }.into()) } // Benchmark `remove_vc_registry_item`. There are no worst conditions. The benchmark showed that // execution time is constant irrespective of encrypted_data size. remove_vc_registry_item { let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); + let identity: Identity = frame_benchmarking::account::("TEST_B", 0u32, USER_SEED).into(); VCManagement::::set_admin(RawOrigin::Root.into(), account.clone())?; let assertion = Assertion::A1; - VCManagement::::add_vc_registry_item(RawOrigin::Signed(account.clone()).into(), VC_INDEX, account.clone(), assertion, VC_HASH)?; + VCManagement::::add_vc_registry_item(RawOrigin::Signed(account.clone()).into(), VC_INDEX, identity, assertion, VC_HASH)?; }: _(RawOrigin::Signed(account), VC_INDEX) verify { assert_last_event::(Event::VCRegistryItemRemoved { index: VC_INDEX }.into()) @@ -228,10 +232,10 @@ benchmarks! { let assertion = Assertion::A1; for i in 0..x { let seed = USER_SEED - i; - let candidate:T::AccountId = frame_benchmarking::account("TEST_A", 0u32, seed); + let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, seed).into(); let seed_hash_u8_32 = convert_u32_array_to_u8_array([seed; 8]); let hash: H256 = seed_hash_u8_32.into(); - VCManagement::::add_vc_registry_item(RawOrigin::Signed(account.clone()).into(), hash, candidate.clone(), assertion.clone(), VC_HASH)?; + VCManagement::::add_vc_registry_item(RawOrigin::Signed(account.clone()).into(), hash, identity, assertion.clone(), VC_HASH)?; } }: _(RawOrigin::Signed(account)) verify { diff --git a/pallets/vc-management/src/lib.rs b/pallets/vc-management/src/lib.rs index a37674cf71..ec62b54282 100644 --- a/pallets/vc-management/src/lib.rs +++ b/pallets/vc-management/src/lib.rs @@ -34,7 +34,6 @@ pub mod weights; pub use crate::weights::WeightInfo; -use core_primitives::{Assertion, SchemaIndex, SCHEMA_CONTENT_LEN, SCHEMA_ID_LEN}; pub use pallet::*; use sp_core::H256; use sp_std::vec::Vec; @@ -51,7 +50,9 @@ pub type VCIndex = H256; #[frame_support::pallet] pub mod pallet { use super::*; - use core_primitives::{ErrorDetail, VCMPError}; + use core_primitives::{ + Assertion, ErrorDetail, Identity, SchemaIndex, VCMPError, SCHEMA_CONTENT_LEN, SCHEMA_ID_LEN, + }; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -73,9 +74,10 @@ pub mod pallet { } // a map VCIndex -> VC context + // TODO: to be removed in P-350 #[pallet::storage] #[pallet::getter(fn vc_registry)] - pub type VCRegistry = StorageMap<_, Blake2_128Concat, VCIndex, VCContext>; + pub type VCRegistry = StorageMap<_, Blake2_128Concat, VCIndex, VCContext>; // the admin account #[pallet::storage] @@ -128,7 +130,7 @@ pub mod pallet { // event that should be triggered by TEECallOrigin // a VC is just issued VCIssued { - account: T::AccountId, + identity: Identity, assertion: Assertion, index: VCIndex, req_ext_hash: H256, @@ -166,18 +168,18 @@ pub mod pallet { // copied from core_primitives::VCMPError, we use events instead of pallet::errors, // see https://github.com/litentry/litentry-parachain/issues/1275 RequestVCFailed { - account: Option, + identity: Option, assertion: Assertion, detail: ErrorDetail, req_ext_hash: H256, }, UnclassifiedError { - account: Option, + identity: Option, detail: ErrorDetail, req_ext_hash: H256, }, VCRegistryItemAdded { - account: T::AccountId, + identity: Identity, assertion: Assertion, index: VCIndex, }, @@ -280,7 +282,10 @@ pub mod pallet { let who = T::ExtrinsicWhitelistOrigin::ensure_origin(origin)?; VCRegistry::::try_mutate(index, |context| { let mut c = context.take().ok_or(Error::::VCNotExist)?; - ensure!(who == c.subject, Error::::VCSubjectMismatch); + ensure!( + Some(who.clone()).encode() == c.subject.to_account_id().encode(), + Error::::VCSubjectMismatch + ); ensure!(c.status == Status::Active, Error::::VCAlreadyDisabled); c.status = Status::Disabled; *context = Some(c); @@ -294,7 +299,10 @@ pub mod pallet { pub fn revoke_vc(origin: OriginFor, index: VCIndex) -> DispatchResultWithPostInfo { let who = T::ExtrinsicWhitelistOrigin::ensure_origin(origin)?; let context = VCRegistry::::get(index).ok_or(Error::::VCNotExist)?; - ensure!(who == context.subject, Error::::VCSubjectMismatch); + ensure!( + Some(who.clone()).encode() == context.subject.to_account_id().encode(), + Error::::VCSubjectMismatch + ); VCRegistry::::remove(index); Self::deposit_event(Event::VCRevoked { account: who, index }); Ok(().into()) @@ -396,7 +404,7 @@ pub mod pallet { pub fn add_vc_registry_item( origin: OriginFor, index: VCIndex, - subject: T::AccountId, + identity: Identity, assertion: Assertion, hash: H256, ) -> DispatchResultWithPostInfo { @@ -405,9 +413,9 @@ pub mod pallet { ensure!(!VCRegistry::::contains_key(index), Error::::VCAlreadyExists); VCRegistry::::insert( index, - VCContext::::new(subject.clone(), assertion.clone(), hash), + VCContext::new(identity.clone(), assertion.clone(), hash), ); - Self::deposit_event(Event::VCRegistryItemAdded { account: subject, assertion, index }); + Self::deposit_event(Event::VCRegistryItemAdded { identity, assertion, index }); Ok(().into()) } @@ -443,7 +451,7 @@ pub mod pallet { #[pallet::weight(::WeightInfo::vc_issued())] pub fn vc_issued( origin: OriginFor, - account: T::AccountId, + identity: Identity, assertion: Assertion, index: H256, hash: H256, @@ -453,9 +461,9 @@ pub mod pallet { ensure!(!VCRegistry::::contains_key(index), Error::::VCAlreadyExists); VCRegistry::::insert( index, - VCContext::::new(account.clone(), assertion.clone(), hash), + VCContext::new(identity.clone(), assertion.clone(), hash), ); - Self::deposit_event(Event::VCIssued { account, assertion, index, req_ext_hash }); + Self::deposit_event(Event::VCIssued { identity, assertion, index, req_ext_hash }); Ok(Pays::No.into()) } @@ -463,7 +471,7 @@ pub mod pallet { #[pallet::weight(::WeightInfo::some_error())] pub fn some_error( origin: OriginFor, - account: Option, + identity: Option, error: VCMPError, req_ext_hash: H256, ) -> DispatchResultWithPostInfo { @@ -471,13 +479,13 @@ pub mod pallet { match error { VCMPError::RequestVCFailed(assertion, detail) => Self::deposit_event(Event::RequestVCFailed { - account, + identity, assertion, detail, req_ext_hash, }), VCMPError::UnclassifiedError(detail) => - Self::deposit_event(Event::UnclassifiedError { account, detail, req_ext_hash }), + Self::deposit_event(Event::UnclassifiedError { identity, detail, req_ext_hash }), } Ok(Pays::No.into()) } diff --git a/pallets/vc-management/src/tests.rs b/pallets/vc-management/src/tests.rs index d0aa819b68..32507d15a7 100644 --- a/pallets/vc-management/src/tests.rs +++ b/pallets/vc-management/src/tests.rs @@ -14,7 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . -use crate::{mock::*, Assertion, Error, ShardIdentifier, Status}; +use crate::{mock::*, Error, ShardIdentifier, Status}; +use core_primitives::{Assertion, Identity}; use frame_support::{assert_noop, assert_ok}; use sp_core::H256; @@ -85,7 +86,7 @@ fn request_vc_13_with_unauthorized_delegatee_fails() { fn vc_issued_works() { new_test_ext().execute_with(|| { let teerex_signer: SystemAccountId = test_utils::get_signer(TEST8_SIGNER_PUB); - let alice: SystemAccountId = test_utils::get_signer(ALICE_PUBKEY); + let alice: Identity = test_utils::get_signer(ALICE_PUBKEY); assert_ok!(VCManagement::vc_issued( RuntimeOrigin::signed(teerex_signer), alice.clone(), @@ -110,7 +111,7 @@ fn vc_issued_with_unpriviledged_origin_fails() { assert_noop!( VCManagement::vc_issued( RuntimeOrigin::signed(bob), - alice, + alice.into(), Assertion::A1, H256::default(), H256::default(), @@ -128,7 +129,7 @@ fn vc_issued_with_duplicated_index_fails() { let alice: SystemAccountId = test_utils::get_signer(ALICE_PUBKEY); assert_ok!(VCManagement::vc_issued( RuntimeOrigin::signed(teerex_signer.clone()), - alice.clone(), + alice.clone().into(), Assertion::A1, VC_INDEX, VC_HASH, @@ -137,7 +138,7 @@ fn vc_issued_with_duplicated_index_fails() { assert_noop!( VCManagement::vc_issued( RuntimeOrigin::signed(teerex_signer), - alice, + alice.into(), Assertion::A1, VC_INDEX, VC_HASH, @@ -155,7 +156,7 @@ fn disable_vc_works() { let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); assert_ok!(VCManagement::vc_issued( RuntimeOrigin::signed(teerex_signer), - bob.clone(), + bob.clone().into(), Assertion::A1, VC_INDEX, VC_HASH, @@ -189,7 +190,7 @@ fn disable_vc_with_other_subject_fails() { let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); assert_ok!(VCManagement::vc_issued( RuntimeOrigin::signed(teerex_signer), - bob, + bob.into(), Assertion::A1, VC_INDEX, VC_HASH, @@ -211,7 +212,7 @@ fn revoke_vc_works() { let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); assert_ok!(VCManagement::vc_issued( RuntimeOrigin::signed(teerex_signer), - bob.clone(), + bob.clone().into(), Assertion::A1, VC_INDEX, VC_HASH, @@ -243,7 +244,7 @@ fn revoke_vc_with_other_subject_fails() { let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); assert_ok!(VCManagement::vc_issued( RuntimeOrigin::signed(teerex_signer), - bob, + bob.into(), Assertion::A1, VC_INDEX, VC_HASH, @@ -508,7 +509,7 @@ fn manual_add_remove_vc_registry_item_works() { VCManagement::add_vc_registry_item( RuntimeOrigin::signed(bob.clone()), VC_INDEX, - bob.clone(), + bob.clone().into(), Assertion::A1, VC_HASH ), @@ -518,14 +519,14 @@ fn manual_add_remove_vc_registry_item_works() { assert_ok!(VCManagement::add_vc_registry_item( RuntimeOrigin::signed(alice.clone()), VC_INDEX, - alice.clone(), + alice.clone().into(), Assertion::A1, VC_HASH )); // Check result assert!(VCManagement::vc_registry(VC_INDEX).is_some()); System::assert_last_event(RuntimeEvent::VCManagement(crate::Event::VCRegistryItemAdded { - account: alice.clone(), + identity: alice.clone().into(), assertion: Assertion::A1, index: VC_INDEX, })); @@ -554,7 +555,7 @@ fn manual_add_clear_vc_registry_item_works() { VCManagement::add_vc_registry_item( RuntimeOrigin::signed(bob.clone()), VC_INDEX, - bob.clone(), + bob.clone().into(), Assertion::A1, VC_HASH ), @@ -564,14 +565,14 @@ fn manual_add_clear_vc_registry_item_works() { assert_ok!(VCManagement::add_vc_registry_item( RuntimeOrigin::signed(alice.clone()), VC_INDEX, - alice.clone(), + alice.clone().into(), Assertion::A1, VC_HASH )); // Check result assert!(VCManagement::vc_registry(VC_INDEX).is_some()); System::assert_last_event(RuntimeEvent::VCManagement(crate::Event::VCRegistryItemAdded { - account: alice.clone(), + identity: alice.clone().into(), assertion: Assertion::A1, index: VC_INDEX, })); diff --git a/pallets/vc-management/src/vc_context.rs b/pallets/vc-management/src/vc_context.rs index 43020941b5..24815cbd51 100644 --- a/pallets/vc-management/src/vc_context.rs +++ b/pallets/vc-management/src/vc_context.rs @@ -15,11 +15,10 @@ // along with Litentry. If not, see . use codec::{Decode, Encode, MaxEncodedLen}; +use core_primitives::{Assertion, Identity}; use scale_info::TypeInfo; use sp_core::H256; -use crate::{Assertion, Config}; - #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] pub enum Status { #[codec(index = 0)] @@ -30,15 +29,13 @@ pub enum Status { } #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[scale_info(skip_type_params(T))] -#[codec(mel_bound())] -pub struct VCContext { +pub struct VCContext { // To be discussed: shall we make it public? // pros: easier for the user to disable/revoke VCs, we'll need the AccountId to verify // the owner of VC. An alternative is to store such information within TEE. // cons: this information is then public, everyone knows e.g. ALICE owns VC ID 1234 + 4321 // It's not bad though as it helps to verify the ownership of VC - pub subject: T::AccountId, + pub subject: Identity, // requested assertion type pub assertion: Assertion, // hash of the VC, computed via blake2_256 @@ -47,8 +44,8 @@ pub struct VCContext { pub status: Status, } -impl VCContext { - pub fn new(subject: T::AccountId, assertion: Assertion, hash: H256) -> Self { +impl VCContext { + pub fn new(subject: Identity, assertion: Assertion, hash: H256) -> Self { Self { subject, assertion, hash, status: Status::Active } } } diff --git a/precompiles/bridge-transfer/Cargo.toml b/precompiles/bridge-transfer/Cargo.toml index 320a558546..a90dd3e709 100644 --- a/precompiles/bridge-transfer/Cargo.toml +++ b/precompiles/bridge-transfer/Cargo.toml @@ -6,7 +6,7 @@ version = '0.9.17' [dependencies] log = { version = "0.4", default-features = false } -num_enum = { version = "0.5.3", default-features = false } +num_enum = { version = "0.7.1", default-features = false } parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } rustc-hex = { version = "2.0.1", default-features = false } diff --git a/precompiles/parachain-staking/Cargo.toml b/precompiles/parachain-staking/Cargo.toml index df5627ee6e..920147bdbd 100644 --- a/precompiles/parachain-staking/Cargo.toml +++ b/precompiles/parachain-staking/Cargo.toml @@ -6,7 +6,7 @@ version = '0.9.17' [dependencies] log = { version = "0.4", default-features = false } -num_enum = { version = "0.5.3", default-features = false } +num_enum = { version = "0.7.1", default-features = false } parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } rustc-hex = { version = "2.0.1", default-features = false } diff --git a/precompiles/utils/Cargo.toml b/precompiles/utils/Cargo.toml index f5f107e445..1c5333f58b 100644 --- a/precompiles/utils/Cargo.toml +++ b/precompiles/utils/Cargo.toml @@ -9,9 +9,9 @@ version = '0.9.17' evm = { git = "https://github.com/rust-blockchain/evm", rev = "b7b82c7e1fc57b7449d6dfa6826600de37cc1e65", default-features = false, optional = true } impl-trait-for-tuples = "0.2.2" log = { version = "0.4", default-features = false } -num_enum = { version = "0.5.3", default-features = false } +num_enum = { version = "0.7.1", default-features = false } sha3 = { version = "0.10", default-features = false } -similar-asserts = { version = "1.1.0", optional = true } +similar-asserts = { version = "1.5.0", optional = true } precompile-utils-macro = { path = "macro" } diff --git a/precompiles/utils/macro/Cargo.toml b/precompiles/utils/macro/Cargo.toml index c2a0c9ed6a..81a17076bc 100644 --- a/precompiles/utils/macro/Cargo.toml +++ b/precompiles/utils/macro/Cargo.toml @@ -12,7 +12,7 @@ name = "tests" path = "tests/tests.rs" [dependencies] -num_enum = { version = "0.5.3", default-features = false } +num_enum = { version = "0.7.1", default-features = false } proc-macro2 = { version = "1" } quote = { version = "1" } sha3 = { version = "0.10", default-features = false } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index fd71773e7f..b42ebebf71 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -5,22 +5,27 @@ name = 'core-primitives' version = '0.9.12' [dependencies] +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } strum = { version = "0.25.0", default-features = false } strum_macros = { version = "0.25.3", default-features = false } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +pallet-evm = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.42", default-features = false } ring = { version = "0.16.20", default-features = false, features = ["alloc"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +itp-utils = { path = "../../tee-worker/core-primitives/utils", default-features = false } litentry-macros = { path = "macros" } [features] default = ["std"] std = [ + "serde/std", "strum/std", "codec/std", "scale-info/std", @@ -28,5 +33,8 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-std/std", + "sp-io/std", "ring/std", + "itp-utils/std", + "pallet-evm/std", ] diff --git a/primitives/core/src/assertion.rs b/primitives/core/src/assertion.rs index 85e4eac057..642448e71e 100644 --- a/primitives/core/src/assertion.rs +++ b/primitives/core/src/assertion.rs @@ -18,8 +18,8 @@ // when requesting VCs. use crate::{ - AccountId, BnbDigitDomainType, BoundedWeb3Network, GenericDiscordRoleType, OneBlockCourseType, - VIP3MembershipCardLevel, Web3Network, + all_web3networks, AccountId, BnbDigitDomainType, BoundedWeb3Network, EVMTokenType, + GenericDiscordRoleType, OneBlockCourseType, VIP3MembershipCardLevel, Web3Network, }; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -40,7 +40,14 @@ pub struct AchainableAmountHolding { #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, MaxEncodedLen, TypeInfo)] pub struct AchainableAmountToken { pub name: ParameterString, - pub chain: Web3Network, + + // Considering the uniformity of the structure, all relevant chain structures should be changed + // to BoundedWeb3Network. However, this would be a significant modification for the previous + // VC. Considering the tight timeline for this New Year compain, we will temporarily only + // change this AchainableAmountToken' chain field to BoundedWeb3Network. Afterwards, it needs + // to be modified to be consistent. + pub chain: BoundedWeb3Network, + pub amount: ParameterString, pub token: Option, } @@ -163,20 +170,20 @@ impl AchainableParams { } } - pub fn chain(&self) -> Web3Network { + pub fn chains(&self) -> Vec { match self { - AchainableParams::AmountHolding(p) => p.chain, - AchainableParams::AmountToken(p) => p.chain, - AchainableParams::Amount(p) => p.chain, - AchainableParams::Amounts(p) => p.chain, - AchainableParams::Basic(p) => p.chain, - AchainableParams::BetweenPercents(p) => p.chain, - AchainableParams::ClassOfYear(p) => p.chain, - AchainableParams::DateInterval(p) => p.chain, - AchainableParams::DatePercent(p) => p.chain, - AchainableParams::Date(p) => p.chain, - AchainableParams::Token(p) => p.chain, - AchainableParams::Mirror(p) => p.chain, + AchainableParams::AmountHolding(arg) => vec![arg.chain], + AchainableParams::AmountToken(arg) => arg.chain.to_vec(), + AchainableParams::Amount(arg) => vec![arg.chain], + AchainableParams::Amounts(arg) => vec![arg.chain], + AchainableParams::Basic(arg) => vec![arg.chain], + AchainableParams::BetweenPercents(arg) => vec![arg.chain], + AchainableParams::ClassOfYear(arg) => vec![arg.chain], + AchainableParams::DateInterval(arg) => vec![arg.chain], + AchainableParams::DatePercent(arg) => vec![arg.chain], + AchainableParams::Date(arg) => vec![arg.chain], + AchainableParams::Token(arg) => vec![arg.chain], + AchainableParams::Mirror(arg) => vec![arg.chain], } } } @@ -238,6 +245,18 @@ pub enum Assertion { #[codec(index = 19)] WeirdoGhostGangHolder, + + #[codec(index = 20)] + LITStaking, + + #[codec(index = 21)] + EVMAmountHolding(EVMTokenType), // (evm_token_type) + + #[codec(index = 22)] + BRC20AmountHolder, + + #[codec(index = 23)] + CryptoSummary, } impl Assertion { @@ -252,27 +271,41 @@ impl Assertion { // the broader `Web3Network` (see network.rs) pub fn get_supported_web3networks(&self) -> Vec { match self { + // A1, any web3 network is allowed + Self::A1 => all_web3networks(), // LIT holder, not including `LitentryRococo` as it's not supported by any data provider Self::A4(..) => vec![Web3Network::Litentry, Web3Network::Litmus, Web3Network::Ethereum], // DOT holder Self::A7(..) => vec![Web3Network::Polkadot], // WBTC/ETH holder - Self::A10(..) | Self::A11(..) => vec![Web3Network::Ethereum], + Self::A10(..) | + Self::A11(..) | + Self::VIP3MembershipCard(..) | + Self::WeirdoGhostGangHolder => vec![Web3Network::Ethereum], // total tx over `networks` Self::A8(network) => network.to_vec(), // polkadot paticipation Self::A14 => vec![Web3Network::Polkadot], // Achainable Assertions - Self::Achainable(a) => vec![a.chain()], + Self::Achainable(arg) => arg.chains(), // Oneblock Assertion Self::Oneblock(..) => vec![Web3Network::Polkadot, Web3Network::Kusama], - Self::WeirdoGhostGangHolder => vec![Web3Network::Ethereum], // SPACEID Assertions Self::BnbDomainHolding | Self::BnbDigitDomainClub(..) => vec![Web3Network::Bsc], - // VIP3 Member Card - Self::VIP3MembershipCard(..) => vec![Web3Network::Ethereum], + // LITStaking + Self::LITStaking => vec![Web3Network::Litentry], + // EVM Amount Holding + Self::EVMAmountHolding(_) | Self::CryptoSummary => + vec![Web3Network::Ethereum, Web3Network::Bsc], + // BRC20 Holder + Self::BRC20AmountHolder => vec![Web3Network::BitcoinP2tr], // we don't care about any specific web3 network - _ => vec![], + Self::A2(..) | + Self::A3(..) | + Self::A6 | + Self::A13(..) | + Self::A20 | + Self::GenericDiscordRole(..) => vec![], } } } diff --git a/primitives/core/src/error.rs b/primitives/core/src/error.rs index bb90a63735..6e93ebe6ea 100644 --- a/primitives/core/src/error.rs +++ b/primitives/core/src/error.rs @@ -62,6 +62,9 @@ pub enum ErrorDetail { // of verification data #[codec(index = 10)] VerifyWeb3SignatureFailed, + // error when trying to build vc but no eligible identity is found + #[codec(index = 11)] + NoEligibleIdentity, } // We could have used Into, but we want it to be more explicit, similar to `into_iter` diff --git a/primitives/core/src/evm_amount_holding.rs b/primitives/core/src/evm_amount_holding.rs new file mode 100644 index 0000000000..d4afdd495d --- /dev/null +++ b/primitives/core/src/evm_amount_holding.rs @@ -0,0 +1,26 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, MaxEncodedLen, TypeInfo)] +pub enum EVMTokenType { + #[codec(index = 0)] + Ton, + #[codec(index = 1)] + Trx, +} diff --git a/tee-worker/litentry/primitives/src/identity.rs b/primitives/core/src/identity.rs similarity index 70% rename from tee-worker/litentry/primitives/src/identity.rs rename to primitives/core/src/identity.rs index 0d4d7fe740..35902f4660 100644 --- a/tee-worker/litentry/primitives/src/identity.rs +++ b/primitives/core/src/identity.rs @@ -14,22 +14,26 @@ // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -use core::fmt::{Debug, Formatter}; -#[cfg(all(not(feature = "sgx"), feature = "std"))] -use serde::{Deserialize, Serialize}; +pub extern crate alloc; +use crate::{ + all_bitcoin_web3networks, all_evm_web3networks, all_substrate_web3networks, AccountId, + Web3Network, +}; +use alloc::{format, str, string::String}; use codec::{Decode, Encode, Error, Input, MaxEncodedLen}; +use core::fmt::{Debug, Formatter}; use itp_utils::{ hex::{decode_hex, hex_encode}, if_production_or, }; use pallet_evm::{AddressMapping, HashedAddressMapping as GenericHashedAddressMapping}; -use parentchain_primitives::{AccountId, Web3Network}; use scale_info::{meta_type, Type, TypeDefSequence, TypeInfo}; -use sp_core::{crypto::AccountId32, ed25519, sr25519, ByteArray, H160}; +use sp_core::{ + crypto::{AccountId32, ByteArray}, + ecdsa, ed25519, sr25519, H160, +}; +use sp_io::hashing::blake2_256; use sp_runtime::{ traits::{BlakeTwo256, ConstU32}, BoundedVec, @@ -58,9 +62,7 @@ impl Encode for IdentityString { } #[derive(Eq, PartialEq, Clone, MaxEncodedLen, Default)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct IdentityString { - #[cfg_attr(feature = "std", serde(flatten))] pub inner: IdentityInnerString, } @@ -92,7 +94,6 @@ impl Debug for IdentityString { } #[derive(Encode, Decode, Copy, Clone, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct Address20([u8; 20]); impl AsRef<[u8; 20]> for Address20 { @@ -130,7 +131,6 @@ impl Debug for Address20 { } #[derive(Encode, Decode, Copy, Clone, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct Address32([u8; 32]); impl AsRef<[u8; 32]> for Address32 { fn as_ref(&self) -> &[u8; 32] { @@ -198,11 +198,72 @@ impl Debug for Address32 { } } +// TODO: maybe use macros to reduce verbosity +#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct Address33([u8; 33]); +impl AsRef<[u8; 33]> for Address33 { + fn as_ref(&self) -> &[u8; 33] { + &self.0 + } +} + +impl Default for Address33 { + fn default() -> Self { + Address33([0u8; 33]) + } +} + +impl From<[u8; 33]> for Address33 { + fn from(value: [u8; 33]) -> Self { + Self(value) + } +} + +impl<'a> TryFrom<&'a [u8]> for Address33 { + type Error = (); + fn try_from(x: &'a [u8]) -> Result { + if x.len() == 33 { + let mut data = [0; 33]; + data.copy_from_slice(x); + Ok(Address33(data)) + } else { + Err(()) + } + } +} + +impl From for ecdsa::Public { + fn from(value: Address33) -> Self { + let raw: [u8; 33] = *value.as_ref(); + ecdsa::Public::from_raw(raw) + } +} + +impl From<&Address33> for ecdsa::Public { + fn from(value: &Address33) -> Self { + (*value).into() + } +} + +impl From for Address33 { + fn from(k: ecdsa::Public) -> Self { + k.0.into() + } +} + +impl Debug for Address33 { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + if_production_or!( + f.debug_tuple("Address33").finish(), + f.debug_tuple("Address33").field(&self.0).finish() + ) + } +} + /// Web2 and Web3 Identity based on handle/public key /// We only include the network categories (substrate/evm) without concrete types /// see https://github.com/litentry/litentry-parachain/issues/1841 #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, EnumIter)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum Identity { // web2 #[codec(index = 0)] @@ -217,6 +278,12 @@ pub enum Identity { Substrate(Address32), #[codec(index = 4)] Evm(Address20), + // bitcoin addresses are derived (one-way hash) from the pubkey + // by using `Address33` as the Identity handle, it requires that pubkey + // is retrievable by the wallet API when verifying the bitcoin account. + // e.g. unisat-wallet: https://docs.unisat.io/dev/unisat-developer-service/unisat-wallet#getpublickey + #[codec(index = 5)] + Bitcoin(Address33), } impl Identity { @@ -225,7 +292,7 @@ impl Identity { } pub fn is_web3(&self) -> bool { - matches!(self, Self::Substrate(..) | Self::Evm(..)) + matches!(self, Self::Substrate(..) | Self::Evm(..) | Self::Bitcoin(..)) } pub fn is_substrate(&self) -> bool { @@ -236,33 +303,44 @@ impl Identity { matches!(self, Self::Evm(..)) } + pub fn is_bitcoin(&self) -> bool { + matches!(self, Self::Bitcoin(..)) + } + + pub fn default_web3networks(&self) -> Vec { + match self { + Identity::Substrate(_) => all_substrate_web3networks(), + Identity::Evm(_) => all_evm_web3networks(), + Identity::Bitcoin(_) => all_bitcoin_web3networks(), + Identity::Twitter(_) | Identity::Discord(_) | Identity::Github(_) => Vec::new(), + } + } + // check if the given web3networks match the identity pub fn matches_web3networks(&self, networks: &Vec) -> bool { - (self.is_substrate() && !networks.is_empty() && networks.iter().all(|n| n.is_substrate())) - || (self.is_evm() && !networks.is_empty() && networks.iter().all(|n| n.is_evm())) - || (self.is_web2() && networks.is_empty()) + match self { + Identity::Substrate(_) => + !networks.is_empty() && networks.iter().all(|n| n.is_substrate()), + Identity::Evm(_) => !networks.is_empty() && networks.iter().all(|n| n.is_evm()), + Identity::Bitcoin(_) => !networks.is_empty() && networks.iter().all(|n| n.is_bitcoin()), + Identity::Twitter(_) | Identity::Discord(_) | Identity::Github(_) => + networks.is_empty(), + } } /// Currently we only support mapping from Address32/Address20 to AccountId, not opposite. pub fn to_account_id(&self) -> Option { match self { - Identity::Substrate(address) => { - let mut data = [0u8; 32]; - data.copy_from_slice(address.as_ref()); - Some(AccountId32::from(data)) - }, - Identity::Evm(address) => { - let substrate_version = - HashedAddressMapping::into_account_id(H160::from_slice(address.as_ref())); - Some(AccountId32::from(Into::<[u8; 32]>::into(substrate_version))) - }, - _ => None, + Identity::Substrate(address) => Some(address.into()), + Identity::Evm(address) => + Some(HashedAddressMapping::into_account_id(H160::from_slice(address.as_ref()))), + Identity::Bitcoin(address) => Some(blake2_256(address.as_ref()).into()), + Identity::Twitter(_) | Identity::Discord(_) | Identity::Github(_) => None, } } - #[cfg(any(feature = "std", feature = "sgx"))] - pub fn from_did(s: &str) -> Result> { - let did_prefix = std::string::String::from("did:litentry:"); + pub fn from_did(s: &str) -> Result { + let did_prefix = String::from("did:litentry:"); if s.starts_with(&did_prefix) { let did_suffix = &s[did_prefix.len()..]; let v: Vec<&str> = did_suffix.split(':').collect(); @@ -281,6 +359,13 @@ impl Identity { .try_into() .map_err(|_| "Address20 conversion error")?; return Ok(Identity::Evm(handle)) + } else if v[0] == "bitcoin" { + let handle = decode_hex(v[1]) + .unwrap() + .as_slice() + .try_into() + .map_err(|_| "Address33 conversion error")?; + return Ok(Identity::Bitcoin(handle)) } else if v[0] == "github" { return Ok(Identity::Github(IdentityString::new(v[1].as_bytes().to_vec()))) } else if v[0] == "discord" { @@ -288,39 +373,37 @@ impl Identity { } else if v[0] == "twitter" { return Ok(Identity::Twitter(IdentityString::new(v[1].as_bytes().to_vec()))) } else { - return Err("Unknown did type".into()) + return Err("Unknown did type") } } else { - return Err("Wrong did suffix".into()) + return Err("Wrong did suffix") } } - Err("Wrong did prefix".into()) + Err("Wrong did prefix") } - #[cfg(any(feature = "std", feature = "sgx"))] - pub fn to_did( - &self, - ) -> Result> { - Ok(std::format!( + pub fn to_did(&self) -> Result { + Ok(format!( "did:litentry:{}", match self { - Identity::Evm(address) => std::format!("evm:{}", &hex_encode(address.as_ref())), + Identity::Evm(address) => format!("evm:{}", &hex_encode(address.as_ref())), Identity::Substrate(address) => - std::format!("substrate:{}", &hex_encode(address.as_ref())), - Identity::Twitter(handle) => std::format!( + format!("substrate:{}", &hex_encode(address.as_ref())), + Identity::Bitcoin(address) => format!("bitcoin:{}", &hex_encode(address.as_ref())), + Identity::Twitter(handle) => format!( "twitter:{}", - std::str::from_utf8(handle.inner_ref()) + str::from_utf8(handle.inner_ref()) .map_err(|_| "twitter handle conversion error")? ), - Identity::Discord(handle) => std::format!( + Identity::Discord(handle) => format!( "discord:{}", - std::str::from_utf8(handle.inner_ref()) + str::from_utf8(handle.inner_ref()) .map_err(|_| "discord handle conversion error")? ), - Identity::Github(handle) => std::format!( + Identity::Github(handle) => format!( "github:{}", - std::str::from_utf8(handle.inner_ref()) + str::from_utf8(handle.inner_ref()) .map_err(|_| "github handle conversion error")? ), } @@ -358,6 +441,12 @@ impl From for Identity { } } +impl From for Identity { + fn from(value: Address33) -> Self { + Identity::Bitcoin(value) + } +} + impl From<[u8; 32]> for Identity { fn from(value: [u8; 32]) -> Self { Identity::Substrate(value.into()) @@ -370,6 +459,12 @@ impl From<[u8; 20]> for Identity { } } +impl From<[u8; 33]> for Identity { + fn from(value: [u8; 33]) -> Self { + Identity::Bitcoin(value.into()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -388,6 +483,7 @@ mod tests { Identity::Github(..) => true, Identity::Substrate(..) => false, Identity::Evm(..) => false, + Identity::Bitcoin(..) => false, } ) }) @@ -404,6 +500,7 @@ mod tests { Identity::Github(..) => false, Identity::Substrate(..) => true, Identity::Evm(..) => true, + Identity::Bitcoin(..) => true, } ) }) @@ -420,6 +517,7 @@ mod tests { Identity::Github(..) => false, Identity::Substrate(..) => true, Identity::Evm(..) => false, + Identity::Bitcoin(..) => false, } ) }) @@ -436,6 +534,24 @@ mod tests { Identity::Github(..) => false, Identity::Substrate(..) => false, Identity::Evm(..) => true, + Identity::Bitcoin(..) => false, + } + ) + }) + } + + #[test] + fn is_bitcoin_works() { + Identity::iter().for_each(|identity| { + assert_eq!( + identity.is_bitcoin(), + match identity { + Identity::Twitter(..) => false, + Identity::Discord(..) => false, + Identity::Github(..) => false, + Identity::Substrate(..) => false, + Identity::Evm(..) => false, + Identity::Bitcoin(..) => true, } ) }) @@ -501,6 +617,14 @@ mod tests { assert_eq!(Identity::from_did(did_str).unwrap(), identity); } + #[test] + fn test_bitcoin_did() { + let identity = Identity::Bitcoin([0; 33].into()); + let did_str = "did:litentry:bitcoin:0x000000000000000000000000000000000000000000000000000000000000000000"; + assert_eq!(identity.to_did().unwrap(), did_str); + assert_eq!(Identity::from_did(did_str).unwrap(), identity); + } + #[test] fn test_discord_did() { let identity = Identity::Discord(IdentityString::new("discord_handle".as_bytes().to_vec())); diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 76a0831052..8f41ee4af7 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -38,6 +38,9 @@ pub use error::*; mod vc; pub use vc::*; +pub mod identity; +pub use identity::*; + mod oneblock; pub use oneblock::*; @@ -52,6 +55,10 @@ pub use bnb_domain::*; mod generic_discord_role; pub use generic_discord_role::*; + +mod evm_amount_holding; +pub use evm_amount_holding::*; + /// Common types of parachains. mod types { use sp_runtime::{ diff --git a/primitives/core/src/network.rs b/primitives/core/src/network.rs index 45ed984446..34fcb119ce 100644 --- a/primitives/core/src/network.rs +++ b/primitives/core/src/network.rs @@ -30,7 +30,16 @@ pub type BoundedWeb3Network = BoundedVec bool { matches!(self, Self::Ethereum | Self::Bsc) } + + pub fn is_bitcoin(&self) -> bool { + matches!( + self, + Self::BitcoinP2tr | + Self::BitcoinP2pkh | + Self::BitcoinP2sh | + Self::BitcoinP2wpkh | + Self::BitcoinP2wsh + ) + } } pub fn all_web3networks() -> Vec { @@ -109,6 +141,10 @@ pub fn all_evm_web3networks() -> Vec { Web3Network::iter().filter(|n| n.is_evm()).collect() } +pub fn all_bitcoin_web3networks() -> Vec { + Web3Network::iter().filter(|n| n.is_bitcoin()).collect() +} + #[cfg(test)] mod tests { use super::*; @@ -134,6 +170,11 @@ mod tests { Web3Network::SubstrateTestnet => false, Web3Network::Ethereum => true, Web3Network::Bsc => true, + Web3Network::BitcoinP2tr => false, + Web3Network::BitcoinP2pkh => false, + Web3Network::BitcoinP2sh => false, + Web3Network::BitcoinP2wpkh => false, + Web3Network::BitcoinP2wsh => false, } ) }) @@ -154,6 +195,36 @@ mod tests { Web3Network::SubstrateTestnet => true, Web3Network::Ethereum => false, Web3Network::Bsc => false, + Web3Network::BitcoinP2tr => false, + Web3Network::BitcoinP2pkh => false, + Web3Network::BitcoinP2sh => false, + Web3Network::BitcoinP2wpkh => false, + Web3Network::BitcoinP2wsh => false, + } + ) + }) + } + + #[test] + fn is_bitcoin_works() { + Web3Network::iter().for_each(|network| { + assert_eq!( + network.is_bitcoin(), + match network { + Web3Network::Polkadot => false, + Web3Network::Kusama => false, + Web3Network::Litentry => false, + Web3Network::Litmus => false, + Web3Network::LitentryRococo => false, + Web3Network::Khala => false, + Web3Network::SubstrateTestnet => false, + Web3Network::Ethereum => false, + Web3Network::Bsc => false, + Web3Network::BitcoinP2tr => true, + Web3Network::BitcoinP2pkh => true, + Web3Network::BitcoinP2sh => true, + Web3Network::BitcoinP2wpkh => true, + Web3Network::BitcoinP2wsh => true, } ) }) diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index efedaf29de..c54f9b5d04 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -28,9 +28,8 @@ use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use frame_support::{ construct_runtime, ord_parameter_types, parameter_types, traits::{ - ConstU128, ConstU32, ConstU64, ConstU8, Contains, ContainsLengthBound, EitherOfDiverse, - EnsureOrigin, Everything, FindAuthor, InstanceFilter, OnFinalize, SortedMembers, - WithdrawReasons, + ConstU128, ConstU32, ConstU64, ConstU8, Contains, ContainsLengthBound, EnsureOrigin, + Everything, FindAuthor, InstanceFilter, OnFinalize, SortedMembers, WithdrawReasons, }, weights::{constants::RocksDbWeight, ConstantMultiplier, IdentityFee, Weight}, ConsensusEngineId, PalletId, RuntimeDebug, @@ -856,19 +855,19 @@ impl pallet_parachain_staking::Config for Runtime { type Currency = Balances; type MonetaryGovernanceOrigin = EnsureRootOrAllCouncil; /// Minimum round length is 2 minutes (10 * 12 second block times) - type MinBlocksPerRound = ConstU32<{ 2 * MINUTES }>; + type MinBlocksPerRound = ConstU32<{ prod_or_fast!(2 * MINUTES, 2) }>; /// Blocks per round - type DefaultBlocksPerRound = ConstU32<{ 2 * MINUTES }>; + type DefaultBlocksPerRound = ConstU32<{ prod_or_fast!(2 * MINUTES, 2) }>; /// Rounds before the collator leaving the candidates request can be executed - type LeaveCandidatesDelay = ConstU32<28>; + type LeaveCandidatesDelay = ConstU32<{ prod_or_fast!(28, 1) }>; /// Rounds before the candidate bond increase/decrease can be executed - type CandidateBondLessDelay = ConstU32<28>; + type CandidateBondLessDelay = ConstU32<{ prod_or_fast!(28, 1) }>; /// Rounds before the delegator exit can be executed - type LeaveDelegatorsDelay = ConstU32<28>; + type LeaveDelegatorsDelay = ConstU32<{ prod_or_fast!(28, 1) }>; /// Rounds before the delegator revocation can be executed - type RevokeDelegationDelay = ConstU32<28>; + type RevokeDelegationDelay = ConstU32<{ prod_or_fast!(28, 1) }>; /// Rounds before the delegator bond increase/decrease can be executed - type DelegationBondLessDelay = ConstU32<28>; + type DelegationBondLessDelay = ConstU32<{ prod_or_fast!(28, 1) }>; /// Rounds before the reward is paid type RewardPaymentDelay = ConstU32<2>; /// Minimum collators selected per round, default at genesis and minimum forever after @@ -1027,7 +1026,6 @@ impl pallet_identity_management::Config for Runtime { type TEECallOrigin = EnsureEnclaveSigner; type DelegateeAdminOrigin = EnsureRootOrAllCouncil; type ExtrinsicWhitelistOrigin = IMPExtrinsicWhitelist; - type UpdateIDGraphHashOrigin = EitherOfDiverse; } impl pallet_group::Config for Runtime { diff --git a/runtime/rococo/src/weights/pallet_identity_management.rs b/runtime/rococo/src/weights/pallet_identity_management.rs index ea1cb3f73b..bbe38fdeb7 100644 --- a/runtime/rococo/src/weights/pallet_identity_management.rs +++ b/runtime/rococo/src/weights/pallet_identity_management.rs @@ -160,18 +160,4 @@ impl pallet_identity_management::WeightInfo for WeightI .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(1)) } - /// Storage: Teerex EnclaveIndex (r:1 w:0) - /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: IdentityManagement IDGraphHash (r:0 w:1) - /// Proof: IdentityManagement IDGraphHash (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) - fn update_id_graph_hash() -> Weight { - // Proof Size summary in bytes: - // Measured: `255` - // Estimated: `3720` - // Minimum execution time: 22_335_000 picoseconds. - Weight::from_parts(22_875_000, 0) - .saturating_add(Weight::from_parts(0, 3720)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } } diff --git a/scripts/clean-local-docker.sh b/scripts/clean-local-docker.sh index 09f9bd3d47..b8ae7bcf79 100755 --- a/scripts/clean-local-docker.sh +++ b/scripts/clean-local-docker.sh @@ -40,7 +40,6 @@ IMG=$(docker images --filter=reference="generated-$CHAIN*" --format "{{.Reposito [ -z "$IMG" ] || docker rmi -f $IMG rm -rf "$LITENTRY_PARACHAIN_DIR" -rm -rf "$ROOTDIR/ts-tests/bridge/bob.json" -rm -rf "$ROOTDIR/ts-tests/bridge/data/" - +rm -rf "$ROOTDIR/ts-tests/common/bob.json" +rm -rf "$ROOTDIR/ts-tests/common/data/" echo "cleaned up." diff --git a/scripts/fork-parachain-and-launch.sh b/scripts/fork-parachain-and-launch.sh index abe5bc91e4..3fb61b76ba 100755 --- a/scripts/fork-parachain-and-launch.sh +++ b/scripts/fork-parachain-and-launch.sh @@ -52,7 +52,7 @@ FORK_CHAIN=${ORIG_CHAIN}-dev case "$ORIG_CHAIN" in rococo) - ENDPOINT="${2:-wss://rpc.rococo-parachain-sg.litentry.io}" + ENDPOINT="${2:-wss://rpc.rococo-parachain.litentry.io}" ;; litmus) ENDPOINT="${2:-wss://rpc.litmus-parachain.litentry.io}" diff --git a/scripts/launch-standalone.sh b/scripts/launch-standalone.sh index a6e159c6a1..0c78c2ecdf 100755 --- a/scripts/launch-standalone.sh +++ b/scripts/launch-standalone.sh @@ -27,3 +27,5 @@ echo "Starting litentry-collator in standalone mode ..." $PARACHAIN_BIN --dev --unsafe-ws-external --unsafe-rpc-external \ --port "${CollatorPort:-30333}" --ws-port "${CollatorWSPort:-9944}" --rpc-port "${CollatorRPCPort:-9933}" \ &> "$LITENTRY_PARACHAIN_DIR/para.alice.log" & + +sleep 10 diff --git a/scripts/run-ts-test.sh b/scripts/run-ts-test.sh index 11d9702907..9f227e6253 100755 --- a/scripts/run-ts-test.sh +++ b/scripts/run-ts-test.sh @@ -34,4 +34,5 @@ fi if $evm; then pnpm run test-evm-transfer 2>&1 | tee "$LITENTRY_PARACHAIN_DIR/parachain_ci_test.log" pnpm run test-evm-contract 2>&1 | tee "$LITENTRY_PARACHAIN_DIR/parachain_ci_test.log" + pnpm run test-precompile-contract 2>&1 | tee "$LITENTRY_PARACHAIN_DIR/parachain_ci_test.log" fi diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 508e2b5845..4e92ea7348 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -590,6 +590,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.10.0-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" + [[package]] name = "beef" version = "0.5.2" @@ -643,6 +649,38 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitcoin" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5973a027b341b462105675962214dfe3c938ad9afd395d84b28602608bdcec7b" +dependencies = [ + "bech32", + "bitcoin-internals", + "bitcoin_hashes", + "core2 0.3.3", + "hex-conservative", + "hex_lit", + "secp256k1 0.28.0", +] + +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "core2 0.3.3", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -1051,7 +1089,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" dependencies = [ - "core2", + "core2 0.4.0", "multibase", "multihash 0.16.3", "serde 1.0.193", @@ -1267,17 +1305,30 @@ name = "core-primitives" version = "0.9.12" dependencies = [ "frame-support", + "itp-utils", "litentry-macros 0.9.12", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", "parity-scale-codec", "ring 0.16.20", "scale-info", + "serde 1.0.193", "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", "sp-runtime", "sp-std 5.0.0", "strum 0.25.0", "strum_macros 0.25.3", ] +[[package]] +name = "core2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" +dependencies = [ + "memchr 2.6.3", +] + [[package]] name = "core2" version = "0.4.0" @@ -3882,12 +3933,27 @@ dependencies = [ "serde 1.0.193", ] +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +dependencies = [ + "core2 0.3.3", +] + [[package]] name = "hex-literal" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + [[package]] name = "hkdf" version = "0.12.3" @@ -6061,6 +6127,7 @@ dependencies = [ "frame-support", "hex 0.4.0", "hex 0.4.3", + "hex-literal", "http 0.2.1", "http 0.2.9", "http_req 0.8.1 (git+https://github.com/integritee-network/http_req?branch=master)", @@ -6076,12 +6143,14 @@ dependencies = [ "lc-stf-task-sender", "litentry-primitives", "log 0.4.20", + "pallet-parachain-staking", "parity-scale-codec", "rust-base58 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "rust-base58 0.0.4 (git+https://github.com/mesalock-linux/rust-base58-sgx)", "serde 1.0.193", "serde_json 1.0.103", "sgx_tstd", + "sp-core", "ss58-registry", "thiserror 1.0.9", "url 2.1.1", @@ -7010,6 +7079,9 @@ dependencies = [ name = "litentry-primitives" version = "0.1.0" dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx?rev=sgx_1.1.3)", + "base64 0.13.1", + "bitcoin", "core-primitives", "hex 0.4.3", "itp-sgx-crypto", @@ -7021,6 +7093,7 @@ dependencies = [ "rand 0.7.3 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", "ring 0.16.20", "scale-info", + "secp256k1 0.28.0", "serde 1.0.193", "sgx_tstd", "sp-core", @@ -7628,7 +7701,7 @@ dependencies = [ "blake2b_simd", "blake2s_simd", "blake3", - "core2", + "core2 0.4.0", "digest 0.10.7", "multihash-derive 0.8.0", "sha2 0.10.7", @@ -7642,7 +7715,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" dependencies = [ - "core2", + "core2 0.4.0", "multihash-derive 0.8.0", "unsigned-varint 0.7.1", ] @@ -8113,40 +8186,40 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ - "num_enum_derive 0.5.11", + "num_enum_derive 0.6.1", ] [[package]] name = "num_enum" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" dependencies = [ - "num_enum_derive 0.6.1", + "num_enum_derive 0.7.1", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.32", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ - "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.32", @@ -8795,7 +8868,7 @@ dependencies = [ "frame-support", "frame-system", "log 0.4.20", - "num_enum 0.5.11", + "num_enum 0.7.1", "pallet-bridge", "pallet-bridge-transfer", "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", @@ -8844,7 +8917,7 @@ dependencies = [ "frame-support", "frame-system", "log 0.4.20", - "num_enum 0.5.11", + "num_enum 0.7.1", "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", "pallet-parachain-staking", "parity-scale-codec", @@ -10118,7 +10191,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples", "log 0.4.20", - "num_enum 0.5.11", + "num_enum 0.7.1", "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", "parity-scale-codec", "precompile-utils-macro", @@ -10134,7 +10207,7 @@ dependencies = [ name = "precompile-utils-macro" version = "0.9.17" dependencies = [ - "num_enum 0.5.11", + "num_enum 0.7.1", "proc-macro2", "quote", "sha3", @@ -12564,7 +12637,17 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ - "secp256k1-sys", + "secp256k1-sys 0.6.1", +] + +[[package]] +name = "secp256k1" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +dependencies = [ + "bitcoin_hashes", + "secp256k1-sys 0.9.1", ] [[package]] @@ -12576,6 +12659,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" +dependencies = [ + "cc", +] + [[package]] name = "secrecy" version = "0.8.0" @@ -13415,7 +13507,7 @@ dependencies = [ "regex 1.9.5", "scale-info", "schnorrkel", - "secp256k1", + "secp256k1 0.24.3", "secrecy", "serde 1.0.193", "sp-core-hashing 5.0.0", @@ -13541,7 +13633,7 @@ dependencies = [ "log 0.4.20", "parity-scale-codec", "rustversion", - "secp256k1", + "secp256k1 0.24.3", "sp-core", "sp-externalities", "sp-keystore", diff --git a/tee-worker/app-libs/stf/src/getter.rs b/tee-worker/app-libs/stf/src/getter.rs index eb8680e6a3..d2ccec900a 100644 --- a/tee-worker/app-libs/stf/src/getter.rs +++ b/tee-worker/app-libs/stf/src/getter.rs @@ -100,7 +100,7 @@ pub enum PublicGetter { #[codec(index = 1)] nonce(Identity), #[codec(index = 2)] - all_id_graph_hash, + id_graph_hash(Identity), } #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] @@ -250,7 +250,7 @@ impl ExecuteGetter for TrustedGetterSigned { None }, // litentry - TrustedGetter::id_graph(who) => Some(IdentityManagement::get_id_graph(&who).encode()), + TrustedGetter::id_graph(who) => Some(IdentityManagement::id_graph(&who).encode()), // TODO: we need to re-think it // currently, _who is ignored meaning it's actually not a "trusted" getter. @@ -280,8 +280,8 @@ impl ExecuteGetter for PublicGetter { } else { None }, - PublicGetter::all_id_graph_hash => - Some(IdentityManagement::all_id_graph_hash().encode()), + PublicGetter::id_graph_hash(identity) => + IdentityManagement::id_graph_hash(&identity).map(|h| h.encode()), } } diff --git a/tee-worker/app-libs/stf/src/trusted_call.rs b/tee-worker/app-libs/stf/src/trusted_call.rs index 12a579dbd5..3168d9bc16 100644 --- a/tee-worker/app-libs/stf/src/trusted_call.rs +++ b/tee-worker/app-libs/stf/src/trusted_call.rs @@ -38,9 +38,7 @@ use codec::{Compact, Decode, Encode}; use frame_support::{ensure, traits::UnfilteredDispatchable}; #[cfg(feature = "evm")] use ita_sgx_runtime::{AddressMapping, HashedAddressMapping}; -pub use ita_sgx_runtime::{ - Balance, ConvertAccountId, IDGraph, Index, Runtime, SgxParentchainTypeConverter, System, -}; +pub use ita_sgx_runtime::{Balance, IDGraph, Index, Runtime, System}; use itp_node_api::metadata::{provider::AccessNodeMetadata, NodeMetadataTrait}; use itp_node_api_metadata::{ pallet_balances::BalancesCallIndexes, pallet_imp::IMPCallIndexes, @@ -60,9 +58,8 @@ pub use itp_types::{OpaqueCall, H256}; use itp_utils::stringify::account_id_to_string; pub use litentry_primitives::{ aes_encrypt_default, all_evm_web3networks, all_substrate_web3networks, AesOutput, Assertion, - ErrorDetail, IMPError, Identity, LitentryMultiSignature, ParentchainAccountId, - ParentchainBlockNumber, RequestAesKey, RequestAesKeyNonce, VCMPError, ValidationData, - Web3Network, + ErrorDetail, IMPError, Identity, LitentryMultiSignature, ParentchainBlockNumber, RequestAesKey, + RequestAesKeyNonce, VCMPError, ValidationData, Web3Network, }; use log::*; use sp_core::{ @@ -678,9 +675,6 @@ where req_ext_hash, ) => { debug!("link_identity, who: {}", account_id_to_string(&who)); - let account = SgxParentchainTypeConverter::convert( - who.to_account_id().ok_or(Self::Error::InvalidAccount)?, - ); let verification_done = Self::link_identity_internal( shard, signer.to_account_id().ok_or(Self::Error::InvalidAccount)?, @@ -696,7 +690,7 @@ where push_call_imp_some_error( calls, node_metadata_repo.clone(), - Some(account), + Some(who.clone()), e.to_imp_error(), req_ext_hash, ); @@ -736,101 +730,90 @@ where }, TrustedCall::deactivate_identity(signer, who, identity, maybe_key, req_ext_hash) => { debug!("deactivate_identity, who: {}", account_id_to_string(&who)); - let account = SgxParentchainTypeConverter::convert( - who.to_account_id().ok_or(Self::Error::InvalidAccount)?, - ); let call_index = node_metadata_repo .get_from_metadata(|m| m.identity_deactivated_call_indexes())??; + let old_id_graph = IMT::id_graph(&who); Self::deactivate_identity_internal( signer.to_account_id().ok_or(Self::Error::InvalidAccount)?, who.clone(), - identity.clone(), + identity, ) .map_err(|e| { debug!("pushing error event ... error: {}", e); push_call_imp_some_error( calls, node_metadata_repo.clone(), - Some(account.clone()), + Some(who.clone()), e.to_imp_error(), req_ext_hash, ); e })?; - debug!("pushing identity_deactivated event ..."); - let id_graph_hash: H256 = blake2_256(&IMT::get_id_graph(&who).encode()).into(); + let id_graph_hash: H256 = IMT::id_graph_hash(&who).ok_or(StfError::EmptyIDGraph)?; + debug!("pushing identity_deactivated event ..."); calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( call_index, - account, + who.clone(), id_graph_hash, req_ext_hash, )))); - let mut mutated_id_graph = IDGraph::::default(); - if let Some(identity_context) = IMT::id_graphs(&who, &identity) { - if let Some(key) = maybe_key { - mutated_id_graph.push((identity, identity_context)); - return Ok(TrustedCallResult::DeactivateIdentity(DeactivateIdentityResult { - mutated_id_graph: aes_encrypt_default(&key, &mutated_id_graph.encode()), - id_graph_hash, - })) - } - } else { - // if should not happen, so we just log the error here - error!("failed to get identity_context for {:?}, {:?}", &who, &identity); + let mut mutated_id_graph = IMT::id_graph(&who); + mutated_id_graph.retain(|i| !old_id_graph.contains(i)); + + if let Some(key) = maybe_key { + return Ok(TrustedCallResult::DeactivateIdentity(DeactivateIdentityResult { + mutated_id_graph: aes_encrypt_default(&key, &mutated_id_graph.encode()), + id_graph_hash, + })) } Ok(TrustedCallResult::Empty) }, TrustedCall::activate_identity(signer, who, identity, maybe_key, req_ext_hash) => { debug!("activate_identity, who: {}", account_id_to_string(&who)); - let account = SgxParentchainTypeConverter::convert( - who.to_account_id().ok_or(Self::Error::InvalidAccount)?, - ); let call_index = node_metadata_repo .get_from_metadata(|m| m.identity_activated_call_indexes())??; + let old_id_graph = IMT::id_graph(&who); Self::activate_identity_internal( signer.to_account_id().ok_or(Self::Error::InvalidAccount)?, who.clone(), - identity.clone(), + identity, ) .map_err(|e| { debug!("pushing error event ... error: {}", e); push_call_imp_some_error( calls, node_metadata_repo.clone(), - Some(account.clone()), + Some(who.clone()), e.to_imp_error(), req_ext_hash, ); e })?; + let id_graph_hash: H256 = IMT::id_graph_hash(&who).ok_or(StfError::EmptyIDGraph)?; + debug!("pushing identity_activated event ..."); - let id_graph_hash: H256 = blake2_256(&IMT::get_id_graph(&who).encode()).into(); calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( call_index, - account, + who.clone(), id_graph_hash, req_ext_hash, )))); - let mut mutated_id_graph = IDGraph::::default(); - if let Some(identity_context) = IMT::id_graphs(&who, &identity) { - if let Some(key) = maybe_key { - mutated_id_graph.push((identity, identity_context)); - return Ok(TrustedCallResult::ActivateIdentity(ActivateIdentityResult { - mutated_id_graph: aes_encrypt_default(&key, &mutated_id_graph.encode()), - id_graph_hash, - })) - } - } else { - // if should not happen, so we just log the error here - error!("failed to get identity_context for {:?}, {:?}", &who, &identity); + let mut mutated_id_graph = IMT::id_graph(&who); + mutated_id_graph.retain(|i| !old_id_graph.contains(i)); + + if let Some(key) = maybe_key { + return Ok(TrustedCallResult::ActivateIdentity(ActivateIdentityResult { + mutated_id_graph: aes_encrypt_default(&key, &mutated_id_graph.encode()), + id_graph_hash, + })) } Ok(TrustedCallResult::Empty) @@ -859,12 +842,9 @@ where assertion ); - let account = SgxParentchainTypeConverter::convert( - who.to_account_id().ok_or(Self::Error::InvalidAccount)?, - ); Self::request_vc_internal( signer.to_account_id().ok_or(Self::Error::InvalidAccount)?, - who, + who.clone(), assertion, top_hash, req_ext_hash, @@ -876,7 +856,7 @@ where push_call_vcmp_some_error( calls, node_metadata_repo, - Some(account), + Some(who), e.to_vcmp_error(), req_ext_hash, ); @@ -899,9 +879,6 @@ where account_id_to_string(&who), assertion ); - let account = SgxParentchainTypeConverter::convert( - who.to_account_id().ok_or(Self::Error::InvalidAccount)?, - ); Self::request_vc_callback_internal( signer.to_account_id().ok_or(Self::Error::InvalidAccount)?, @@ -912,7 +889,7 @@ where push_call_vcmp_some_error( calls, node_metadata_repo.clone(), - Some(account.clone()), + Some(who.clone()), e.to_vcmp_error(), req_ext_hash, ); @@ -925,7 +902,7 @@ where calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( call_index, - account, + who, assertion, vc_index, vc_hash, @@ -956,69 +933,55 @@ where ensure_self(&signer, &who), Self::Error::Dispatch("Unauthorized signer".to_string()) ); - let account = SgxParentchainTypeConverter::convert( - who.to_account_id().ok_or(Self::Error::InvalidAccount)?, - ); let call_index = node_metadata_repo .get_from_metadata(|m| m.identity_networks_set_call_indexes())??; + let old_id_graph = IMT::id_graph(&who); - IMTCall::set_identity_networks { - who: who.clone(), - identity: identity.clone(), - web3networks, - } - .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) - .map_err(|e| Self::Error::Dispatch(format!(" error: {:?}", e.error)))?; + IMTCall::set_identity_networks { who: who.clone(), identity, web3networks } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) + .map_err(|e| Self::Error::Dispatch(format!(" error: {:?}", e.error)))?; + + let id_graph_hash: H256 = IMT::id_graph_hash(&who).ok_or(StfError::EmptyIDGraph)?; debug!("pushing identity_networks_set event ..."); - let id_graph_hash: H256 = blake2_256(&IMT::get_id_graph(&who).encode()).into(); calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( call_index, - account, + who.clone(), id_graph_hash, req_ext_hash, )))); - let mut mutated_id_graph = IDGraph::::default(); - if let Some(identity_context) = IMT::id_graphs(&who, &identity) { - if let Some(key) = maybe_key { - mutated_id_graph.push((identity, identity_context)); - return Ok(TrustedCallResult::SetIdentityNetworks( - SetIdentityNetworksResult { - mutated_id_graph: aes_encrypt_default( - &key, - &mutated_id_graph.encode(), - ), - id_graph_hash, - }, - )) - } - } else { - // if should not happen, so we just log the error here - error!("failed to get identity_context for {:?}, {:?}", &who, &identity); + let mut mutated_id_graph = IMT::id_graph(&who); + mutated_id_graph.retain(|i| !old_id_graph.contains(i)); + + if let Some(key) = maybe_key { + return Ok(TrustedCallResult::SetIdentityNetworks(SetIdentityNetworksResult { + mutated_id_graph: aes_encrypt_default(&key, &mutated_id_graph.encode()), + id_graph_hash, + })) } Ok(TrustedCallResult::Empty) }, - TrustedCall::handle_imp_error(_enclave_account, account, e, req_ext_hash) => { + TrustedCall::handle_imp_error(_enclave_account, identity, e, req_ext_hash) => { // checking of `_enclave_account` is not strictly needed, as this trusted call can // only be constructed internally push_call_imp_some_error( calls, node_metadata_repo, - account.and_then(|g| g.to_account_id()), + identity, e.clone(), req_ext_hash, ); Err(e.into()) }, - TrustedCall::handle_vcmp_error(_enclave_account, account, e, req_ext_hash) => { + TrustedCall::handle_vcmp_error(_enclave_account, identity, e, req_ext_hash) => { // checking of `_enclave_account` is not strictly needed, as this trusted call can // only be constructed internally push_call_vcmp_some_error( calls, node_metadata_repo, - account.and_then(|g| g.to_account_id()), + identity, e.clone(), req_ext_hash, ); @@ -1118,7 +1081,7 @@ where pub fn push_call_imp_some_error( calls: &mut Vec, node_metadata_repo: Arc, - account: Option, + identity: Option, e: IMPError, req_ext_hash: H256, ) where @@ -1130,7 +1093,7 @@ pub fn push_call_imp_some_error( match node_metadata_repo.get_from_metadata(|m| m.imp_some_error_call_indexes()) { Ok(Ok(call_index)) => calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( call_index, - account, + identity, e, req_ext_hash, )))), @@ -1142,7 +1105,7 @@ pub fn push_call_imp_some_error( pub fn push_call_vcmp_some_error( calls: &mut Vec, node_metadata_repo: Arc, - account: Option, + identity: Option, e: VCMPError, req_ext_hash: H256, ) where @@ -1153,7 +1116,7 @@ pub fn push_call_vcmp_some_error( match node_metadata_repo.get_from_metadata(|m| m.vcmp_some_error_call_indexes()) { Ok(Ok(call_index)) => calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( call_index, - account, + identity, e, req_ext_hash, )))), @@ -1162,29 +1125,6 @@ pub fn push_call_vcmp_some_error( } } -pub fn push_call_imp_update_id_graph_hash( - calls: &mut Vec, - node_metadata_repo: Arc, - account: ParentchainAccountId, - id_graph_hash: H256, - req_ext_hash: H256, -) where - NodeMetadataRepository: AccessNodeMetadata, - NodeMetadataRepository::MetadataType: NodeMetadataTrait, -{ - debug!("pushing IMP::update_id_graph_hash call ..."); - match node_metadata_repo.get_from_metadata(|m| m.update_id_graph_hash_call_indexes()) { - Ok(Ok(call_index)) => calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( - call_index, - account, - id_graph_hash, - req_ext_hash, - )))), - Ok(e) => warn!("error getting IMP::update_id_graph_hash call indexes: {:?}", e), - Err(e) => warn!("error getting IMP::update_id_graph_hash call indexes: {:?}", e), - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs index 1798062957..597f4ce1e9 100644 --- a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs +++ b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs @@ -20,16 +20,18 @@ extern crate sgx_tstd as std; use super::*; use crate::{ helpers::{ - enclave_signer_account, ensure_enclave_signer_account, ensure_enclave_signer_or_self, - get_expected_raw_message, verify_web3_identity, + ensure_enclave_signer_account, ensure_enclave_signer_or_self, get_expected_raw_message, + verify_web3_identity, }, trusted_call_result::{LinkIdentityResult, TrustedCallResult}, - AccountId, ConvertAccountId, SgxParentchainTypeConverter, ShardIdentifier, StfError, StfResult, - H256, + AccountId, ShardIdentifier, StfError, StfResult, H256, }; use codec::Encode; -use frame_support::{dispatch::UnfilteredDispatchable, ensure}; -use ita_sgx_runtime::{IDGraph, RuntimeOrigin, System}; +use frame_support::{dispatch::UnfilteredDispatchable, ensure, sp_runtime::traits::One}; +use ita_sgx_runtime::{ + pallet_imt::{get_eligible_identities, IdentityContext}, + BlockNumber, Parentchain, RuntimeOrigin, System, +}; use itp_node_api::metadata::NodeMetadataTrait; use itp_node_api_metadata::pallet_imp::IMPCallIndexes; use itp_node_api_metadata_provider::AccessNodeMetadata; @@ -40,11 +42,9 @@ use lc_stf_task_sender::{ AssertionBuildRequest, RequestType, Web2IdentityVerificationRequest, }; use litentry_primitives::{ - Assertion, ErrorDetail, Identity, IdentityNetworkTuple, RequestAesKey, ValidationData, - Web3Network, + Assertion, ErrorDetail, Identity, RequestAesKey, ValidationData, Web3Network, }; use log::*; -use sp_core::blake2_256; use std::{sync::Arc, vec::Vec}; #[cfg(not(feature = "production"))] @@ -169,30 +169,34 @@ impl TrustedCallSigned { ), } - let id_graph = IMT::get_id_graph(&who); + let mut id_graph = IMT::id_graph(&who); + if id_graph.is_empty() { + // we are safe to use `default_web3networks` and `Active` as IDGraph would be non-empty otherwise + id_graph.push(( + who.clone(), + IdentityContext::new(BlockNumber::one(), who.default_web3networks()), + )); + } let assertion_networks = assertion.get_supported_web3networks(); - let identities: Vec = id_graph - .into_iter() - .filter(|item| item.1.is_active()) - .map(|item| { - let mut networks = item.1.web3networks.to_vec(); - // filter out the web3networks which are not supported by this specific `assertion`. - // We do it here before every request sending because: - // - it's a common step for all assertion buildings, for those assertions which only - // care about web2 identities, this step will empty `IdentityContext.web3networks` - // - it helps to reduce the request size a bit - networks.retain(|n| assertion_networks.contains(n)); - (item.0, networks) - }) - .collect(); + let identities = get_eligible_identities(id_graph, assertion_networks); + + ensure!( + !identities.is_empty(), + StfError::RequestVCFailed(assertion, ErrorDetail::NoEligibleIdentity) + ); + + let parachain_block_number = Parentchain::block_number(); + let sidechain_block_number = System::block_number(); + let assertion_build: RequestType = AssertionBuildRequest { shard: *shard, signer, - enclave_account: enclave_signer_account(), who, assertion: assertion.clone(), identities, top_hash, + parachain_block_number, + sidechain_block_number, maybe_key, req_ext_hash, } @@ -256,19 +260,14 @@ impl TrustedCallSigned { NodeMetadataRepository::MetadataType: NodeMetadataTrait, { debug!("link_identity_callback, who: {}", account_id_to_string(&who)); - let account = SgxParentchainTypeConverter::convert( - who.to_account_id().ok_or(StfError::InvalidAccount)?, - ); - // the pallet extrinsic doesn't accept customised return type, so // we have to do the if-condition outside of extrinsic call - let old_id_graph_len = IMT::id_graph_lens(&who); - let mut mutated_id_graph = IDGraph::::default(); + let old_id_graph = IMT::id_graph(&who); Self::link_identity_callback_internal( signer.to_account_id().ok_or(StfError::InvalidAccount)?, who.clone(), - identity.clone(), + identity, web3networks, ) .map_err(|e| { @@ -276,35 +275,28 @@ impl TrustedCallSigned { push_call_imp_some_error( calls, node_metadata_repo.clone(), - Some(account.clone()), + Some(who.clone()), e.to_imp_error(), req_ext_hash, ); e })?; - debug!("pushing identity_linked event ..."); - let id_graph = IMT::get_id_graph(&who); - let id_graph_hash: H256 = blake2_256(&id_graph.encode()).into(); + let id_graph_hash: H256 = IMT::id_graph_hash(&who).ok_or(StfError::EmptyIDGraph)?; + debug!("pushing identity_linked event ..."); // push `identity_linked` call let call_index = node_metadata_repo.get_from_metadata(|m| m.identity_linked_call_indexes())??; calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( call_index, - account, + who.clone(), id_graph_hash, req_ext_hash, )))); - if old_id_graph_len == 0 { - mutated_id_graph = id_graph; - } else if let Some(identity_context) = IMT::id_graphs(&who, &identity) { - mutated_id_graph.push((identity, identity_context)) - } else { - // if should not happen, so we just log the error here - error!("failed to get identity_context for {:?}, {:?}", &who, &identity); - } + let mut mutated_id_graph = IMT::id_graph(&who); + mutated_id_graph.retain(|i| !old_id_graph.contains(i)); if let Some(key) = maybe_key { return Ok(TrustedCallResult::LinkIdentity(LinkIdentityResult { diff --git a/tee-worker/cli/src/base_cli/commands/litentry/dump_id_graph_hash.rs b/tee-worker/cli/src/base_cli/commands/litentry/id_graph_hash.rs similarity index 53% rename from tee-worker/cli/src/base_cli/commands/litentry/dump_id_graph_hash.rs rename to tee-worker/cli/src/base_cli/commands/litentry/id_graph_hash.rs index d4e25c9119..9abf282b20 100644 --- a/tee-worker/cli/src/base_cli/commands/litentry/dump_id_graph_hash.rs +++ b/tee-worker/cli/src/base_cli/commands/litentry/id_graph_hash.rs @@ -18,32 +18,26 @@ use crate::{command_utils::get_worker_api_direct, Cli, CliResult, CliResultOk}; use codec::Decode; use itc_rpc_client::direct_client::DirectApi; use itp_types::ShardIdentifier; -use itp_utils::ToHexPrefixed; +use litentry_primitives::Identity; -// usage: ./bin/litentry-cli dump-id-graph-hash +// usage: ./bin/litentry-cli id-graph-hash did-identity // -// this command prints a list of (AccountId32, H256) collections which represent the IDGraph owner and hash, respectively -// example result: -// ``` -// 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d, 0xd47cd39a19bc0094a4ec6ebf147cd1bde820e8d07574df02505607ec53c39ceb -// 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48, 0x6fd466064ab4aa8eeae19e0ddc25a99fefd3bf326caef98cf0c24b6467805f9d -// ``` -// please note the evm address is converted to substrate account to faciliate the population of on-chain storage, -// as the parachain extrinsic `update_id_graph_hash` expects a T::AccountId, same as other extrinsic callbacks +// this command prints the id graph hash of the given identity in did form #[derive(Parser)] -pub struct DumpIDGraphHashCommand {} +pub struct IDGraphHashCommand { + /// identity to query, in did form + did: String, +} -impl DumpIDGraphHashCommand { +impl IDGraphHashCommand { pub(crate) fn run(&self, cli: &Cli) -> CliResult { let direct_api = get_worker_api_direct(cli); let mrenclave = direct_api.get_state_mrenclave().unwrap(); let shard = ShardIdentifier::decode(&mut &mrenclave[..]).unwrap(); - let id_graph_hash = direct_api.get_all_id_graph_hash(&shard).unwrap(); - - id_graph_hash.iter().for_each(|(identity, hash)| { - println!("{}, {:?}", identity.to_account_id().unwrap().to_hex(), hash) - }); + let identity = Identity::from_did(self.did.as_str()).unwrap(); + let id_graph_hash = direct_api.get_id_graph_hash(&shard, &identity).unwrap(); + println!("{:?}", id_graph_hash); Ok(CliResultOk::None) } diff --git a/tee-worker/cli/src/base_cli/commands/litentry/link_identity.rs b/tee-worker/cli/src/base_cli/commands/litentry/link_identity.rs index 28544ab0d8..e79b7aaf02 100644 --- a/tee-worker/cli/src/base_cli/commands/litentry/link_identity.rs +++ b/tee-worker/cli/src/base_cli/commands/litentry/link_identity.rs @@ -17,7 +17,7 @@ use super::IMP; use crate::{ command_utils::{get_chain_api, *}, - Cli, CliError, CliResult, CliResultOk, + Cli, CliResult, CliResultOk, }; use base58::FromBase58; use codec::{Decode, Encode}; @@ -33,8 +33,8 @@ use substrate_api_client::{ac_compose_macros::compose_extrinsic, SubmitAndWatch, pub struct LinkIdentityCommand { /// AccountId in ss58check format account: String, - /// Identity to be created - identity: String, + /// Identity to be created, in did form + did: String, /// Shard identifier shard: String, } @@ -56,16 +56,9 @@ impl LinkIdentityCommand { let who = sr25519_core::Pair::from_string(&self.account, None).unwrap(); chain_api.set_signer(who.clone().into()); - let identity: Result = serde_json::from_str(self.identity.as_str()); - if let Err(e) = identity { - warn!("Deserialize Identity error: {:?}", e.to_string()); - return Err(CliError::Extrinsic { - msg: format!("Deserialize Identity error: {:?}", e.to_string()), - }) - } - + let identity = Identity::from_did(self.did.as_str()).unwrap(); let tee_shielding_key = get_shielding_key(cli).unwrap(); - let encrypted_identity = tee_shielding_key.encrypt(&identity.unwrap().encode()).unwrap(); + let encrypted_identity = tee_shielding_key.encrypt(&identity.encode()).unwrap(); // TODO: the params are incorrect - and need to be reworked too let vdata: Option> = None; diff --git a/tee-worker/cli/src/base_cli/commands/litentry/mod.rs b/tee-worker/cli/src/base_cli/commands/litentry/mod.rs index 26e784e28e..820e551764 100644 --- a/tee-worker/cli/src/base_cli/commands/litentry/mod.rs +++ b/tee-worker/cli/src/base_cli/commands/litentry/mod.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . -pub mod dump_id_graph_hash; +pub mod id_graph_hash; pub mod link_identity; pub mod set_heartbeat_timeout; diff --git a/tee-worker/cli/src/base_cli/mod.rs b/tee-worker/cli/src/base_cli/mod.rs index c825c74bca..7270dd8dbb 100644 --- a/tee-worker/cli/src/base_cli/mod.rs +++ b/tee-worker/cli/src/base_cli/mod.rs @@ -21,7 +21,7 @@ use crate::{ faucet::FaucetCommand, listen::ListenCommand, litentry::{ - dump_id_graph_hash::DumpIDGraphHashCommand, link_identity::LinkIdentityCommand, + id_graph_hash::IDGraphHashCommand, link_identity::LinkIdentityCommand, set_heartbeat_timeout::SetHeartbeatTimeoutCommand, }, register_tcb_info::RegisterTcbInfoCommand, @@ -93,8 +93,8 @@ pub enum BaseCommand { /// set heartbeat timeout storage SetHeartbeatTimeout(SetHeartbeatTimeoutCommand), - /// dump all IDGraph hashes stored in sidechain - DumpIDGraphHash(DumpIDGraphHashCommand), + /// get the IDGraph hash of the given identity + IDGraphHash(IDGraphHashCommand), } impl BaseCommand { @@ -115,7 +115,7 @@ impl BaseCommand { BaseCommand::PrintSgxMetadataRaw => print_sgx_metadata_raw(cli), BaseCommand::LinkIdentity(cmd) => cmd.run(cli), BaseCommand::SetHeartbeatTimeout(cmd) => cmd.run(cli), - BaseCommand::DumpIDGraphHash(cmd) => cmd.run(cli), + BaseCommand::IDGraphHash(cmd) => cmd.run(cli), } } } diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs index 400066ec6e..b740d93d2a 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs @@ -24,14 +24,13 @@ use crate::{ use ita_stf::{trusted_call_result::RequestVCResult, Index, TrustedCall, TrustedCallSigning}; use itp_stf_primitives::types::KeyPair; use itp_utils::hex::decode_hex; -use lc_credentials::Credential; use litentry_primitives::{ aes_decrypt, AchainableAmount, AchainableAmountHolding, AchainableAmountToken, AchainableAmounts, AchainableBasic, AchainableBetweenPercents, AchainableClassOfYear, AchainableDate, AchainableDateInterval, AchainableDatePercent, AchainableParams, - AchainableToken, Assertion, ContestType, GenericDiscordRoleType, Identity, OneBlockCourseType, - ParameterString, RequestAesKey, SoraQuizType, VIP3MembershipCardLevel, Web3Network, - REQUEST_AES_KEY_LEN, + AchainableToken, Assertion, BoundedWeb3Network, ContestType, EVMTokenType, + GenericDiscordRoleType, Identity, OneBlockCourseType, ParameterString, RequestAesKey, + SoraQuizType, VIP3MembershipCardLevel, Web3Network, REQUEST_AES_KEY_LEN, }; use sp_core::Pair; @@ -51,11 +50,23 @@ use sp_core::Pair; // // ./bin/litentry-cli trusted -d request-vc \ // did:litentry:substrate:0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48 vip3-membership-card gold +// +// ./bin/litentry-cli trusted -d request-vc \ +// did:litentry:substrate:0x52a6c52dc82940a36fefd1474cc0778517bb1a56b7bda0e308b6c19152dd7510 achainable amount-token test-name -c=bsc,ethereum 1 token-value pub fn to_para_str(s: &str) -> ParameterString { ParameterString::truncate_from(s.as_bytes().to_vec()) } +pub fn to_chains(networks: &[String]) -> BoundedWeb3Network { + let networks: Vec = networks + .iter() + .map(|n| n.as_str().try_into().expect("cannot convert to Web3Network")) + .collect(); + + networks.try_into().unwrap() +} + #[derive(Parser)] pub struct RequestVcCommand { /// did account to whom the vc will be issued @@ -66,7 +77,7 @@ pub struct RequestVcCommand { } // see `assertion.rs` -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] pub enum Command { A1, A2(A2Arg), @@ -80,6 +91,7 @@ pub enum Command { A13(A13Arg), A14, A20, + BnbDomainHolding, #[clap(subcommand)] Oneblock(OneblockCommand), #[clap(subcommand)] @@ -89,14 +101,17 @@ pub enum Command { #[clap(subcommand)] VIP3MembershipCard(VIP3MembershipCardLevelCommand), WeirdoGhostGangHolder, + #[clap(subcommand)] + EVMAmountHolding(EVMAmountHoldingCommand), + CryptoSummary, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct A2Arg { pub guild_id: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct A3Arg { pub guild_id: String, pub channel_id: String, @@ -104,30 +119,30 @@ pub struct A3Arg { } // used in A4/A7/A10/A11 -#[derive(Args)] +#[derive(Args, Debug)] pub struct HolderArg { pub minimum_amount: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct A8Arg { #[clap(num_args = 0.., value_delimiter = ',')] pub networks: Vec, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct A13Arg { pub account: String, } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] pub enum OneblockCommand { Completion, Outstanding, Participation, } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] pub enum AchainableCommand { AmountHolding(AmountHoldingArg), AmountToken(AmountTokenArg), @@ -142,7 +157,7 @@ pub enum AchainableCommand { Token(TokenArg), } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] pub enum GenericDiscordRoleCommand { #[clap(subcommand)] Contest(ContestCommand), @@ -150,27 +165,33 @@ pub enum GenericDiscordRoleCommand { SoraQuiz(SoraQuizCommand), } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] pub enum VIP3MembershipCardLevelCommand { Gold, Silver, } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] pub enum ContestCommand { Legend, Popularity, Participant, } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] pub enum SoraQuizCommand { Attendee, Master, } +#[derive(Subcommand, Debug)] +pub enum EVMAmountHoldingCommand { + Ton, + Trx, +} + // I haven't found a good way to use common args for subcommands -#[derive(Args)] +#[derive(Args, Debug)] pub struct AmountHoldingArg { pub name: String, pub chain: String, @@ -179,22 +200,34 @@ pub struct AmountHoldingArg { pub token: Option, } -#[derive(Args)] +// positional args (to vec) + required arg + optional arg is a nightmare combination for clap parser, +// additionally, only the last positional argument, or second to last positional argument may be set to `.num_args()` +// +// the best bet is to use a flag explicitly, be sure to use euqal form for `chain`, e.g.: +// -- name -c=bsc,ethereum 10 +// -- name -c=bsc,ethereum 10 token +#[derive(Args, Debug)] pub struct AmountTokenArg { pub name: String, - pub chain: String, + #[clap( + short, long, + num_args = 1.., + required = true, + value_delimiter = ',', + )] + pub chain: Vec, pub amount: String, pub token: Option, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct AmountArg { pub name: String, pub chain: String, pub amount: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct AmountsArg { pub name: String, pub chain: String, @@ -202,13 +235,13 @@ pub struct AmountsArg { pub amount2: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct BasicArg { pub name: String, pub chain: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct BetweenPercentsArg { pub name: String, pub chain: String, @@ -216,13 +249,13 @@ pub struct BetweenPercentsArg { pub less_than_or_equal_to: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct ClassOfYearArg { pub name: String, pub chain: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct DateIntervalArg { pub name: String, pub chain: String, @@ -230,7 +263,7 @@ pub struct DateIntervalArg { pub end_date: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct DatePercentArg { pub name: String, pub chain: String, @@ -239,14 +272,14 @@ pub struct DatePercentArg { pub percent: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct DateArg { pub name: String, pub chain: String, pub date: String, } -#[derive(Args)] +#[derive(Args, Debug)] pub struct TokenArg { pub name: String, pub chain: String, @@ -263,6 +296,8 @@ impl RequestVcCommand { let nonce = get_layer_two_nonce!(alice, cli, trusted_cli); println!(">>>nonce: {}", nonce); + println!(">>>command: {:#?}", self.command); + let assertion = match &self.command { Command::A1 => Assertion::A1, Command::A2(arg) => Assertion::A2(to_para_str(&arg.guild_id)), @@ -274,14 +309,7 @@ impl RequestVcCommand { Command::A4(arg) => Assertion::A4(to_para_str(&arg.minimum_amount)), Command::A6 => Assertion::A6, Command::A7(arg) => Assertion::A7(to_para_str(&arg.minimum_amount)), - Command::A8(arg) => { - let networks: Vec = arg - .networks - .iter() - .map(|n| n.as_str().try_into().expect("cannot convert to Web3Network")) - .collect(); - Assertion::A8(networks.try_into().unwrap()) - }, + Command::A8(arg) => Assertion::A8(to_chains(&arg.networks)), Command::A10(arg) => Assertion::A10(to_para_str(&arg.minimum_amount)), Command::A11(arg) => Assertion::A11(to_para_str(&arg.minimum_amount)), Command::A13(arg) => { @@ -290,6 +318,7 @@ impl RequestVcCommand { }, Command::A14 => Assertion::A14, Command::A20 => Assertion::A20, + Command::BnbDomainHolding => Assertion::BnbDomainHolding, Command::Oneblock(c) => match c { OneblockCommand::Completion => Assertion::Oneblock(OneBlockCourseType::CourseCompletion), @@ -315,11 +344,7 @@ impl RequestVcCommand { AchainableCommand::AmountToken(arg) => Assertion::Achainable(AchainableParams::AmountToken(AchainableAmountToken { name: to_para_str(&arg.name), - chain: arg - .chain - .as_str() - .try_into() - .expect("cannot convert to Web3Network"), + chain: to_chains(&arg.chain), amount: to_para_str(&arg.amount), token: arg.token.as_ref().map(|s| to_para_str(s)), })), @@ -446,6 +471,11 @@ impl RequestVcCommand { Assertion::VIP3MembershipCard(VIP3MembershipCardLevel::Silver), }, Command::WeirdoGhostGangHolder => Assertion::WeirdoGhostGangHolder, + Command::EVMAmountHolding(c) => match c { + EVMAmountHoldingCommand::Ton => Assertion::EVMAmountHolding(EVMTokenType::Ton), + EVMAmountHoldingCommand::Trx => Assertion::EVMAmountHolding(EVMTokenType::Trx), + }, + Command::CryptoSummary => Assertion::CryptoSummary, }; let key = Self::random_aes_key(); @@ -463,9 +493,9 @@ impl RequestVcCommand { match perform_trusted_operation::(cli, trusted_cli, &top) { Ok(mut vc) => { let decrypted = aes_decrypt(&key, &mut vc.vc_payload).unwrap(); - let credential: Credential = serde_json::from_slice(&decrypted).unwrap(); + let credential_str = String::from_utf8(decrypted).expect("Found invalid UTF-8"); println!("----Generated VC-----"); - println!("{:?}", credential); + println!("{}", credential_str); }, Err(e) => { println!("{:?}", e); diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs index 7550a12ea2..d909a05212 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs @@ -25,13 +25,12 @@ use crate::{ use ita_stf::{trusted_call_result::RequestVCResult, Index, TrustedCall, TrustedCallSigning}; use itp_stf_primitives::types::KeyPair; use itp_utils::hex::decode_hex; -use lc_credentials::Credential; use litentry_primitives::{ aes_decrypt, AchainableAmount, AchainableAmountHolding, AchainableAmountToken, AchainableAmounts, AchainableBasic, AchainableBetweenPercents, AchainableClassOfYear, AchainableDate, AchainableDateInterval, AchainableDatePercent, AchainableParams, - AchainableToken, Assertion, ContestType, GenericDiscordRoleType, Identity, OneBlockCourseType, - RequestAesKey, SoraQuizType, VIP3MembershipCardLevel, Web3Network, + AchainableToken, Assertion, ContestType, EVMTokenType, GenericDiscordRoleType, Identity, + OneBlockCourseType, RequestAesKey, SoraQuizType, VIP3MembershipCardLevel, Web3Network, }; use sp_core::Pair; @@ -97,6 +96,7 @@ impl RequestVcDirectCommand { }, Command::A14 => Assertion::A14, Command::A20 => Assertion::A20, + Command::BnbDomainHolding => Assertion::BnbDomainHolding, Command::Oneblock(c) => match c { OneblockCommand::Completion => Assertion::Oneblock(OneBlockCourseType::CourseCompletion), @@ -122,11 +122,7 @@ impl RequestVcDirectCommand { AchainableCommand::AmountToken(arg) => Assertion::Achainable(AchainableParams::AmountToken(AchainableAmountToken { name: to_para_str(&arg.name), - chain: arg - .chain - .as_str() - .try_into() - .expect("cannot convert to Web3Network"), + chain: to_chains(&arg.chain), amount: to_para_str(&arg.amount), token: arg.token.as_ref().map(|s| to_para_str(s)), })), @@ -253,6 +249,11 @@ impl RequestVcDirectCommand { Assertion::VIP3MembershipCard(VIP3MembershipCardLevel::Silver), }, Command::WeirdoGhostGangHolder => Assertion::WeirdoGhostGangHolder, + Command::EVMAmountHolding(c) => match c { + EVMAmountHoldingCommand::Ton => Assertion::EVMAmountHolding(EVMTokenType::Ton), + EVMAmountHoldingCommand::Trx => Assertion::EVMAmountHolding(EVMTokenType::Trx), + }, + Command::CryptoSummary => Assertion::CryptoSummary, }; let mut key: RequestAesKey = RequestAesKey::default(); @@ -276,9 +277,9 @@ impl RequestVcDirectCommand { match perform_direct_operation::(cli, trusted_cli, &top, key) { Ok(mut vc) => { let decrypted = aes_decrypt(&key, &mut vc.vc_payload).unwrap(); - let credential: Credential = serde_json::from_slice(&decrypted).unwrap(); + let credential_str = String::from_utf8(decrypted).expect("Found invalid UTF-8"); println!("----Generated VC-----"); - println!("{:?}", credential); + println!("{}", credential_str); }, Err(e) => { println!("{:?}", e); diff --git a/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts b/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts index e8c09d3380..60ea972254 100644 --- a/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts +++ b/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts @@ -26,13 +26,30 @@ export default { Github: "IdentityString", Substrate: "Address32", Evm: "Address20", + Bitcoin: "Address33", }, }, Address32: "[u8;32]", Address20: "[u8;20]", + Address33: "[u8;33]", IdentityString: "Vec", Web3Network: { - _enum: ["Polkadot", "Kusama", "Litentry", "Litmus", "LitentryRococo", "Khala", "SubstrateTestnet", "Ethereum", "Bsc"], + _enum: [ + "Polkadot", + "Kusama", + "Litentry", + "Litmus", + "LitentryRococo", + "Khala", + "SubstrateTestnet", + "Ethereum", + "Bsc", + "BitcoinP2tr", + "BitcoinP2pkh", + "BitcoinP2sh", + "BitcoinP2wpkh", + "BitcoinP2wsh", + ], }, LitentryValidationData: { _enum: { @@ -58,6 +75,7 @@ export default { _enum: { Substrate: "Web3CommonValidationData", Evm: "Web3CommonValidationData", + Bitcoin: "Web3CommonValidationData", }, }, Web3CommonValidationData: { @@ -67,14 +85,20 @@ export default { LitentryMultiSignature: { _enum: { - Ed25519: "ed25519::Signature", - Sr25519: "sr25519::Signature", - Ecdsa: "ecdsa::Signature", + Ed25519: "Ed25519Signature", + Sr25519: "Sr25519Signature", + Ecdsa: "EcdsaSignature", Ethereum: "EthereumSignature", EthereumPrettified: "EthereumSignature", + Bitcoin: "BitcoinSignature", + BitcoinPrettified: "BitcoinSignature", }, }, + Ed25519Signature: "([u8; 64])", + Sr25519Signature: "([u8; 64])", + EcdsaSignature: "([u8; 65])", EthereumSignature: "([u8; 65])", + BitcoinSignature: "([u8; 65])", IdentityGenericEvent: { who: "AccountId", diff --git a/tee-worker/client-api/parachain-api/prepare-build/interfaces/sidechain/definitions.ts b/tee-worker/client-api/parachain-api/prepare-build/interfaces/sidechain/definitions.ts index ccd7f115f4..c111ab837b 100644 --- a/tee-worker/client-api/parachain-api/prepare-build/interfaces/sidechain/definitions.ts +++ b/tee-worker/client-api/parachain-api/prepare-build/interfaces/sidechain/definitions.ts @@ -37,6 +37,7 @@ export default { _enum: { some_value: "u32", nonce: "(LitentryIdentity)", + id_graph_hash: "(LitentryIdentity)", }, }, @@ -58,6 +59,8 @@ export default { SetIdentityNetworksFailed: "(ErrorDetail)", InvalidAccount: "Null", UnclassifiedError: "Null", + RemoveIdentityFailed: "(ErrorDetail)", + EmptyIDGraph: "Null", }, }, ErrorDetail: { @@ -73,6 +76,7 @@ export default { UnexpectedMessage: "Null", __Unused_WrongSignatureType: "Null", VerifyWeb3SignatureFailed: "Null", + NoEligibleIdentity: "Null", }, }, // teerex diff --git a/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts b/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts index d698d1b165..4f29666552 100644 --- a/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts +++ b/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts @@ -27,6 +27,10 @@ export default { BnbDigitDomainClub: "BnbDigitDomainType", VIP3MembershipCard: "VIP3MembershipCardLevel", WeirdoGhostGangHolder: "Null", + LITStaking: "Null", + EVMAmountHolding: "EVMTokenType", + BRC20AmountHolder: "Null", + CyptoSummary: "Null", }, }, AssertionSupportedNetwork: { @@ -139,5 +143,9 @@ export default { VIP3MembershipCardLevel: { _enum: ["Gold", "Silver"], }, + // EVMAmountHolding + EVMTokenType: { + _enum: ["Ton", "Trx"], + }, }, }; diff --git a/tee-worker/core-primitives/stf-primitives/src/error.rs b/tee-worker/core-primitives/stf-primitives/src/error.rs index c222ea0a1b..c69514f109 100644 --- a/tee-worker/core-primitives/stf-primitives/src/error.rs +++ b/tee-worker/core-primitives/stf-primitives/src/error.rs @@ -70,6 +70,8 @@ pub enum StfError { #[codec(index = 16)] #[display(fmt = "RemovingIdentityFailed: {:?}", _0)] RemoveIdentityFailed(ErrorDetail), + #[codec(index = 17)] + EmptyIDGraph, } impl From for StfError { diff --git a/tee-worker/core-primitives/substrate-sgx/sp-io/src/lib.rs b/tee-worker/core-primitives/substrate-sgx/sp-io/src/lib.rs index 9e39143376..6962f6c164 100644 --- a/tee-worker/core-primitives/substrate-sgx/sp-io/src/lib.rs +++ b/tee-worker/core-primitives/substrate-sgx/sp-io/src/lib.rs @@ -668,8 +668,13 @@ pub mod crypto { sig: &[u8; 65], msg: &[u8; 32], ) -> Result<[u8; 33], EcdsaVerifyError> { - warn!("crypto::secp256k1_ecdsa_recover unimplemented"); - Ok([0; 33]) + let rs = libsecp256k1::Signature::parse_standard_slice(&sig[0..64]) + .map_err(|_| EcdsaVerifyError::BadRS)?; + let v = libsecp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] }) + .map_err(|_| EcdsaVerifyError::BadV)?; + let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v) + .map_err(|_| EcdsaVerifyError::BadSignature)?; + Ok(pubkey.serialize_compressed()) } } diff --git a/tee-worker/core-primitives/utils/src/hex.rs b/tee-worker/core-primitives/utils/src/hex.rs index f9d30b5149..4c167af6f3 100644 --- a/tee-worker/core-primitives/utils/src/hex.rs +++ b/tee-worker/core-primitives/utils/src/hex.rs @@ -100,7 +100,7 @@ mod tests { fn hex_encode_decode_works_empty_input_for_decode() { let data = String::new(); - let decoded_data = decode_hex(&data).unwrap(); + let decoded_data = decode_hex(data).unwrap(); assert!(decoded_data.is_empty()); } diff --git a/tee-worker/core-primitives/utils/src/stringify.rs b/tee-worker/core-primitives/utils/src/stringify.rs index 7018038ad8..e514fdbecb 100644 --- a/tee-worker/core-primitives/utils/src/stringify.rs +++ b/tee-worker/core-primitives/utils/src/stringify.rs @@ -30,3 +30,7 @@ pub fn public_to_string(t: &T) -> String { pub fn account_id_to_string(account: &AccountId) -> String { format!("0x{}", HexDisplay::from(&account.encode())) } + +pub fn account_id_to_string_without_prefix(account: &AccountId) -> String { + format!("{}", HexDisplay::from(&account.encode())) +} diff --git a/tee-worker/core/rpc-client/src/direct_client.rs b/tee-worker/core/rpc-client/src/direct_client.rs index 274e9d1061..7814b0e7e3 100644 --- a/tee-worker/core/rpc-client/src/direct_client.rs +++ b/tee-worker/core/rpc-client/src/direct_client.rs @@ -65,7 +65,7 @@ pub trait DirectApi { fn get_state_metadata_raw(&self) -> Result; fn get_next_nonce(&self, shard: &ShardIdentifier, account: &AccountId) -> Result; fn get_state_mrenclave(&self) -> Result; - fn get_all_id_graph_hash(&self, shard: &ShardIdentifier) -> Result>; + fn get_id_graph_hash(&self, shard: &ShardIdentifier, identity: &Identity) -> Result; } impl DirectClient { @@ -264,11 +264,11 @@ impl DirectApi for DirectClient { Ok(mrenclave) } - fn get_all_id_graph_hash(&self, shard: &ShardIdentifier) -> Result> { - let getter = Getter::public(PublicGetter::all_id_graph_hash); + fn get_id_graph_hash(&self, shard: &ShardIdentifier, identity: &Identity) -> Result { + let getter = Getter::public(PublicGetter::id_graph_hash(identity.clone())); self.get_state(*shard, &getter) .ok_or_else(|| Error::Status("failed to get state".to_string())) - .and_then(|v| Vec::<(Identity, H256)>::decode(&mut v.as_slice()).map_err(Error::Codec)) + .and_then(|v| H256::decode(&mut v.as_slice()).map_err(Error::Codec)) } } diff --git a/tee-worker/core/rpc-client/src/mock.rs b/tee-worker/core/rpc-client/src/mock.rs index f0e09625ac..8f582ecc27 100644 --- a/tee-worker/core/rpc-client/src/mock.rs +++ b/tee-worker/core/rpc-client/src/mock.rs @@ -120,7 +120,7 @@ impl DirectApi for DirectClientMock { unimplemented!() } - fn get_all_id_graph_hash(&self, _shard: &ShardIdentifier) -> Result> { + fn get_id_graph_hash(&self, _shard: &ShardIdentifier, _identity: &Identity) -> Result { unimplemented!() } } diff --git a/tee-worker/docker/lit-di-bitcoin-identity-test.yml b/tee-worker/docker/lit-di-bitcoin-identity-test.yml new file mode 100644 index 0000000000..8a4b5a2c59 --- /dev/null +++ b/tee-worker/docker/lit-di-bitcoin-identity-test.yml @@ -0,0 +1,24 @@ +services: + lit-di-bitcoin-identity-test: + image: litentry/litentry-cli:latest + container_name: litentry-di-bitcoin-identity-test + volumes: + - ../ts-tests:/ts-tests + - ../client-api:/client-api + - ../cli:/usr/local/worker-cli + build: + context: .. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node: + condition: service_healthy + litentry-worker-1: + condition: service_healthy + networks: + - litentry-test-network + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-di-bitcoin-identity 2>&1' " + restart: "no" +networks: + litentry-test-network: + driver: bridge diff --git a/tee-worker/enclave-runtime/Cargo.lock b/tee-worker/enclave-runtime/Cargo.lock index 93d7440815..277b721f63 100644 --- a/tee-worker/enclave-runtime/Cargo.lock +++ b/tee-worker/enclave-runtime/Cargo.lock @@ -257,6 +257,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "bech32" +version = "0.10.0-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" + [[package]] name = "binary-merkle-tree" version = "4.0.0-dev" @@ -271,6 +277,38 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitcoin" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5973a027b341b462105675962214dfe3c938ad9afd395d84b28602608bdcec7b" +dependencies = [ + "bech32", + "bitcoin-internals", + "bitcoin_hashes", + "core2", + "hex-conservative", + "hex_lit", + "secp256k1 0.28.0", +] + +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "core2", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -570,17 +608,30 @@ name = "core-primitives" version = "0.9.12" dependencies = [ "frame-support", + "itp-utils", "litentry-macros 0.9.12", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", "parity-scale-codec", "ring 0.16.20", "scale-info", + "serde 1.0.188", "sp-core", + "sp-io", "sp-runtime", "sp-std", "strum", "strum_macros", ] +[[package]] +name = "core2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" +dependencies = [ + "memchr 2.6.3", +] + [[package]] name = "cpufeatures" version = "0.2.9" @@ -972,9 +1023,27 @@ checksum = "a49a4e11987c51220aa89dbe1a5cc877f5079fa6864c0a5b4533331db44e9365" dependencies = [ "auto_impl", "ethereum", - "evm-core", - "evm-gasometer", - "evm-runtime", + "evm-core 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-gasometer 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-runtime 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", + "sha3 0.10.8", +] + +[[package]] +name = "evm" +version = "0.39.1" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "auto_impl", + "ethereum", + "evm-core 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "evm-gasometer 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "evm-runtime 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", "log", "parity-scale-codec", "primitive-types", @@ -994,14 +1063,34 @@ dependencies = [ "scale-info", ] +[[package]] +name = "evm-core" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-info", +] + [[package]] name = "evm-gasometer" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d43eadc395bd1a52990787ca1495c26b0248165444912be075c28909a853b8c" dependencies = [ - "evm-core", - "evm-runtime", + "evm-core 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-runtime 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types", +] + +[[package]] +name = "evm-gasometer" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "evm-core 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "evm-runtime 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", "primitive-types", ] @@ -1012,7 +1101,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aa5b32f59ec582a5651978004e5c784920291263b7dcb6de418047438e37f4f" dependencies = [ "auto_impl", - "evm-core", + "evm-core 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types", + "sha3 0.10.8", +] + +[[package]] +name = "evm-runtime" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "auto_impl", + "evm-core 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", "primitive-types", "sha3 0.10.8", ] @@ -1100,12 +1200,43 @@ dependencies = [ "sp-std", ] +[[package]] +name = "fp-account" +version = "1.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "hex 0.4.3", + "libsecp256k1", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", +] + [[package]] name = "fp-evm" version = "3.0.0-dev" source = "git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" dependencies = [ - "evm", + "evm 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fp-evm" +version = "3.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "evm 0.39.1 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", "frame-support", "parity-scale-codec", "scale-info", @@ -1532,12 +1663,27 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +dependencies = [ + "core2", +] + [[package]] name = "hex-literal" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + [[package]] name = "hmac" version = "0.12.1" @@ -1704,7 +1850,7 @@ dependencies = [ "parity-scale-codec", "serde 1.0.188", "sgx_tstd", - "substrate-fixed", + "substrate-fixed 0.5.9 (git+https://github.com/encointer/substrate-fixed?tag=v0.5.9)", "thiserror", "url", ] @@ -1741,7 +1887,7 @@ dependencies = [ "frame-system", "itp-sgx-runtime-primitives", "pallet-balances", - "pallet-evm", + "pallet-evm 6.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", "pallet-identity-management-tee", "pallet-parentchain", "pallet-sudo", @@ -2088,7 +2234,7 @@ dependencies = [ "lc-stf-task-sender", "parity-scale-codec", "sgx_tstd", - "substrate-fixed", + "substrate-fixed 0.5.9 (git+https://github.com/encointer/substrate-fixed?tag=v0.5.9)", ] [[package]] @@ -2664,6 +2810,7 @@ dependencies = [ "sgx_tstd", "sp-core", "sp-io", + "sp-runtime", "thiserror", ] @@ -2746,6 +2893,7 @@ dependencies = [ "blake2-rfc", "frame-support", "hex 0.4.0", + "hex-literal", "http", "http_req", "itc-rest-client", @@ -2758,11 +2906,13 @@ dependencies = [ "lc-stf-task-sender", "litentry-primitives", "log", + "pallet-parachain-staking", "parity-scale-codec", "rust-base58 0.0.4 (git+https://github.com/mesalock-linux/rust-base58-sgx)", "serde 1.0.188", "serde_json 1.0.107", "sgx_tstd", + "sp-core", "ss58-registry", "thiserror", "url", @@ -3038,15 +3188,18 @@ dependencies = [ name = "litentry-primitives" version = "0.1.0" dependencies = [ + "bitcoin", "core-primitives", + "hex 0.4.3", "itp-sgx-crypto", "itp-utils", "log", - "pallet-evm", + "pallet-evm 6.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", "parity-scale-codec", "rand 0.7.3", "ring 0.16.20", "scale-info", + "secp256k1 0.28.0", "serde 1.0.188", "sgx_tstd", "sp-core", @@ -3306,6 +3459,20 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "pallet-authorship" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-balances" version = "4.0.0-dev" @@ -3325,12 +3492,35 @@ name = "pallet-evm" version = "6.0.0-dev" source = "git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" dependencies = [ - "evm", - "fp-account", - "fp-evm", + "evm 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fp-account 1.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "fp-evm 3.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "frame-support", + "frame-system", + "hex 0.4.3", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "rlp", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm" +version = "6.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "evm 0.39.1 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "fp-account 1.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", "frame-support", "frame-system", "hex 0.4.3", + "hex-literal", "impl-trait-for-tuples", "log", "parity-scale-codec", @@ -3359,6 +3549,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-parachain-staking" +version = "0.1.0" +dependencies = [ + "core-primitives", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-balances", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-staking", + "sp-std", + "substrate-fixed 0.5.9 (git+https://github.com/encointer/substrate-fixed)", +] + [[package]] name = "pallet-parentchain" version = "0.9.0" @@ -3372,6 +3581,26 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-session" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", +] + [[package]] name = "pallet-sudo" version = "4.0.0-dev" @@ -4024,7 +4253,17 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ - "secp256k1-sys", + "secp256k1-sys 0.6.1", +] + +[[package]] +name = "secp256k1" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +dependencies = [ + "bitcoin_hashes", + "secp256k1-sys 0.9.1", ] [[package]] @@ -4036,6 +4275,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" +dependencies = [ + "cc", +] + [[package]] name = "secrecy" version = "0.8.0" @@ -4537,7 +4785,7 @@ dependencies = [ "primitive-types", "scale-info", "schnorrkel", - "secp256k1", + "secp256k1 0.24.3", "secrecy", "sp-core-hashing", "sp-debug-derive", @@ -4679,6 +4927,19 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "sp-session" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-staking", + "sp-std", +] + [[package]] name = "sp-staking" version = "4.0.0-dev" @@ -4875,6 +5136,26 @@ dependencies = [ "typenum 1.16.0", ] +[[package]] +name = "substrate-fixed" +version = "0.5.9" +source = "git+https://github.com/encointer/substrate-fixed#a75f3ba3f7c7893fb420500639cc055f964b1b88" +dependencies = [ + "parity-scale-codec", + "scale-info", + "substrate-typenum", +] + +[[package]] +name = "substrate-typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f0091e93c2c75b233ae39424c52cb8a662c0811fb68add149e20e5d7e8a788" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + [[package]] name = "subtle" version = "2.5.0" @@ -5135,7 +5416,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "digest 0.10.7", "static_assertions", ] diff --git a/tee-worker/enclave-runtime/src/stf_task_handler.rs b/tee-worker/enclave-runtime/src/stf_task_handler.rs index da62a05a08..11876a4418 100644 --- a/tee-worker/enclave-runtime/src/stf_task_handler.rs +++ b/tee-worker/enclave-runtime/src/stf_task_handler.rs @@ -72,6 +72,8 @@ pub unsafe extern "C" fn run_stf_task_handler(dpc: *const u8, dpc_size: usize) - data_provider_config.contest_participant_discord_role_id, ); dpc.set_vip3_url(data_provider_config.vip3_url); + dpc.set_geniidata_url(data_provider_config.geniidata_url); + dpc.set_geniidata_api_key(data_provider_config.geniidata_api_key); }, Err(e) => { error!("Error while setting data provider config: {:?}", e); diff --git a/tee-worker/enclave-runtime/src/test/tests_main.rs b/tee-worker/enclave-runtime/src/test/tests_main.rs index 28c3312693..64416fb0dd 100644 --- a/tee-worker/enclave-runtime/src/test/tests_main.rs +++ b/tee-worker/enclave-runtime/src/test/tests_main.rs @@ -154,8 +154,8 @@ pub extern "C" fn test_main_entrance() -> size_t { tls_ra::seal_handler::test::seal_state_works, tls_ra::seal_handler::test::seal_state_fails_for_invalid_state, tls_ra::seal_handler::test::unseal_seal_state_works, - tls_ra::tests::test_tls_ra_server_client_networking, tls_ra::tests::test_state_and_key_provisioning, + tls_ra::tests::test_tls_ra_server_client_networking, // RPC tests direct_rpc_tests::get_state_request_works, diff --git a/tee-worker/enclave-runtime/src/vc_issuance_task.rs b/tee-worker/enclave-runtime/src/vc_issuance_task.rs index 425c1cca7f..bdbce0ecc7 100644 --- a/tee-worker/enclave-runtime/src/vc_issuance_task.rs +++ b/tee-worker/enclave-runtime/src/vc_issuance_task.rs @@ -77,6 +77,8 @@ pub unsafe extern "C" fn run_vc_issuance(dpc: *const u8, dpc_size: usize) -> sgx data_provider_config.contest_participant_discord_role_id, ); dpc.set_vip3_url(data_provider_config.vip3_url); + dpc.set_geniidata_url(data_provider_config.geniidata_url); + dpc.set_geniidata_api_key(data_provider_config.geniidata_api_key); }, Err(e) => { error!("Error while setting data provider config: {:?}", e); diff --git a/tee-worker/litentry/core/assertion-build/Cargo.toml b/tee-worker/litentry/core/assertion-build/Cargo.toml index 30393c2404..f23f8835bf 100644 --- a/tee-worker/litentry/core/assertion-build/Cargo.toml +++ b/tee-worker/litentry/core/assertion-build/Cargo.toml @@ -39,11 +39,15 @@ itp-utils = { path = "../../../core-primitives/utils", default-features = false frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +hex-literal = { version = "0.4" } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + # litentry lc-credentials = { path = "../credentials", default-features = false } lc-data-providers = { path = "../data-providers", default-features = false } lc-stf-task-sender = { path = "../stf-task/sender", default-features = false } litentry-primitives = { path = "../../primitives", default-features = false } +pallet-parachain-staking = { path = "../../../../pallets/parachain-staking", default-features = false } [dev-dependencies] env_logger = "0.10.0" diff --git a/tee-worker/litentry/core/assertion-build/src/a13.rs b/tee-worker/litentry/core/assertion-build/src/a13.rs index e93d06599b..b89026ba07 100644 --- a/tee-worker/litentry/core/assertion-build/src/a13.rs +++ b/tee-worker/litentry/core/assertion-build/src/a13.rs @@ -47,9 +47,7 @@ pub fn build( .collect(); // if the signer can't be found in the delegatee list OR not the enclave account - if !(keys.iter().any(|k| k.ends_with(hex::encode(&req.signer).as_str())) - || req.signer == req.enclave_account) - { + if !(keys.iter().any(|k| k.ends_with(hex::encode(&req.signer).as_str()))) { return Err(Error::RequestVCFailed( Assertion::A13(who.clone()), ErrorDetail::UnauthorizedSigner, diff --git a/tee-worker/litentry/core/assertion-build/src/a2.rs b/tee-worker/litentry/core/assertion-build/src/a2.rs index 270a0cb969..60f9420934 100644 --- a/tee-worker/litentry/core/assertion-build/src/a2.rs +++ b/tee-worker/litentry/core/assertion-build/src/a2.rs @@ -103,11 +103,12 @@ mod tests { let req = AssertionBuildRequest { shard: ShardIdentifier::default(), signer: AccountId::from([0; 32]), - enclave_account: AccountId::from([0; 32]), who: AccountId::from([0; 32]).into(), assertion: Assertion::A2(guild_id.clone()), identities, top_hash: Default::default(), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, maybe_key: None, req_ext_hash: Default::default(), }; diff --git a/tee-worker/litentry/core/assertion-build/src/a20.rs b/tee-worker/litentry/core/assertion-build/src/a20.rs index d062403248..08e22d4ffa 100644 --- a/tee-worker/litentry/core/assertion-build/src/a20.rs +++ b/tee-worker/litentry/core/assertion-build/src/a20.rs @@ -48,9 +48,14 @@ impl RestPath for EarlyBirdResponse { } pub fn build(req: &AssertionBuildRequest) -> Result { + // Note: it's not perfectly implemented here + // it only attests if the main address meets the criteria, but we should have implemented + // the supported web3networks and attested the linked identities. + // However, this VC is probably too old to change let who = match req.who { Identity::Substrate(account) => account_id_to_string(&account), Identity::Evm(account) => account_id_to_string(&account), + Identity::Bitcoin(account) => account_id_to_string(&account), _ => unreachable!(), }; debug!("Assertion A20 build, who: {:?}", who); diff --git a/tee-worker/litentry/core/assertion-build/src/a3.rs b/tee-worker/litentry/core/assertion-build/src/a3.rs index 74d3bf8a92..b0209afdab 100644 --- a/tee-worker/litentry/core/assertion-build/src/a3.rs +++ b/tee-worker/litentry/core/assertion-build/src/a3.rs @@ -136,11 +136,12 @@ mod tests { let req = AssertionBuildRequest { shard: ShardIdentifier::default(), signer: AccountId::from([0; 32]), - enclave_account: AccountId::from([0; 32]), who: AccountId::from([0; 32]).into(), assertion: Assertion::A3(guild_id.clone(), channel_id.clone(), role_id.clone()), identities, top_hash: Default::default(), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, maybe_key: None, req_ext_hash: Default::default(), }; diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs b/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs index c3933e6fd7..77c2615edd 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs @@ -20,32 +20,29 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam #[cfg(all(not(feature = "std"), feature = "sgx"))] extern crate sgx_tstd as std; -use crate::{achainable::request_achainable_balance, *}; -use lc_credentials::litentry_profile::token_balance::TokenBalanceInfo; -use lc_data_providers::{ETokenAddress, TokenFromString}; +use crate::{ + achainable::{query_lit_holding_amount, request_achainable_balance}, + *, +}; +use lc_credentials::{ + achainable::lit_holding_amount::AchainableLitHoldingAmountUpdate, + litentry_profile::token_balance::TokenBalanceInfo, +}; +use lc_data_providers::{ + achainable_names::AchainableNameAmountToken, ETokenAddress, TokenFromString, +}; -// Input params: -// { -// "name": "ERC20 balance over {amount}", -// "address": "0xb59490ab09a0f526cc7305822ac65f2ab12f9723", -// "params": { -// "chain": "ethereum", -// "amount": "0", -// "token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" -// } -// } - -/// LIT / USDC / USDT Holder +/// ERC20 Holder: USDC and others /// assertions:[ /// { /// and:[ /// { -/// src:$lit_holding_amount, +/// src:$usdc_holding_amount, /// op: >=, /// dst:100 /// }, /// { -/// src:$lit_holding_amount, +/// src:$usdc_holding_amount, /// op: <, /// dst:200 /// }, @@ -57,35 +54,49 @@ pub fn build_amount_token( req: &AssertionBuildRequest, param: AchainableAmountToken, ) -> Result { - debug!("Assertion Achainable build_amount_token, who: {:?}", account_id_to_string(&req.who)); + debug!("Assertion Building AchainableAmountToken"); let identities = transpose_identity(&req.identities); - let addresses = identities - .into_iter() - .flat_map(|(_, addresses)| addresses) - .collect::>(); + let achainable_param = AchainableParams::AmountToken(param.clone()); + + let mut credential = Credential::new(&req.who, &req.shard).map_err(|e| { + Error::RequestVCFailed( + Assertion::Achainable(achainable_param.clone()), + e.into_error_detail(), + ) + })?; - let token = ETokenAddress::from_vec(param.clone().token.unwrap_or_default()); - let achainable_param = AchainableParams::AmountToken(param); - let balance = request_achainable_balance(addresses, achainable_param.clone())? - .parse::() - .map_err(|_| { + let amount_token_name = + AchainableNameAmountToken::from(achainable_param.name()).map_err(|e| { Error::RequestVCFailed( Assertion::Achainable(achainable_param.clone()), - ErrorDetail::ParseError, + e.into_error_detail(), ) })?; - match Credential::new(&req.who, &req.shard) { - Ok(mut credential_unsigned) => { - credential_unsigned.update_token_balance(token, balance); - Ok(credential_unsigned) + match amount_token_name { + AchainableNameAmountToken::LITHoldingAmount => { + let lit_holding_amount = query_lit_holding_amount(&achainable_param, &identities)?; + credential.update_lit_holding_amount(lit_holding_amount); }, - Err(e) => { - error!("Generate unsigned credential failed {:?}", e); - Err(Error::RequestVCFailed( - Assertion::Achainable(achainable_param), - e.into_error_detail(), - )) + _ => { + // Token Holder + let addresses = identities + .into_iter() + .flat_map(|(_, addresses)| addresses) + .collect::>(); + let token = ETokenAddress::from_vec(param.token.unwrap_or_default()); + let balance = request_achainable_balance(addresses, achainable_param.clone())? + .parse::() + .map_err(|_| { + Error::RequestVCFailed( + Assertion::Achainable(achainable_param.clone()), + ErrorDetail::ParseError, + ) + })?; + + credential.update_token_balance(token, balance); }, } + + Ok(credential) } diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs b/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs index 050edcbe64..cc305089e1 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs @@ -20,31 +20,15 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam #[cfg(all(not(feature = "std"), feature = "sgx"))] extern crate sgx_tstd as std; -use crate::{achainable::request_uniswap_v2_or_v3_user, *}; -use lc_data_providers::ConvertParameterString; -use std::string::ToString; +use crate::{ + achainable::{request_achainable, request_uniswap_v2_or_v3_user}, + *, +}; +use lc_credentials::achainable::{bab_holder::UpdateBABHolder, uniswap_user::UpdateUniswapUser}; +use lc_data_providers::achainable_names::AchainableNameBasic; -/// NOTE: -/// Build is uniswap v2/v3 user -/// name: Because it is necessary to request the interface four times to determine whether the uniswapv2/v3 user requires it, we can agree on the name here as IsUniswapV23User. -/// chain: ethereum -/// -/// assertions":[ -/// { -/// { -/// src:$is_uniswap_v2_user, -/// op:==, -/// dst:true -/// }, -/// { -/// src: is_uniswap_v3_user, -/// op: ==, -/// dst: true -/// } -/// } -/// pub fn build_basic(req: &AssertionBuildRequest, param: AchainableBasic) -> Result { - debug!("Assertion Achainable build_basic, who: {:?}", account_id_to_string(&req.who)); + debug!("Assertion Achainable building Basic"); let identities = transpose_identity(&req.identities); let addresses = identities @@ -53,47 +37,29 @@ pub fn build_basic(req: &AssertionBuildRequest, param: AchainableBasic) -> Resul .collect::>(); let achainable_param = AchainableParams::Basic(param.clone()); - check_uniswap_v23_user_inputs(&achainable_param, ¶m)?; - - let (v2_user, v3_user) = request_uniswap_v2_or_v3_user(addresses, achainable_param.clone())?; - match Credential::new(&req.who, &req.shard) { - Ok(mut credential_unsigned) => { - let (desc, subtype) = get_uniswap_v23_info(); - credential_unsigned.add_subject_info(desc, subtype); - credential_unsigned.update_uniswap_v23_info(v2_user, v3_user); + let mut credential = Credential::new(&req.who, &req.shard).map_err(|e| { + Error::RequestVCFailed( + Assertion::Achainable(achainable_param.clone()), + e.into_error_detail(), + ) + })?; - Ok(credential_unsigned) + let basic_name = AchainableNameBasic::from(param.name).map_err(|e| { + Error::RequestVCFailed( + Assertion::Achainable(achainable_param.clone()), + e.into_error_detail(), + ) + })?; + match basic_name { + AchainableNameBasic::UniswapV23User => { + let (v2_user, v3_user) = request_uniswap_v2_or_v3_user(addresses, achainable_param)?; + credential.update_uniswap_user(v2_user, v3_user); }, - Err(e) => { - error!("Generate unsigned credential failed {:?}", e); - Err(Error::RequestVCFailed( - Assertion::Achainable(achainable_param), - e.into_error_detail(), - )) + AchainableNameBasic::BABHolder => { + let is_bab_holder = request_achainable(addresses, achainable_param)?; + credential.update_bab_holder(is_bab_holder); }, } -} - -fn get_uniswap_v23_info() -> (&'static str, &'static str) { - ( - "You are a trader or liquidity provider of Uniswap V2 or V3. - Uniswap V2 Factory Contract: 0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f. - Uniswap V3 Factory Contract: 0x1f98431c8ad98523631ae4a59f267346ea31f984.", - "Uniswap V2/V3 User", - ) -} - -fn check_uniswap_v23_user_inputs( - achainable_param: &AchainableParams, - param: &AchainableBasic, -) -> Result { - let name = achainable_param.to_string(¶m.name)?; - if name != "Uniswap V2/V3 user" { - return Err(Error::RequestVCFailed( - Assertion::Achainable(achainable_param.clone()), - ErrorDetail::StfError(ErrorString::truncate_from("Invalid name".to_string().into())), - )) - } - Ok(true) + Ok(credential) } diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs b/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs index c5f9af7830..4d765c556a 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs @@ -28,11 +28,15 @@ use self::{ }; use crate::*; use lc_data_providers::{ - achainable::{AchainableClient, AchainableTagDeFi, HoldingAmount, Params}, - DataProviderConfigReader, ReadDataProviderConfig, + achainable::{ + AchainableClient, AchainableTagDeFi, HoldingAmount, Params, ParamsBasicTypeWithAmountToken, + }, + achainable_names::{AchainableNameAmountToken, GetAchainableName}, + DataProviderConfigReader, ReadDataProviderConfig, LIT_TOKEN_ADDRESS, }; use lc_stf_task_sender::AssertionBuildRequest; use litentry_primitives::AchainableParams; +use std::string::ToString; pub mod amount; pub mod amount_holding; @@ -156,3 +160,61 @@ pub fn request_achainable_balance( Ok(balance) } + +pub fn query_lit_holding_amount( + aparam: &AchainableParams, + identities: &Vec<(Web3Network, Vec)>, +) -> Result { + let mut total_lit_balance = 0_f64; + + let data_provider_config = DataProviderConfigReader::read() + .map_err(|e| Error::RequestVCFailed(Assertion::Achainable(aparam.clone()), e))?; + let mut client: AchainableClient = AchainableClient::new(&data_provider_config); + + for (network, addresses) in identities { + let (q_name, q_network, q_token) = if *network == Web3Network::Ethereum { + ( + AchainableNameAmountToken::ERC20BalanceOverAmount, + Web3Network::Ethereum, + Some(LIT_TOKEN_ADDRESS.to_string()), + ) + } else if *network == Web3Network::Bsc { + ( + AchainableNameAmountToken::BEP20BalanceOverAmount, + Web3Network::Bsc, + Some(LIT_TOKEN_ADDRESS.to_string()), + ) + } else if *network == Web3Network::Litentry { + (AchainableNameAmountToken::BalanceOverAmount, Web3Network::Litentry, None) + } else if *network == Web3Network::Litmus { + (AchainableNameAmountToken::BalanceOverAmount, Web3Network::Litmus, None) + } else { + continue + }; + + let q_param = ParamsBasicTypeWithAmountToken::new( + q_name.name().to_string(), + &q_network, + "0".to_string(), + q_token, + ); + + let params = Params::ParamsBasicTypeWithAmountToken(q_param); + let balance = client + .holding_amount(addresses.clone(), params) + .map_err(|e| { + Error::RequestVCFailed(Assertion::Achainable(aparam.clone()), e.into_error_detail()) + })? + .parse::() + .map_err(|_| { + Error::RequestVCFailed( + Assertion::Achainable(aparam.clone()), + ErrorDetail::ParseError, + ) + })?; + + total_lit_balance += balance; + } + + Ok(total_lit_balance as usize) +} diff --git a/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs b/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs new file mode 100644 index 0000000000..09be442d97 --- /dev/null +++ b/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs @@ -0,0 +1,51 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use crate::*; +use lc_credentials::brc20::amount_holder::BRC20AmountHolderCredential; +use lc_data_providers::geniidata::GeniidataClient; + +pub fn build(req: &AssertionBuildRequest) -> Result { + let identities = transpose_identity(&req.identities); + let addresses = identities + .into_iter() + .flat_map(|(_, addresses)| addresses) + .collect::>(); + + let mut credential_unsigned = Credential::new(&req.who, &req.shard).map_err(|e| { + error!("Generate unsigned credential failed {:?}", e); + Error::RequestVCFailed(Assertion::BRC20AmountHolder, e.into_error_detail()) + })?; + let mut client = GeniidataClient::new() + .map_err(|e| Error::RequestVCFailed(Assertion::BRC20AmountHolder, e))?; + let response = client.create_brc20_amount_holder_sum(addresses).map_err(|e| { + Error::RequestVCFailed( + Assertion::BRC20AmountHolder, + ErrorDetail::DataProviderError(ErrorString::truncate_from( + format!("{e:?}").as_bytes().to_vec(), + )), + ) + })?; + credential_unsigned.update_brc20_amount_holder_credential(&response); + + Ok(credential_unsigned) +} diff --git a/tee-worker/litentry/core/assertion-build/src/brc20/mod.rs b/tee-worker/litentry/core/assertion-build/src/brc20/mod.rs new file mode 100644 index 0000000000..028dceb3dc --- /dev/null +++ b/tee-worker/litentry/core/assertion-build/src/brc20/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +pub mod amount_holder; diff --git a/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs b/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs index fafa1bb7ba..3b16bebcea 100644 --- a/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs +++ b/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs @@ -125,13 +125,14 @@ mod tests { let req: AssertionBuildRequest = AssertionBuildRequest { shard: ShardIdentifier::default(), signer: AccountId::from([0; 32]), - enclave_account: AccountId::from([0; 32]), who: AccountId::from([0; 32]).into(), assertion: Assertion::GenericDiscordRole(GenericDiscordRoleType::Contest( ContestType::Legend, )), identities, top_hash: Default::default(), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, maybe_key: None, req_ext_hash: Default::default(), }; @@ -167,13 +168,14 @@ mod tests { let req: AssertionBuildRequest = AssertionBuildRequest { shard: ShardIdentifier::default(), signer: AccountId::from([0; 32]), - enclave_account: AccountId::from([0; 32]), who: AccountId::from([0; 32]).into(), assertion: Assertion::GenericDiscordRole(GenericDiscordRoleType::SoraQuiz( SoraQuizType::Attendee, )), identities, top_hash: Default::default(), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, maybe_key: None, req_ext_hash: Default::default(), }; diff --git a/tee-worker/litentry/core/assertion-build/src/lib.rs b/tee-worker/litentry/core/assertion-build/src/lib.rs index 63fb2bb6ea..297333b264 100644 --- a/tee-worker/litentry/core/assertion-build/src/lib.rs +++ b/tee-worker/litentry/core/assertion-build/src/lib.rs @@ -46,29 +46,39 @@ pub mod a3; pub mod a6; pub mod a8; pub mod achainable; +pub mod brc20; pub mod generic_discord_role; pub mod holding_time; +pub mod lit_staking; pub mod nodereal; pub mod oneblock; pub mod vip3; use blake2_rfc::blake2b::Blake2b; use itp_types::AccountId; -use itp_utils::stringify::account_id_to_string; +use itp_utils::stringify::{account_id_to_string, account_id_to_string_without_prefix}; use lc_credentials::Credential; use lc_data_providers::achainable::web3_network_to_chain; use lc_stf_task_sender::AssertionBuildRequest; use litentry_primitives::{ - AchainableAmount, AchainableAmountHolding, AchainableAmountToken, AchainableAmounts, - AchainableBasic, AchainableBetweenPercents, AchainableDate, AchainableDateInterval, - AchainableDatePercent, AchainableParams, AchainableToken, Assertion, ErrorDetail, ErrorString, - Identity, IdentityNetworkTuple, IntoErrorDetail, OneBlockCourseType, ParameterString, - VCMPError as Error, Web3Network, ASSERTION_FROM_DATE, + p2pkh_address, p2sh_address, p2tr_address, p2wpkh_address, AchainableAmount, + AchainableAmountHolding, AchainableAmountToken, AchainableAmounts, AchainableBasic, + AchainableBetweenPercents, AchainableDate, AchainableDateInterval, AchainableDatePercent, + AchainableParams, AchainableToken, Assertion, ErrorDetail, ErrorString, Identity, + IdentityNetworkTuple, IntoErrorDetail, OneBlockCourseType, ParameterString, VCMPError as Error, + Web3Network, ASSERTION_FROM_DATE, }; use log::*; use rust_base58::ToBase58; use ss58_registry::Ss58AddressFormat; -use std::{collections::HashSet, format, string::String, sync::Arc, vec, vec::Vec}; +use std::{ + collections::HashSet, + format, + string::{String, ToString}, + sync::Arc, + vec, + vec::Vec, +}; pub type Result = core::result::Result; @@ -97,6 +107,12 @@ pub fn transpose_identity( addresses.push((address, n)); networks_set.insert(n); }, + Identity::Bitcoin(address) => { + let address = account_id_to_string_without_prefix(address.as_ref()); + let address = pubkey_to_address(&n, &address); + addresses.push((address, n)); + networks_set.insert(n); + }, _ => {}, }; }); @@ -148,6 +164,25 @@ pub fn ss58_address_of( Ok(bytes.to_base58()) } +fn pubkey_to_address(network: &Web3Network, pubkey: &str) -> String { + match network { + Web3Network::BitcoinP2tr => p2tr_address(pubkey), + Web3Network::BitcoinP2pkh => p2pkh_address(pubkey), + Web3Network::BitcoinP2sh => p2sh_address(pubkey), + Web3Network::BitcoinP2wpkh => p2wpkh_address(pubkey), + Web3Network::BitcoinP2wsh => "".to_string(), + Web3Network::Polkadot + | Web3Network::Kusama + | Web3Network::Litentry + | Web3Network::Litmus + | Web3Network::LitentryRococo + | Web3Network::Khala + | Web3Network::SubstrateTestnet + | Web3Network::Ethereum + | Web3Network::Bsc => "".to_string(), + } +} + #[cfg(test)] mod tests { use super::*; @@ -214,4 +249,42 @@ mod tests { ); assert_eq!(result.get(4).unwrap(), &(Web3Network::Bsc, vec![[4u8; 20].to_hex()])); } + + #[test] + fn pubkey_to_address_works() { + // p2wpkh + let addr = "bc1qlht0muueu6wln5qqwtvczjepnfeerpaw480067"; + let pubkey = "0272cbf3e56e238897ca9ee9ca9594a82803cfdf19121bd939cbe3f2e1bcaffc7b"; + let network = Web3Network::BitcoinP2wpkh; + let gen_addr = pubkey_to_address(&network, &pubkey); + assert_eq!(addr, gen_addr); + + // p2sh + let addr = "35KQSeZpaABvNWmKAMXo7mAAtXZBqCd4sw"; + let pubkey = "02e46883d2101f09e875dd4a67ee5c2dce9d821b9a610a7e12ab0de7494b19b7d0"; + let network = Web3Network::BitcoinP2sh; + let gen_addr = pubkey_to_address(&network, &pubkey); + assert_eq!(addr, gen_addr); + + // p2tr + let addr = "bc1pwgqves622fs5s42h4sr8hu9y6ej232hga8uxal7xgkcsy8a3ryqqvgku7t"; + let pubkey = "031d867537093a8eaace96717ba0aa226a5bf368c6c84ca5dfb214d380bc91afbe"; + let network = Web3Network::BitcoinP2tr; + let gen_addr = pubkey_to_address(&network, &pubkey); + assert_eq!(addr, gen_addr); + + // p2pkh + let addr = "1CY8nArJbvLSHQmKp3SiG8T5WSBfnMJpJx"; + let pubkey = "02784a686e5ffc74d713f66cb8885d6b75c062c61df5f6de8f86f07c340ebc183c"; + let network = Web3Network::BitcoinP2pkh; + let gen_addr = pubkey_to_address(&network, &pubkey); + assert_eq!(addr, gen_addr); + + // p2wsh + let addr = ""; + let pubkey = "02e46883d2101f09e875dd4a67ee5c2dce9d821b9a610a7e12ab0de7494b19b7d0"; + let network = Web3Network::BitcoinP2wsh; + let gen_addr = pubkey_to_address(&network, &pubkey); + assert_eq!(addr, gen_addr); + } } diff --git a/tee-worker/litentry/core/assertion-build/src/lit_staking.rs b/tee-worker/litentry/core/assertion-build/src/lit_staking.rs new file mode 100644 index 0000000000..223c0f41a0 --- /dev/null +++ b/tee-worker/litentry/core/assertion-build/src/lit_staking.rs @@ -0,0 +1,227 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::*; +use codec::Decode; +use frame_support::{StorageHasher, Twox64Concat}; +use http::header::CONNECTION; +use itc_rest_client::{ + error::Error as RestClientError, + http_client::{DefaultSend, HttpClient}, + rest_client::{Headers, RestClient}, + RestPath, RestPost, +}; +use itp_stf_primitives::types::AccountId; +use itp_utils::hex_display::AsBytesRef; +use lc_credentials::litentry_profile::lit_staking::UpdateLITStakingAmountCredential; +use lc_data_providers::build_client; +use litentry_primitives::ParentchainBalance; +use pallet_parachain_staking::types::Delegator; +use serde::{Deserialize, Serialize}; +use std::string::ToString; + +const LIT_TOKEN_DECIMALS: u128 = 1_000_000_000_000; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct JsonRPCRequest { + id: usize, + jsonrpc: String, + method: String, + params: Vec, +} + +impl JsonRPCRequest { + pub fn state_getstorage(params: &str) -> Self { + Self { + id: 1, + jsonrpc: "2.0".to_string(), + method: "state_getStorage".to_string(), + params: vec![params.to_string()], + } + } +} + +impl RestPath for JsonRPCRequest { + fn get_path(path: String) -> core::result::Result { + Ok(path) + } +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct JsonRPCResponse { + pub id: usize, + pub jsonrpc: String, + pub result: Option, +} + +pub struct LitentryStakingClient { + client: RestClient>, +} + +impl Default for LitentryStakingClient { + fn default() -> Self { + Self::new() + } +} + +impl LitentryStakingClient { + pub fn new() -> Self { + let mut headers = Headers::new(); + headers.insert(CONNECTION.as_str(), "close"); + let client = build_client("https://litentry-rpc.dwellir.com:443", headers); + LitentryStakingClient { client } + } + + pub fn send_request(&mut self, data: &JsonRPCRequest) -> Result { + self.client + .post_capture::(String::default(), data) + .map_err(|e| { + Error::RequestVCFailed( + Assertion::LITStaking, + ErrorDetail::DataProviderError(ErrorString::truncate_from( + format!("{e:?}").as_bytes().to_vec(), + )), + ) + }) + } +} + +pub trait QueryParachainStaking { + fn query_delegator_state(&mut self, key: &str) -> Result>; +} + +impl QueryParachainStaking for LitentryStakingClient { + fn query_delegator_state(&mut self, key: &str) -> Result> { + let data = JsonRPCRequest::state_getstorage(key); + let res = self.send_request(&data)?; + + Ok(res.result) + } +} + +pub struct DelegatorState; +impl DelegatorState { + pub fn query_lit_staking( + &mut self, + client: &mut LitentryStakingClient, + identities: &[Identity], + ) -> Result { + let mut total_staking_amount = 0_u128; + + for identity in identities { + let storage_key = DelegatorState::delegator_state_storage_key(identity)?; + let storage_in_hex = client.query_delegator_state(&storage_key)?; + + if let Some(storage_in_hex) = storage_in_hex { + let delegator = DelegatorState::decode_delegator(&storage_in_hex)?; + let total = delegator.total; + + total_staking_amount += total; + } + } + + Ok(total_staking_amount / LIT_TOKEN_DECIMALS) + } + + fn delegator_state_storage_key(identity: &Identity) -> Result { + // encoded partial key: ParachainStaking DelegatorState + // 0xa686a3043d0adcf2fa655e57bc595a78131da8bc800de21b19b3ba9ed33cfacc + let params = "0xa686a3043d0adcf2fa655e57bc595a78131da8bc800de21b19b3ba9ed33cfacc"; + let acc = identity + .to_account_id() + .ok_or(Error::RequestVCFailed(Assertion::LITStaking, ErrorDetail::ParseError))?; + let cocat = Twox64Concat::hash(acc.as_ref()); + + Ok(params.to_string() + &hex::encode(&cocat)) + } + + fn decode_delegator(storage_in_hex: &str) -> Result> { + // Remove 0x + if let Some(storage_in_hex_without_prefix) = storage_in_hex.strip_prefix("0x") { + if let Ok(decoded) = hex::decode(storage_in_hex_without_prefix) { + if let Ok(delegator) = + Delegator::::decode(&mut decoded.as_bytes_ref()) + { + return Ok(delegator) + } + } + } + + Err(Error::RequestVCFailed( + Assertion::LITStaking, + ErrorDetail::DataProviderError(ErrorString::truncate_from( + "Invalid ParachainStaking DelegatorState".as_bytes().to_vec(), + )), + )) + } +} + +pub fn build(req: &AssertionBuildRequest) -> Result { + debug!("Assertion building LIT staking amount"); + + let mut identities = vec![]; + req.identities.iter().for_each(|identity| { + identities.push(identity.0.clone()); + }); + + let mut client = LitentryStakingClient::new(); + let staking_amount = DelegatorState.query_lit_staking(&mut client, &identities)?; + match Credential::new(&req.who, &req.shard) { + Ok(mut credential_unsigned) => { + credential_unsigned.update_lit_staking_amount(staking_amount); + Ok(credential_unsigned) + }, + Err(e) => { + error!("Generate unsigned credential failed {:?}", e); + Err(Error::RequestVCFailed(Assertion::LITStaking, e.into_error_detail())) + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::{crypto::Ss58Codec, ed25519}; + + #[test] + fn decode_delegator_works() { + let delegator_in_hex = "0xa06c6b3f286fc8fe59cfe3d85ea8df043ddc08742ff9cfe2ed3d6ba1da1f4b4a18149ccce9d526a65ba54fccc24f5d1dee62f9b87915d4004eb932959d468a9e6200f008d0236e040000000000000000001acba651394c1b8d65d06702662921077e58b09d0fc15f3126a427144f83c95f00b0b522399003000000000000000000223188d5f28ee27f7e9067e89bc52fca8f1da20c6a7548a21cef18d8934f820f006048774c3a0400000000000000000024f07a3858f8d4dece9806b6c7e4ee165ba8c98e6ff4d066dd0284d46aee967d00e046350eb003000000000000000000e0d0031d0a450dfc4bb16333fe575dfb8452bb0f79c768181478190a5d9a653f00b064d8ab8103000000000000000000fc7a9dd32be14db4695555aa9a2abd240a8c2160f84ccb403a985701dd13fe5000409dde18410300000000000000000000d04f567cab160000000000000000000000000000000000000000000000000000"; + let d = DelegatorState::decode_delegator(delegator_in_hex); + assert!(d.is_ok()); + } + + #[test] + fn decode_delegator_should_fail_works() { + let delegator_in_hex = "0xa0"; + let d = DelegatorState::decode_delegator(delegator_in_hex); + assert!(d.is_err()); + } + + #[test] + fn delegator_state_storage_key_works() { + let address = "4A2nt96tH4ej9B3S9sagrKdJubYvu8No4hE9kjjrXMWvGLYW"; + + let pubkey = ed25519::Public::from_ss58check(address).unwrap(); + let acc = AccountId::new(pubkey.0); + let identity = Identity::from(acc); + + let storage_key = DelegatorState::delegator_state_storage_key(&identity).unwrap(); + let target_key = "0xa686a3043d0adcf2fa655e57bc595a78131da8bc800de21b19b3ba9ed33cfacc01e6a2b4eb558329a06c6b3f286fc8fe59cfe3d85ea8df043ddc08742ff9cfe2ed3d6ba1da1f4b4a".to_string(); + assert_eq!(storage_key, target_key); + } +} diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs new file mode 100644 index 0000000000..30759ff7a7 --- /dev/null +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs @@ -0,0 +1,334 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use core::result; + +use crate::*; +use lc_credentials::{ + nodereal::amount_holding::evm_amount_holding::{ + EVMAmountHoldingAssertionUpdate, EVMTokenAddress, TokenDecimals, + }, + Credential, +}; +use lc_data_providers::{ + nodereal_jsonrpc::{ + FungibleApiList, GetTokenBalance20Param, NoderealChain, NoderealJsonrpcClient, + }, + Error as DataProviderError, +}; +use litentry_primitives::EVMTokenType; + +fn get_holding_balance( + token_type: EVMTokenType, + addresses: Vec<(Web3Network, String)>, +) -> result::Result { + let mut eth_client = NoderealJsonrpcClient::new(NoderealChain::Eth); + let mut bsc_client = NoderealJsonrpcClient::new(NoderealChain::Bsc); + let mut total_balance = 0_f64; + + let decimals = token_type.get_decimals(); + + for address in addresses.iter() { + let param = GetTokenBalance20Param { + contract_address: token_type.get_address(address.0).unwrap_or_default().into(), + address: address.1.clone(), + block_number: "latest".into(), + }; + match address.0 { + Web3Network::Bsc => match bsc_client.get_token_balance_20(¶m) { + Ok(balance) => { + total_balance += balance; + }, + Err(err) => return Err(err), + }, + Web3Network::Ethereum => match eth_client.get_token_balance_20(¶m) { + Ok(balance) => { + total_balance += balance; + }, + Err(err) => return Err(err), + }, + _ => {}, + } + } + + Ok(total_balance / decimals) +} + +pub fn build(req: &AssertionBuildRequest, token_type: EVMTokenType) -> Result { + debug!("evm amount holding: {:?}", token_type); + + let identities: Vec<(Web3Network, Vec)> = transpose_identity(&req.identities); + let addresses = identities + .into_iter() + .filter(|(newtwork_type, _)| newtwork_type.is_evm()) + .flat_map(|(newtwork_type, addresses)| { + addresses.into_iter().map(move |address| (newtwork_type, address)) + }) + .collect::>(); + + let result = get_holding_balance(token_type.clone(), addresses).map_err(|e| { + Error::RequestVCFailed( + Assertion::EVMAmountHolding(token_type.clone()), + ErrorDetail::DataProviderError(ErrorString::truncate_from( + format!("{e:?}").as_bytes().to_vec(), + )), + ) + }); + + match result { + Ok(value) => match Credential::new(&req.who, &req.shard) { + Ok(mut credential_unsigned) => { + credential_unsigned.update_evm_amount_holding_assertion(token_type, value); + Ok(credential_unsigned) + }, + Err(e) => { + error!("Generate unsigned credential failed {:?}", e); + Err(Error::RequestVCFailed( + Assertion::EVMAmountHolding(token_type), + e.into_error_detail(), + )) + }, + }, + Err(e) => Err(e), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itp_stf_primitives::types::ShardIdentifier; + use itp_utils::hex::decode_hex; + use lc_credentials::assertion_logic::{AssertionLogic, Op}; + use lc_data_providers::GLOBAL_DATA_PROVIDER_CONFIG; + use lc_mock_server::run; + + fn create_ton_token_assertion_logic() -> Box { + Box::new(AssertionLogic::Item { src: "$token".into(), op: Op::Equal, dst: "TON".into() }) + } + + fn create_ton_network_assertion_logic() -> Box { + Box::new(AssertionLogic::Or { + items: vec![ + Box::new(AssertionLogic::And { + items: vec![ + Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "ethereum".into(), + }), + Box::new(AssertionLogic::Item { + src: "$address".into(), + op: Op::Equal, + dst: "0x582d872a1b094fc48f5de31d3b73f2d9be47def1".into(), + }), + ], + }), + Box::new(AssertionLogic::And { + items: vec![ + Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "bsc".into(), + }), + Box::new(AssertionLogic::Item { + src: "$address".into(), + op: Op::Equal, + dst: "0x76a797a59ba2c17726896976b7b3747bfd1d220f".into(), + }), + ], + }), + ], + }) + } + + fn init() { + let _ = env_logger::builder().is_test(true).try_init(); + let url = run(0).unwrap() + "/nodereal_jsonrpc/"; + GLOBAL_DATA_PROVIDER_CONFIG + .write() + .unwrap() + .set_nodereal_api_key("d416f55179dbd0e45b1a8ed030e3".into()); + GLOBAL_DATA_PROVIDER_CONFIG + .write() + .unwrap() + .set_nodereal_api_chain_network_url(url); + } + + #[test] + fn build_evm_amount_holding_works() { + init(); + let identities: Vec = vec![ + (Identity::Evm([0; 20].into()), vec![Web3Network::Ethereum]), + (Identity::Evm([0; 20].into()), vec![Web3Network::Ethereum, Web3Network::Bsc]), + ]; + + let req: AssertionBuildRequest = AssertionBuildRequest { + shard: ShardIdentifier::default(), + signer: AccountId::from([0; 32]), + who: AccountId::from([0; 32]).into(), + assertion: Assertion::EVMAmountHolding(EVMTokenType::Ton), + identities, + top_hash: Default::default(), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, + maybe_key: None, + req_ext_hash: Default::default(), + }; + + match build(&req, EVMTokenType::Ton) { + Ok(credential) => { + log::info!("build EVMAmount holding done"); + assert_eq!( + *(credential.credential_subject.assertions.first().unwrap()), + AssertionLogic::And { + items: vec![ + create_ton_token_assertion_logic(), + create_ton_network_assertion_logic(), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::GreaterEq, + dst: "0".into() + }), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::LessThan, + dst: "1".into() + }) + ] + } + ); + assert_eq!(*(credential.credential_subject.values.first().unwrap()), false); + }, + Err(e) => { + panic!("build EVMAmount holding failed with error {:?}", e); + }, + } + } + + #[test] + fn build_evm_amount_holding_lt_min_works() { + init(); + let address = decode_hex("0x85be4e2ccc9c85be8783798b6e8a101bdac6467f".as_bytes().to_vec()) + .unwrap() + .as_slice() + .try_into() + .unwrap(); + let identities: Vec = + vec![(Identity::Evm(address), vec![Web3Network::Ethereum])]; + + let req: AssertionBuildRequest = AssertionBuildRequest { + shard: ShardIdentifier::default(), + signer: AccountId::from([0; 32]), + who: AccountId::from([0; 32]).into(), + assertion: Assertion::EVMAmountHolding(EVMTokenType::Ton), + identities, + top_hash: Default::default(), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, + maybe_key: None, + req_ext_hash: Default::default(), + }; + + match build(&req, EVMTokenType::Ton) { + Ok(credential) => { + log::info!("build EVMAmount holding done"); + assert_eq!( + *(credential.credential_subject.assertions.first().unwrap()), + AssertionLogic::And { + items: vec![ + create_ton_token_assertion_logic(), + create_ton_network_assertion_logic(), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::GreaterEq, + dst: "100".into() + }), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::LessThan, + dst: "200".into() + }) + ] + } + ); + assert_eq!(*(credential.credential_subject.values.first().unwrap()), true); + }, + Err(e) => { + panic!("build EVMAmount holding failed with error {:?}", e); + }, + } + } + + #[test] + fn build_evm_amount_holding_gte_max_works() { + init(); + let address = decode_hex("0x90d53026a47ac20609accc3f2ddc9fb9b29bb310".as_bytes().to_vec()) + .unwrap() + .as_slice() + .try_into() + .unwrap(); + let identities: Vec = + vec![(Identity::Evm(address), vec![Web3Network::Ethereum])]; + + let req: AssertionBuildRequest = AssertionBuildRequest { + shard: ShardIdentifier::default(), + signer: AccountId::from([0; 32]), + who: AccountId::from([0; 32]).into(), + assertion: Assertion::EVMAmountHolding(EVMTokenType::Ton), + identities, + top_hash: Default::default(), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, + maybe_key: None, + req_ext_hash: Default::default(), + }; + + match build(&req, EVMTokenType::Ton) { + Ok(credential) => { + log::info!("build EVMAmount holding done"); + assert_eq!( + *(credential.credential_subject.assertions.first().unwrap()), + AssertionLogic::And { + items: vec![ + create_ton_token_assertion_logic(), + create_ton_network_assertion_logic(), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::GreaterEq, + dst: "0".into() + }), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::LessThan, + dst: "1".into() + }) + ] + } + ); + assert_eq!(*(credential.credential_subject.values.first().unwrap()), false); + }, + Err(e) => { + panic!("build EVMAmount holding failed with error {:?}", e); + }, + } + } +} diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/mod.rs new file mode 100644 index 0000000000..09c8ec0fdf --- /dev/null +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +pub mod evm_amount_holding; diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs new file mode 100644 index 0000000000..240374d32d --- /dev/null +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs @@ -0,0 +1,42 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use lc_credentials::nodereal::crypto_summary::{ + summary::CryptoSummaryCredentialUpdate, CryptoSummaryClient, +}; + +use crate::*; + +pub fn build(req: &AssertionBuildRequest) -> Result { + let identities = transpose_identity(&req.identities); + let (txs, summary) = CryptoSummaryClient::new() + .logic(&identities) + .map_err(|e| Error::RequestVCFailed(Assertion::CryptoSummary, e))?; + + let mut credential_unsigned = Credential::new(&req.who, &req.shard).map_err(|e| { + error!("Generate unsigned credential failed {:?}", e); + Error::RequestVCFailed(Assertion::CryptoSummary, e.into_error_detail()) + })?; + credential_unsigned.update_crypto_summary_credential(txs, summary); + + Ok(credential_unsigned) +} diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/mod.rs index 41346f95d0..84a4bff2f7 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/mod.rs @@ -20,5 +20,7 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam #[cfg(all(not(feature = "std"), feature = "sgx"))] extern crate sgx_tstd as std; +pub mod amount_holding; pub mod bnb_domain; +pub mod crypto_summary; pub mod nft_holder; diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs index f5838e1c4d..f4d3b13eb6 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs @@ -24,7 +24,7 @@ use core::result; use lc_credentials::nodereal::nft_holder::weirdo_ghost_gang_holder::WeirdoGhostGangHolderAssertionUpdate; use lc_data_providers::nodereal_jsonrpc::{ - GetTokenBalance721Param, NftApiList, NoderealChain, NoderealJsonrpcClient, NoderealNetwork, + GetTokenBalance721Param, NftApiList, NoderealChain, NoderealJsonrpcClient, }; use crate::*; @@ -58,7 +58,7 @@ pub fn build(req: &AssertionBuildRequest) -> Result { debug!("WeirdoGhostGang holder"); let mut has_nft = false; - let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth, NoderealNetwork::Mainnet); + let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth); let identities: Vec<(Web3Network, Vec)> = transpose_identity(&req.identities); let addresses = identities @@ -137,11 +137,12 @@ mod tests { let req: AssertionBuildRequest = AssertionBuildRequest { shard: ShardIdentifier::default(), signer: AccountId::from([0; 32]), - enclave_account: AccountId::from([0; 32]), who: AccountId::from([0; 32]).into(), assertion: Assertion::WeirdoGhostGangHolder, identities, top_hash: Default::default(), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, maybe_key: None, req_ext_hash: Default::default(), }; diff --git a/tee-worker/litentry/core/credentials/src/achainable/bab_holder.rs b/tee-worker/litentry/core/credentials/src/achainable/bab_holder.rs new file mode 100644 index 0000000000..2fdf6c5ab9 --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/achainable/bab_holder.rs @@ -0,0 +1,40 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{ + assertion_logic::{AssertionLogic, Op}, + Credential, +}; + +const BAB_HOLDER_DESCRIPTIONS: &str = "You are a holder of a certain kind of NFT"; +const BAB_HOLDER_TYPE: &str = "NFT Holder"; +const BAB_HOLDER_BREAKDOWN: &str = "is_bab_holder"; + +pub trait UpdateBABHolder { + fn update_bab_holder(&mut self, is_bab_holder: bool); +} + +impl UpdateBABHolder for Credential { + fn update_bab_holder(&mut self, is_bab_holder: bool) { + let bab_holder = AssertionLogic::new_item(BAB_HOLDER_BREAKDOWN, Op::Equal, "true"); + + let assertion = AssertionLogic::new_and().add_item(bab_holder); + self.credential_subject.assertions.push(assertion); + self.credential_subject.values.push(is_bab_holder); + + self.add_subject_info(BAB_HOLDER_DESCRIPTIONS, BAB_HOLDER_TYPE); + } +} diff --git a/tee-worker/litentry/core/credentials/src/achainable/lit_holding_amount.rs b/tee-worker/litentry/core/credentials/src/achainable/lit_holding_amount.rs new file mode 100644 index 0000000000..60e8434f74 --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/achainable/lit_holding_amount.rs @@ -0,0 +1,75 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{ + assertion_logic::AssertionLogic, nodereal::bnb_domain::RangeCredentialDetail, Credential, +}; +use std::vec::Vec; + +// Type / Info +const LIT_HOLDING_AMOUNT_INFO: (&str, &str) = + ("Token holding amount", "The amount of a particular token you are holding"); +const LIT_HOLDING_AMOUNT_BREAKDOWN: &str = "lit_holding_amount"; +const LIT_BALANCE_RANGE: [usize; 10] = [0, 1, 50, 100, 200, 500, 800, 1200, 1600, 3000]; + +pub struct LitHoldingAmount { + pub amount: usize, +} + +impl LitHoldingAmount { + pub fn new(amount: usize) -> Self { + Self { amount } + } +} + +impl RangeCredentialDetail for LitHoldingAmount { + fn get_info(&self) -> (&'static str, &'static str) { + LIT_HOLDING_AMOUNT_INFO + } + + fn get_range(&self) -> Vec { + LIT_BALANCE_RANGE.to_vec() + } + + fn get_last_value(&self) -> usize { + 3000 + } + + fn get_breakdown(&self) -> &'static str { + LIT_HOLDING_AMOUNT_BREAKDOWN + } +} + +pub trait AchainableLitHoldingAmountUpdate { + fn update_lit_holding_amount(&mut self, balance: usize); +} + +impl AchainableLitHoldingAmountUpdate for Credential { + fn update_lit_holding_amount(&mut self, amount: usize) { + let lit_holding_amount = LitHoldingAmount::new(amount); + let items = lit_holding_amount.get_assertion_items(amount); + let mut assertion = AssertionLogic::new_and(); + for item in items { + assertion = assertion.add_item(item); + } + + self.credential_subject.assertions.push(assertion); + self.credential_subject.values.push(true); + + let info = lit_holding_amount.get_info(); + self.add_subject_info(info.1, info.0); + } +} diff --git a/tee-worker/litentry/core/credentials/src/achainable/mod.rs b/tee-worker/litentry/core/credentials/src/achainable/mod.rs index 23e93ac365..a795bf7892 100644 --- a/tee-worker/litentry/core/credentials/src/achainable/mod.rs +++ b/tee-worker/litentry/core/credentials/src/achainable/mod.rs @@ -15,3 +15,6 @@ // along with Litentry. If not, see . pub mod amount_holding_time; +pub mod bab_holder; +pub mod lit_holding_amount; +pub mod uniswap_user; diff --git a/tee-worker/litentry/core/credentials/src/achainable/uniswap_user.rs b/tee-worker/litentry/core/credentials/src/achainable/uniswap_user.rs new file mode 100644 index 0000000000..18f7855687 --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/achainable/uniswap_user.rs @@ -0,0 +1,48 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{ + assertion_logic::{AssertionLogic, Op}, + Credential, +}; +use std::string::ToString; + +const UNISWAP_USER_DESCRIPTIONS: &str = + "You are a trader or liquidity provider of Uniswap V2 or V3. +Uniswap V2 Factory Contract: 0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f. +Uniswap V3 Factory Contract: 0x1f98431c8ad98523631ae4a59f267346ea31f984."; +const UNISWAP_USER_TYPE: &str = "Uniswap V2/V3 User"; + +pub trait UpdateUniswapUser { + fn update_uniswap_user(&mut self, is_v2_holder: bool, is_v3_holder: bool); +} + +impl UpdateUniswapUser for Credential { + fn update_uniswap_user(&mut self, is_v2_holder: bool, is_v3_holder: bool) { + let uniswap_v2 = + AssertionLogic::new_item("$is_uniswap_v2_user", Op::Equal, &is_v2_holder.to_string()); + let uniswap_v3 = + AssertionLogic::new_item("$is_uniswap_v3_user", Op::Equal, &is_v3_holder.to_string()); + + let assertion = AssertionLogic::new_and().add_item(uniswap_v2).add_item(uniswap_v3); + self.credential_subject.assertions.push(assertion); + + // Always true + self.credential_subject.values.push(true); + + self.add_subject_info(UNISWAP_USER_DESCRIPTIONS, UNISWAP_USER_TYPE); + } +} diff --git a/tee-worker/litentry/core/credentials/src/brc20/amount_holder.rs b/tee-worker/litentry/core/credentials/src/brc20/amount_holder.rs new file mode 100644 index 0000000000..b70173ac3b --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/brc20/amount_holder.rs @@ -0,0 +1,184 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{ + assertion_logic::{AssertionLogic, Op}, + litentry_profile::{BalanceRange, BalanceRangeIndex}, + Credential, +}; +use lc_data_providers::geniidata::ResponseItem; +use std::vec::Vec; + +const VC_BRC20_AMOUNT_HOLDER_DESCRIPTIONS: &str = + "The amount of a particular token you are holding"; +const VC_BRC20_AMOUNT_HOLDER_TYPE: &str = "Token holding amount list"; + +// Keep all name in lowercase here by purpose +const BRC20_TOKENS: [&str; 7] = ["ordi", "sats", "rats", "mmss", "long", "cats", "btcs"]; +const ORDI_TOKEN_BALANCE_RANGE: [f64; 8] = [0.0, 1.0, 5.0, 20.0, 50.0, 100.0, 200.0, 500.0]; +const SATS_TOKEN_BALANCE_RANGE: [f64; 8] = [ + 0.0, + 40_000_000.0, + 200_000_000.0, + 500_000_000.0, + 1_000_000_000.0, + 2_000_000_000.0, + 4_000_000_000.0, + 6_000_000_000.0, +]; +const RATS_TOKEN_BALANCE_RANGE: [f64; 8] = + [0.0, 40_000.0, 200_000.0, 1_000_000.0, 2_000_000.0, 4_000_000.0, 10_000_000.0, 20_000_000.0]; +const MMSS_TOKEN_BALANCE_RANGE: [f64; 8] = [0.0, 20.0, 50.0, 100.0, 200.0, 500.0, 1000.0, 2000.0]; +const LONG_TOKEN_BALANCE_RANGE: [f64; 8] = [0.0, 20.0, 50.0, 200.0, 500.0, 1000.0, 2000.0, 3000.0]; +const CATS_TOKEN_BALANCE_RANGE: [f64; 7] = + [0.0, 10_000.0, 50_000.0, 100_000.0, 200_000.0, 500_000.0, 800_000.0]; +const BTCS_TOKEN_BALANCE_RANGE: [f64; 8] = [0.0, 5.0, 20.0, 50.0, 100.0, 200.0, 500.0, 800.0]; + +enum BRC20Token { + Ordi, + Sats, + Rats, + Mmss, + Long, + Cats, + Btcs, + Unknown, +} + +struct AssertionKeys { + token: &'static str, + holding_amount: &'static str, +} + +const ASSERTION_KEYS: AssertionKeys = + AssertionKeys { token: "$token", holding_amount: "$holding_amount" }; + +pub trait BRC20AmountHolderCredential { + fn update_brc20_amount_holder_credential(&mut self, response_items: &[ResponseItem]); +} + +impl BRC20AmountHolderCredential for Credential { + fn update_brc20_amount_holder_credential(&mut self, response_items: &[ResponseItem]) { + for item in response_items { + if BRC20_TOKENS.contains(&item.tick.to_lowercase().as_str()) { + let token = tick_to_brctoken(&item.tick); + let balance: f64 = item.overall_balance.parse().unwrap_or(0.0); + update_assertion(token, balance, self); + } + } + + self.add_subject_info(VC_BRC20_AMOUNT_HOLDER_DESCRIPTIONS, VC_BRC20_AMOUNT_HOLDER_TYPE); + } +} + +// TODO: the following part is exactly the same structure from 'token_balance.rs'. +// Anyway the refactor is planned later. So continue using the same mechanism. +fn update_assertion(token: BRC20Token, balance: f64, credential: &mut Credential) { + let mut assertion = AssertionLogic::new_and(); + + assertion = assertion.add_item(AssertionLogic::new_item( + ASSERTION_KEYS.token, + Op::Equal, + brctoken_to_tick(&token), + )); + + let range = get_balance_range(&token); + let index = BalanceRange::index(&range, balance); + match index { + Some(index) => { + let min = format!("{}", range[index]); + let max = format!("{}", range[index + 1]); + let min_item = + AssertionLogic::new_item(ASSERTION_KEYS.holding_amount, Op::GreaterEq, &min); + let max_item = + AssertionLogic::new_item(ASSERTION_KEYS.holding_amount, Op::LessThan, &max); + + assertion = assertion.add_item(min_item); + assertion = assertion.add_item(max_item); + + credential.credential_subject.values.push(index != 0); + }, + None => { + let min_item = AssertionLogic::new_item( + ASSERTION_KEYS.holding_amount, + Op::GreaterEq, + &format!("{}", get_token_range_last(&token)), + ); + assertion = assertion.add_item(min_item); + + credential.credential_subject.values.push(true); + }, + } + + credential.credential_subject.assertions.push(assertion); +} + +fn tick_to_brctoken(tick: &str) -> BRC20Token { + match tick { + "ordi" => BRC20Token::Ordi, + "sats" => BRC20Token::Sats, + "rats" => BRC20Token::Rats, + "MMSS" => BRC20Token::Mmss, + "long" => BRC20Token::Long, + "cats" => BRC20Token::Cats, + "BTCs" => BRC20Token::Btcs, + _ => BRC20Token::Unknown, + } +} + +fn brctoken_to_tick(token: &BRC20Token) -> &'static str { + match token { + BRC20Token::Ordi => "$ordi", + BRC20Token::Sats => "$sats", + BRC20Token::Rats => "$rats", + BRC20Token::Mmss => "$MMSS", + BRC20Token::Long => "$long", + BRC20Token::Cats => "$cats", + BRC20Token::Btcs => "$BTCs", + _ => "Unknown", + } +} + +fn get_balance_range(token: &BRC20Token) -> Vec { + match token { + BRC20Token::Ordi => ORDI_TOKEN_BALANCE_RANGE.to_vec(), + BRC20Token::Sats => SATS_TOKEN_BALANCE_RANGE.to_vec(), + BRC20Token::Rats => RATS_TOKEN_BALANCE_RANGE.to_vec(), + BRC20Token::Mmss => MMSS_TOKEN_BALANCE_RANGE.to_vec(), + BRC20Token::Long => LONG_TOKEN_BALANCE_RANGE.to_vec(), + BRC20Token::Cats => CATS_TOKEN_BALANCE_RANGE.to_vec(), + BRC20Token::Btcs => BTCS_TOKEN_BALANCE_RANGE.to_vec(), + _ => { + vec![] + }, + } +} + +fn get_token_range_last(token: &BRC20Token) -> f64 { + match token { + BRC20Token::Ordi => *ORDI_TOKEN_BALANCE_RANGE.last().unwrap_or(&500.0), + BRC20Token::Sats => *SATS_TOKEN_BALANCE_RANGE.last().unwrap_or(&6_000_000_000.0), + BRC20Token::Rats => *RATS_TOKEN_BALANCE_RANGE.last().unwrap_or(&20_000_000.0), + BRC20Token::Mmss => *MMSS_TOKEN_BALANCE_RANGE.last().unwrap_or(&2000.0), + BRC20Token::Long => *LONG_TOKEN_BALANCE_RANGE.last().unwrap_or(&3000.0), + BRC20Token::Cats => *CATS_TOKEN_BALANCE_RANGE.last().unwrap_or(&800_000.0), + BRC20Token::Btcs => *BTCS_TOKEN_BALANCE_RANGE.last().unwrap_or(&800.0), + _ => 0.0, + } +} + +#[cfg(test)] +mod tests {} diff --git a/tee-worker/litentry/core/credentials/src/brc20/mod.rs b/tee-worker/litentry/core/credentials/src/brc20/mod.rs new file mode 100644 index 0000000000..40eee4424d --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/brc20/mod.rs @@ -0,0 +1,17 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +pub mod amount_holder; diff --git a/tee-worker/litentry/core/credentials/src/lib.rs b/tee-worker/litentry/core/credentials/src/lib.rs index cd5ff9cd19..d8fa42071a 100644 --- a/tee-worker/litentry/core/credentials/src/lib.rs +++ b/tee-worker/litentry/core/credentials/src/lib.rs @@ -38,9 +38,9 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam use codec::{Decode, Encode}; use itp_stf_primitives::types::ShardIdentifier; use itp_time_utils::{from_iso8601, now_as_iso8601}; -use itp_types::AccountId; +use itp_types::{AccountId, BlockNumber as SidechainBlockNumber}; use itp_utils::stringify::account_id_to_string; -use litentry_primitives::{Identity, Web3Network}; +use litentry_primitives::{Identity, ParentchainBlockNumber, Web3Network}; use log::*; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; @@ -81,6 +81,7 @@ pub mod litentry_profile; pub mod oneblock; pub mod schema; use assertion_logic::{AssertionLogic, Op}; +pub mod brc20; pub mod generic_discord_role; pub mod nodereal; pub mod vip3; @@ -140,6 +141,7 @@ pub struct CredentialSubject { pub description: String, #[serde(rename = "type")] pub types: String, + pub assertion_text: String, /// (Optional) Data source definitions for trusted data providers #[serde(skip_serializing_if = "Option::is_none")] pub data_source: Option>, @@ -157,10 +159,6 @@ impl CredentialSubject { pub fn is_empty(&self) -> bool { self.id.is_empty() } - - pub fn set_endpoint(&mut self, endpoint: String) { - self.endpoint = endpoint; - } } /// Verifiable Credentials JSON Schema 2022, W3C, 8 November 2022 @@ -223,6 +221,9 @@ pub struct Credential { /// The TEE enclave who issued the credential pub issuer: Issuer, pub issuance_date: String, + /// The parachain and sidechain block number on which the state is read and calculated + pub parachain_block_number: ParentchainBlockNumber, + pub sidechain_block_number: SidechainBlockNumber, /// Digital proof with the signature of Issuer #[serde(skip_serializing_if = "Option::is_none")] pub proof: Option, @@ -250,7 +251,7 @@ impl Credential { vc.issuer.mrenclave = shard.encode().to_base58(); vc.issuer.name = LITENTRY_ISSUER_NAME.to_string(); vc.credential_subject.id = - subject.to_did().map_err(|err| Error::ParseError(format!("{}", err)))?; + subject.to_did().map_err(|err| Error::ParseError(err.to_string()))?; vc.issuance_date = now_as_iso8601(); vc.credential_schema = None; vc.proof = None; @@ -319,10 +320,11 @@ impl Credential { return Err(Error::EmptyCredentialIssuer) } - let exported = vc.to_json()?; - if exported.len() > MAX_CREDENTIAL_SIZE { - return Err(Error::CredentialIsTooLong) - } + // TODO: Do we need to set size restrictions + // let exported = vc.to_json()?; + // if exported.len() > MAX_CREDENTIAL_SIZE { + // return Err(Error::CredentialIsTooLong) + // } if vc.proof.is_none() { return Err(Error::InvalidProof) @@ -513,19 +515,6 @@ impl Credential { self.credential_subject.values.push(value); } - pub fn update_uniswap_v23_info(&mut self, v2_user: bool, v3_user: bool) { - let uniswap_v2 = - AssertionLogic::new_item("$is_uniswap_v2_user", Op::Equal, &v2_user.to_string()); - let uniswap_v3 = - AssertionLogic::new_item("$is_uniswap_v3_user", Op::Equal, &v3_user.to_string()); - - let assertion = AssertionLogic::new_and().add_item(uniswap_v2).add_item(uniswap_v3); - self.credential_subject.assertions.push(assertion); - - // Always true - self.credential_subject.values.push(true); - } - pub fn update_class_of_year(&mut self, ret: bool, date: String) { let mut and_logic = AssertionLogic::new_and(); diff --git a/tee-worker/litentry/core/credentials/src/litentry_profile/lit_staking.rs b/tee-worker/litentry/core/credentials/src/litentry_profile/lit_staking.rs new file mode 100644 index 0000000000..cd5ac65ed1 --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/litentry_profile/lit_staking.rs @@ -0,0 +1,75 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{ + assertion_logic::AssertionLogic, nodereal::bnb_domain::RangeCredentialDetail, Credential, +}; +use std::vec::Vec; + +// VC type / info +const LIT_STAKING_INFOS: (&str, &str) = ("LIT staking amount", "The amount of LIT you are staking"); + +// [x-y) +pub const LIT_STAKING_AMOUNT_RANGE: [usize; 10] = [0, 1, 50, 100, 200, 500, 800, 1200, 1600, 3000]; + +pub struct LITStakingAmount { + pub amount: u128, +} + +impl LITStakingAmount { + pub fn new(amount: u128) -> Self { + Self { amount } + } +} + +impl RangeCredentialDetail for LITStakingAmount { + fn get_info(&self) -> (&'static str, &'static str) { + LIT_STAKING_INFOS + } + + fn get_range(&self) -> Vec { + LIT_STAKING_AMOUNT_RANGE.to_vec() + } + + fn get_last_value(&self) -> usize { + 3000 + } + + fn get_breakdown(&self) -> &'static str { + "$lit_staking_amount" + } +} + +pub trait UpdateLITStakingAmountCredential { + fn update_lit_staking_amount(&mut self, amount: u128); +} + +impl UpdateLITStakingAmountCredential for Credential { + fn update_lit_staking_amount(&mut self, amount: u128) { + let lit_staking = LITStakingAmount::new(amount); + let items = lit_staking.get_assertion_items(amount as usize); + let mut assertion = AssertionLogic::new_and(); + for item in items { + assertion = assertion.add_item(item); + } + + self.credential_subject.assertions.push(assertion); + self.credential_subject.values.push(true); + + let info = lit_staking.get_info(); + self.add_subject_info(info.1, info.0); + } +} diff --git a/tee-worker/litentry/core/credentials/src/litentry_profile/mod.rs b/tee-worker/litentry/core/credentials/src/litentry_profile/mod.rs index 2c73b0aa7b..630660173d 100644 --- a/tee-worker/litentry/core/credentials/src/litentry_profile/mod.rs +++ b/tee-worker/litentry/core/credentials/src/litentry_profile/mod.rs @@ -15,6 +15,7 @@ // along with Litentry. If not, see . pub mod holding_amount; +pub mod lit_staking; pub mod mirror; pub mod token_balance; diff --git a/tee-worker/litentry/core/credentials/src/litentry_profile/token_balance.rs b/tee-worker/litentry/core/credentials/src/litentry_profile/token_balance.rs index 613411308b..21f9da8744 100644 --- a/tee-worker/litentry/core/credentials/src/litentry_profile/token_balance.rs +++ b/tee-worker/litentry/core/credentials/src/litentry_profile/token_balance.rs @@ -32,6 +32,31 @@ const USDT_C_TOKEN_BALANCE_RANGE: [f64; 10] = const LIT_TOKEN_BALANCE_RANGE: [f64; 8] = [0.0, 100.0, 200.0, 500.0, 800.0, 1200.0, 1600.0, 3000.0]; const WBTC_TOKEN_BALANCE_RANGE: [f64; 10] = [0.0, 0.001, 0.05, 0.1, 0.5, 10.0, 50.0, 100.0, 500.0, 1000.0]; +const CRV_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 1.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0, 1800.0]; +const MATIC_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 1.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0, 1800.0]; +const DYDX_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 1.0, 20.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0]; +const AMP_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 2000.0, 5000.0, 10000.0, 18000.0, 30000.0, 50000.0, 80000.0, 120000.0]; +const CVX_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 1.0, 20.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0]; +const TUSD_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 1.0, 20.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0]; +const USDD_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 1.0, 20.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0]; +const GUSD_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 1.0, 20.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0]; +const LINK_TOKEN_BALANCE_RANGE: [f64; 9] = [0.0, 1.0, 5.0, 20.0, 40.0, 80.0, 150.0, 240.0, 400.0]; +const GRT_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 1.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0, 1800.0]; +const COMP_TOKEN_BALANCE_RANGE: [f64; 11] = + [0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 80.0, 150.0, 250.0, 400.0]; +const PEOPLE_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 200.0, 500.0, 1000.0, 1800.0, 3000.0, 5000.0, 8000.0, 12000.0]; +const GTC_TOKEN_BALANCE_RANGE: [f64; 9] = + [0.0, 1.0, 20.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0]; pub trait TokenBalanceInfo { fn update_token_balance(&mut self, token: ETokenAddress, balance: f64); @@ -81,27 +106,68 @@ fn update_assertion(token: ETokenAddress, balance: f64, credential: &mut Credent fn get_token_info(token: &ETokenAddress) -> (&'static str, &'static str) { match token { - ETokenAddress::Lit | ETokenAddress::Usdc | ETokenAddress::Usdt | ETokenAddress::Wbtc => - VC_TOKEN_BALANCE_INFOS[0], + ETokenAddress::WBTC + | ETokenAddress::LIT + | ETokenAddress::USDC + | ETokenAddress::USDT + | ETokenAddress::CRV + | ETokenAddress::MATIC + | ETokenAddress::DYDX + | ETokenAddress::AMP + | ETokenAddress::CVX + | ETokenAddress::TUSD + | ETokenAddress::USDD + | ETokenAddress::GUSD + | ETokenAddress::LINK + | ETokenAddress::GRT + | ETokenAddress::COMP + | ETokenAddress::PEOPLE + | ETokenAddress::GTC => VC_TOKEN_BALANCE_INFOS[0], _ => ("UnknownType", ("UnkonwDescription")), } } fn get_assertion_content(token: &ETokenAddress) -> &'static str { match token { - ETokenAddress::Lit => "$lit_holding_amount", - ETokenAddress::Usdc => "$usdc_holding_amount", - ETokenAddress::Usdt => "$usdt_holding_amount", - ETokenAddress::Wbtc => "$wbtc_holding_amount", + ETokenAddress::WBTC => "$wbtc_holding_amount", + ETokenAddress::LIT => "$lit_holding_amount", + ETokenAddress::USDC => "$usdc_holding_amount", + ETokenAddress::USDT => "$usdt_holding_amount", + ETokenAddress::CRV => "$crv_holding_amount", + ETokenAddress::MATIC => "$matic_holding_amount", + ETokenAddress::DYDX => "$dydx_holding_amount", + ETokenAddress::AMP => "$amp_holding_amount", + ETokenAddress::CVX => "$cvx_holding_amount", + ETokenAddress::TUSD => "$tusd_holding_amount", + ETokenAddress::USDD => "$usdd_holding_amount", + ETokenAddress::GUSD => "$gusd_holding_amount", + ETokenAddress::LINK => "$link_holding_amount", + ETokenAddress::GRT => "$grt_holding_amount", + ETokenAddress::COMP => "$comp_holding_amount", + ETokenAddress::PEOPLE => "$people_holding_amount", + ETokenAddress::GTC => "$gtc_holding_amount", _ => "Unknown", } } fn get_balance_range(token: &ETokenAddress) -> Vec { match token { - ETokenAddress::Lit => LIT_TOKEN_BALANCE_RANGE.to_vec(), - ETokenAddress::Usdc | ETokenAddress::Usdt => USDT_C_TOKEN_BALANCE_RANGE.to_vec(), - ETokenAddress::Wbtc => WBTC_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::WBTC => WBTC_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::LIT => LIT_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::USDC | ETokenAddress::USDT => USDT_C_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::CRV => CRV_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::MATIC => MATIC_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::DYDX => DYDX_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::AMP => AMP_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::CVX => CVX_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::TUSD => TUSD_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::USDD => USDD_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::GUSD => GUSD_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::LINK => LINK_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::GRT => GRT_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::COMP => COMP_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::PEOPLE => PEOPLE_TOKEN_BALANCE_RANGE.to_vec(), + ETokenAddress::GTC => GTC_TOKEN_BALANCE_RANGE.to_vec(), _ => { vec![] }, @@ -110,10 +176,23 @@ fn get_balance_range(token: &ETokenAddress) -> Vec { fn get_token_range_last(token: &ETokenAddress) -> f64 { match token { - ETokenAddress::Lit => *LIT_TOKEN_BALANCE_RANGE.last().unwrap_or(&3000.0), - ETokenAddress::Usdc | ETokenAddress::Usdt => + ETokenAddress::WBTC => *WBTC_TOKEN_BALANCE_RANGE.last().unwrap_or(&1000.0), + ETokenAddress::LIT => *LIT_TOKEN_BALANCE_RANGE.last().unwrap_or(&3000.0), + ETokenAddress::USDC | ETokenAddress::USDT => *USDT_C_TOKEN_BALANCE_RANGE.last().unwrap_or(&1200.0), - ETokenAddress::Wbtc => *WBTC_TOKEN_BALANCE_RANGE.last().unwrap_or(&1000.0), + ETokenAddress::CRV => *CRV_TOKEN_BALANCE_RANGE.last().unwrap_or(&1800.0), + ETokenAddress::MATIC => *MATIC_TOKEN_BALANCE_RANGE.last().unwrap_or(&1800.0), + ETokenAddress::DYDX => *DYDX_TOKEN_BALANCE_RANGE.last().unwrap_or(&1200.0), + ETokenAddress::AMP => *AMP_TOKEN_BALANCE_RANGE.last().unwrap_or(&1200.0), + ETokenAddress::CVX => *CVX_TOKEN_BALANCE_RANGE.last().unwrap_or(&12000.0), + ETokenAddress::TUSD => *TUSD_TOKEN_BALANCE_RANGE.last().unwrap_or(&1200.0), + ETokenAddress::USDD => *USDD_TOKEN_BALANCE_RANGE.last().unwrap_or(&1200.0), + ETokenAddress::GUSD => *GUSD_TOKEN_BALANCE_RANGE.last().unwrap_or(&1200.0), + ETokenAddress::LINK => *LINK_TOKEN_BALANCE_RANGE.last().unwrap_or(&400.0), + ETokenAddress::GRT => *GRT_TOKEN_BALANCE_RANGE.last().unwrap_or(&1800.0), + ETokenAddress::COMP => *COMP_TOKEN_BALANCE_RANGE.last().unwrap_or(&400.0), + ETokenAddress::PEOPLE => *PEOPLE_TOKEN_BALANCE_RANGE.last().unwrap_or(&12000.0), + ETokenAddress::GTC => *GTC_TOKEN_BALANCE_RANGE.last().unwrap_or(&1200.0), _ => 0.0, } } diff --git a/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/evm_amount_holding.rs b/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/evm_amount_holding.rs new file mode 100644 index 0000000000..4bd0ed6f3e --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/evm_amount_holding.rs @@ -0,0 +1,177 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use lc_data_providers::achainable::web3_network_to_chain; +use litentry_primitives::{all_evm_web3networks, EVMTokenType, Web3Network}; + +use crate::{ + assertion_logic::{AssertionLogic, Op}, + litentry_profile::{BalanceRange, BalanceRangeIndex}, + Credential, +}; +pub trait EVMTokenAddress { + fn get_address(&self, network: Web3Network) -> Option<&'static str>; +} + +impl EVMTokenAddress for EVMTokenType { + fn get_address(&self, network: Web3Network) -> Option<&'static str> { + match (self, network) { + (EVMTokenType::Ton, Web3Network::Bsc) => + Some("0x76a797a59ba2c17726896976b7b3747bfd1d220f"), + (EVMTokenType::Ton, Web3Network::Ethereum) => + Some("0x582d872a1b094fc48f5de31d3b73f2d9be47def1"), + (EVMTokenType::Trx, Web3Network::Bsc) => + Some("0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3"), + (EVMTokenType::Trx, Web3Network::Ethereum) => + Some("0x50327c6c5a14dcade707abad2e27eb517df87ab5"), + _ => None, + } + } +} + +const EVM_HOLDING_AMOUNT_RANGE: [f64; 10] = + [0.0, 1.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0, 1600.0, 3000.0]; + +const TYPE: &str = "Token Holding Amount"; +const DESCRIPTION: &str = "The amount of a particular token you are holding"; + +struct AssertionKeys { + token: &'static str, + network: &'static str, + address: &'static str, + holding_amount: &'static str, +} + +const ASSERTION_KEYS: AssertionKeys = AssertionKeys { + token: "$token", + network: "$network", + address: "$address", + holding_amount: "$holding_amount", +}; + +trait AssertionTokenName { + fn get_name(&self) -> &'static str; +} + +impl AssertionTokenName for EVMTokenType { + fn get_name(&self) -> &'static str { + match self { + EVMTokenType::Ton => "TON", + EVMTokenType::Trx => "TRX", + } + } +} + +pub trait TokenDecimals { + fn get_decimals(&self) -> f64; +} + +impl TokenDecimals for EVMTokenType { + fn get_decimals(&self) -> f64 { + match self { + // TON on BSCÐ decimals are both 9 + // https://bscscan.com/token/0x76a797a59ba2c17726896976b7b3747bfd1d220f + // https://etherscan.io/token/0x582d872a1b094fc48f5de31d3b73f2d9be47def1 + EVMTokenType::Ton => 1_000_000_000.0, + + // TRX on BSCÐ decimals are both 6 + // https://bscscan.com/token/0xce7de646e7208a4ef112cb6ed5038fa6cc6b12e3 + // https://etherscan.io/token/0x50327c6c5a14dcade707abad2e27eb517df87ab5 + EVMTokenType::Trx => 1_000_000.0, + } + } +} + +pub trait EVMAmountHoldingAssertionUpdate { + fn update_evm_amount_holding_assertion(&mut self, token_type: EVMTokenType, amount: f64); +} + +impl EVMAmountHoldingAssertionUpdate for Credential { + fn update_evm_amount_holding_assertion(&mut self, token_type: EVMTokenType, amount: f64) { + self.add_subject_info(DESCRIPTION, TYPE); + + update_assertion(token_type, amount, self); + } +} + +fn update_assertion(token_type: EVMTokenType, balance: f64, credential: &mut Credential) { + let mut assertion = AssertionLogic::new_and(); + + assertion = assertion.add_item(AssertionLogic::new_item( + ASSERTION_KEYS.token, + Op::Equal, + token_type.get_name(), + )); + + let mut network_assertion = AssertionLogic::new_or(); + for newtork in all_evm_web3networks() { + match create_network_assertion_logic(newtork, token_type.clone()) { + Some(network_assertion_item) => { + network_assertion = network_assertion.add_item(network_assertion_item); + }, + None => continue, + } + } + + assertion = assertion.add_item(network_assertion); + + let index = BalanceRange::index(&EVM_HOLDING_AMOUNT_RANGE, balance); + match index { + Some(index) => { + let min = format!("{}", &EVM_HOLDING_AMOUNT_RANGE[index]); + let max = format!("{}", &EVM_HOLDING_AMOUNT_RANGE[index + 1]); + let min_item = + AssertionLogic::new_item(ASSERTION_KEYS.holding_amount, Op::GreaterEq, &min); + let max_item = + AssertionLogic::new_item(ASSERTION_KEYS.holding_amount, Op::LessThan, &max); + + assertion = assertion.add_item(min_item); + assertion = assertion.add_item(max_item); + + credential.credential_subject.values.push(index != 0); + }, + None => { + let min_item = AssertionLogic::new_item( + ASSERTION_KEYS.holding_amount, + Op::GreaterEq, + &format!("{}", &EVM_HOLDING_AMOUNT_RANGE.last().unwrap()), + ); + assertion = assertion.add_item(min_item); + + credential.credential_subject.values.push(true); + }, + } + + credential.credential_subject.assertions.push(assertion); +} + +fn create_network_assertion_logic( + network: Web3Network, + token_type: EVMTokenType, +) -> Option { + let mut assertion = AssertionLogic::new_and(); + assertion = assertion.add_item(AssertionLogic::new_item( + ASSERTION_KEYS.network, + Op::Equal, + web3_network_to_chain(&network).as_str(), + )); + assertion = assertion.add_item(AssertionLogic::new_item( + ASSERTION_KEYS.address, + Op::Equal, + token_type.get_address(network)?, + )); + Some(assertion) +} diff --git a/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/mod.rs b/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/mod.rs new file mode 100644 index 0000000000..09c8ec0fdf --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +pub mod evm_amount_holding; diff --git a/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs new file mode 100644 index 0000000000..fc6ecf3d9e --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs @@ -0,0 +1,444 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use self::summary::{ + CRYPTO_SUMMARY_NFT_ADDRESSES_ETH, CRYPTO_SUMMARY_TOKEN_ADDRESSES_BSC, + CRYPTO_SUMMARY_TOKEN_ADDRESSES_ETH, +}; +use crate::*; +use lc_data_providers::{ + achainable::web3_network_to_chain, + nodereal_jsonrpc::{ + FungibleApiList, GetNFTHoldingsParam, NftApiList, NoderealChain, NoderealJsonrpcClient, + TransactionCount, + }, +}; +use litentry_primitives::{ErrorDetail, IntoErrorDetail}; +use serde::{Deserialize, Serialize}; +use std::{string::String, vec, vec::Vec}; + +pub mod summary; + +const ETH_TOKEN_DECIMALS: f64 = 1_000_000_000_000_000_000.0; + +/* +SUMMARY: { + TOKEN: [ + { + network: BSC, + list: [ + { + name: PEPE, + address: 0x123, + }, + { + name: SHIB, + address: 0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE , + }, + { + name: BNB, + address: "", // No smart contract address + }, + + //... + ] + }, + { + network: Ethereum, + list: [ + { + name: PEPE, + address: 0x123, + }, + { + name: SHIB, + address: 0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE , + }, + { + name: ETH, + address: "", // No smart contract address + }, + //... + ] + }, + ], + NFT: [ + { + network: Ethereum, + list: [ + { + name: Moonbirds, + address: 0x23581767a106ae21c074b2276D25e5C3e136a68b + } + ] + } + ] +} + +*/ + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ResponseToken { + token_address: String, + token_balance: String, + token_name: String, + token_symbol: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ResponseTokenResult { + pub total_count: String, + pub native_token_balance: String, + pub details: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct NameAndAddress { + pub name: String, + pub address: String, +} + +impl NameAndAddress { + pub fn new(name: &str, address: &str) -> Self { + Self { name: name.to_string(), address: address.to_string() } + } +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Item { + pub network: String, + pub list: Vec, +} + +impl Item { + pub fn new(network: String, list: Vec) -> Self { + let mut item = Item::default(); + + if !list.is_empty() { + item.network = network; + item.list = list; + } + + item + } + + pub fn is_empty(&self) -> bool { + self.list.is_empty() + } +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct TokenAndNFT { + #[serde(rename = "TOKEN")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub token: Vec, + + #[serde(rename = "NFT")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub nft: Vec, +} + +impl TokenAndNFT { + pub fn add_token(&mut self, item: Item) { + if !item.is_empty() { + self.token.push(item); + } + } + + pub fn add_nft(&mut self, item: Item) { + if !item.is_empty() { + self.nft.push(item); + } + } +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct SummaryHoldings { + #[serde(rename = "SUMMARRY")] + pub summary: TokenAndNFT, +} + +impl SummaryHoldings { + pub fn is_empty(&self) -> bool { + self.summary.token.is_empty() && self.summary.nft.is_empty() + } + + pub fn construct(bsc_token: &[bool], eth_token: &[bool], eth_nft: &[bool]) -> Self { + let bsc_token_list = Self::collect_list(&CRYPTO_SUMMARY_TOKEN_ADDRESSES_BSC, bsc_token); + let eth_token_list = Self::collect_list(&CRYPTO_SUMMARY_TOKEN_ADDRESSES_ETH, eth_token); + let eth_nft_list = Self::collect_list(&CRYPTO_SUMMARY_NFT_ADDRESSES_ETH, eth_nft); + + let mut token_and_nft = TokenAndNFT::default(); + token_and_nft + .add_token(Item::new(web3_network_to_chain(&Web3Network::Bsc), bsc_token_list)); + token_and_nft + .add_token(Item::new(web3_network_to_chain(&Web3Network::Ethereum), eth_token_list)); + token_and_nft + .add_nft(Item::new(web3_network_to_chain(&Web3Network::Ethereum), eth_nft_list)); + + SummaryHoldings { summary: token_and_nft } + } + + fn collect_list(source: &[(&str, &str)], flags: &[bool]) -> Vec { + let mut list = vec![]; + for (index, is_holder) in flags.iter().enumerate() { + if *is_holder { + let (address, name) = source[index]; + let name_and_address = NameAndAddress::new(name, address); + list.push(name_and_address) + } + } + + list + } +} + +pub struct CryptoSummaryClient { + pub eth_client: NoderealJsonrpcClient, + pub bsc_client: NoderealJsonrpcClient, +} + +impl Default for CryptoSummaryClient { + fn default() -> Self { + Self::new() + } +} + +impl CryptoSummaryClient { + pub fn new() -> Self { + let eth_client = NoderealJsonrpcClient::new(NoderealChain::Eth); + let bsc_client = NoderealJsonrpcClient::new(NoderealChain::Bsc); + + Self { eth_client, bsc_client } + } + + pub fn logic( + &mut self, + identities: &Vec<(Web3Network, Vec)>, + ) -> core::result::Result<(u64, SummaryHoldings), ErrorDetail> { + // Corresponds one-to-one with CRYPTO_SUMMARY_TOKEN_ADDRESSES_ETH + let mut flag_bsc_token: [bool; 12] = [false; 12]; + let mut flag_eth_token: [bool; 15] = [false; 15]; + let mut flag_eth_nft: [bool; 15] = [false; 15]; + + // ETH and BNB holder use different APIs than other tokens, so they need to be handled separately + let mut is_eth_holder = false; + let mut is_bnb_holder = false; + + let mut total_txs = 0_u64; + + for (network, addresses) in identities { + if *network == Web3Network::Bsc { + // Query Token + for address in addresses { + let res = self + .bsc_client + .get_token_holdings(address) + .map_err(|e| e.into_error_detail())?; + + let result: ResponseTokenResult = + serde_json::from_value(res.result).map_err(|_| ErrorDetail::ParseError)?; + let mut token_addresses = vec![]; + if let Some(details) = result.details { + details.iter().for_each(|detail| { + token_addresses.push(detail.token_address.clone()); + }); + Self::update_holding_flag( + &mut flag_bsc_token, + &CRYPTO_SUMMARY_TOKEN_ADDRESSES_BSC, + &token_addresses, + ); + } + + // Query BNB balance on BSC + if !is_bnb_holder { + let native_balance = result.native_token_balance; + let balance = get_native_token_balance(&native_balance); + if balance > 0.0 { + is_bnb_holder = true; + } + } + + // Total txs on BSC + let tx = self + .bsc_client + .get_transaction_count(address) + .map_err(|e| e.into_error_detail())?; + total_txs += tx; + } + } + + if *network == Web3Network::Ethereum { + for address in addresses { + // Query Token + let res_token = self + .eth_client + .get_token_holdings(address) + .map_err(|e| e.into_error_detail())?; + let result: ResponseTokenResult = serde_json::from_value(res_token.result) + .map_err(|_| ErrorDetail::ParseError)?; + + let mut token_addresses = vec![]; + if let Some(details) = result.details { + details.iter().for_each(|detail| { + token_addresses.push(detail.token_address.clone()); + }); + Self::update_holding_flag( + &mut flag_eth_token, + &CRYPTO_SUMMARY_TOKEN_ADDRESSES_ETH, + &token_addresses, + ); + } + + // Query ETH balance on Ethereum + if !is_eth_holder { + let native_balance = result.native_token_balance; + let balance = get_native_token_balance(&native_balance); + if balance > 0.0 { + is_eth_holder = true; + } + } + + // Query NFT + let param = GetNFTHoldingsParam { + account_address: address.to_string(), + token_type: "ERC721".to_string(), + page: 1, + page_size: 100, + }; + + let res_nft = self + .eth_client + .get_nft_holdings(¶m) + .map_err(|e| e.into_error_detail())?; + + let details = res_nft.details; + + let mut nft_addresses = vec![]; + details.iter().for_each(|detail| { + nft_addresses.push(detail.token_address.clone()); + }); + + Self::update_holding_flag( + &mut flag_eth_nft, + &CRYPTO_SUMMARY_NFT_ADDRESSES_ETH, + &nft_addresses, + ); + + // Total txs on Ethereum + let tx = self + .eth_client + .get_transaction_count(address) + .map_err(|e| e.into_error_detail())?; + total_txs += tx; + } + } + } + + // Update ETH and BNB + flag_bsc_token[11] = is_bnb_holder; + flag_eth_token[14] = is_eth_holder; + + Ok((total_txs, SummaryHoldings::construct(&flag_bsc_token, &flag_eth_token, &flag_eth_nft))) + } + + fn update_holding_flag( + flag_array: &mut [bool], + source: &[(&'static str, &'static str)], + token_addresses: &[String], + ) { + for (index, is_holder) in flag_array.iter_mut().enumerate() { + if !*is_holder { + let (token_address, _token_name) = source[index]; + if token_addresses.contains(&token_address.to_lowercase()) { + *is_holder = true; + } + } + } + } +} + +fn get_native_token_balance(native_balance: &str) -> f64 { + let native_balance = u64::from_str_radix(&native_balance[2..], 16).unwrap_or_default() as f64; + native_balance / ETH_TOKEN_DECIMALS +} + +#[cfg(test)] +mod tests { + use super::{get_native_token_balance, CryptoSummaryClient, SummaryHoldings}; + use crate::nodereal::crypto_summary::summary::CRYPTO_SUMMARY_NFT_ADDRESSES_ETH; + + #[test] + fn summary_construct_works() { + let flag_bsc_token = + [false, true, true, true, true, true, true, true, true, true, true, true]; + let flag_eth_token = [ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, true, + ]; + let flag_eth_nft = [ + true, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, + ]; + + let summary = SummaryHoldings::construct(&flag_bsc_token, &flag_eth_token, &flag_eth_nft); + assert!(!summary.is_empty()); + } + + #[test] + fn update_nft_holding_flag_works() { + let nft_addresses = vec![ + "0x9401518f4EBBA857BAA879D9f76E1Cc8b31ed197".to_lowercase(), + "0x6339e5E072086621540D0362C4e3Cea0d643E114".to_lowercase(), + ]; + let mut flag_eth_nft = [ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, + ]; + CryptoSummaryClient::update_holding_flag( + &mut flag_eth_nft, + &CRYPTO_SUMMARY_NFT_ADDRESSES_ETH, + &nft_addresses, + ); + assert!(flag_eth_nft.contains(&true)); + } + + #[test] + fn update_bsc_holding_flag_works() { + let bsc_addresses = vec![ + "0x9401518f4EBBA857BAA879D9f76E1Cc8b31ed197".to_lowercase(), + "0x6339e5E072086621540D0362C4e3Cea0d643E114".to_lowercase(), + ]; + let mut flag_bsc = + [false, false, false, false, false, false, false, false, false, false, false, false]; + CryptoSummaryClient::update_holding_flag( + &mut flag_bsc, + &CRYPTO_SUMMARY_NFT_ADDRESSES_ETH, + &bsc_addresses, + ); + assert!(flag_bsc.contains(&true)); + } + + #[test] + fn get_native_token_balance_works() { + let native_token_balance = + "0x00000000000000000000000000000000000000000000000000c92180664030d4"; + let balance = get_native_token_balance(native_token_balance); + assert_eq!(balance, 0.05661330567385518); + } +} diff --git a/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/summary.rs b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/summary.rs new file mode 100644 index 0000000000..3d768c4735 --- /dev/null +++ b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/summary.rs @@ -0,0 +1,166 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use super::{Item, SummaryHoldings}; +use crate::*; + +const VC_CRYPTO_SUMMARY_DESCRIPTIONS: &str = "Generate a summary of your on-chain identity"; +const VC_CRYPTO_SUMMARY_TYPE: &str = "IDHub Crypto Summary"; + +/// Doesn't exists on BSC including: +/// PEPE, BLUR, WLD +pub const CRYPTO_SUMMARY_TOKEN_ADDRESSES_BSC: [(&str, &str); 12] = [ + ("0xb1547683DA678f2e1F003A780143EC10Af8a832B", "SHIB"), + ("0xBf5140A22578168FD562DCcF235E5D43A02ce9B1", "UNI"), + ("0x2170Ed0880ac9A755fd29B2688956BD959F933F8", "ETH"), + ("0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD", "LINK"), + ("0xa050FFb3eEb8200eEB7F61ce34FF644420FD3522", "ARB"), + ("0x101d82428437127bF1608F699CD651e6Abf9766E", "BAT"), + ("0xa2B726B1145A4773F68593CF171187d8EBe4d495", "INJ"), + ("0xfb6115445Bff7b52FeB98650C87f44907E58f802", "AAVE"), + ("0x49BA054B9664e99ac335667a917c63bB94332E84", "FTT"), + ("0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", "CAKE"), + ("0xb59490aB09A0f526Cc7305822aC65f2Ab12f9723", "LIT"), + ("", "BNB"), // Keep address empty +]; + +pub const CRYPTO_SUMMARY_TOKEN_ADDRESSES_ETH: [(&str, &str); 15] = [ + ("0x6982508145454Ce325dDbE47a25d4ec3d2311933", "PEPE"), + ("0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", "SHIB"), + ("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", "UNI"), + ("0xB8c77482e45F1F44dE1745F52C74426C631bDD52", "BNB"), + ("0x514910771AF9Ca656af840dff83E8264EcF986CA", "LINK"), + ("0x5283d291dbcf85356a21ba090e6db59121208b44", "BLUR"), + ("0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1", "ARB"), + ("0x0d8775f648430679a709e98d2b0cb6250d2887ef", "BAT"), + ("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30", "INJ"), + ("0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9", "AAVE"), + ("0x163f8c2467924be0ae7b5347228cabf260318753", "WLD"), + ("0x50D1c9771902476076eCFc8B2A83Ad6b9355a4c9", "FTT"), + ("0x152649eA73beAb28c5b49B26eb48f7EAD6d4c898", "CAKE"), + ("0xb59490aB09A0f526Cc7305822aC65f2Ab12f9723", "LIT"), + ("", "ETH"), // Keep address empty +]; + +pub const CRYPTO_SUMMARY_NFT_ADDRESSES_ETH: [(&str, &str); 15] = [ + ("0x9401518f4EBBA857BAA879D9f76E1Cc8b31ed197", "The Weirdo Ghost Gang"), + ("0x23581767a106ae21c074b2276D25e5C3e136a68b", "Moonbirds"), + ("0x142e03367eDE17Cd851477A4287D1F35676E6dC2", "Yogapetz"), + ("0x59325733eb952a92e069C87F0A6168b29E80627f", "Mocaverse"), + ("0xeC19CAeF9C046f5f87A497154766742ab9C90820", "y00ts"), + ("0x5CC5B05a8A13E3fBDB0BB9FcCd98D38e50F90c38", "The Sandbox"), + ("0x596a5CD859AD53fEc23Cd3fCD77522f0B407920d", "MATR1X KUKU"), + ("0x8a90CAb2b38dba80c64b7734e58Ee1dB38B8992e", "Doodles"), + ("0x49cF6f5d44E70224e2E23fDcdd2C053F30aDA28B", "CloneX"), + ("0xd774557b647330C91Bf44cfEAB205095f7E6c367", "Nakamigos"), + ("0x6339e5E072086621540D0362C4e3Cea0d643E114", "Opepen"), + ("0xe1dC516B1486Aba548eecD2947A11273518434a4", "The Grapes"), + ("0x769272677faB02575E84945F03Eca517ACc544C", "The Captainz"), + ("0x39ee2c7b3cb80254225884ca001F57118C8f21B6", "The Potatoz"), + ("0x7Bd29408f11D2bFC23c34f18275bBf23bB716Bc7", "Meebits"), +]; + +pub trait CryptoSummaryCredentialUpdate { + fn update_crypto_summary_credential(&mut self, txs: u64, summary: SummaryHoldings); +} + +impl CryptoSummaryCredentialUpdate for Credential { + fn update_crypto_summary_credential(&mut self, txs: u64, summary: SummaryHoldings) { + let (value, and_logic) = build_assertions(txs, summary); + + self.credential_subject.assertions.push(and_logic); + self.credential_subject.values.push(value); + + self.add_subject_info(VC_CRYPTO_SUMMARY_DESCRIPTIONS, VC_CRYPTO_SUMMARY_TYPE); + } +} + +fn build_assertions(txs: u64, summary: SummaryHoldings) -> (bool, AssertionLogic) { + let is_empty = summary.is_empty(); + + let mut and_logic = AssertionLogic::new_and(); + + // Total Txs + let txs_item = AssertionLogic::new_item("$total_txs", Op::Equal, &txs.to_string()); + and_logic = and_logic.add_item(txs_item); + + // TOKENs + let token_assertion = token_items("TOKEN", summary.summary.token); + and_logic = and_logic.add_item(token_assertion); + + // NFTs + let nft_assertion = token_items("NFT", summary.summary.nft); + and_logic = and_logic.add_item(nft_assertion); + + (!is_empty, and_logic) +} + +fn token_items(name: &str, items: Vec) -> AssertionLogic { + let mut and_logic = AssertionLogic::new_and(); + + let kind_item = AssertionLogic::new_item("$kind", Op::Equal, name); + and_logic = and_logic.add_item(kind_item); + + for item in items { + let mut item_logic = AssertionLogic::new_and(); + + let network_item = AssertionLogic::new_item("$network", Op::Equal, &item.network); + item_logic = item_logic.add_item(network_item); + + for token in item.list { + let mut inner_logic = AssertionLogic::new_and(); + + let name_item = AssertionLogic::new_item("$token_name", Op::Equal, &token.name); + inner_logic = inner_logic.add_item(name_item); + + let address_item = + AssertionLogic::new_item("$token_address", Op::Equal, &token.address); + inner_logic = inner_logic.add_item(address_item); + + item_logic = item_logic.add_item(inner_logic) + } + + and_logic = and_logic.add_item(item_logic); + } + + and_logic +} + +#[cfg(test)] +mod tests { + use crate::nodereal::crypto_summary::SummaryHoldings; + + use super::build_assertions; + + #[test] + fn build_assertions_works() { + let flag_bsc_token = + [false, true, true, true, true, true, true, true, true, true, true, true]; + let flag_eth_token = [ + false, false, true, false, false, false, false, false, false, false, false, false, + false, false, false, + ]; + let flag_eth_nft = [ + true, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, + ]; + + let txs = 100_u64; + let summary = SummaryHoldings::construct(&flag_bsc_token, &flag_eth_token, &flag_eth_nft); + let (value, _logic) = build_assertions(txs, summary); + assert!(value); + } +} diff --git a/tee-worker/litentry/core/credentials/src/nodereal/mod.rs b/tee-worker/litentry/core/credentials/src/nodereal/mod.rs index 41346f95d0..84a4bff2f7 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/mod.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/mod.rs @@ -20,5 +20,7 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam #[cfg(all(not(feature = "std"), feature = "sgx"))] extern crate sgx_tstd as std; +pub mod amount_holding; pub mod bnb_domain; +pub mod crypto_summary; pub mod nft_holder; diff --git a/tee-worker/litentry/core/credentials/src/templates/credential.json b/tee-worker/litentry/core/credentials/src/templates/credential.json index e642e3122e..c26af00bf1 100644 --- a/tee-worker/litentry/core/credentials/src/templates/credential.json +++ b/tee-worker/litentry/core/credentials/src/templates/credential.json @@ -14,10 +14,13 @@ }, "subject":"", "issuanceDate":"", + "parachainBlockNumber": 0, + "sidechainBlockNumber": 0, "credentialSubject":{ "id":"", "description":"", "type":"", + "assertionText":"", "assertions":[ ], diff --git a/tee-worker/litentry/core/data-providers/src/achainable.rs b/tee-worker/litentry/core/data-providers/src/achainable.rs index 9e6f51bd9b..2fa0939be9 100644 --- a/tee-worker/litentry/core/data-providers/src/achainable.rs +++ b/tee-worker/litentry/core/data-providers/src/achainable.rs @@ -158,6 +158,10 @@ impl ReqBody { pub fn new(address: String, params: Params) -> Self { ReqBody { name: params.name(), address, params, include_metadata: true } } + + pub fn new_with_false_metadata(address: String, params: Params) -> Self { + ReqBody { name: params.name(), address, params, include_metadata: false } + } } pub trait AchainableSystemLabelName { @@ -175,6 +179,11 @@ pub fn web3_network_to_chain(network: &Web3Network) -> String { Web3Network::SubstrateTestnet => "substrate_testnet".into(), Web3Network::Ethereum => "ethereum".into(), Web3Network::Bsc => "bsc".into(), + Web3Network::BitcoinP2tr => "bitcoin_p2tr".into(), + Web3Network::BitcoinP2pkh => "bitcoin_p2pkh".into(), + Web3Network::BitcoinP2sh => "bitcoin_p2sh".into(), + Web3Network::BitcoinP2wpkh => "bitcoin_p2wpkh".into(), + Web3Network::BitcoinP2wsh => "bitcoin_p2wsh".into(), } } @@ -236,7 +245,9 @@ impl TryFrom for Params { let token = if p.token.is_some() { Some(ap.to_string(&p.token.unwrap())?) } else { None }; - let p = ParamsBasicTypeWithAmountToken::new(name, network, amount, token); + // At this step, we do not care about the content inside the chains and instead use real chain data to fill in the request + // so use network[0] as a placehold. + let p = ParamsBasicTypeWithAmountToken::new(name, &network[0], amount, token); Ok(Params::ParamsBasicTypeWithAmountToken(p)) }, AchainableParams::Amount(p) => { @@ -807,6 +818,12 @@ impl AchainableUtils for AchainableClient { }) .flatten(); if let Some(display_text) = display_text { + // If it is a newly created brand new account, Achainable returns `Address has no [token] balance`, + // so it will not be parsed and will directly return 0 + if display_text.contains("Address has no") { + return Ok(0_f64) + } + // TODO: // text field format: Balance over 0 (Balance is 588.504602529) let split_text = display_text.split("Balance is ").collect::>(); @@ -837,7 +854,7 @@ impl HoldingAmount for AchainableClient { fn holding_amount(&mut self, addresses: Vec, param: Params) -> Result { let mut total_balance = 0_f64; for address in addresses.iter() { - let body = ReqBody::new(address.into(), param.clone()); + let body = ReqBody::new_with_false_metadata(address.into(), param.clone()); let balance = self.post(SystemLabelReqPath::default(), &body).and_then(Self::get_balance)?; total_balance += balance; diff --git a/tee-worker/litentry/core/data-providers/src/achainable_names.rs b/tee-worker/litentry/core/data-providers/src/achainable_names.rs index 21e8b41f10..d3b6f09921 100644 --- a/tee-worker/litentry/core/data-providers/src/achainable_names.rs +++ b/tee-worker/litentry/core/data-providers/src/achainable_names.rs @@ -25,6 +25,35 @@ pub trait GetAchainableName { fn name(&self) -> &'static str; } +#[derive(Debug)] +pub enum AchainableNameBasic { + BABHolder, + UniswapV23User, // custom name +} + +impl GetAchainableName for AchainableNameBasic { + fn name(&self) -> &'static str { + match self { + AchainableNameBasic::BABHolder => "BAB token holder", + AchainableNameBasic::UniswapV23User => "Uniswap V2/V3 user", + } + } +} + +impl AchainableNameBasic { + pub fn from(param: ParameterString) -> Result { + let name_str = vec_to_string(param.to_vec())?; + + if name_str == AchainableNameBasic::BABHolder.name() { + return Ok(AchainableNameBasic::BABHolder) + } else if name_str == AchainableNameBasic::UniswapV23User.name() { + return Ok(AchainableNameBasic::UniswapV23User) + } + + Err(Error::AchainableError("Invalid Achainable Name in Basic Type".to_string())) + } +} + #[derive(Debug)] pub enum AchainableNameMirror { IsAPublicationOnMirror, @@ -88,3 +117,40 @@ impl AchainableNameAmount { Err(Error::AchainableError("Invalid Achainable Name".to_string())) } } + +#[derive(Debug, PartialEq)] +pub enum AchainableNameAmountToken { + BEP20BalanceOverAmount, + ERC20BalanceOverAmount, + BalanceOverAmount, + LITHoldingAmount, // Custom Name +} + +impl GetAchainableName for AchainableNameAmountToken { + fn name(&self) -> &'static str { + match self { + AchainableNameAmountToken::BEP20BalanceOverAmount => "BEP20 balance over {amount}", + AchainableNameAmountToken::ERC20BalanceOverAmount => "ERC20 balance over {amount}", + AchainableNameAmountToken::BalanceOverAmount => "Balance over {amount}", + AchainableNameAmountToken::LITHoldingAmount => "LIT Holding Amount", + } + } +} + +impl AchainableNameAmountToken { + pub fn from(param: ParameterString) -> Result { + let name_str = vec_to_string(param.to_vec())?; + + if name_str == AchainableNameAmountToken::BEP20BalanceOverAmount.name() { + return Ok(AchainableNameAmountToken::BEP20BalanceOverAmount) + } else if name_str == AchainableNameAmountToken::ERC20BalanceOverAmount.name() { + return Ok(AchainableNameAmountToken::ERC20BalanceOverAmount) + } else if name_str == AchainableNameAmountToken::BalanceOverAmount.name() { + return Ok(AchainableNameAmountToken::BalanceOverAmount) + } else if name_str == AchainableNameAmountToken::LITHoldingAmount.name() { + return Ok(AchainableNameAmountToken::LITHoldingAmount) + } + + Err(Error::AchainableError("Unsupported name in this Type".to_string())) + } +} diff --git a/tee-worker/litentry/core/data-providers/src/geniidata.rs b/tee-worker/litentry/core/data-providers/src/geniidata.rs new file mode 100644 index 0000000000..8857fb8d0d --- /dev/null +++ b/tee-worker/litentry/core/data-providers/src/geniidata.rs @@ -0,0 +1,115 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use crate::{ + build_client_with_cert, DataProviderConfigReader, Error as DataProviderError, + ReadDataProviderConfig, +}; +use http::header::{ACCEPT, CONNECTION}; +use http_req::response::Headers; +use itc_rest_client::{ + error::Error as RestClientError, + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, + RestGet, RestPath, +}; +use litentry_primitives::ErrorDetail; +use serde::{Deserialize, Serialize}; +use std::{ + format, str, + string::{String, ToString}, + vec, + vec::Vec, +}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct ResponseItem { + pub tick: String, + pub address: String, + pub overall_balance: String, + pub transferable_balance: String, + pub available_balance: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct ReponseData { + pub count: u64, + pub limit: String, + pub offset: String, + pub list: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct GeniidataResponse { + pub code: u64, + pub message: String, + pub data: ReponseData, +} + +impl RestPath for GeniidataResponse { + fn get_path(path: String) -> core::result::Result { + Ok(path) + } +} + +// According to https://geniidata.readme.io/reference/get-brc20-tick-list-copy, the maximum limit is i32::MAX +const GENIIDATA_QUERY_LIMIT: &str = "2147483647"; + +pub struct GeniidataClient { + client: RestClient>, +} + +impl GeniidataClient { + pub fn new() -> core::result::Result { + let data_provider_config = DataProviderConfigReader::read()?; + + let mut headers = Headers::new(); + headers.insert(CONNECTION.as_str(), "close"); + headers.insert(ACCEPT.as_str(), "application/json"); + headers.insert("api-key", data_provider_config.geniidata_api_key.as_str()); + + let client = build_client_with_cert(data_provider_config.geniidata_url.as_str(), headers); + + Ok(GeniidataClient { client }) + } + + pub fn create_brc20_amount_holder_sum( + &mut self, + addresses: Vec, + ) -> Result, DataProviderError> { + let mut all_items: Vec = Vec::new(); + for address in addresses { + let query = + vec![("limit", GENIIDATA_QUERY_LIMIT), ("offset", "0"), ("address", &address)]; + let response = self + .client + .get_with::("".to_string(), query.as_slice()) + .map_err(|e| { + DataProviderError::GeniiDataError(format!("GeniiData response error: {}", e)) + })?; + + all_items.extend(response.data.list); + } + + Ok(all_items) + } +} diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index 25e03c500f..30216ff696 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -73,6 +73,7 @@ pub mod achainable; pub mod achainable_names; pub mod discord_litentry; pub mod discord_official; +pub mod geniidata; pub mod nodereal; pub mod nodereal_jsonrpc; pub mod twitter_official; @@ -85,13 +86,39 @@ pub const WETH_TOKEN_ADDRESS: &str = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 pub const USDT_TOKEN_ADDRESS: &str = "0xdac17f958d2ee523a2206206994597c13d831ec7"; pub const USDC_TOKEN_ADDRESS: &str = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"; pub const LIT_TOKEN_ADDRESS: &str = "0xb59490ab09a0f526cc7305822ac65f2ab12f9723"; +pub const CRV_TOKEN_ADDRESS: &str = "0xd533a949740bb3306d119cc777fa900ba034cd52"; +pub const MATIC_TOKEN_ADDRESS: &str = "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"; +pub const DYDX_TOKEN_ADDRESS: &str = "0x92d6c1e31e14520e676a687f0a93788b716beff5"; +pub const AMP_TOKEN_ADDRESS: &str = "0xff20817765cb7f73d4bde2e66e067e58d11095c2"; +pub const CVX_TOKEN_ADDRESS: &str = "0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b"; +pub const TUSD_TOKEN_ADDRESS: &str = "0x0000000000085d4780b73119b644ae5ecd22b376"; +pub const USDD_TOKEN_ADDRESS: &str = "0x0c10bf8fcb7bf5412187a595ab97a3609160b5c6"; +pub const GUSD_TOKEN_ADDRESS: &str = "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd"; +pub const LINK_TOKEN_ADDRESS: &str = "0x514910771af9ca656af840dff83e8264ecf986ca"; +pub const GRT_TOKEN_ADDRESS: &str = "0xc944e90c64b2c07662a292be6244bdf05cda44a7"; +pub const COMP_TOKEN_ADDRESS: &str = "0xc00e94cb662c3520282e6f5717214004a7f26888"; +pub const PEOPLE_TOKEN_ADDRESS: &str = "0x7a58c0be72be218b41c608b7fe7c5bb630736c71"; +pub const GTC_TOKEN_ADDRESS: &str = "0xde30da39c46104798bb5aa3fe8b9e0e1f348163f"; #[derive(Debug, PartialEq, PartialOrd)] pub enum ETokenAddress { - Wbtc, - Lit, - Usdc, - Usdt, + WBTC, + LIT, + USDC, + USDT, + CRV, + MATIC, + DYDX, + AMP, + CVX, + TUSD, + USDD, + GUSD, + LINK, + GRT, + COMP, + PEOPLE, + GTC, Unknown, } @@ -102,13 +129,39 @@ impl TokenFromString for ETokenAddress { fn from_vec(vec: ParameterString) -> ETokenAddress { let address = vec_to_string(vec.to_vec()).unwrap_or_default(); if address == WBTC_TOKEN_ADDRESS { - ETokenAddress::Wbtc + ETokenAddress::WBTC } else if address == LIT_TOKEN_ADDRESS { - ETokenAddress::Lit - } else if address == USDT_TOKEN_ADDRESS { - ETokenAddress::Usdt + ETokenAddress::LIT } else if address == USDC_TOKEN_ADDRESS { - ETokenAddress::Usdc + ETokenAddress::USDC + } else if address == USDT_TOKEN_ADDRESS { + ETokenAddress::USDT + } else if address == CRV_TOKEN_ADDRESS { + ETokenAddress::CRV + } else if address == MATIC_TOKEN_ADDRESS { + ETokenAddress::MATIC + } else if address == DYDX_TOKEN_ADDRESS { + ETokenAddress::DYDX + } else if address == AMP_TOKEN_ADDRESS { + ETokenAddress::AMP + } else if address == CVX_TOKEN_ADDRESS { + ETokenAddress::CVX + } else if address == TUSD_TOKEN_ADDRESS { + ETokenAddress::TUSD + } else if address == USDD_TOKEN_ADDRESS { + ETokenAddress::USDD + } else if address == GUSD_TOKEN_ADDRESS { + ETokenAddress::GUSD + } else if address == LINK_TOKEN_ADDRESS { + ETokenAddress::LINK + } else if address == GRT_TOKEN_ADDRESS { + ETokenAddress::GRT + } else if address == COMP_TOKEN_ADDRESS { + ETokenAddress::COMP + } else if address == PEOPLE_TOKEN_ADDRESS { + ETokenAddress::PEOPLE + } else if address == GTC_TOKEN_ADDRESS { + ETokenAddress::GTC } else { ETokenAddress::Unknown } @@ -139,6 +192,8 @@ pub struct DataProviderConfig { pub contest_popularity_discord_role_id: String, pub contest_participant_discord_role_id: String, pub vip3_url: String, + pub geniidata_url: String, + pub geniidata_api_key: String, } impl Default for DataProviderConfig { @@ -172,6 +227,8 @@ impl DataProviderConfig { contest_popularity_discord_role_id: "".to_string(), contest_participant_discord_role_id: "".to_string(), vip3_url: "".to_string(), + geniidata_url: "".to_string(), + geniidata_api_key: "".to_string(), } } pub fn set_twitter_official_url(&mut self, v: String) { @@ -262,6 +319,14 @@ impl DataProviderConfig { debug!("set_vip3_url: {:?}", v); self.vip3_url = v; } + pub fn set_geniidata_url(&mut self, v: String) { + debug!("set_geniidata_url: {:?}", v); + self.geniidata_url = v; + } + pub fn set_geniidata_api_key(&mut self, v: String) { + debug!("set_geniidata_api_key: {:?}", v); + self.geniidata_api_key = v; + } } lazy_static! { @@ -298,6 +363,9 @@ pub enum Error { #[error("Nodereal error: {0}")] NoderealError(String), + + #[error("GeniiData error: {0}")] + GeniiDataError(String), } impl IntoErrorDetail for Error { @@ -355,3 +423,29 @@ impl ConvertParameterString for AchainableParams { }) } } + +pub fn hex_to_decimal(hex_string: &str) -> f64 { + let parts: Vec<&str> = hex_string.split('.').collect(); + + let integer_part = u64::from_str_radix(parts[0], 16).unwrap_or_default(); + + if parts.len() > 1 { + let decimal_part = u64::from_str_radix(parts[1], 16).unwrap(); + let decimal_str = format!("{}.{}", integer_part, decimal_part); + decimal_str.parse::().unwrap_or_default() + } else { + integer_part as f64 + } +} + +#[cfg(test)] +mod tests { + use crate::hex_to_decimal; + + #[test] + fn hex_to_decimal_works() { + let hex_string = "0000000000000000000000000000000000000000000000000000000babf2cf8b"; + let d = hex_to_decimal(hex_string); + assert_eq!(d, 50129457035.0); + } +} diff --git a/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs b/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs index 15131d5283..a028429bc5 100644 --- a/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs +++ b/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs @@ -17,7 +17,7 @@ #[cfg(all(not(feature = "std"), feature = "sgx"))] use crate::sgx_reexport_prelude::*; -use crate::{build_client, Error, HttpError, GLOBAL_DATA_PROVIDER_CONFIG}; +use crate::{build_client, hex_to_decimal, Error, HttpError, GLOBAL_DATA_PROVIDER_CONFIG}; use http::header::CONNECTION; use http_req::response::Headers; use itc_rest_client::{ @@ -67,22 +67,6 @@ impl NoderealChain { } } -pub enum NoderealNetwork { - Mainnet, - Testnet, - Goerli, -} - -impl NoderealNetwork { - pub fn to_string(&self) -> &'static str { - match self { - NoderealNetwork::Mainnet => "mainnet", - NoderealNetwork::Testnet => "testnet", - NoderealNetwork::Goerli => "goerli", - } - } -} - #[derive(Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct ReqPath { @@ -116,7 +100,7 @@ pub struct NoderealJsonrpcClient { } impl NoderealJsonrpcClient { - pub fn new(chain: NoderealChain, network: NoderealNetwork) -> Self { + pub fn new(chain: NoderealChain) -> Self { let api_key = GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().nodereal_api_key.clone(); let api_retry_delay = GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().nodereal_api_retry_delay; let api_retry_times = GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().nodereal_api_retry_times; @@ -125,9 +109,7 @@ impl NoderealJsonrpcClient { .unwrap() .nodereal_api_chain_network_url .clone(); - let base_url = api_url - .replace("{chain}", chain.to_string()) - .replace("{network}", network.to_string()); + let base_url = api_url.replace("{chain}", chain.to_string()); let mut headers = Headers::new(); headers.insert(CONNECTION.as_str(), "close"); @@ -244,6 +226,7 @@ pub trait NftApiList { fn get_token_balance_721(&mut self, param: &GetTokenBalance721Param) -> Result; } +// NFT API impl NftApiList for NoderealJsonrpcClient { // https://docs.nodereal.io/reference/nr_getnftholdings fn get_nft_holdings( @@ -285,14 +268,145 @@ impl NftApiList for NoderealJsonrpcClient { params, id: Id::Number(1), }; - self.post(&req_body) - .map_err(|e| Error::RequestError(format!("{:?}", e))) - .map(|resp| { + + match self.post(&req_body) { + Ok(resp) => { // result example: '0x', '0x8' debug!("get_token_balance_721, response: {:?}", resp); - let result = resp.result.as_str().unwrap(); - usize::from_str_radix(&result[2..], 16).unwrap_or_default() - }) + match resp.result.as_str() { + Some(result) => Ok(usize::from_str_radix(&result[2..], 16).unwrap_or_default()), + None => Err(Error::RequestError(format!( + "Cannot tansform response result {:?} to &str", + resp.result + ))), + } + }, + Err(e) => Err(Error::RequestError(format!("{:?}", e))), + } + } +} + +#[derive(Serialize, Debug)] +pub struct GetTokenBalance20Param { + // The address of the contract + pub contract_address: String, + // Target address + pub address: String, + // The block number in hex format or the string 'latest' or 'earliest' on which the balance will be checked + pub block_number: String, +} + +// Fungible Tokens API +pub trait FungibleApiList { + fn get_token_balance_20(&mut self, param: &GetTokenBalance20Param) -> Result; + fn get_token_holdings(&mut self, address: &str) -> Result; +} + +impl FungibleApiList for NoderealJsonrpcClient { + // https://docs.nodereal.io/reference/nr_gettokenbalance20 + fn get_token_balance_20(&mut self, param: &GetTokenBalance20Param) -> Result { + let params: Vec = + vec![param.contract_address.clone(), param.address.clone(), param.block_number.clone()]; + debug!("get_token_balance_20: {:?}", param); + let req_body = RpcRequest { + jsonrpc: "2.0".to_string(), + method: "nr_getTokenBalance20".to_string(), + params, + id: Id::Number(1), + }; + + match self.post(&req_body) { + Ok(resp) => { + // result example: '0x', '0x8' + debug!("get_token_balance_20, response: {:?}", resp); + match resp.result.as_str() { + Some(result) => Ok(hex_to_decimal(&result[2..])), + None => Err(Error::RequestError(format!( + "Cannot tansform response result {:?} to &str", + resp.result + ))), + } + }, + Err(e) => Err(Error::RequestError(format!("{:?}", e))), + } + } + + fn get_token_holdings(&mut self, address: &str) -> Result { + let params: Vec = vec![address.to_string(), "0x1".to_string(), "0x64".to_string()]; + debug!("get_token_holdings: {:?}", params); + + let req_body = RpcRequest { + jsonrpc: "2.0".to_string(), + method: "nr_getTokenHoldings".to_string(), + params: params.to_vec(), + id: Id::Number(1), + }; + + self.post(&req_body) + } +} + +pub trait EthBalance { + fn get_balance(&mut self, address: &str) -> Result; +} + +impl EthBalance for NoderealJsonrpcClient { + fn get_balance(&mut self, address: &str) -> Result { + let params = vec![address.to_string(), "latest".to_string()]; + + let req_body = RpcRequest { + jsonrpc: "2.0".to_string(), + method: "eth_getBalance".to_string(), + params, + id: Id::Number(1), + }; + + match self.post(&req_body) { + Ok(resp) => { + // result example: '0x', '0x8' + debug!("eth_getBalance, response: {:?}", resp); + match resp.result.as_str() { + Some(result) => Ok(hex_to_decimal(&result[2..])), + None => Err(Error::RequestError(format!( + "Cannot tansform response result {:?} to &str", + resp.result + ))), + } + }, + Err(e) => Err(Error::RequestError(format!("{:?}", e))), + } + } +} + +pub trait TransactionCount { + fn get_transaction_count(&mut self, address: &str) -> Result; +} + +impl TransactionCount for NoderealJsonrpcClient { + fn get_transaction_count(&mut self, address: &str) -> Result { + let params = vec![address.to_string(), "latest".to_string()]; + + let req_body = RpcRequest { + jsonrpc: "2.0".to_string(), + method: "eth_getTransactionCount".to_string(), + params, + id: Id::Number(1), + }; + + match self.post(&req_body) { + Ok(resp) => { + // result example: '0x', '0x8' + debug!("eth_getTransactionCount, response: {:?}", resp); + match resp.result.as_str() { + Some(result) => Ok(hex_to_decimal(&result[2..]) as u64), + None => Err(Error::RequestError(format!( + "Cannot tansform response result {:?} to &str", + resp.result + ))), + } + }, + Err(e) => Err(Error::RequestError(format!("{:?}", e))), + } } } @@ -317,7 +431,7 @@ mod tests { #[test] fn does_get_nft_holdings_works() { init(); - let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth, NoderealNetwork::Mainnet); + let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth); let param = GetNFTHoldingsParam { account_address: "0x49AD262C49C7aA708Cc2DF262eD53B64A17Dd5EE".into(), token_type: "ERC721".into(), @@ -336,7 +450,7 @@ mod tests { #[test] fn does_get_token_balance_721_works() { init(); - let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth, NoderealNetwork::Mainnet); + let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth); let param = GetTokenBalance721Param { token_address: "0x07D971C03553011a48E951a53F48632D37652Ba1".into(), account_address: "0x49AD262C49C7aA708Cc2DF262eD53B64A17Dd5EE".into(), @@ -345,4 +459,17 @@ mod tests { let result = client.get_token_balance_721(¶m).unwrap(); assert_eq!(result, 1); } + + #[test] + fn does_get_token_balance_20_works() { + init(); + let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth); + let param = GetTokenBalance20Param { + contract_address: "0x76A797A59Ba2C17726896976B7B3747BfD1d220f".into(), + address: "0x85Be4e2ccc9c85BE8783798B6e8A101BDaC6467F".into(), + block_number: "latest".into(), + }; + let result = client.get_token_balance_20(¶m).unwrap(); + assert_eq!(result, 800.1); + } } diff --git a/tee-worker/litentry/core/mock-server/src/nodereal_jsonrpc.rs b/tee-worker/litentry/core/mock-server/src/nodereal_jsonrpc.rs index c72d5fa4eb..fc7643d5b5 100644 --- a/tee-worker/litentry/core/mock-server/src/nodereal_jsonrpc.rs +++ b/tee-worker/litentry/core/mock-server/src/nodereal_jsonrpc.rs @@ -28,6 +28,14 @@ pub(crate) fn query() -> impl Filter = json + .get("params") + .unwrap() + .as_array() + .unwrap() + .iter() + .filter_map(|v| v.as_str().map(String::from)) + .collect(); match method { "nr_getNFTHoldings" => { let result = GetNFTHoldingsResult { @@ -54,6 +62,22 @@ pub(crate) fn query() -> impl Filter { + let value = match params[1].as_str() { + // 100_000_000_000 + "0x85be4e2ccc9c85be8783798b6e8a101bdac6467f" => "0x174876E800", + // 3000 + "0x90d53026a47ac20609accc3f2ddc9fb9b29bb310" => "0xBB8", + // 800.1 + _ => "0x320.1", + }; + let body = RpcResponse { + jsonrpc: "2.0".into(), + id: Id::Number(1), + result: serde_json::to_value(value).unwrap(), + }; + Response::builder().body(serde_json::to_string(&body).unwrap()) + }, _ => Response::builder().status(404).body(String::from("Error query")), } }) diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs index 7c3f7e08cb..3a720057d2 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs @@ -122,6 +122,19 @@ where Assertion::WeirdoGhostGangHolder => lc_assertion_build::nodereal::nft_holder::weirdo_ghost_gang_holder::build(&self.req), + + Assertion::LITStaking => lc_assertion_build::lit_staking::build(&self.req), + + Assertion::EVMAmountHolding(token_type) => + lc_assertion_build::nodereal::amount_holding::evm_amount_holding::build( + &self.req, token_type, + ), + + Assertion::BRC20AmountHolder => + lc_assertion_build::brc20::amount_holder::build(&self.req), + + Assertion::CryptoSummary => + lc_assertion_build::nodereal::crypto_summary::build(&self.req), }?; // post-process the credential @@ -133,11 +146,14 @@ where ) })?; + credential.parachain_block_number = self.req.parachain_block_number; + credential.sidechain_block_number = self.req.sidechain_block_number; + let data_provider_config = DataProviderConfigReader::read() .map_err(|e| VCMPError::RequestVCFailed(self.req.assertion.clone(), e))?; - credential - .credential_subject - .set_endpoint(data_provider_config.credential_endpoint); + credential.credential_subject.endpoint = data_provider_config.credential_endpoint; + + credential.credential_subject.assertion_text = format!("{:?}", self.req.assertion); credential.issuer.id = Identity::Substrate(enclave_account.into()).to_did().map_err(|e| { diff --git a/tee-worker/litentry/core/stf-task/receiver/src/mock.rs b/tee-worker/litentry/core/stf-task/receiver/src/mock.rs index 5417849d82..452411ea4d 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/mock.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/mock.rs @@ -37,11 +37,12 @@ pub fn construct_assertion_request(assertion: Assertion) -> RequestType { let request: RequestType = AssertionBuildRequest { shard, signer: public_id.into(), - enclave_account: public_id.into(), who: public_id.into(), assertion, identities: vec![], maybe_key: Some(key), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, top_hash: H256::zero(), req_ext_hash: H256::zero(), } diff --git a/tee-worker/litentry/core/stf-task/sender/Cargo.toml b/tee-worker/litentry/core/stf-task/sender/Cargo.toml index 04d55f4a85..86e47cd01c 100644 --- a/tee-worker/litentry/core/stf-task/sender/Cargo.toml +++ b/tee-worker/litentry/core/stf-task/sender/Cargo.toml @@ -27,7 +27,7 @@ sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "polkad itp-types = { path = "../../../../core-primitives/types", default-features = false } # litentry -itp-stf-primitives = { default-features = false, path = "../../../../core-primitives/stf-primitives" } +itp-stf-primitives = { path = "../../../../core-primitives/stf-primitives", default-features = false } litentry-primitives = { path = "../../../primitives", default-features = false } [features] @@ -44,4 +44,6 @@ std = [ "thiserror", "url", "itp-types/std", + "itp-stf-primitives/std", + "litentry-primitives/std", ] diff --git a/tee-worker/litentry/core/stf-task/sender/src/lib.rs b/tee-worker/litentry/core/stf-task/sender/src/lib.rs index a4ff25aac3..96042e12af 100644 --- a/tee-worker/litentry/core/stf-task/sender/src/lib.rs +++ b/tee-worker/litentry/core/stf-task/sender/src/lib.rs @@ -30,14 +30,15 @@ pub mod sgx_reexport_prelude { pub use url_sgx as url; } -use itp_types::{AccountId, H256}; +// TODO: the sidechain block number type is chaotic from upstream +use itp_types::{AccountId, BlockNumber as SidechainBlockNumber, H256}; pub mod error; pub mod stf_task_sender; use codec::{Decode, Encode}; pub use error::Result; -use itp_stf_primitives::types::ShardIdentifier; use litentry_primitives::{ - Assertion, Identity, IdentityNetworkTuple, RequestAesKey, Web2ValidationData, Web3Network, + Assertion, Identity, IdentityNetworkTuple, ParentchainBlockNumber, RequestAesKey, + ShardIdentifier, Web2ValidationData, Web3Network, }; use sp_runtime::traits::ConstU32; use sp_std::prelude::Vec; @@ -85,11 +86,12 @@ pub type MaxIdentityLength = ConstU32<64>; pub struct AssertionBuildRequest { pub shard: ShardIdentifier, pub signer: AccountId, - pub enclave_account: AccountId, pub who: Identity, pub assertion: Assertion, pub identities: Vec, pub top_hash: H256, + pub parachain_block_number: ParentchainBlockNumber, + pub sidechain_block_number: SidechainBlockNumber, pub maybe_key: Option, pub req_ext_hash: H256, } diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs index c14a874f00..4155fd20e5 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs @@ -19,15 +19,13 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam #[cfg(all(not(feature = "std"), feature = "sgx"))] pub use crate::sgx_reexport_prelude::*; -mod vc_handling; - use crate::vc_handling::VCRequestHandler; use codec::{Decode, Encode}; +use frame_support::{ensure, sp_runtime::traits::One}; pub use futures; -use ita_sgx_runtime::{Hash, Runtime}; +use ita_sgx_runtime::{pallet_imt::get_eligible_identities, BlockNumber, Hash, Runtime}; use ita_stf::{ - aes_encrypt_default, helpers::enclave_signer_account, trusted_call_result::RequestVCResult, - ConvertAccountId, Getter, OpaqueCall, SgxParentchainTypeConverter, TrustedCall, + aes_encrypt_default, trusted_call_result::RequestVCResult, Getter, OpaqueCall, TrustedCall, TrustedCallSigned, TrustedOperation, H256, }; use itp_extrinsics_factory::CreateExtrinsics; @@ -39,15 +37,16 @@ use itp_sgx_crypto::{ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_executor::traits::StfEnclaveSigning; use itp_stf_state_handler::handle_state::HandleState; -use itp_storage::{storage_map_key, StorageHasher}; +use itp_storage::{storage_map_key, storage_value_key, StorageHasher}; use itp_top_pool_author::traits::AuthorApi; -use itp_types::parentchain::ParentchainId; +use itp_types::{parentchain::ParentchainId, BlockNumber as SidechainBlockNumber}; use lc_stf_task_receiver::StfTaskContext; use lc_stf_task_sender::AssertionBuildRequest; use lc_vc_task_sender::init_vc_task_sender_storage; use litentry_primitives::{ - aes_decrypt, AesOutput, Identity, IdentityNetworkTuple, RequestAesKey, ShardIdentifier, + aes_decrypt, AesOutput, Identity, ParentchainBlockNumber, RequestAesKey, ShardIdentifier, }; +use log::*; use pallet_identity_management_tee::{identity_context::sort_id_graph, IdentityContext}; use std::{ format, @@ -57,6 +56,8 @@ use std::{ }; use threadpool::ThreadPool; +mod vc_handling; + pub fn run_vc_handler_runner( context: Arc>, extrinsic_factory: Arc, @@ -89,7 +90,7 @@ pub fn run_vc_handler_runner( extrinsic_factory_pool, node_metadata_repo_pool, )) { - log::warn!("Unable to submit response back to the handler: {:?}", e); + warn!("Unable to submit response back to the handler: {:?}", e); } }); } @@ -133,11 +134,11 @@ where .to_call() .ok_or_else(|| "Failed to convert trusted operation to trusted call".to_string())?; - if let TrustedCall::request_vc(signer, who, assertion, maybe_key, _hash) = + if let TrustedCall::request_vc(signer, who, assertion, maybe_key, req_ext_hash) = trusted_call.call.clone() { let key = maybe_key.ok_or_else(|| "User shielding key not provided".to_string())?; - let identities: Vec = context + let (identities, parachain_block_number, sidechain_block_number) = context .state_handler .execute_on_current(&shard, |state, _| { let prefix_key = storage_map_key( @@ -146,45 +147,62 @@ where &who, &StorageHasher::Blake2_128Concat, ); - let mut id_graph = - state.iter_prefix::>(&prefix_key).unwrap(); + + // `None` means empty IDGraph, thus `unwrap_or_default` + let mut id_graph = state + .iter_prefix::>(&prefix_key) + .unwrap_or_default(); // Sorts the IDGraph in place sort_id_graph::(&mut id_graph); + if id_graph.is_empty() { + // we are safe to use `default_web3networks` and `Active` as IDGraph would be non-empty otherwise + id_graph.push(( + who.clone(), + IdentityContext::new(BlockNumber::one(), who.default_web3networks()), + )); + } + + // should never be `None`, but use `unwrap_or_default` to not panic + let parachain_block_number = state + .get(&storage_value_key("Parentchain", "Number")) + .and_then(|v| ParentchainBlockNumber::decode(&mut v.as_slice()).ok()) + .unwrap_or_default(); + let sidechain_block_number = state + .get(&storage_value_key("System", "Number")) + .and_then(|v| SidechainBlockNumber::decode(&mut v.as_slice()).ok()) + .unwrap_or_default(); + let assertion_networks = assertion.clone().get_supported_web3networks(); - let identities: Vec = id_graph - .into_iter() - .filter(|item| item.1.is_active()) - .map(|item| { - let mut networks = item.1.web3networks.to_vec(); - networks.retain(|n| assertion_networks.contains(n)); - (item.0, networks) - }) - .collect(); - - identities + ( + get_eligible_identities(id_graph, assertion_networks), + parachain_block_number, + sidechain_block_number, + ) }) .map_err(|e| format!("Failed to fetch sidechain data due to: {:?}", e))?; + ensure!(!identities.is_empty(), "No eligible identity".to_string()); + let signer = signer .to_account_id() .ok_or_else(|| "Invalid signer account, failed to convert".to_string())?; - let assertion_build: AssertionBuildRequest = AssertionBuildRequest { + let req = AssertionBuildRequest { shard, signer, - enclave_account: enclave_signer_account(), who, assertion, identities, - maybe_key, top_hash: H256::zero(), - req_ext_hash: H256::zero(), + parachain_block_number, + sidechain_block_number, + maybe_key, + req_ext_hash, }; - let vc_request_handler = - VCRequestHandler { req: assertion_build, context: context.clone() }; + let vc_request_handler = VCRequestHandler { req, context: context.clone() }; let response = vc_request_handler .process() .map_err(|e| format!("Failed to build assertion due to: {:?}", e))?; @@ -194,15 +212,9 @@ where .unwrap() .unwrap(); let result = aes_encrypt_default(&key, &response.vc_payload); - let account = SgxParentchainTypeConverter::convert( - match response.assertion_request.who.to_account_id() { - Some(s) => s, - None => return Err("Failed to convert account".to_string()), - }, - ); let call = OpaqueCall::from_tuple(&( call_index, - account, + response.assertion_request.who, response.assertion_request.assertion, response.vc_index, response.vc_hash, diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs index 9a526c2b99..1ad285938d 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs @@ -98,6 +98,19 @@ where Assertion::WeirdoGhostGangHolder => lc_assertion_build::nodereal::nft_holder::weirdo_ghost_gang_holder::build(&self.req), + + Assertion::LITStaking => lc_assertion_build::lit_staking::build(&self.req), + + Assertion::EVMAmountHolding(token_type) => + lc_assertion_build::nodereal::amount_holding::evm_amount_holding::build( + &self.req, token_type, + ), + + Assertion::BRC20AmountHolder => + lc_assertion_build::brc20::amount_holder::build(&self.req), + + Assertion::CryptoSummary => + lc_assertion_build::nodereal::crypto_summary::build(&self.req), }?; // post-process the credential @@ -111,9 +124,7 @@ where let data_provider_config = DataProviderConfigReader::read() .map_err(|e| VCMPError::RequestVCFailed(self.req.assertion.clone(), e))?; - credential - .credential_subject - .set_endpoint(data_provider_config.credential_endpoint); + credential.credential_subject.endpoint = data_provider_config.credential_endpoint; credential.issuer.id = Identity::Substrate(enclave_account.into()).to_did().map_err(|e| { diff --git a/tee-worker/litentry/pallets/identity-management/src/identity_context.rs b/tee-worker/litentry/pallets/identity-management/src/identity_context.rs index 36a91a7112..1dff25389e 100644 --- a/tee-worker/litentry/pallets/identity-management/src/identity_context.rs +++ b/tee-worker/litentry/pallets/identity-management/src/identity_context.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . -use crate::{BlockNumberOf, Config, Web3Network}; +use crate::{BlockNumberOf, Config, IDGraph, Web3Network}; use codec::{Decode, Encode}; use core::cmp::Ordering; -use litentry_primitives::Identity; +use litentry_primitives::{Identity, IdentityNetworkTuple}; use scale_info::TypeInfo; use sp_std::vec::Vec; @@ -83,3 +83,32 @@ pub fn sort_id_graph(id_graph: &mut [(Identity, IdentityContext)]) } }); } + +// get the active identities in the `id_graph` whose web3networks match the `desired_web3networks`, +// return a `Vec<(Identity, Vec)` with retained web3networks +pub fn get_eligible_identities( + id_graph: IDGraph, + desired_web3networks: Vec, +) -> Vec { + id_graph + .into_iter() + .filter_map(|item| { + if item.1.is_active() { + let mut networks = item.1.web3networks.to_vec(); + // filter out identities whose web3networks are not supported by this specific `assertion`. + // We do it here before every request sending because: + // - it's a common step for all assertion buildings, for those assertions which only + // care about web2 identities, this step will empty `IdentityContext.web3networks` + // - it helps to reduce the request size a bit + networks.retain(|n| desired_web3networks.contains(n)); + if networks.is_empty() && item.0.is_web3() { + None + } else { + Some((item.0, networks)) + } + } else { + None + } + }) + .collect() +} diff --git a/tee-worker/litentry/pallets/identity-management/src/lib.rs b/tee-worker/litentry/pallets/identity-management/src/lib.rs index 68e5c43d84..d5f12ee04b 100644 --- a/tee-worker/litentry/pallets/identity-management/src/lib.rs +++ b/tee-worker/litentry/pallets/identity-management/src/lib.rs @@ -42,7 +42,8 @@ use frame_support::{pallet_prelude::*, sp_runtime::traits::One, traits::StorageV use frame_system::pallet_prelude::*; pub use litentry_primitives::{ - all_evm_web3networks, all_substrate_web3networks, Identity, ParentchainBlockNumber, Web3Network, + all_bitcoin_web3networks, all_evm_web3networks, all_substrate_web3networks, Identity, + ParentchainBlockNumber, Web3Network, }; use sp_core::{blake2_256, H256}; use sp_std::{vec, vec::Vec}; @@ -94,8 +95,6 @@ pub mod pallet { IdentityNotExist, /// creating the prime identity manually is disallowed LinkPrimeIdentityDisallowed, - /// deactivate prime identity should be disallowed - DeactivatePrimeIdentityDisallowed, /// IDGraph len limit reached IDGraphLenLimitReached, /// identity doesn't match the network types @@ -147,25 +146,7 @@ pub mod pallet { Error::::WrongWeb3NetworkTypes ); - // if there's no IDGraph, create one - `who` will be the prime identity - // please note the web3networks for the prime identity will be all avaiable networks - if IDGraphs::::get(&who, &who).is_none() { - ensure!( - !LinkedIdentities::::contains_key(&who), - Error::::IdentityAlreadyLinked - ); - - let prime_identity_web3networks = match who { - Identity::Substrate(_) => all_substrate_web3networks(), - Identity::Evm(_) => all_evm_web3networks(), - _ => vec![], - }; - let context = >::new( - ::BlockNumber::one(), - prime_identity_web3networks, - ); - Self::insert_identity_with_limit(&who, &who, context)?; - } + Self::maybe_create_id_graph(&who)?; let context = >::new(>::block_number(), web3networks); @@ -182,8 +163,8 @@ pub mod pallet { identity: Identity, ) -> DispatchResult { T::ManageOrigin::ensure_origin(origin)?; + Self::maybe_create_id_graph(&who)?; ensure!(IDGraphs::::contains_key(&who, &identity), Error::::IdentityNotExist); - ensure!(identity != who, Error::::DeactivatePrimeIdentityDisallowed); IDGraphs::::try_mutate(&who, &identity, |context| { let mut c = context.take().ok_or(Error::::IdentityNotExist)?; @@ -203,7 +184,9 @@ pub mod pallet { identity: Identity, ) -> DispatchResult { T::ManageOrigin::ensure_origin(origin)?; + Self::maybe_create_id_graph(&who)?; ensure!(IDGraphs::::contains_key(&who, &identity), Error::::IdentityNotExist); + IDGraphs::::try_mutate(&who, &identity, |context| { let mut c = context.take().ok_or(Error::::IdentityNotExist)?; c.activate(); @@ -223,7 +206,9 @@ pub mod pallet { web3networks: Vec, ) -> DispatchResult { T::ManageOrigin::ensure_origin(origin)?; + Self::maybe_create_id_graph(&who)?; ensure!(IDGraphs::::contains_key(&who, &identity), Error::::IdentityNotExist); + IDGraphs::::try_mutate(&who, &identity, |context| { let mut c = context.take().ok_or(Error::::IdentityNotExist)?; ensure!( @@ -274,6 +259,25 @@ pub mod pallet { } impl Pallet { + // try to create an IDGraph if there's none - `who` will be the prime identity + // please note the web3networks for the prime identity will be all avaiable networks + fn maybe_create_id_graph(who: &Identity) -> Result<(), DispatchError> { + if IDGraphs::::get(who, who).is_none() { + ensure!( + !LinkedIdentities::::contains_key(who), + Error::::IdentityAlreadyLinked + ); + + let context = >::new( + ::BlockNumber::one(), + who.default_web3networks(), + ); + + Self::insert_identity_with_limit(who, who, context)?; + } + Ok(()) + } + fn insert_identity_with_limit( owner: &Identity, identity: &Identity, @@ -293,7 +297,10 @@ pub mod pallet { } // get the whole IDGraph, sorted by `link_block` (earliest -> latest) - pub fn get_id_graph(who: &Identity) -> IDGraph { + // + // TODO: shall we change the return type to Option> and return + // `None` if the IDGraph doesn't exist? + pub fn id_graph(who: &Identity) -> IDGraph { let mut id_graph = IDGraphs::iter_prefix(who).collect::>(); // Initial sort to ensure a deterministic order @@ -302,10 +309,14 @@ pub mod pallet { id_graph } - pub fn all_id_graph_hash() -> Vec<(Identity, H256)> { - IDGraphLens::::iter_keys() - .map(|k| (k.clone(), H256::from(blake2_256(&Self::get_id_graph(&k).encode())))) - .collect() + // get the IDGraph hash of the given `who` + pub fn id_graph_hash(who: &Identity) -> Option { + let id_graph = Self::id_graph(who); + if id_graph.is_empty() { + None + } else { + Some(H256::from(blake2_256(&id_graph.encode()))) + } } // get count of all keys account + identity in the IDGraphs diff --git a/tee-worker/litentry/pallets/identity-management/src/tests.rs b/tee-worker/litentry/pallets/identity-management/src/tests.rs index 717fdf05af..8660d370a8 100644 --- a/tee-worker/litentry/pallets/identity-management/src/tests.rs +++ b/tee-worker/litentry/pallets/identity-management/src/tests.rs @@ -14,13 +14,38 @@ // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . -use crate::{mock::*, Error, IDGraph, Identity, IdentityContext, IdentityStatus, Web3Network}; +use crate::{ + get_eligible_identities, mock::*, Error, IDGraph, Identity, IdentityContext, IdentityStatus, + Web3Network, +}; use frame_support::{assert_err, assert_noop, assert_ok, traits::Get}; use sp_runtime::AccountId32; pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); +#[test] +fn get_eligible_identities_works() { + let mut id_graph = IDGraph::::default(); + id_graph.push(( + alice_substrate_identity(), + IdentityContext::new(1u64, vec![Web3Network::Litentry, Web3Network::Khala]), + )); + id_graph.push((alice_twitter_identity(1), IdentityContext::new(2u64, vec![]))); + let desired_web3networks = vec![Web3Network::Litentry, Web3Network::Polkadot]; + let mut identities = get_eligible_identities(id_graph.clone(), desired_web3networks.clone()); + assert_eq!(identities.len(), 2); + assert_eq!(identities[0].1, vec![Web3Network::Litentry]); + assert_eq!(identities[1].1, vec![]); + + // `alice_evm_identity` should be filtered out + id_graph.push((alice_evm_identity(), IdentityContext::new(1u64, vec![Web3Network::Bsc]))); + identities = get_eligible_identities(id_graph, desired_web3networks); + assert_eq!(identities.len(), 2); + assert_eq!(identities[0].1, vec![Web3Network::Litentry]); + assert_eq!(identities[1].1, vec![]); +} + #[test] fn link_twitter_identity_works() { new_test_ext().execute_with(|| { @@ -238,7 +263,7 @@ fn deactivate_identity_works() { } ); - let id_graph = IMT::get_id_graph(&who.clone()); + let id_graph = IMT::id_graph(&who.clone()); assert_eq!(id_graph.len(), 2); assert_eq!(crate::IDGraphLens::::get(&who.clone()), 2); @@ -256,24 +281,21 @@ fn deactivate_identity_works() { } ); - let id_graph = IMT::get_id_graph(&who.clone()) + let id_graph = IMT::id_graph(&who.clone()) .into_iter() .filter(|(_, c)| c.is_active()) .collect::>(); // "1": because of the main id is added by default when first calling link_identity. assert_eq!(id_graph.len(), 1); - assert_eq!(IMT::get_id_graph(&who.clone()).len(), 2); + assert_eq!(IMT::id_graph(&who.clone()).len(), 2); // identity is only deactivated, so it still exists assert_eq!(crate::IDGraphLens::::get(&who.clone()), 2); - assert_noop!( - IMT::deactivate_identity( - RuntimeOrigin::signed(ALICE), - who.clone(), - bob_substrate_identity(), - ), - Error::::DeactivatePrimeIdentityDisallowed - ); + assert_ok!(IMT::deactivate_identity( + RuntimeOrigin::signed(ALICE), + who.clone(), + bob_substrate_identity(), + )); }); } @@ -296,7 +318,7 @@ fn activate_identity_works() { status: IdentityStatus::Active } ); - let id_graph = IMT::get_id_graph(&who.clone()); + let id_graph = IMT::id_graph(&who.clone()); assert_eq!(id_graph.len(), 2); assert_eq!(crate::IDGraphLens::::get(&who.clone()), 2); @@ -313,7 +335,7 @@ fn activate_identity_works() { status: IdentityStatus::Inactive } ); - let id_graph = IMT::get_id_graph(&who.clone()) + let id_graph = IMT::id_graph(&who.clone()) .into_iter() .filter(|(_, c)| c.is_active()) .collect::>(); @@ -328,7 +350,7 @@ fn activate_identity_works() { alice_substrate_identity(), )); - let id_graph = IMT::get_id_graph(&who.clone()); + let id_graph = IMT::id_graph(&who.clone()); assert_eq!(id_graph.len(), 2); assert_eq!(crate::IDGraphLens::::get(&who.clone()), 2); }); @@ -418,7 +440,7 @@ fn get_id_graph_works() { )); } // the full id_graph should have 22 elements, including the prime_id - let id_graph = IMT::get_id_graph(&who); + let id_graph = IMT::id_graph(&who); assert_eq!(id_graph.len(), 22); assert_eq!(id_graph.get(0).unwrap().0, who); @@ -450,7 +472,7 @@ fn get_id_graph_identities_within_same_block() { )); } - let id_graph = IMT::get_id_graph(&who); + let id_graph = IMT::id_graph(&who); let sorted_identities = [ alice_evm_identity(), who.clone(), @@ -483,7 +505,7 @@ fn get_id_graph_identities_within_same_block() { )); } - let id_graph = IMT::get_id_graph(&who); + let id_graph = IMT::id_graph(&who); for (i, identity) in sorted_identities.iter().enumerate() { assert_eq!(&id_graph.get(i).unwrap().0, identity); @@ -535,7 +557,7 @@ fn remove_one_identity_works() { // alice's IDGraph should have 3 entries: // alice's identity itself, bob_substrate_identity, alice_twitter_identity - assert_eq!(IMT::get_id_graph(&alice).len(), 3); + assert_eq!(IMT::id_graph(&alice).len(), 3); assert_ok!(IMT::remove_identity( RuntimeOrigin::signed(ALICE), @@ -543,7 +565,7 @@ fn remove_one_identity_works() { vec![bob_substrate_identity()], )); - assert_eq!(IMT::get_id_graph(&alice).len(), 2); + assert_eq!(IMT::id_graph(&alice).len(), 2); }); } @@ -566,11 +588,11 @@ fn remove_whole_identity_graph_works() { // alice's IDGraph should have 3 entries: // alice's identity itself, bob_substrate_identity, alice_twitter_identity - assert_eq!(IMT::get_id_graph(&alice).len(), 3); + assert_eq!(IMT::id_graph(&alice).len(), 3); assert_ok!(IMT::remove_identity(RuntimeOrigin::signed(ALICE), alice.clone(), vec![],)); - assert_eq!(IMT::get_id_graph(&alice).len(), 0); + assert_eq!(IMT::id_graph(&alice).len(), 0); }); } diff --git a/tee-worker/litentry/primitives/Cargo.toml b/tee-worker/litentry/primitives/Cargo.toml index 421dd12823..2ad014ae67 100644 --- a/tee-worker/litentry/primitives/Cargo.toml +++ b/tee-worker/litentry/primitives/Cargo.toml @@ -5,14 +5,16 @@ name = "litentry-primitives" version = "0.1.0" [dependencies] +bitcoin = { version = "0.31.0", default-features = false, features = ["secp-recovery", "no-std"] } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -hex = { version = "0.4.3", default-features = false, optional = true } +hex = { version = "0.4.3", default-features = false } log = { version = "0.4", default-features = false } pallet-evm = { default-features = false, git = "https://github.com/integritee-network/frontier.git", branch = "bar/polkadot-v0.9.42" } rand = { version = "0.7", optional = true } rand-sgx = { package = "rand", git = "https://github.com/mesalock-linux/rand-sgx", tag = "sgx_1.1.3", features = ["sgx_tstd"], optional = true } ring = { version = "0.16.20", default-features = false } scale-info = { version = "2.4.0", default-features = false, features = ["derive"] } +secp256k1 = { version = "0.28.0", default-features = false } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } @@ -22,6 +24,7 @@ strum = { version = "0.25.0", default-features = false } strum_macros = { version = "0.25.0", default-features = false } # sgx dependencies +base64_sgx = { package = "base64", rev = "sgx_1.1.3", git = "https://github.com/mesalock-linux/rust-base64-sgx", optional = true } sgx_tstd = { git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "master", optional = true, features = ["net", "thread"] } # internal dependencies @@ -30,6 +33,9 @@ itp-utils = { path = "../../core-primitives/utils", default-features = false } parentchain-primitives = { package = "core-primitives", path = "../../../primitives/core", default-features = false } teerex-primitives = { path = "../../../primitives/teerex", default-features = false } +[dev-dependencies] +base64 = { version = "0.13", features = ["alloc"] } + [features] default = ["std"] production = [] @@ -53,4 +59,6 @@ std = [ "teerex-primitives/std", "rand", "log/std", + "bitcoin/std", + "secp256k1/std", ] diff --git a/tee-worker/litentry/primitives/src/bitcoin_address.rs b/tee-worker/litentry/primitives/src/bitcoin_address.rs new file mode 100644 index 0000000000..32dcdcafcb --- /dev/null +++ b/tee-worker/litentry/primitives/src/bitcoin_address.rs @@ -0,0 +1,57 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use bitcoin::{ + address::Address, key::PublicKey, network::Network, secp256k1::Secp256k1, XOnlyPublicKey, +}; +use core::str::FromStr; +use std::string::{String, ToString}; + +// Some dependency conflict of bitcoin crate with enclave building +// when putting these functions into core-premitives/utils. +pub fn p2wpkh_address(pubkey_string: &str) -> String { + let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); + let address = Address::p2wpkh(&pubkey, Network::Bitcoin); + if let Ok(address) = address { + return address.to_string() + } + "".to_string() +} + +pub fn p2sh_address(pubkey_string: &str) -> String { + let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); + let address = Address::p2shwpkh(&pubkey, Network::Bitcoin); + if let Ok(address) = address { + return address.to_string() + } + "".to_string() +} + +pub fn p2tr_address(pubkey_string: &str) -> String { + let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); + let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner); + // unisat wallet uses is this way + let secp = Secp256k1::verification_only(); + let address = Address::p2tr(&secp, xonly_pubkey, None, Network::Bitcoin); + address.to_string() +} + +pub fn p2pkh_address(pubkey_string: &str) -> String { + let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); + let address = Address::p2pkh(&pubkey, Network::Bitcoin); + address.to_string() +} diff --git a/tee-worker/litentry/primitives/src/bitcoin_signature.rs b/tee-worker/litentry/primitives/src/bitcoin_signature.rs new file mode 100644 index 0000000000..cb6db71a23 --- /dev/null +++ b/tee-worker/litentry/primitives/src/bitcoin_signature.rs @@ -0,0 +1,72 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . +#[cfg(feature = "std")] +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone, Debug)] +pub struct BitcoinSignature(pub [u8; 65]); + +impl TryFrom<&[u8]> for BitcoinSignature { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() == 65 { + let mut inner = [0u8; 65]; + inner.copy_from_slice(data); + Ok(BitcoinSignature(inner)) + } else { + Err(()) + } + } +} + +#[cfg(feature = "std")] +impl Serialize for BitcoinSignature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&hex::encode(self)) + } +} + +#[cfg(feature = "std")] +impl<'de> Deserialize<'de> for BitcoinSignature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let signature_hex = hex::decode(&String::deserialize(deserializer)?) + .map_err(|e| de::Error::custom(format!("{:?}", e)))?; + BitcoinSignature::try_from(signature_hex.as_ref()) + .map_err(|e| de::Error::custom(format!("{:?}", e))) + } +} + +impl AsRef<[u8; 65]> for BitcoinSignature { + fn as_ref(&self) -> &[u8; 65] { + &self.0 + } +} + +impl AsRef<[u8]> for BitcoinSignature { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} diff --git a/tee-worker/litentry/primitives/src/lib.rs b/tee-worker/litentry/primitives/src/lib.rs index f3a8f1b3af..9d8dbb7426 100644 --- a/tee-worker/litentry/primitives/src/lib.rs +++ b/tee-worker/litentry/primitives/src/lib.rs @@ -25,44 +25,52 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam mod aes; mod aes_request; +mod bitcoin_address; +mod bitcoin_signature; mod ethereum_signature; -mod identity; mod validation_data; pub use aes::*; pub use aes_request::*; +pub use bitcoin_address::*; +pub use bitcoin_signature::*; pub use ethereum_signature::*; -pub use identity::*; use sp_std::{boxed::Box, fmt::Debug, vec::Vec}; pub use validation_data::*; +use bitcoin::sign_message::{signed_msg_hash, MessageSignature}; use codec::{Decode, Encode, MaxEncodedLen}; use itp_sgx_crypto::ShieldingCryptoDecrypt; use itp_utils::hex::hex_encode; use log::error; pub use parentchain_primitives::{ - all_evm_web3networks, all_substrate_web3networks, all_web3networks, - AccountId as ParentchainAccountId, AchainableAmount, AchainableAmountHolding, + all_bitcoin_web3networks, all_evm_web3networks, all_substrate_web3networks, all_web3networks, + identity::*, AccountId as ParentchainAccountId, AchainableAmount, AchainableAmountHolding, AchainableAmountToken, AchainableAmounts, AchainableBasic, AchainableBetweenPercents, AchainableClassOfYear, AchainableDate, AchainableDateInterval, AchainableDatePercent, AchainableMirror, AchainableParams, AchainableToken, AmountHoldingTimeType, Assertion, Balance as ParentchainBalance, BlockNumber as ParentchainBlockNumber, BnbDigitDomainType, - BoundedWeb3Network, ContestType, ErrorDetail, ErrorString, GenericDiscordRoleType, - Hash as ParentchainHash, Header as ParentchainHeader, IMPError, Index as ParentchainIndex, - IntoErrorDetail, OneBlockCourseType, ParameterString, SchemaContentString, SchemaIdString, - Signature as ParentchainSignature, SoraQuizType, VCMPError, VIP3MembershipCardLevel, - Web3Network, ASSERTION_FROM_DATE, MINUTES, + BoundedWeb3Network, ContestType, EVMTokenType, ErrorDetail, ErrorString, + GenericDiscordRoleType, Hash as ParentchainHash, Header as ParentchainHeader, IMPError, + Index as ParentchainIndex, IntoErrorDetail, OneBlockCourseType, ParameterString, + SchemaContentString, SchemaIdString, Signature as ParentchainSignature, SoraQuizType, + VCMPError, VIP3MembershipCardLevel, Web3Network, ASSERTION_FROM_DATE, MINUTES, }; use scale_info::TypeInfo; use sp_core::{ecdsa, ed25519, sr25519, ByteArray}; -use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256}; +use sp_io::{ + crypto::secp256k1_ecdsa_recover, + hashing::{blake2_256, keccak_256}, +}; use sp_runtime::traits::Verify; use std::string::{String, ToString}; -pub use teerex_primitives::{decl_rsa_request, ShardIdentifier}; +pub use teerex_primitives::{decl_rsa_request, ShardIdentifier, SidechainBlockNumber}; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; +pub const LITENTRY_PRETTIFIED_MESSAGE_PREFIX: &str = "Litentry authorization token: "; + #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum LitentryMultiSignature { @@ -78,9 +86,15 @@ pub enum LitentryMultiSignature { /// An ECDSA/keccak256 signature. An Ethereum signature. hash message with keccak256 #[codec(index = 3)] Ethereum(EthereumSignature), - /// Same as the above, but the payload bytes are hex-encoded and prepended with a readable prefix + /// Same as above, but the payload bytes are prepended with a readable prefix and `0x` #[codec(index = 4)] EthereumPrettified(EthereumSignature), + /// Bitcoin signed message, a hex-encoded string of original &[u8] message, without `0x` prefix + #[codec(index = 5)] + Bitcoin(BitcoinSignature), + /// Same as above, but the payload bytes are prepended with a readable prefix and `0x` + #[codec(index = 6)] + BitcoinPrettified(BitcoinSignature), } impl LitentryMultiSignature { @@ -90,6 +104,7 @@ impl LitentryMultiSignature { self.verify_substrate(substrate_wrap(msg).as_slice(), address) || self.verify_substrate(msg, address), Identity::Evm(address) => self.verify_evm(msg, address), + Identity::Bitcoin(address) => self.verify_bitcoin(msg, address), _ => false, } } @@ -105,11 +120,10 @@ impl LitentryMultiSignature { Err(()) => false, }, (Self::Ecdsa(ref sig), who) => { - let m = sp_io::hashing::blake2_256(msg); + let m = blake2_256(msg); match sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) { Ok(pubkey) => - &sp_io::hashing::blake2_256(pubkey.as_ref()) - == >::as_ref(who), + &blake2_256(pubkey.as_ref()) == >::as_ref(who), _ => false, } }, @@ -119,34 +133,61 @@ impl LitentryMultiSignature { fn verify_evm(&self, msg: &[u8], signer: &Address20) -> bool { match self { - Self::Ethereum(ref sig) => { - let data = msg; - return verify_evm_signature(evm_eip191_wrap(data).as_slice(), sig, signer) - || verify_evm_signature(data, sig, signer) - }, + Self::Ethereum(ref sig) => + return verify_evm_signature(evm_eip191_wrap(msg).as_slice(), sig, signer) + || verify_evm_signature(msg, sig, signer), Self::EthereumPrettified(ref sig) => { - let user_readable_message = - "Litentry authorization token: ".to_string() + &hex_encode(msg); - let data = user_readable_message.as_bytes(); - return verify_evm_signature(evm_eip191_wrap(data).as_slice(), sig, signer) - || verify_evm_signature(data, sig, signer) + let prettified_msg = + LITENTRY_PRETTIFIED_MESSAGE_PREFIX.to_string() + &hex_encode(msg); + let msg = prettified_msg.as_bytes(); + return verify_evm_signature(evm_eip191_wrap(msg).as_slice(), sig, signer) + || verify_evm_signature(msg, sig, signer) + }, + _ => false, + } + } + + fn verify_bitcoin(&self, msg: &[u8], signer: &Address33) -> bool { + match self { + Self::Bitcoin(ref sig) => + verify_bitcoin_signature(hex::encode(msg).as_str(), sig, signer), + Self::BitcoinPrettified(ref sig) => { + let prettified_msg = + LITENTRY_PRETTIFIED_MESSAGE_PREFIX.to_string() + &hex_encode(msg); + verify_bitcoin_signature(prettified_msg.as_str(), sig, signer) }, _ => false, } } } -fn verify_evm_signature(data: &[u8], sig: &EthereumSignature, who: &Address20) -> bool { - let digest = keccak_256(data); +pub fn verify_evm_signature(msg: &[u8], sig: &EthereumSignature, who: &Address20) -> bool { + let digest = keccak_256(msg); return match recover_evm_address(&digest, sig.as_ref()) { Ok(recovered_evm_address) => recovered_evm_address == who.as_ref().as_slice(), Err(_e) => { - error!("Could not verify evm signature msg: {:?}, signer {:?}", data, who); + error!("Could not verify evm signature msg: {:?}, signer {:?}", msg, who); false }, } } +pub fn verify_bitcoin_signature(msg: &str, sig: &BitcoinSignature, who: &Address33) -> bool { + if let Ok(msg_sig) = MessageSignature::from_slice(sig.as_ref()) { + let msg_hash = signed_msg_hash(msg); + let secp = secp256k1::Secp256k1::new(); + return match msg_sig.recover_pubkey(&secp, msg_hash) { + Ok(recovered_pub_key) => &recovered_pub_key.inner.serialize() == who.as_ref(), + Err(_) => { + error!("Could not recover pubkey from bitcoin msg: {:?}, signer {:?}", msg, who); + false + }, + } + } + + false +} + impl From for LitentryMultiSignature { fn from(x: ed25519::Signature) -> Self { Self::Ed25519(x) @@ -209,3 +250,30 @@ pub struct BroadcastedRequest { pub payload: String, pub rpc_method: String, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn verify_bitcoin_signature_works() { + // generated by unisat-wallet API: https://docs.unisat.io/dev/unisat-developer-service/unisat-wallet + let msg: Vec = vec![ + 3, 93, 250, 112, 216, 101, 89, 57, 83, 88, 100, 252, 203, 15, 64, 127, 138, 37, 2, 40, + 147, 95, 245, 27, 97, 202, 62, 205, 151, 0, 175, 177, + ]; + let pubkey: Vec = vec![ + 3, 93, 250, 112, 216, 101, 89, 57, 83, 88, 100, 252, 203, 15, 64, 127, 138, 37, 2, 40, + 147, 95, 245, 27, 97, 202, 62, 205, 151, 0, 175, 177, 216, + ]; + let sig: Vec = base64::decode("G2LhyYzWT2o8UoBsuhJsqFgwm3tlE0cW4aseCXKqVuNATk6K/uEHlPzDFmtlMADywDHl5vLCWcNpwmQLD7n/yvc=").unwrap(); + + let pubkey_ref: &[u8] = pubkey.as_ref(); + let sig_ref: &[u8] = sig.as_ref(); + assert!(verify_bitcoin_signature( + hex::encode(msg).as_str(), + &sig_ref.try_into().unwrap(), + &pubkey_ref.try_into().unwrap() + )); + } +} diff --git a/tee-worker/litentry/primitives/src/validation_data.rs b/tee-worker/litentry/primitives/src/validation_data.rs index be0da9e31d..aac3427799 100644 --- a/tee-worker/litentry/primitives/src/validation_data.rs +++ b/tee-worker/litentry/primitives/src/validation_data.rs @@ -64,6 +64,8 @@ pub enum Web3ValidationData { Substrate(Web3CommonValidationData), #[codec(index = 1)] Evm(Web3CommonValidationData), + #[codec(index = 2)] + Bitcoin(Web3CommonValidationData), } impl Web3ValidationData { @@ -71,6 +73,7 @@ impl Web3ValidationData { match self { Self::Substrate(data) => &data.message, Self::Evm(data) => &data.message, + Self::Bitcoin(data) => &data.message, } } @@ -78,6 +81,7 @@ impl Web3ValidationData { match self { Self::Substrate(data) => &data.signature, Self::Evm(data) => &data.signature, + Self::Bitcoin(data) => &data.signature, } } } diff --git a/tee-worker/service/src/main_impl.rs b/tee-worker/service/src/main_impl.rs index f399835609..019fbdfe2e 100644 --- a/tee-worker/service/src/main_impl.rs +++ b/tee-worker/service/src/main_impl.rs @@ -160,6 +160,7 @@ pub(crate) fn main() { enclave_metrics_receiver, ))); + #[cfg(feature = "dcap")] let quoting_enclave_target_info = match enclave.qe_get_target_info() { Ok(target_info) => Some(target_info), Err(e) => { @@ -167,6 +168,7 @@ pub(crate) fn main() { None }, }; + #[cfg(feature = "dcap")] let quote_size = match enclave.qe_get_quote_size() { Ok(size) => Some(size), Err(e) => { @@ -175,6 +177,11 @@ pub(crate) fn main() { }, }; + #[cfg(not(feature = "dcap"))] + let quoting_enclave_target_info = None; + #[cfg(not(feature = "dcap"))] + let quote_size = None; + let data_provider_config = get_data_provider_config(&config); if let Some(run_config) = config.run_config() { @@ -1168,5 +1175,11 @@ fn get_data_provider_config(config: &Config) -> DataProviderConfig { if let Ok(v) = env::var("VIP3_URL") { data_provider_config.set_vip3_url(v); } + if let Ok(v) = env::var("GENIIDATA_URL") { + data_provider_config.set_geniidata_url(v); + } + if let Ok(v) = env::var("GENIIDATA_API_KEY") { + data_provider_config.set_geniidata_api_key(v); + } data_provider_config } diff --git a/tee-worker/service/src/prometheus_metrics.rs b/tee-worker/service/src/prometheus_metrics.rs index 1bb7e1e20f..66fc15e94f 100644 --- a/tee-worker/service/src/prometheus_metrics.rs +++ b/tee-worker/service/src/prometheus_metrics.rs @@ -265,6 +265,7 @@ fn handle_stf_call_request(req: RequestType, time: f64) { Identity::Github(_) => "Github", Identity::Substrate(_) => "Substrate", Identity::Evm(_) => "Evm", + Identity::Bitcoin(_) => "Bitcoin", }, RequestType::AssertionVerification(request) => match request.assertion { Assertion::A1 => "A1", @@ -286,6 +287,10 @@ fn handle_stf_call_request(req: RequestType, time: f64) { Assertion::GenericDiscordRole(_) => "GenericDiscordRole", Assertion::VIP3MembershipCard(..) => "VIP3MembershipCard", Assertion::WeirdoGhostGangHolder => "WeirdoGhostGangHolder", + Assertion::LITStaking => "LITStaking", + Assertion::EVMAmountHolding(_) => "EVMAmountHolding", + Assertion::BRC20AmountHolder => "BRC20AmountHolder", + Assertion::CryptoSummary => "CryptoSummary", }, }; inc_stf_calls(category, label); diff --git a/tee-worker/service/src/running-mode-config.json b/tee-worker/service/src/running-mode-config.json index 5fa6212dc2..117cd7e55c 100644 --- a/tee-worker/service/src/running-mode-config.json +++ b/tee-worker/service/src/running-mode-config.json @@ -17,11 +17,13 @@ "nodereal_api_retry_delay": 5000, "nodereal_api_retry_times": 2, "nodereal_api_url": "https://open-platform.nodereal.io/", - "nodereal_api_chain_network_url": "https://{chain}-{network}.nodereal.io/", + "nodereal_api_chain_network_url": "https://{chain}-mainnet.nodereal.io/", "contest_legend_discord_role_id": "CONTEST_LEGEND_DISCORD_ROLE_ID", "contest_popularity_discord_role_id": "CONTEST_POPULARITY_DISCORD_ROLE_ID", "contest_participant_discord_role_id": "CONTEST_PARTICIPANT_DISCORD_ROLE_ID", - "vip3_url": "https://dappapi.vip3.io/" + "vip3_url": "https://dappapi.vip3.io/", + "geniidata_url": "https://api.geniidata.com/api/1/brc20/balance?", + "geniidata_api_key": "142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac" }, "mock": { "twitter_official_url": "http://localhost:19527", @@ -41,11 +43,13 @@ "nodereal_api_retry_delay": 5000, "nodereal_api_retry_times": 2, "nodereal_api_url": "https://open-platform.nodereal.io/", - "nodereal_api_chain_network_url": "https://{chain}-{network}.nodereal.io/", + "nodereal_api_chain_network_url": "https://{chain}-mainnet.nodereal.io/", "contest_legend_discord_role_id": "CONTEST_LEGEND_DISCORD_ROLE_ID", "contest_popularity_discord_role_id": "CONTEST_POPULARITY_DISCORD_ROLE_ID", "contest_participant_discord_role_id": "CONTEST_PARTICIPANT_DISCORD_ROLE_ID", - "vip3_url": "https://dappapi.vip3.io/" + "vip3_url": "https://dappapi.vip3.io/", + "geniidata_url": "https://api.geniidata.com/api/1/brc20/balance?", + "geniidata_api_key": "142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac" }, "prod": { "twitter_official_url": "https://api.twitter.com", @@ -65,11 +69,13 @@ "nodereal_api_retry_delay": 5000, "nodereal_api_retry_times": 2, "nodereal_api_url": "https://open-platform.nodereal.io/", - "nodereal_api_chain_network_url": "https://{chain}-{network}.nodereal.io/", + "nodereal_api_chain_network_url": "https://{chain}-mainnet.nodereal.io/", "contest_legend_discord_role_id": "CONTEST_LEGEND_DISCORD_ROLE_ID", "contest_popularity_discord_role_id": "CONTEST_POPULARITY_DISCORD_ROLE_ID", "contest_participant_discord_role_id": "CONTEST_PARTICIPANT_DISCORD_ROLE_ID", - "vip3_url": "https://dappapi.vip3.io/" + "vip3_url": "https://dappapi.vip3.io/", + "geniidata_url": "https://api.geniidata.com/api/1/brc20/balance?", + "geniidata_api_key": "142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac" }, "staging": { "twitter_official_url": "https://api.twitter.com", @@ -89,10 +95,12 @@ "nodereal_api_retry_delay": 5000, "nodereal_api_retry_times": 2, "nodereal_api_url": "https://open-platform.nodereal.io/", - "nodereal_api_chain_network_url": "https://{chain}-{network}.nodereal.io/", + "nodereal_api_chain_network_url": "https://{chain}-mainnet.nodereal.io/", "contest_legend_discord_role_id": "CONTEST_LEGEND_DISCORD_ROLE_ID", "contest_popularity_discord_role_id": "CONTEST_POPULARITY_DISCORD_ROLE_ID", "contest_participant_discord_role_id": "CONTEST_PARTICIPANT_DISCORD_ROLE_ID", - "vip3_url": "https://dappapi.vip3.io/" + "vip3_url": "https://dappapi.vip3.io/", + "geniidata_url": "https://api.geniidata.com/api/1/brc20/balance?", + "geniidata_api_key": "142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac" } } \ No newline at end of file diff --git a/tee-worker/sidechain/consensus/aura/src/slot_proposer.rs b/tee-worker/sidechain/consensus/aura/src/slot_proposer.rs index 0a893d9a32..2baea76519 100644 --- a/tee-worker/sidechain/consensus/aura/src/slot_proposer.rs +++ b/tee-worker/sidechain/consensus/aura/src/slot_proposer.rs @@ -137,6 +137,7 @@ impl< sidechain_db .set_block_number(&sidechain_db.get_block_number().map_or(1, |n| n + 1)); sidechain_db.set_timestamp(&now_as_millis()); + sidechain_db.set_parentchain_block_number(latest_parentchain_header); sidechain_db }, ) diff --git a/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs b/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs index dc461aace3..c4c7a00220 100644 --- a/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs +++ b/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs @@ -330,12 +330,8 @@ where TCS: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, G: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, { - debug!("Author submit and watch AesRequest.."); - let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; - - info!("Got request hex: {:?}", &hex_encoded_params[0]); - std::println!("Got request hex: {:?}", &hex_encoded_params[0]); + info!("author_submitAndWatchAesRequest, request hex: {:?}", &hex_encoded_params[0]); let request = AesRequest::from_hex(&hex_encoded_params[0].clone()).map_err(|e| format!("{:?}", e))?; diff --git a/tee-worker/sidechain/state/Cargo.toml b/tee-worker/sidechain/state/Cargo.toml index 1b7207555e..538fb34c50 100644 --- a/tee-worker/sidechain/state/Cargo.toml +++ b/tee-worker/sidechain/state/Cargo.toml @@ -26,9 +26,6 @@ sp-io = { optional = true, default-features = false, features = ["disable_oom", # substrate deps sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -# test deps -[dev-dependencies] sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } [features] @@ -45,6 +42,7 @@ std = [ # optional std crates "codec/std", "thiserror", + "sp-runtime/std", ] sgx = [ # teaclave diff --git a/tee-worker/sidechain/state/src/lib.rs b/tee-worker/sidechain/state/src/lib.rs index 7517f72c27..01f5e086ec 100644 --- a/tee-worker/sidechain/state/src/lib.rs +++ b/tee-worker/sidechain/state/src/lib.rs @@ -42,6 +42,7 @@ use its_primitives::{ }; use sp_core::H256; use sp_io::KillStorageResult; +use sp_runtime::traits::Header as ParentchainHeaderTrait; /// Contains the necessary data to update the `SidechainDB` when importing a `SidechainBlock`. #[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)] @@ -160,6 +161,14 @@ pub trait SidechainSystemExt { /// Resets the events. fn reset_events(&mut self); + + /// Litentry: set the parentchain block number from the parentchain header + /// The reasons to put it here instead of calling `ParentchainPalletInterface::update_parentchain_block` somewhere are: + /// 1. The Stf::update_parentchain_block is too heavy weighted, where the whole state is loaded upon each parentchain + /// block import - btw it's not reachable for now as `storage_hashes_to_update_on_block` is always empty + /// 2. It represents the parentchain block number on which the current sidechain block is built, it's more natural to + /// call it in the state preprocessing before proposing a sidechain block + fn set_parentchain_block_number(&mut self, header: &PH); } impl SidechainSystemExt for T { @@ -192,4 +201,8 @@ impl SidechainSystemExt for T { self.clear_with_name("System", "EventCount"); self.clear_prefix_with_name("System", "EventTopics"); } + + fn set_parentchain_block_number(&mut self, header: &PH) { + self.set_with_name("Parentchain", "Number", header.number()) + } } diff --git a/tee-worker/ts-tests/integration-tests/common/common-types.ts b/tee-worker/ts-tests/integration-tests/common/common-types.ts index 67fadfbcf8..84fa7a930e 100644 --- a/tee-worker/ts-tests/integration-tests/common/common-types.ts +++ b/tee-worker/ts-tests/integration-tests/common/common-types.ts @@ -5,6 +5,7 @@ import { Metadata, TypeRegistry } from '@polkadot/types'; import { Wallet } from 'ethers'; import type { KeyringPair } from '@polkadot/keyring/types'; import type { HexString } from '@polkadot/util/types'; +import bitcore from 'bitcore-lib'; // If there are types already defined in the client-api, please avoid redefining these types. // Instead, make every effort to use the types that have been generated within the client-api. @@ -14,6 +15,9 @@ interface EthersWalletItem { interface SubstrateWalletItem { [key: string]: KeyringPair; } +interface BitcoinWalletItem { + [key: string]: bitcore.PrivateKey; +} export type IntegrationTestContext = { tee: WebSocketAsPromised; api: ApiPromise; @@ -21,9 +25,9 @@ export type IntegrationTestContext = { mrEnclave: HexString; ethersWallet: EthersWalletItem; substrateWallet: SubstrateWalletItem; + bitcoinWallet: BitcoinWalletItem; sidechainMetaData: Metadata; sidechainRegistry: TypeRegistry; - web3Signers: Web3Wallets[]; chainIdentifier: number; requestId: number; }; @@ -31,6 +35,7 @@ export type IntegrationTestContext = { export type Web3Wallets = { substrateWallet: KeyringPair; evmWallet: Wallet; + bitcoinWallet: bitcore.PrivateKey; }; export type JsonRpcRequest = { diff --git a/tee-worker/ts-tests/integration-tests/common/di-utils.ts b/tee-worker/ts-tests/integration-tests/common/di-utils.ts index 4d786c75e0..4242dfc1c6 100644 --- a/tee-worker/ts-tests/integration-tests/common/di-utils.ts +++ b/tee-worker/ts-tests/integration-tests/common/di-utils.ts @@ -4,15 +4,14 @@ import { Codec } from '@polkadot/types/types'; import { TypeRegistry } from '@polkadot/types'; import { Bytes } from '@polkadot/types-codec'; import { IntegrationTestContext, JsonRpcRequest } from './common-types'; -import { WorkerRpcReturnValue, TrustedCallSigned, Getter } from 'parachain-api'; -import { encryptWithTeeShieldingKey, Signer, encryptWithAes } from './utils'; +import { WorkerRpcReturnValue, TrustedCallSigned, Getter, CorePrimitivesIdentity } from 'parachain-api'; +import { encryptWithTeeShieldingKey, Signer, encryptWithAes, sleep } from './utils'; import { aesKey, decodeRpcBytesAsString, keyNonce } from './call'; import { createPublicKey, KeyObject } from 'crypto'; import WebSocketAsPromised from 'websocket-as-promised'; -import { u32, Option, u8, Vector } from 'scale-ts'; -import { Index } from '@polkadot/types/interfaces'; +import { H256, Index } from '@polkadot/types/interfaces'; import { blake2AsHex } from '@polkadot/util-crypto'; -import type { LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext } from 'sidechain-api'; +import type { PalletIdentityManagementTeeIdentityContext } from 'sidechain-api'; import { createJsonRpcRequest, nextRequestId } from './helpers'; // Send the request to worker ws @@ -94,9 +93,21 @@ export const createSignedTrustedCall = async ( if (withWrappedBytes) { payload = u8aConcat(stringToU8a(''), payload, stringToU8a('')); } - const signature = parachainApi.createType('LitentryMultiSignature', { - [signer.type()]: u8aToHex(await signer.sign(payload)), - }); + + let signature; + + // for bitcoin signature, we expect a hex-encoded `string` without `0x` prefix + // TODO: any better idiomatic way? + if (signer.type() === 'bitcoin') { + const payloadStr = u8aToHex(payload).substring(2); + signature = parachainApi.createType('LitentryMultiSignature', { + [signer.type()]: u8aToHex(await signer.sign(payloadStr)), + }); + } else { + signature = parachainApi.createType('LitentryMultiSignature', { + [signer.type()]: u8aToHex(await signer.sign(payload)), + }); + } return parachainApi.createType('TrustedCallSigned', { call: call, index: nonce, @@ -115,9 +126,18 @@ export const createSignedTrustedGetter = async ( [variant]: parachainApi.createType(argType, params), }); const payload = getter.toU8a(); - const signature = parachainApi.createType('LitentryMultiSignature', { - [signer.type()]: u8aToHex(await signer.sign(payload)), - }); + + let signature; + if (signer.type() === 'bitcoin') { + const payloadStr = u8aToHex(payload).substring(2); + signature = parachainApi.createType('LitentryMultiSignature', { + [signer.type()]: u8aToHex(await signer.sign(payloadStr)), + }); + } else { + signature = parachainApi.createType('LitentryMultiSignature', { + [signer.type()]: u8aToHex(await signer.sign(payload)), + }); + } return parachainApi.createType('TrustedGetterSigned', { getter: getter, signature: signature, @@ -138,7 +158,7 @@ export async function createSignedTrustedCallLinkIdentity( mrenclave: string, nonce: Codec, signer: Signer, - subject: LitentryPrimitivesIdentity, + primeIdentity: CorePrimitivesIdentity, identity: string, validationData: string, web3networks: string, @@ -154,7 +174,7 @@ export async function createSignedTrustedCallLinkIdentity( signer, mrenclave, nonce, - [subject.toHuman(), subject.toHuman(), identity, validationData, web3networks, aesKey, hash] + [primeIdentity.toHuman(), primeIdentity.toHuman(), identity, validationData, web3networks, aesKey, hash] ); } @@ -163,7 +183,7 @@ export async function createSignedTrustedCallSetIdentityNetworks( mrenclave: string, nonce: Codec, signer: Signer, - subject: LitentryPrimitivesIdentity, + primeIdentity: CorePrimitivesIdentity, identity: string, web3networks: string, aesKey: string, @@ -178,7 +198,7 @@ export async function createSignedTrustedCallSetIdentityNetworks( signer, mrenclave, nonce, - [subject.toHuman(), subject.toHuman(), identity, web3networks, aesKey, hash] + [primeIdentity.toHuman(), primeIdentity.toHuman(), identity, web3networks, aesKey, hash] ); } @@ -187,7 +207,7 @@ export async function createSignedTrustedCallRequestVc( mrenclave: string, nonce: Codec, signer: Signer, - subject: LitentryPrimitivesIdentity, + primeIdentity: CorePrimitivesIdentity, assertion: string, aesKey: string, hash: string @@ -198,7 +218,7 @@ export async function createSignedTrustedCallRequestVc( signer, mrenclave, nonce, - [subject.toHuman(), subject.toHuman(), assertion, aesKey, hash] + [primeIdentity.toHuman(), primeIdentity.toHuman(), assertion, aesKey, hash] ); } export async function createSignedTrustedCallDeactivateIdentity( @@ -206,7 +226,7 @@ export async function createSignedTrustedCallDeactivateIdentity( mrenclave: string, nonce: Codec, signer: Signer, - subject: LitentryPrimitivesIdentity, + primeIdentity: CorePrimitivesIdentity, identity: string, aesKey: string, hash: string @@ -217,7 +237,7 @@ export async function createSignedTrustedCallDeactivateIdentity( signer, mrenclave, nonce, - [subject.toHuman(), subject.toHuman(), identity, aesKey, hash] + [primeIdentity.toHuman(), primeIdentity.toHuman(), identity, aesKey, hash] ); } export async function createSignedTrustedCallActivateIdentity( @@ -225,7 +245,7 @@ export async function createSignedTrustedCallActivateIdentity( mrenclave: string, nonce: Codec, signer: Signer, - subject: LitentryPrimitivesIdentity, + primeIdentity: CorePrimitivesIdentity, identity: string, aesKey: string, hash: string @@ -236,20 +256,20 @@ export async function createSignedTrustedCallActivateIdentity( signer, mrenclave, nonce, - [subject.toHuman(), subject.toHuman(), identity, aesKey, hash] + [primeIdentity.toHuman(), primeIdentity.toHuman(), identity, aesKey, hash] ); } export async function createSignedTrustedGetterIdGraph( parachainApi: ApiPromise, signer: Signer, - subject: LitentryPrimitivesIdentity + primeIdentity: CorePrimitivesIdentity ): Promise { const getterSigned = await createSignedTrustedGetter( parachainApi, ['id_graph', '(LitentryIdentity)'], signer, - subject.toHuman() + primeIdentity.toHuman() ); return parachainApi.createType('Getter', { trusted: getterSigned }) as unknown as Getter; // @fixme 1878; } @@ -257,13 +277,29 @@ export async function createSignedTrustedGetterIdGraph( export const getSidechainNonce = async ( context: IntegrationTestContext, teeShieldingKey: KeyObject, - subject: LitentryPrimitivesIdentity + primeIdentity: CorePrimitivesIdentity ): Promise => { - const getterPublic = createPublicGetter(context.api, ['nonce', '(LitentryIdentity)'], subject.toHuman()); + const getterPublic = createPublicGetter(context.api, ['nonce', '(LitentryIdentity)'], primeIdentity.toHuman()); const getter = context.api.createType('Getter', { public: getterPublic }) as unknown as Getter; // @fixme 1878 - const nonce = await sendRequestFromGetter(context, teeShieldingKey, getter); - const nonceValue = decodeNonce(nonce.value.toHex()); - return context.api.createType('Index', nonceValue) as Index; + const res = await sendRequestFromGetter(context, teeShieldingKey, getter); + const nonce = context.api.createType('Option', hexToU8a(res.value.toHex())).unwrap(); + return context.api.createType('Index', nonce); +}; + +export const getIdGraphHash = async ( + context: IntegrationTestContext, + teeShieldingKey: KeyObject, + primeIdentity: CorePrimitivesIdentity +): Promise => { + const getterPublic = createPublicGetter( + context.api, + ['id_graph_hash', '(LitentryIdentity)'], + primeIdentity.toHuman() + ); + const getter = context.api.createType('Getter', { public: getterPublic }) as unknown as Getter; // @fixme 1878 + const res = await sendRequestFromGetter(context, teeShieldingKey, getter); + const hash = context.api.createType('Option', hexToU8a(res.value.toHex())).unwrap(); + return context.api.createType('H256', hash); }; export const sendRequestFromTrustedCall = async ( @@ -300,6 +336,9 @@ export const sendRequestFromGetter = async ( // this is what `state_executeGetter` expects in rust const requestParam = await createRsaRequest(context.api, context.mrEnclave, teeShieldingKey, true, getter.toU8a()); const request = createJsonRpcRequest('state_executeGetter', [u8aToHex(requestParam)], nextRequestId(context)); + // in multiworker setup in some cases state might not be immediately propagated to other nodes so we wait 1 sec + // hopefully we will query correct state + await sleep(1); return sendRequest(context.tee, request, context.api); }; @@ -368,19 +407,12 @@ export const createAesRequest = async ( .toU8a(); }; -export function decodeNonce(nonceInHex: string) { - const optionalType = Option(Vector(u8)); - const encodedNonce = optionalType.dec(nonceInHex) as number[]; - const nonce = u32.dec(new Uint8Array(encodedNonce)); - return nonce; -} - export function decodeIdGraph(sidechainRegistry: TypeRegistry, value: Bytes) { const idgraphBytes = sidechainRegistry.createType('Option', hexToU8a(value.toHex())); return sidechainRegistry.createType( - 'Vec<(LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext)>', + 'Vec<(CorePrimitivesIdentity, PalletIdentityManagementTeeIdentityContext)>', idgraphBytes.unwrap() - ) as unknown as [LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][]; + ) as unknown as [CorePrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][]; } export function getTopHash(parachainApi: ApiPromise, call: TrustedCallSigned) { diff --git a/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts b/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts index 318ef4725d..bc5853364e 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts @@ -4,11 +4,11 @@ import { hexToU8a, u8aToHex } from '@polkadot/util'; import Ajv from 'ajv'; import { assert, expect } from 'chai'; import * as ed from '@noble/ed25519'; -import { parseIdGraph, parseIdentity } from './identity-helper'; -import type { LitentryPrimitivesIdentity, PalletIdentityManagementTeeError } from 'sidechain-api'; -import { TeerexPrimitivesEnclave } from 'parachain-api'; +import { parseIdGraph } from './identity-helper'; +import type { PalletIdentityManagementTeeError } from 'sidechain-api'; +import { TeerexPrimitivesEnclave, CorePrimitivesIdentity } from 'parachain-api'; import type { IntegrationTestContext } from '../common-types'; -import type { KeyringPair } from '@polkadot/keyring/types'; +import { getIdGraphHash } from '../di-utils'; import type { HexString } from '@polkadot/util/types'; import { jsonSchema } from './vc-helper'; import { aesKey } from '../call'; @@ -25,6 +25,7 @@ import { Bytes } from '@polkadot/types-codec'; import { Signer, decryptWithAes } from './crypto'; import { blake2AsHex } from '@polkadot/util-crypto'; import { PalletIdentityManagementTeeIdentityContext } from 'sidechain-api'; +import { KeyObject } from 'crypto'; export async function assertFailedEvent( context: IntegrationTestContext, @@ -58,8 +59,8 @@ export async function assertFailedEvent( } export function assertIdGraph( - actual: [LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][], - expected: [LitentryPrimitivesIdentity, boolean][] + actual: [CorePrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][], + expected: [CorePrimitivesIdentity, boolean][] ) { assert.equal(actual.length, expected.length); expected.forEach((expected, i) => { @@ -75,50 +76,14 @@ export function assertIdGraph( }); } -export async function assertIdentityCreated( - context: IntegrationTestContext, - signers: KeyringPair | KeyringPair[], - events: any[], - aesKey: HexString, - expectedIdentities: LitentryPrimitivesIdentity[] -) { - for (let index = 0; index < events.length; index++) { - const signer = Array.isArray(signers) ? signers[index] : signers; - const expectedIdentity = expectedIdentities[index]; - const expectedIdentityTarget = expectedIdentity[`as${expectedIdentity.type}`]; - const eventData = events[index].data; - const who = eventData.account.toHex(); - const eventIdentity = parseIdentity(context.sidechainRegistry, eventData.identity, aesKey); - const eventIdentityTarget = eventIdentity[`as${eventIdentity.type}`]; - // Check identity caller - assert.equal(who, u8aToHex(signer.addressRaw), 'Check IdentityCreated error: signer should be equal to who'); - - // Check identity type - assert.equal( - expectedIdentity.type, - eventIdentity.type, - 'Check IdentityCreated error: eventIdentity type should be equal to expectedIdentity type' - ); - // Check identity in event - assert.equal( - expectedIdentityTarget.toString(), - eventIdentityTarget.toString(), - 'Check IdentityCreated error: eventIdentityTarget should be equal to expectedIdentityTarget' - ); - } - console.log(colors.green('assertIdentityCreated complete')); -} - -export async function assertIdentityDeactivated(signers: KeyringPair | KeyringPair[], events: any[]) { +export async function assertIdentityDeactivated(context: IntegrationTestContext, signer: Signer, events: any[]) { for (let index = 0; index < events.length; index++) { - const signer = Array.isArray(signers) ? signers[index] : signers; - const eventData = events[index].data; - const who = eventData.account.toHex(); - - assert.equal( - who, - u8aToHex(signer.addressRaw), + const who = eventData.primeIdentity; + const signerIdentity = await signer.getIdentity(context); + assert.deepEqual( + who.toHuman(), + signerIdentity.toHuman(), 'Check IdentityDeactivated error: signer should be equal to who' ); } @@ -126,18 +91,16 @@ export async function assertIdentityDeactivated(signers: KeyringPair | KeyringPa console.log(colors.green('assertIdentityDeactivated complete')); } -export async function assertIdentityActivated( - context: IntegrationTestContext, - signers: KeyringPair | KeyringPair[], - events: any[] -) { +export async function assertIdentityActivated(context: IntegrationTestContext, signer: Signer, events: any[]) { for (let index = 0; index < events.length; index++) { - const signer = Array.isArray(signers) ? signers[index] : signers; - const eventData = events[index].data; - const who = eventData.account.toHex(); - - assert.equal(who, u8aToHex(signer.addressRaw), 'Check IdentityActivated error: signer should be equal to who'); + const who = eventData.primeIdentity; + const signerIdentity = await signer.getIdentity(context); + assert.deepEqual( + who.toHuman(), + signerIdentity.toHuman(), + 'Check IdentityActivated error: signer should be equal to who' + ); } console.log(colors.green('assertIdentityActivated complete')); @@ -210,7 +173,8 @@ export async function checkJson(vc: any, proofJson: any): Promise { } // for IdGraph mutation, assert the corresponding event is emitted for the given signer and the id_graph_hash matches -export async function assertIdGraphMutation( +export async function assertIdGraphMutationEvent( + context: IntegrationTestContext, signer: Signer, events: any[], idGraphHashResults: any[] | undefined, @@ -220,27 +184,15 @@ export async function assertIdGraphMutation( if (idGraphHashResults != undefined) { assert.equal(idGraphHashResults!.length, expectedLength); } - const signerAddress = u8aToHex(signer.getAddressInSubstrateFormat()); + + const signerIdentity = await signer.getIdentity(context); events.forEach((e, i) => { - assert.equal(signerAddress, e.data.account.toHex()); + assert.deepEqual(signerIdentity.toHuman(), e.data.primeIdentity.toHuman()); if (idGraphHashResults != undefined) { assert.equal(idGraphHashResults![i], e.data.idGraphHash.toHex()); } }); - console.log(colors.green('assertIdGraphMutation passed')); -} - -export async function assertIdentity( - context: IntegrationTestContext, - events: any[], - expectedIdentities: LitentryPrimitivesIdentity[] -) { - assert.isAtLeast(events.length, 1, 'Check assertIdentity error: events length should be greater than 1'); - for (let index = 0; index < events.length; index++) { - const identity = parseIdentity(context.sidechainRegistry, events[index].data.identity, aesKey); - assert.deepEqual(identity.toString(), expectedIdentities[index].toString()); - } - console.log(colors.green('assertIdentity passed')); + console.log(colors.green('assertIdGraphMutationEvent passed')); } export function assertWorkerError( @@ -254,21 +206,26 @@ export function assertWorkerError( // a common assertion for all DI requests that might mutate the IdGraph // returns the `id_graph_hash` in the `returnValue` -export function assertIdGraphMutationResult( +export async function assertIdGraphMutationResult( context: IntegrationTestContext, + teeShieldingKey: KeyObject, + identity: CorePrimitivesIdentity, returnValue: WorkerRpcReturnValue, resultType: | 'LinkIdentityResult' | 'DeactivateIdentityResult' | 'ActivateIdentityResult' | 'SetIdentityNetworksResult', - expectedIdGraph: [LitentryPrimitivesIdentity, boolean][] -): HexString { + expectedIdGraph: [CorePrimitivesIdentity, boolean][] +): Promise { const decodedResult = context.api.createType(resultType, returnValue.value) as any; - assert.isNotNull(decodedResult.mutated_id_graph); const idGraph = parseIdGraph(context.sidechainRegistry, decodedResult.mutated_id_graph, aesKey); assertIdGraph(idGraph, expectedIdGraph); + const queriedIdGraphHash = (await getIdGraphHash(context, teeShieldingKey, identity)).toHex(); + assert.equal(u8aToHex(decodedResult.id_graph_hash), queriedIdGraphHash); + + console.log(colors.green('assertIdGraphMutationResult passed')); return u8aToHex(decodedResult.id_graph_hash); } @@ -285,7 +242,7 @@ export function assertIdGraphMutationResult( TODO: This is incomplete; we still need to further check: https://github.com/litentry/litentry-parachain/issues/1873 */ -export async function assertVc(context: IntegrationTestContext, subject: LitentryPrimitivesIdentity, data: Bytes) { +export async function assertVc(context: IntegrationTestContext, subject: CorePrimitivesIdentity, data: Bytes) { const results = context.api.createType('RequestVCResult', data) as unknown as RequestVCResult; const vcHash = results.vc_hash.toString(); @@ -366,18 +323,18 @@ export async function assertVc(context: IntegrationTestContext, subject: Litentr export async function assertIdGraphHash( context: IntegrationTestContext, - signer: Signer, - idGraph: [LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][] + teeShieldingKey: KeyObject, + identity: CorePrimitivesIdentity, + idGraph: [CorePrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][] ) { const idGraphType = context.sidechainRegistry.createType( - 'Vec<(LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext)>', + 'Vec<(CorePrimitivesIdentity, PalletIdentityManagementTeeIdentityContext)>', idGraph ); - const localIdGraphHash = blake2AsHex(idGraphType.toU8a()); - console.log('local id graph hash: ', localIdGraphHash); + const computedIdGraphHash = blake2AsHex(idGraphType.toU8a()); + console.log('computed id graph hash: ', computedIdGraphHash); - const account = u8aToHex(signer.getAddressInSubstrateFormat()); - const onChainIdGraphHash = (await context.api.query.identityManagement.idGraphHash(account)).toHuman(); - console.log('on-chain id graph hash: ', onChainIdGraphHash); - assert.equal(localIdGraphHash, onChainIdGraphHash); + const queriedIdGraphHash = (await getIdGraphHash(context, teeShieldingKey, identity)).toHex(); + console.log('queried id graph hash: ', queriedIdGraphHash); + assert.equal(computedIdGraphHash, queriedIdGraphHash); } diff --git a/tee-worker/ts-tests/integration-tests/common/utils/context.ts b/tee-worker/ts-tests/integration-tests/common/utils/context.ts index b7c190a22f..2903aa1227 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/context.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/context.ts @@ -9,10 +9,10 @@ import { KeyObject } from 'crypto'; import { getSidechainMetadata } from '../call'; import { getEvmSigner, getSubstrateSigner } from '../helpers'; import type { IntegrationTestContext, Web3Wallets } from '../common-types'; - import { identity, vc, trusted_operations, sidechain } from 'parachain-api'; import crypto from 'crypto'; import type { HexString } from '@polkadot/util/types'; +import bitcore from 'bitcore-lib'; // maximum block number that we wait in listening events before we timeout export const defaultListenTimeoutInBlockNumber = 15; @@ -32,8 +32,7 @@ export async function initWorkerConnection(endpoint: string): Promise { const provider = new WsProvider(substrateEndpoint); await cryptoWaitReady(); @@ -48,6 +47,11 @@ export async function initIntegrationTestContext( const substrateWallet = getSubstrateSigner(); + const bitcoinWallet = { + alice: new bitcore.PrivateKey(), + bob: new bitcore.PrivateKey(), + }; + const types = { ...identity.types, ...vc.types, ...trusted_operations.types, ...sidechain.types }; const api = await ApiPromise.create({ @@ -61,7 +65,6 @@ export async function initIntegrationTestContext( const requestId = 1; const { sidechainMetaData, sidechainRegistry } = await getSidechainMetadata(wsp, api, requestId); - const web3Signers = await generateWeb3Wallets(walletsNumber); const { mrEnclave, teeShieldingKey } = await getEnclave(api); return { tee: wsp, @@ -70,9 +73,9 @@ export async function initIntegrationTestContext( mrEnclave, ethersWallet, substrateWallet, + bitcoinWallet, sidechainMetaData, sidechainRegistry, - web3Signers, chainIdentifier, requestId, }; @@ -118,9 +121,11 @@ export async function generateWeb3Wallets(count: number): Promise for (let i = 0; i < count; i++) { const substratePair = keyring.addFromUri(`${seed}//${i}`); const evmWallet = ethers.Wallet.createRandom(); + const bitcoinPair = new bitcore.PrivateKey(); addresses.push({ substrateWallet: substratePair, evmWallet: evmWallet, + bitcoinWallet: bitcoinPair, }); } return addresses; diff --git a/tee-worker/ts-tests/integration-tests/common/utils/crypto.ts b/tee-worker/ts-tests/integration-tests/common/utils/crypto.ts index a40fc5d03c..d6c6b4e306 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/crypto.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/crypto.ts @@ -1,12 +1,17 @@ import type { HexString } from '@polkadot/util/types'; -import { hexToU8a, stringToU8a } from '@polkadot/util'; +import { bufferToU8a, hexToU8a, isString, stringToU8a, u8aToHex } from '@polkadot/util'; import { KeyObject } from 'crypto'; -import { AesOutput } from 'parachain-api'; +import { AesOutput, CorePrimitivesIdentity } from 'parachain-api'; import crypto from 'crypto'; import { KeyringPair } from '@polkadot/keyring/types'; import { ethers } from 'ethers'; import { blake2AsU8a } from '@polkadot/util-crypto'; -import type { KeypairType } from '@polkadot/util-crypto/types'; +import bitcore from 'bitcore-lib'; +import { IntegrationTestContext } from 'common/common-types'; +import { buildIdentityHelper } from './identity-helper'; + +export type KeypairType = 'ed25519' | 'sr25519' | 'ecdsa' | 'ethereum' | 'bitcoin'; + export function encryptWithTeeShieldingKey(teeShieldingKey: KeyObject, plaintext: Uint8Array): Buffer { return crypto.publicEncrypt( { @@ -59,6 +64,7 @@ export interface Signer { sign(message: HexString | string | Uint8Array): Promise; type(): KeypairType; getAddressInSubstrateFormat(): Uint8Array; + getIdentity(api: IntegrationTestContext): Promise; } export class PolkadotSigner implements Signer { @@ -83,6 +89,10 @@ export class PolkadotSigner implements Signer { getAddressInSubstrateFormat(): Uint8Array { return this.getAddressRaw(); } + + getIdentity(context: IntegrationTestContext): Promise { + return buildIdentityHelper(u8aToHex(this.getAddressRaw()), 'Substrate', context); + } } export class EthersSigner implements Signer { @@ -114,4 +124,43 @@ export class EthersSigner implements Signer { merged.set(address, 4); return blake2AsU8a(merged, 256); } + + getIdentity(context: IntegrationTestContext): Promise { + return buildIdentityHelper(u8aToHex(this.getAddressRaw()), 'Evm', context); + } +} + +export class BitcoinSigner implements Signer { + keypair: bitcore.PrivateKey; + + constructor(keypair: bitcore.PrivateKey) { + this.keypair = keypair; + } + + getAddressRaw(): Uint8Array { + return bufferToU8a(this.keypair.toPublicKey().toBuffer()); + } + + sign(message: HexString | string | Uint8Array): Promise { + return new Promise((resolve, reject) => { + if (isString(message)) { + const sig = new bitcore.Message(message).sign(this.keypair); + resolve(bufferToU8a(Buffer.from(sig, 'base64'))); + } else { + reject('wrong message type'); + } + }); + } + + type(): KeypairType { + return 'bitcoin'; + } + + getAddressInSubstrateFormat(): Uint8Array { + return blake2AsU8a(this.getAddressRaw(), 256); + } + + getIdentity(context: IntegrationTestContext): Promise { + return buildIdentityHelper(u8aToHex(this.getAddressRaw()), 'Bitcoin', context); + } } diff --git a/tee-worker/ts-tests/integration-tests/common/utils/identity-helper.ts b/tee-worker/ts-tests/integration-tests/common/utils/identity-helper.ts index aec1241df6..233e24d986 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/identity-helper.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/identity-helper.ts @@ -5,21 +5,23 @@ import { AesOutput } from 'parachain-api'; import { decryptWithAes, encryptWithTeeShieldingKey, Signer } from './crypto'; import { ethers } from 'ethers'; import type { TypeRegistry } from '@polkadot/types'; -import type { LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext } from 'sidechain-api'; -import type { LitentryValidationData, Web3Network } from 'parachain-api'; +import type { PalletIdentityManagementTeeIdentityContext } from 'sidechain-api'; +import type { LitentryValidationData, Web3Network, CorePrimitivesIdentity } from 'parachain-api'; import type { ApiTypes, SubmittableExtrinsic } from '@polkadot/api/types'; import type { KeyringPair } from '@polkadot/keyring/types'; import type { HexString } from '@polkadot/util/types'; +import { bufferToU8a } from '@polkadot/util'; +import bitcore from 'bitcore-lib'; // blake2_256( + + ) export function generateVerificationMessage( context: IntegrationTestContext, - signer: LitentryPrimitivesIdentity, - identity: LitentryPrimitivesIdentity, + signer: CorePrimitivesIdentity, + identity: CorePrimitivesIdentity, sidechainNonce: number ): HexString { - const encodedIdentity = context.sidechainRegistry.createType('LitentryPrimitivesIdentity', identity).toU8a(); - const encodedWho = context.sidechainRegistry.createType('LitentryPrimitivesIdentity', signer).toU8a(); + const encodedIdentity = context.api.createType('CorePrimitivesIdentity', identity).toU8a(); + const encodedWho = context.api.createType('CorePrimitivesIdentity', signer).toU8a(); const encodedSidechainNonce = context.api.createType('Index', sidechainNonce); const msg = Buffer.concat([encodedSidechainNonce.toU8a(), encodedWho, encodedIdentity]); return blake2AsHex(msg, 256); @@ -27,22 +29,19 @@ export function generateVerificationMessage( export async function buildIdentityHelper( address: HexString | string, - type: LitentryPrimitivesIdentity['type'], + type: CorePrimitivesIdentity['type'], context: IntegrationTestContext -): Promise { +): Promise { const identity = { [type]: address, }; - return context.sidechainRegistry.createType( - 'LitentryPrimitivesIdentity', - identity - ) as unknown as LitentryPrimitivesIdentity; + return context.api.createType('CorePrimitivesIdentity', identity) as unknown as CorePrimitivesIdentity; } export async function buildIdentityFromKeypair( signer: Signer, context: IntegrationTestContext -): Promise { +): Promise { const type: string = (() => { switch (signer.type()) { case 'ethereum': @@ -53,6 +52,8 @@ export async function buildIdentityFromKeypair( return 'Substrate'; case 'ecdsa': return 'Substrate'; + case 'bitcoin': + return 'Bitcoin'; default: return 'Substrate'; } @@ -63,10 +64,7 @@ export async function buildIdentityFromKeypair( [type]: address, }; - return context.sidechainRegistry.createType( - 'LitentryPrimitivesIdentity', - identity - ) as unknown as LitentryPrimitivesIdentity; + return context.api.createType('CorePrimitivesIdentity', identity) as unknown as CorePrimitivesIdentity; } // If multiple transactions are built from multiple accounts, pass the signers as an array. @@ -76,7 +74,7 @@ export async function buildIdentityFromKeypair( export async function buildIdentityTxs( context: IntegrationTestContext, signers: KeyringPair[] | KeyringPair, - identities: LitentryPrimitivesIdentity[], + identities: CorePrimitivesIdentity[], method: 'linkIdentity' | 'deactivateIdentity' | 'activateIdentity', validations?: LitentryValidationData[], web3networks?: Web3Network[][] @@ -134,41 +132,30 @@ export function parseIdGraph( sidechainRegistry: TypeRegistry, idGraphOutput: AesOutput, aesKey: HexString -): [LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][] { +): [CorePrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][] { const decryptedIdGraph = decryptWithAes(aesKey, idGraphOutput, 'hex'); - const idGraph: [LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][] = + const idGraph: [CorePrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][] = sidechainRegistry.createType( - 'Vec<(LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext)>', + 'Vec<(CorePrimitivesIdentity, PalletIdentityManagementTeeIdentityContext)>', decryptedIdGraph - ) as unknown as [LitentryPrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][]; + ) as unknown as [CorePrimitivesIdentity, PalletIdentityManagementTeeIdentityContext][]; return idGraph; } -export function parseIdentity( - sidechainRegistry: TypeRegistry, - identityOutput: AesOutput, - aesKey: HexString -): LitentryPrimitivesIdentity { - const decryptedIdentity = decryptWithAes(aesKey, identityOutput, 'hex'); - const identity = sidechainRegistry.createType( - 'LitentryPrimitivesIdentity', - decryptedIdentity - ) as unknown as LitentryPrimitivesIdentity; - return identity; -} - export async function buildValidations( context: IntegrationTestContext, - signerIdentities: LitentryPrimitivesIdentity[], - identities: LitentryPrimitivesIdentity[], + signerIdentities: CorePrimitivesIdentity[], + identities: CorePrimitivesIdentity[], startingSidechainNonce: number, - network: 'ethereum' | 'substrate' | 'twitter', + network: 'ethereum' | 'substrate' | 'twitter' | 'bitcoin' | 'bitcoinPrettified', substrateSigners?: KeyringPair[] | KeyringPair, - evmSigners?: ethers.Wallet[] + evmSigners?: ethers.Wallet[], + bitcoinSigners?: bitcore.PrivateKey[] | bitcore.PrivateKey ): Promise { let evmSignature: HexString; let substrateSignature: Uint8Array; + let bitcoinSignature: Uint8Array; const validations: LitentryValidationData[] = []; for (let index = 0; index < identities.length; index++) { @@ -222,6 +209,57 @@ export async function buildValidations( substrateValidationData ) as unknown as LitentryValidationData; validations.push(encodedVerifyIdentityValidation); + } else if (network === 'bitcoin') { + const bitcoinValidationData = { + Web3Validation: { + Bitcoin: { + message: '' as HexString, + signature: { + Bitcoin: '' as HexString, + }, + }, + }, + }; + console.log('post verification msg to bitcoin: ', msg); + bitcoinValidationData.Web3Validation.Bitcoin.message = msg; + const bitcoinSigner = Array.isArray(bitcoinSigners!) ? bitcoinSigners![index] : bitcoinSigners!; + // we need to sign the hex string without `0x` prefix, the signature is base64-encoded string + const sig = new bitcore.Message(msg.substring(2)).sign(bitcoinSigner); + bitcoinSignature = bufferToU8a(Buffer.from(sig, 'base64')); + bitcoinValidationData!.Web3Validation.Bitcoin.signature.Bitcoin = u8aToHex(bitcoinSignature); + console.log('bitcoin pubkey: ', u8aToHex(bufferToU8a(bitcoinSigner.toPublicKey().toBuffer()))); + console.log('bitcoin sig (base64): ', sig); + console.log('bitcoin sig (hex): ', u8aToHex(bitcoinSignature)); + const encodedVerifyIdentityValidation: LitentryValidationData = context.api.createType( + 'LitentryValidationData', + bitcoinValidationData + ) as unknown as LitentryValidationData; + validations.push(encodedVerifyIdentityValidation); + } else if (network === 'bitcoinPrettified') { + const bitcoinValidationData = { + Web3Validation: { + Bitcoin: { + message: '' as HexString, + signature: { + BitcoinPrettified: '' as HexString, + }, + }, + }, + }; + console.log('post verification msg to bitcoin: ', msg); + bitcoinValidationData.Web3Validation.Bitcoin.message = msg; + const bitcoinSigner = Array.isArray(bitcoinSigners!) ? bitcoinSigners![index] : bitcoinSigners!; + const sig = new bitcore.Message('Litentry authorization token: ' + msg).sign(bitcoinSigner); + bitcoinSignature = bufferToU8a(Buffer.from(sig, 'base64')); + bitcoinValidationData!.Web3Validation.Bitcoin.signature.BitcoinPrettified = u8aToHex(bitcoinSignature); + console.log('bitcoin pubkey: ', u8aToHex(bufferToU8a(bitcoinSigner.toPublicKey().toBuffer()))); + console.log('bitcoin sig (base64): ', sig); + console.log('bitcoin sig (hex): ', u8aToHex(bitcoinSignature)); + const encodedVerifyIdentityValidation: LitentryValidationData = context.api.createType( + 'LitentryValidationData', + bitcoinValidationData + ) as unknown as LitentryValidationData; + validations.push(encodedVerifyIdentityValidation); } else if (network === 'twitter') { console.log('post verification msg to twitter: ', msg); const twitterValidationData = { diff --git a/tee-worker/ts-tests/integration-tests/common/utils/integration-setup.ts b/tee-worker/ts-tests/integration-tests/common/utils/integration-setup.ts index afa6e98d1f..38aae874b0 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/integration-setup.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/integration-setup.ts @@ -1,12 +1,12 @@ import { ApiPromise } from 'parachain-api'; import { KeyObject } from 'crypto'; import WebSocketAsPromised from 'websocket-as-promised'; -import type { IntegrationTestContext, Web3Wallets } from '../common-types'; +import type { IntegrationTestContext } from '../common-types'; import type { Metadata, TypeRegistry } from '@polkadot/types'; import type { HexString } from '@polkadot/util/types'; import { initIntegrationTestContext } from './context'; -export function describeLitentry(title: string, walletsNumber: number, cb: (context: IntegrationTestContext) => void) { +export function describeLitentry(title: string, cb: (context: IntegrationTestContext) => void) { describe(title, function () { // Set timeout to 6000 seconds this.timeout(6000000); @@ -18,9 +18,9 @@ export function describeLitentry(title: string, walletsNumber: number, cb: (cont teeShieldingKey: {} as KeyObject, ethersWallet: {}, substrateWallet: {}, + bitcoinWallet: {}, sidechainMetaData: {} as Metadata, sidechainRegistry: {} as TypeRegistry, - web3Signers: [] as Web3Wallets[], // default LitentryRococo chainIdentifier: 42, requestId: 0, @@ -29,20 +29,16 @@ export function describeLitentry(title: string, walletsNumber: number, cb: (cont before('Starting Litentry(parachain&tee)', async function () { //env url - const tmp = await initIntegrationTestContext( - process.env.WORKER_ENDPOINT!, - process.env.NODE_ENDPOINT!, - walletsNumber - ); + const tmp = await initIntegrationTestContext(process.env.WORKER_ENDPOINT!, process.env.NODE_ENDPOINT!); context.mrEnclave = tmp.mrEnclave; context.api = tmp.api; context.tee = tmp.tee; context.teeShieldingKey = tmp.teeShieldingKey; context.ethersWallet = tmp.ethersWallet; context.substrateWallet = tmp.substrateWallet; + context.bitcoinWallet = tmp.bitcoinWallet; context.sidechainMetaData = tmp.sidechainMetaData; context.sidechainRegistry = tmp.sidechainRegistry; - context.web3Signers = tmp.web3Signers; context.chainIdentifier = tmp.chainIdentifier; }); diff --git a/tee-worker/ts-tests/integration-tests/common/utils/storage.ts b/tee-worker/ts-tests/integration-tests/common/utils/storage.ts index d83472012a..ede4aced87 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/storage.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/storage.ts @@ -4,7 +4,8 @@ import { StorageEntryMetadataV14, SiLookupTypeId, StorageHasherV14 } from '@polk import { sendRequest } from '../call'; import { blake2128Concat, twox64Concat, identity, createJsonRpcRequest, nextRequestId } from '../helpers'; import type { IntegrationTestContext } from '../common-types'; -import type { PalletIdentityManagementTeeIdentityContext, LitentryPrimitivesIdentity } from 'sidechain-api'; +import type { PalletIdentityManagementTeeIdentityContext } from 'sidechain-api'; +import type { CorePrimitivesIdentity } from 'parachain-api'; import type { HexString } from '@polkadot/util/types'; import type { Metadata } from '@polkadot/types'; @@ -94,7 +95,7 @@ export async function checkIdGraph( context: IntegrationTestContext, pallet: string, method: string, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, identity: HexString ): Promise { await sleep(6000); diff --git a/tee-worker/ts-tests/integration-tests/common/utils/vc-helper.ts b/tee-worker/ts-tests/integration-tests/common/utils/vc-helper.ts index f2a2769888..1feeb3b883 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/vc-helper.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/vc-helper.ts @@ -7,7 +7,7 @@ export async function handleVcEvents( switch (method) { case 'VCIssued': results.push({ - account: events[k].data.account.toHex(), + identity: events[k].data.identity.toHex(), index: events[k].data.index.toHex(), }); break; @@ -56,12 +56,6 @@ export const defaultAssertions = [ A4: '10', }, }, - { - description: "The range of the user's Twitter follower count", - assertion: { - A6: 'A6', - }, - }, { description: 'The length of time a user continues to hold DOT token', assertion: { diff --git a/tee-worker/ts-tests/integration-tests/di_bitcoin_identity.test.ts b/tee-worker/ts-tests/integration-tests/di_bitcoin_identity.test.ts new file mode 100644 index 0000000000..9faa4af64f --- /dev/null +++ b/tee-worker/ts-tests/integration-tests/di_bitcoin_identity.test.ts @@ -0,0 +1,413 @@ +import { randomBytes, KeyObject } from 'crypto'; +import { step } from 'mocha-steps'; +import { assert } from 'chai'; +import { u8aToHex, bufferToU8a } from '@polkadot/util'; +import { + buildIdentityFromKeypair, + buildIdentityHelper, + buildValidations, + initIntegrationTestContext, + EthersSigner, + BitcoinSigner, + assertIdGraphMutationResult, + assertIdGraphHash, +} from './common/utils'; +import { assertIsInSidechainBlock, assertIdGraphMutationEvent } from './common/utils/assertion'; +import { + createSignedTrustedCallLinkIdentity, + createSignedTrustedGetterIdGraph, + createSignedTrustedCallDeactivateIdentity, + createSignedTrustedCallActivateIdentity, + decodeIdGraph, + getSidechainNonce, + getTeeShieldingKey, + sendRequestFromGetter, + sendRequestFromTrustedCall, +} from './common/di-utils'; // @fixme move to a better place +import type { IntegrationTestContext } from './common/common-types'; +import { aesKey } from './common/call'; +import { LitentryValidationData, Web3Network, CorePrimitivesIdentity } from 'parachain-api'; +import { Vec } from '@polkadot/types'; +import { subscribeToEventsWithExtHash } from './common/transactions'; + +describe('Test Identity (bitcoin direct invocation)', function () { + let context: IntegrationTestContext = undefined as any; + let teeShieldingKey: KeyObject = undefined as any; + let aliceBitcoinIdentity: CorePrimitivesIdentity = undefined as any; + let aliceEvmIdentity: CorePrimitivesIdentity; + let bobBitcoinIdentity: CorePrimitivesIdentity; + + // Alice links: + // - alice's evm identity + // - bob's bitcoin identity + const linkIdentityRequestParams: { + nonce: number; + identity: CorePrimitivesIdentity; + validation: LitentryValidationData; + networks: Vec; + }[] = []; + + const deactivateIdentityRequestParams: { + nonce: number; + identity: CorePrimitivesIdentity; + }[] = []; + + const activateIdentityRequestParams: { + nonce: number; + identity: CorePrimitivesIdentity; + }[] = []; + + this.timeout(6000000); + + before(async () => { + context = await initIntegrationTestContext( + process.env.WORKER_ENDPOINT!, // @fixme evil assertion; centralize env access + process.env.NODE_ENDPOINT! // @fixme evil assertion; centralize env access + ); + teeShieldingKey = await getTeeShieldingKey(context); + aliceBitcoinIdentity = await buildIdentityHelper( + u8aToHex(bufferToU8a(context.bitcoinWallet.alice.toPublicKey().toBuffer())), + 'Bitcoin', + context + ); + aliceEvmIdentity = await buildIdentityFromKeypair(new EthersSigner(context.ethersWallet.alice), context); + bobBitcoinIdentity = await buildIdentityHelper( + u8aToHex(bufferToU8a(context.bitcoinWallet.bob.toPublicKey().toBuffer())), + 'Bitcoin', + context + ); + }); + + step('check idGraph from sidechain storage before linking', async function () { + const idGraphGetter = await createSignedTrustedGetterIdGraph( + context.api, + new BitcoinSigner(context.bitcoinWallet.alice), + aliceBitcoinIdentity + ); + const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); + const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); + assert.lengthOf(idGraph, 0); + }); + + step('linking identities (alice bitcoin account)', async function () { + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceBitcoinIdentity)).toNumber(); + const getNextNonce = () => currentNonce++; + + const aliceEvmNonce = getNextNonce(); + const [aliceEvmValidation] = await buildValidations( + context, + [aliceBitcoinIdentity], + [aliceEvmIdentity], + aliceEvmNonce, + 'ethereum', + undefined, + [context.ethersWallet.alice] + ); + const aliceEvmNetworks = context.api.createType('Vec', [ + 'Ethereum', + 'Bsc', + ]) as unknown as Vec; // @fixme #1878 + linkIdentityRequestParams.push({ + nonce: aliceEvmNonce, + identity: aliceEvmIdentity, + validation: aliceEvmValidation, + networks: aliceEvmNetworks, + }); + + // link another bitcoin account with prettified signature + const bobBitcoinNonce = getNextNonce(); + const [bobBitcoinValidation] = await buildValidations( + context, + [aliceBitcoinIdentity], + [bobBitcoinIdentity], + bobBitcoinNonce, + 'bitcoinPrettified', + undefined, + undefined, + context.bitcoinWallet.bob + ); + const bobBitcoinNetowrks = context.api.createType('Vec', [ + 'BitcoinP2tr', + ]) as unknown as Vec; // @fixme #1878 + linkIdentityRequestParams.push({ + nonce: bobBitcoinNonce, + identity: bobBitcoinIdentity, + validation: bobBitcoinValidation, + networks: bobBitcoinNetowrks, + }); + + const identityLinkedEvents: any[] = []; + const idGraphHashResults: any[] = []; + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [ + [ + [aliceBitcoinIdentity, true], + [aliceEvmIdentity, true], + ], + [[bobBitcoinIdentity, true]], + ]; + + for (const { nonce, identity, validation, networks } of linkIdentityRequestParams) { + const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; + const eventsPromise = subscribeToEventsWithExtHash(requestIdentifier, context); + const linkIdentityCall = await createSignedTrustedCallLinkIdentity( + context.api, + context.mrEnclave, + context.api.createType('Index', nonce), + new BitcoinSigner(context.bitcoinWallet.alice), + aliceBitcoinIdentity, + identity.toHex(), + validation.toHex(), + networks.toHex(), + context.api.createType('Option', aesKey).toHex(), + requestIdentifier + ); + + const res = await sendRequestFromTrustedCall(context, teeShieldingKey, linkIdentityCall); + idGraphHashResults.push( + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceBitcoinIdentity, + res, + 'LinkIdentityResult', + expectedIdGraphs[0] + ) + ); + expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); + await assertIsInSidechainBlock('linkIdentityCall', res); + + const events = (await eventsPromise).map(({ event }) => event); + events.forEach((event) => { + if (context.api.events.identityManagement.LinkIdentityFailed.is(event)) { + assert.fail(JSON.stringify(event.toHuman(), null, 4)); + } + if (context.api.events.identityManagement.IdentityLinked.is(event)) { + identityLinkedEvents.push(event); + } + }); + } + + await assertIdGraphMutationEvent( + context, + new BitcoinSigner(context.bitcoinWallet.alice), + identityLinkedEvents, + idGraphHashResults, + 2 + ); + }); + + step('check user sidechain storage after linking', async function () { + const idGraphGetter = await createSignedTrustedGetterIdGraph( + context.api, + new BitcoinSigner(context.bitcoinWallet.alice), + aliceBitcoinIdentity + ); + const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); + const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); + + // according to the order of linkIdentityRequestParams + const expectedWeb3Networks = [['Ethereum', 'Bsc'], ['BitcoinP2tr']]; + let currentIndex = 0; + + for (const { identity } of linkIdentityRequestParams) { + const identityDump = JSON.stringify(identity.toHuman(), null, 4); + console.debug(`checking identity: ${identityDump}`); + const idGraphNode = idGraph.find(([idGraphNodeIdentity]) => idGraphNodeIdentity.eq(identity)); + assert.isDefined(idGraphNode, `identity not found in idGraph: ${identityDump}`); + const [, idGraphNodeContext] = idGraphNode!; + + const web3networks = idGraphNode![1].web3networks.toHuman(); + assert.deepEqual(web3networks, expectedWeb3Networks[currentIndex]); + + assert.equal( + idGraphNodeContext.status.toString(), + 'Active', + `status should be active for identity: ${identityDump}` + ); + console.debug('active ✅'); + + currentIndex++; + } + + await assertIdGraphHash(context, teeShieldingKey, aliceBitcoinIdentity, idGraph); + }); + step('deactivating identity(alice bitcoin account)', async function () { + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceBitcoinIdentity)).toNumber(); + const getNextNonce = () => currentNonce++; + + const aliceEvmNonce = getNextNonce(); + + deactivateIdentityRequestParams.push({ + nonce: aliceEvmNonce, + identity: aliceEvmIdentity, + }); + + const identityDeactivatedEvents: any[] = []; + const idGraphHashResults: any[] = []; + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [[[aliceEvmIdentity, false]]]; + + for (const { nonce, identity } of deactivateIdentityRequestParams) { + const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; + const eventsPromise = subscribeToEventsWithExtHash(requestIdentifier, context); + const deactivateIdentityCall = await createSignedTrustedCallDeactivateIdentity( + context.api, + context.mrEnclave, + context.api.createType('Index', nonce), + new BitcoinSigner(context.bitcoinWallet.alice), + aliceBitcoinIdentity, + identity.toHex(), + context.api.createType('Option', aesKey).toHex(), + requestIdentifier + ); + + const res = await sendRequestFromTrustedCall(context, teeShieldingKey, deactivateIdentityCall); + idGraphHashResults.push( + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceBitcoinIdentity, + res, + 'DeactivateIdentityResult', + expectedIdGraphs[0] + ) + ); + expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); + await assertIsInSidechainBlock('deactivateIdentityCall', res); + + const events = (await eventsPromise).map(({ event }) => event); + events.forEach((event) => { + if (context.api.events.identityManagement.DeactivateIdentityFailed.is(event)) { + assert.fail(JSON.stringify(event.toHuman(), null, 4)); + } + if (context.api.events.identityManagement.IdentityDeactivated.is(event)) { + identityDeactivatedEvents.push(event); + } + }); + } + + await assertIdGraphMutationEvent( + context, + new BitcoinSigner(context.bitcoinWallet.alice), + identityDeactivatedEvents, + idGraphHashResults, + 1 + ); + }); + + step('check idGraph from sidechain storage after deactivating', async function () { + const idGraphGetter = await createSignedTrustedGetterIdGraph( + context.api, + new BitcoinSigner(context.bitcoinWallet.alice), + aliceBitcoinIdentity + ); + const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); + const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); + + for (const { identity } of deactivateIdentityRequestParams) { + const identityDump = JSON.stringify(identity.toHuman(), null, 4); + console.debug(`checking identity: ${identityDump}`); + const idGraphNode = idGraph.find(([idGraphNodeIdentity]) => idGraphNodeIdentity.eq(identity)); + assert.isDefined(idGraphNode, `identity not found in idGraph: ${identityDump}`); + const [, idGraphNodeContext] = idGraphNode!; + + assert.equal( + idGraphNodeContext.status.toString(), + 'Inactive', + `status should be Inactive for identity: ${identityDump}` + ); + console.debug('inactive ✅'); + } + + await assertIdGraphHash(context, teeShieldingKey, aliceBitcoinIdentity, idGraph); + }); + + step('activating identity(alice bitcoin account)', async function () { + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceBitcoinIdentity)).toNumber(); + const getNextNonce = () => currentNonce++; + + const aliceEvmNonce = getNextNonce(); + + activateIdentityRequestParams.push({ + nonce: aliceEvmNonce, + identity: aliceEvmIdentity, + }); + + const identityActivatedEvents: any[] = []; + const idGraphHashResults: any[] = []; + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [[[aliceEvmIdentity, true]]]; + + for (const { nonce, identity } of activateIdentityRequestParams) { + const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; + const eventsPromise = subscribeToEventsWithExtHash(requestIdentifier, context); + const activateIdentityCall = await createSignedTrustedCallActivateIdentity( + context.api, + context.mrEnclave, + context.api.createType('Index', nonce), + new BitcoinSigner(context.bitcoinWallet.alice), + aliceBitcoinIdentity, + identity.toHex(), + context.api.createType('Option', aesKey).toHex(), + requestIdentifier + ); + + const res = await sendRequestFromTrustedCall(context, teeShieldingKey, activateIdentityCall); + idGraphHashResults.push( + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceBitcoinIdentity, + res, + 'ActivateIdentityResult', + expectedIdGraphs[0] + ) + ); + expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); + await assertIsInSidechainBlock('activateIdentityCall', res); + + const events = (await eventsPromise).map(({ event }) => event); + events.forEach((event) => { + if (context.api.events.identityManagement.ActivateIdentityFailed.is(event)) { + assert.fail(JSON.stringify(event.toHuman(), null, 4)); + } + if (context.api.events.identityManagement.IdentityActivated.is(event)) { + identityActivatedEvents.push(event); + } + }); + } + + await assertIdGraphMutationEvent( + context, + new BitcoinSigner(context.bitcoinWallet.alice), + identityActivatedEvents, + idGraphHashResults, + 1 + ); + }); + + step('check idGraph from sidechain storage after activating', async function () { + const idGraphGetter = await createSignedTrustedGetterIdGraph( + context.api, + new BitcoinSigner(context.bitcoinWallet.alice), + aliceBitcoinIdentity + ); + const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); + const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); + + for (const { identity } of linkIdentityRequestParams) { + const identityDump = JSON.stringify(identity.toHuman(), null, 4); + console.debug(`checking identity: ${identityDump}`); + const idGraphNode = idGraph.find(([idGraphNodeIdentity]) => idGraphNodeIdentity.eq(identity)); + assert.isDefined(idGraphNode, `identity not found in idGraph: ${identityDump}`); + const [, idGraphNodeContext] = idGraphNode!; + + assert.equal( + idGraphNodeContext.status.toString(), + 'Active', + `status should be active for identity: ${identityDump}` + ); + console.debug('active ✅'); + } + + await assertIdGraphHash(context, teeShieldingKey, aliceBitcoinIdentity, idGraph); + }); +}); diff --git a/tee-worker/ts-tests/integration-tests/di_evm_identity.test.ts b/tee-worker/ts-tests/integration-tests/di_evm_identity.test.ts index b7d2c5cde3..36a6d947ec 100644 --- a/tee-worker/ts-tests/integration-tests/di_evm_identity.test.ts +++ b/tee-worker/ts-tests/integration-tests/di_evm_identity.test.ts @@ -1,7 +1,7 @@ import { randomBytes, KeyObject } from 'crypto'; import { step } from 'mocha-steps'; import { assert } from 'chai'; -import { u8aToHex, u8aToString } from '@polkadot/util'; +import { u8aToHex } from '@polkadot/util'; import { buildIdentityFromKeypair, buildIdentityHelper, @@ -9,10 +9,9 @@ import { initIntegrationTestContext, EthersSigner, assertIdGraphMutationResult, - assertWorkerError, assertIdGraphHash, } from './common/utils'; -import { assertFailedEvent, assertIsInSidechainBlock, assertIdGraphMutation } from './common/utils/assertion'; +import { assertIsInSidechainBlock, assertIdGraphMutationEvent } from './common/utils/assertion'; import { createSignedTrustedCallLinkIdentity, createSignedTrustedGetterIdGraph, @@ -26,16 +25,15 @@ import { } from './common/di-utils'; // @fixme move to a better place import type { IntegrationTestContext } from './common/common-types'; import { aesKey } from './common/call'; -import { LitentryValidationData, Web3Network } from 'parachain-api'; -import { LitentryPrimitivesIdentity } from 'sidechain-api'; +import { LitentryValidationData, Web3Network, CorePrimitivesIdentity } from 'parachain-api'; import { Vec } from '@polkadot/types'; import { subscribeToEventsWithExtHash } from './common/transactions'; describe('Test Identity (evm direct invocation)', function () { let context: IntegrationTestContext = undefined as any; let teeShieldingKey: KeyObject = undefined as any; - let aliceEvmIdentity: LitentryPrimitivesIdentity = undefined as any; - let bobEvmIdentity: LitentryPrimitivesIdentity; + let aliceEvmIdentity: CorePrimitivesIdentity = undefined as any; + let bobEvmIdentity: CorePrimitivesIdentity; // Alice links: // - a `mock_user` twitter @@ -43,7 +41,7 @@ describe('Test Identity (evm direct invocation)', function () { // - eve's substrate identity (as alice can't link her own substrate again) const linkIdentityRequestParams: { nonce: number; - identity: LitentryPrimitivesIdentity; + identity: CorePrimitivesIdentity; validation: LitentryValidationData; networks: Vec; }[] = []; @@ -52,8 +50,7 @@ describe('Test Identity (evm direct invocation)', function () { before(async () => { context = await initIntegrationTestContext( process.env.WORKER_ENDPOINT!, // @fixme evil assertion; centralize env access - process.env.NODE_ENDPOINT!, // @fixme evil assertion; centralize env access - 0 + process.env.NODE_ENDPOINT! // @fixme evil assertion; centralize env access ); teeShieldingKey = await getTeeShieldingKey(context); @@ -126,7 +123,7 @@ describe('Test Identity (evm direct invocation)', function () { const identityLinkedEvents: any[] = []; const idGraphHashResults: any[] = []; - let expectedIdGraphs: [LitentryPrimitivesIdentity, boolean][][] = [ + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [ [ [aliceEvmIdentity, true], [bobEvmIdentity, true], @@ -152,7 +149,14 @@ describe('Test Identity (evm direct invocation)', function () { const res = await sendRequestFromTrustedCall(context, teeShieldingKey, linkIdentityCall); idGraphHashResults.push( - assertIdGraphMutationResult(context, res, 'LinkIdentityResult', expectedIdGraphs[0]) + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceEvmIdentity, + res, + 'LinkIdentityResult', + expectedIdGraphs[0] + ) ); expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); await assertIsInSidechainBlock('linkIdentityCall', res); @@ -168,7 +172,8 @@ describe('Test Identity (evm direct invocation)', function () { }); } - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new EthersSigner(context.ethersWallet.alice), identityLinkedEvents, idGraphHashResults, @@ -213,7 +218,7 @@ describe('Test Identity (evm direct invocation)', function () { currentIndex++; } - await assertIdGraphHash(context, new EthersSigner(context.ethersWallet.alice), idGraph); + await assertIdGraphHash(context, teeShieldingKey, aliceEvmIdentity, idGraph); }); step('deactivating identity(alice evm account)', async function () { let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceEvmIdentity)).toNumber(); @@ -221,7 +226,7 @@ describe('Test Identity (evm direct invocation)', function () { const deactivateIdentityRequestParams: { nonce: number; - identity: LitentryPrimitivesIdentity; + identity: CorePrimitivesIdentity; }[] = []; const bobEvmNonce = getNextNonce(); @@ -244,7 +249,7 @@ describe('Test Identity (evm direct invocation)', function () { const identityDeactivatedEvents: any[] = []; const idGraphHashResults: any[] = []; - let expectedIdGraphs: [LitentryPrimitivesIdentity, boolean][][] = [ + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [ [[bobEvmIdentity, false]], [[eveSubstrateIdentity, false]], ]; @@ -265,7 +270,14 @@ describe('Test Identity (evm direct invocation)', function () { const res = await sendRequestFromTrustedCall(context, teeShieldingKey, deactivateIdentityCall); idGraphHashResults.push( - assertIdGraphMutationResult(context, res, 'DeactivateIdentityResult', expectedIdGraphs[0]) + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceEvmIdentity, + res, + 'DeactivateIdentityResult', + expectedIdGraphs[0] + ) ); expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); await assertIsInSidechainBlock('deactivateIdentityCall', res); @@ -281,7 +293,8 @@ describe('Test Identity (evm direct invocation)', function () { }); } - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new EthersSigner(context.ethersWallet.alice), identityDeactivatedEvents, idGraphHashResults, @@ -313,7 +326,7 @@ describe('Test Identity (evm direct invocation)', function () { console.debug('inactive ✅'); } - await assertIdGraphHash(context, new EthersSigner(context.ethersWallet.alice), idGraph); + await assertIdGraphHash(context, teeShieldingKey, aliceEvmIdentity, idGraph); }); step('activating identity(alice evm account)', async function () { let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceEvmIdentity)).toNumber(); @@ -321,7 +334,7 @@ describe('Test Identity (evm direct invocation)', function () { const activateIdentityRequestParams: { nonce: number; - identity: LitentryPrimitivesIdentity; + identity: CorePrimitivesIdentity; }[] = []; const bobEvmNonce = getNextNonce(); @@ -344,7 +357,7 @@ describe('Test Identity (evm direct invocation)', function () { const identityActivatedEvents: any[] = []; const idGraphHashResults: any[] = []; - let expectedIdGraphs: [LitentryPrimitivesIdentity, boolean][][] = [ + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [ [[bobEvmIdentity, true]], [[eveSubstrateIdentity, true]], ]; @@ -365,7 +378,14 @@ describe('Test Identity (evm direct invocation)', function () { const res = await sendRequestFromTrustedCall(context, teeShieldingKey, activateIdentityCall); idGraphHashResults.push( - assertIdGraphMutationResult(context, res, 'ActivateIdentityResult', expectedIdGraphs[0]) + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceEvmIdentity, + res, + 'ActivateIdentityResult', + expectedIdGraphs[0] + ) ); expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); await assertIsInSidechainBlock('activateIdentityCall', res); @@ -381,7 +401,8 @@ describe('Test Identity (evm direct invocation)', function () { }); } - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new EthersSigner(context.ethersWallet.alice), identityActivatedEvents, idGraphHashResults, @@ -413,54 +434,6 @@ describe('Test Identity (evm direct invocation)', function () { console.debug('active ✅'); } - await assertIdGraphHash(context, new EthersSigner(context.ethersWallet.alice), idGraph); - }); - - step('deactivating prime identity is disallowed', async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceEvmIdentity)).toNumber(); - const getNextNonce = () => currentNonce++; - const nonce = getNextNonce(); - - // prime identity - const substratePrimeIdentity = await buildIdentityHelper( - u8aToHex(new EthersSigner(context.ethersWallet.alice).getAddressRaw()), - 'Evm', - context - ); - - const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; - const eventsPromise = subscribeToEventsWithExtHash(requestIdentifier, context); - const deactivateIdentityCall = await createSignedTrustedCallDeactivateIdentity( - context.api, - context.mrEnclave, - context.api.createType('Index', nonce), - new EthersSigner(context.ethersWallet.alice), - aliceEvmIdentity, - substratePrimeIdentity.toHex(), - context.api.createType('Option', aesKey).toHex(), - requestIdentifier - ); - - const res = await sendRequestFromTrustedCall(context, teeShieldingKey, deactivateIdentityCall); - assert.isTrue(res.do_watch.isFalse); - assert.isTrue(res.status.asTrustedOperationStatus[0].isInvalid); - assertWorkerError( - context, - (v) => { - assert.isTrue( - v.isDeactivateIdentityFailed, - `expected DeactivateIdentityFailed, received ${v.type} instead` - ); - assert.isTrue( - v.asDeactivateIdentityFailed.isStfError, - `expected StfError, received ${v.asDeactivateIdentityFailed.type} instead` - ); - assert.equal(u8aToString(v.asDeactivateIdentityFailed.asStfError), 'DeactivatePrimeIdentityDisallowed'); - }, - res - ); - - const events = await eventsPromise; - await assertFailedEvent(context, events, 'DeactivateIdentityFailed', 'DeactivatePrimeIdentityDisallowed'); + await assertIdGraphHash(context, teeShieldingKey, aliceEvmIdentity, idGraph); }); }); diff --git a/tee-worker/ts-tests/integration-tests/di_substrate_identity.test.ts b/tee-worker/ts-tests/integration-tests/di_substrate_identity.test.ts index f685bcb033..8f19167d5e 100644 --- a/tee-worker/ts-tests/integration-tests/di_substrate_identity.test.ts +++ b/tee-worker/ts-tests/integration-tests/di_substrate_identity.test.ts @@ -1,7 +1,7 @@ import { randomBytes, KeyObject } from 'crypto'; import { step } from 'mocha-steps'; import { assert } from 'chai'; -import { u8aToHex, u8aToString } from '@polkadot/util'; +import { u8aToHex, u8aToString, bufferToU8a } from '@polkadot/util'; import { assertIdGraphMutationResult, assertIdGraphHash, @@ -12,7 +12,7 @@ import { initIntegrationTestContext, PolkadotSigner, } from './common/utils'; -import { assertFailedEvent, assertIsInSidechainBlock, assertIdGraphMutation } from './common/utils/assertion'; +import { assertFailedEvent, assertIsInSidechainBlock, assertIdGraphMutationEvent } from './common/utils/assertion'; import { createSignedTrustedCallLinkIdentity, createSignedTrustedGetterIdGraph, @@ -27,8 +27,7 @@ import { } from './common/di-utils'; // @fixme move to a better place import type { IntegrationTestContext } from './common/common-types'; import { aesKey } from './common/call'; -import { LitentryValidationData, Web3Network } from 'parachain-api'; -import { LitentryPrimitivesIdentity } from 'sidechain-api'; +import { LitentryValidationData, Web3Network, CorePrimitivesIdentity } from 'parachain-api'; import { Vec } from '@polkadot/types'; import { ethers } from 'ethers'; import type { HexString } from '@polkadot/util/types'; @@ -37,15 +36,16 @@ import { subscribeToEventsWithExtHash } from './common/transactions'; describe('Test Identity (direct invocation)', function () { let context: IntegrationTestContext = undefined as any; let teeShieldingKey: KeyObject = undefined as any; - let aliceSubject: LitentryPrimitivesIdentity = undefined as any; + let aliceSubstrateIdentity: CorePrimitivesIdentity = undefined as any; // Alice links: // - a `mock_user` twitter // - alice's evm identity // - eve's substrate identity (as alice can't link her own substrate again) + // - alice's bitcoin identity const linkIdentityRequestParams: { nonce: number; - identity: LitentryPrimitivesIdentity; + identity: CorePrimitivesIdentity; validation: LitentryValidationData; networks: Vec; }[] = []; @@ -54,18 +54,20 @@ describe('Test Identity (direct invocation)', function () { before(async () => { context = await initIntegrationTestContext( process.env.WORKER_ENDPOINT!, // @fixme evil assertion; centralize env access - process.env.NODE_ENDPOINT!, // @fixme evil assertion; centralize env access - 0 + process.env.NODE_ENDPOINT! // @fixme evil assertion; centralize env access ); teeShieldingKey = await getTeeShieldingKey(context); - aliceSubject = await buildIdentityFromKeypair(new PolkadotSigner(context.substrateWallet.alice), context); + aliceSubstrateIdentity = await buildIdentityFromKeypair( + new PolkadotSigner(context.substrateWallet.alice), + context + ); }); step('check idgraph from sidechain storage before linking', async function () { const idGraphGetter = await createSignedTrustedGetterIdGraph( context.api, new PolkadotSigner(context.substrateWallet.alice), - aliceSubject + aliceSubstrateIdentity ); const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); @@ -75,14 +77,14 @@ describe('Test Identity (direct invocation)', function () { }); step('linking identities (alice)', async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const twitterNonce = getNextNonce(); const twitterIdentity = await buildIdentityHelper('mock_user', 'Twitter', context); const [twitterValidation] = await buildValidations( context, - [aliceSubject], + [aliceSubstrateIdentity], [twitterIdentity], twitterNonce, 'twitter' @@ -99,7 +101,7 @@ describe('Test Identity (direct invocation)', function () { const evmIdentity = await buildIdentityHelper(context.ethersWallet.alice.address, 'Evm', context); const [evmValidation] = await buildValidations( context, - [aliceSubject], + [aliceSubstrateIdentity], [evmIdentity], evmNonce, 'ethereum', @@ -125,7 +127,7 @@ describe('Test Identity (direct invocation)', function () { ); const [eveSubstrateValidation] = await buildValidations( context, - [aliceSubject], + [aliceSubstrateIdentity], [eveSubstrateIdentity], eveSubstrateNonce, 'substrate', @@ -142,15 +144,43 @@ describe('Test Identity (direct invocation)', function () { networks: eveSubstrateNetworks, }); + const bitcoinNonce = getNextNonce(); + const bitcoinIdentity = await buildIdentityHelper( + u8aToHex(bufferToU8a(context.bitcoinWallet.alice.toPublicKey().toBuffer())), + 'Bitcoin', + context + ); + console.log('bitcoin id: ', bitcoinIdentity.toHuman()); + const [bitcoinValidation] = await buildValidations( + context, + [aliceSubstrateIdentity], + [bitcoinIdentity], + bitcoinNonce, + 'bitcoin', + undefined, + undefined, + context.bitcoinWallet.alice + ); + const bitcoinNetworks = context.api.createType('Vec', [ + 'BitcoinP2tr', + ]) as unknown as Vec; // @fixme #1878 + linkIdentityRequestParams.push({ + nonce: bitcoinNonce, + identity: bitcoinIdentity, + validation: bitcoinValidation, + networks: bitcoinNetworks, + }); + const identityLinkedEvents: any[] = []; const idGraphHashResults: any[] = []; - let expectedIdGraphs: [LitentryPrimitivesIdentity, boolean][][] = [ + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [ [ - [aliceSubject, true], + [aliceSubstrateIdentity, true], [twitterIdentity, true], ], [[evmIdentity, true]], [[eveSubstrateIdentity, true]], + [[bitcoinIdentity, true]], ]; for (const { nonce, identity, validation, networks } of linkIdentityRequestParams) { @@ -161,7 +191,7 @@ describe('Test Identity (direct invocation)', function () { context.mrEnclave, context.api.createType('Index', nonce), new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, + aliceSubstrateIdentity, identity.toHex(), validation.toHex(), networks.toHex(), @@ -171,7 +201,14 @@ describe('Test Identity (direct invocation)', function () { const res = await sendRequestFromTrustedCall(context, teeShieldingKey, linkIdentityCall); idGraphHashResults.push( - assertIdGraphMutationResult(context, res, 'LinkIdentityResult', expectedIdGraphs[0]) + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceSubstrateIdentity, + res, + 'LinkIdentityResult', + expectedIdGraphs[0] + ) ); expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); await assertIsInSidechainBlock('linkIdentityCall', res); @@ -187,11 +224,12 @@ describe('Test Identity (direct invocation)', function () { }); } - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new PolkadotSigner(context.substrateWallet.alice), identityLinkedEvents, idGraphHashResults, - 3 + 4 ); }); @@ -199,14 +237,14 @@ describe('Test Identity (direct invocation)', function () { const idGraphGetter = await createSignedTrustedGetterIdGraph( context.api, new PolkadotSigner(context.substrateWallet.alice), - aliceSubject + aliceSubstrateIdentity ); const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); // according to the order of linkIdentityRequestParams - const expectedWeb3Networks = [[], ['Ethereum', 'Bsc'], ['Polkadot', 'Litentry']]; + const expectedWeb3Networks = [[], ['Ethereum', 'Bsc'], ['Polkadot', 'Litentry'], ['BitcoinP2tr']]; let currentIndex = 0; for (const { identity } of linkIdentityRequestParams) { @@ -229,14 +267,16 @@ describe('Test Identity (direct invocation)', function () { currentIndex++; } - await assertIdGraphHash(context, new PolkadotSigner(context.substrateWallet.alice), idGraph); + await assertIdGraphHash(context, teeShieldingKey, aliceSubstrateIdentity, idGraph); }); step('linking invalid identity', async function () { - const aliceSubject = await buildIdentityFromKeypair(new PolkadotSigner(context.substrateWallet.bob), context); - - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + const bobSubstrateIdentity = await buildIdentityFromKeypair( + new PolkadotSigner(context.substrateWallet.bob), + context + ); + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, bobSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const twitterIdentity = await buildIdentityHelper('mock_user', 'Twitter', context); @@ -245,7 +285,7 @@ describe('Test Identity (direct invocation)', function () { const evmIdentity = await buildIdentityHelper(context.ethersWallet.alice.address, 'Evm', context); const [evmValidation] = await buildValidations( context, - [aliceSubject], + [bobSubstrateIdentity], [evmIdentity], evmNonce, 'ethereum', @@ -261,7 +301,7 @@ describe('Test Identity (direct invocation)', function () { context.mrEnclave, context.api.createType('Index', twitterNonce), new PolkadotSigner(context.substrateWallet.bob), - aliceSubject, + bobSubstrateIdentity, twitterIdentity.toHex(), evmValidation.toHex(), evmNetworks.toHex(), @@ -289,7 +329,7 @@ describe('Test Identity (direct invocation)', function () { }); step('linking identity with wrong signature', async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const evmIdentity = await buildIdentityHelper(context.ethersWallet.alice.address, 'Evm', context); const evmNetworks = context.api.createType('Vec', ['Ethereum', 'Bsc']); @@ -320,7 +360,7 @@ describe('Test Identity (direct invocation)', function () { context.mrEnclave, context.api.createType('Index', evmNonce), new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, + aliceSubstrateIdentity, evmIdentity.toHex(), encodedVerifyIdentityValidation.toHex(), evmNetworks.toHex(), @@ -348,14 +388,14 @@ describe('Test Identity (direct invocation)', function () { }); step('linking already linked identity', async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const twitterNonce = getNextNonce(); const twitterIdentity = await buildIdentityHelper('mock_user', 'Twitter', context); const [twitterValidation] = await buildValidations( context, - [aliceSubject], + [aliceSubstrateIdentity], [twitterIdentity], twitterNonce, 'twitter' @@ -369,7 +409,7 @@ describe('Test Identity (direct invocation)', function () { context.mrEnclave, context.api.createType('Index', twitterNonce), new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, + aliceSubstrateIdentity, twitterIdentity.toHex(), twitterValidation.toHex(), twitterNetworks.toHex(), @@ -396,13 +436,13 @@ describe('Test Identity (direct invocation)', function () { await assertFailedEvent(context, events, 'LinkIdentityFailed', 'IdentityAlreadyLinked'); }); - step('deactivating identity', async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + step('deactivating linked identities', async function () { + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const deactivateIdentityRequestParams: { nonce: number; - identity: LitentryPrimitivesIdentity; + identity: CorePrimitivesIdentity; }[] = []; const twitterNonce = getNextNonce(); @@ -432,12 +472,24 @@ describe('Test Identity (direct invocation)', function () { identity: eveSubstrateIdentity, }); + const bitcoinNonce = getNextNonce(); + const bitcoinIdentity = await buildIdentityHelper( + u8aToHex(bufferToU8a(context.bitcoinWallet.alice.toPublicKey().toBuffer())), + 'Bitcoin', + context + ); + deactivateIdentityRequestParams.push({ + nonce: bitcoinNonce, + identity: bitcoinIdentity, + }); + const identityDeactivatedEvents: any[] = []; const idGraphHashResults: any[] = []; - let expectedIdGraphs: [LitentryPrimitivesIdentity, boolean][][] = [ + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [ [[twitterIdentity, false]], [[evmIdentity, false]], [[eveSubstrateIdentity, false]], + [[bitcoinIdentity, false]], ]; for (const { nonce, identity } of deactivateIdentityRequestParams) { @@ -448,7 +500,7 @@ describe('Test Identity (direct invocation)', function () { context.mrEnclave, context.api.createType('Index', nonce), new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, + aliceSubstrateIdentity, identity.toHex(), context.api.createType('Option', aesKey).toHex(), requestIdentifier @@ -456,7 +508,14 @@ describe('Test Identity (direct invocation)', function () { const res = await sendRequestFromTrustedCall(context, teeShieldingKey, deactivateIdentityCall); idGraphHashResults.push( - assertIdGraphMutationResult(context, res, 'DeactivateIdentityResult', expectedIdGraphs[0]) + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceSubstrateIdentity, + res, + 'DeactivateIdentityResult', + expectedIdGraphs[0] + ) ); expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); await assertIsInSidechainBlock('deactivateIdentityCall', res); @@ -471,11 +530,12 @@ describe('Test Identity (direct invocation)', function () { } }); } - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new PolkadotSigner(context.substrateWallet.alice), identityDeactivatedEvents, idGraphHashResults, - 3 + 4 ); }); @@ -483,7 +543,7 @@ describe('Test Identity (direct invocation)', function () { const idGraphGetter = await createSignedTrustedGetterIdGraph( context.api, new PolkadotSigner(context.substrateWallet.alice), - aliceSubject + aliceSubstrateIdentity ); const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); @@ -503,15 +563,15 @@ describe('Test Identity (direct invocation)', function () { console.debug('inactive ✅'); } - await assertIdGraphHash(context, new PolkadotSigner(context.substrateWallet.alice), idGraph); + await assertIdGraphHash(context, teeShieldingKey, aliceSubstrateIdentity, idGraph); }); - step('activating identity', async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + step('activating linked identities', async function () { + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const activateIdentityRequestParams: { nonce: number; - identity: LitentryPrimitivesIdentity; + identity: CorePrimitivesIdentity; }[] = []; const twitterNonce = getNextNonce(); @@ -541,12 +601,24 @@ describe('Test Identity (direct invocation)', function () { identity: eveSubstrateIdentity, }); + const bitcoinNonce = getNextNonce(); + const bitcoinIdentity = await buildIdentityHelper( + u8aToHex(bufferToU8a(context.bitcoinWallet.alice.toPublicKey().toBuffer())), + 'Bitcoin', + context + ); + activateIdentityRequestParams.push({ + nonce: bitcoinNonce, + identity: bitcoinIdentity, + }); + const identityActivatedEvents: any[] = []; const idGraphHashResults: any[] = []; - let expectedIdGraphs: [LitentryPrimitivesIdentity, boolean][][] = [ + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [ [[twitterIdentity, true]], [[evmIdentity, true]], [[eveSubstrateIdentity, true]], + [[bitcoinIdentity, true]], ]; for (const { nonce, identity } of activateIdentityRequestParams) { @@ -557,7 +629,7 @@ describe('Test Identity (direct invocation)', function () { context.mrEnclave, context.api.createType('Index', nonce), new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, + aliceSubstrateIdentity, identity.toHex(), context.api.createType('Option', aesKey).toHex(), requestIdentifier @@ -565,7 +637,14 @@ describe('Test Identity (direct invocation)', function () { const res = await sendRequestFromTrustedCall(context, teeShieldingKey, activateIdentityCall); idGraphHashResults.push( - assertIdGraphMutationResult(context, res, 'ActivateIdentityResult', expectedIdGraphs[0]) + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceSubstrateIdentity, + res, + 'ActivateIdentityResult', + expectedIdGraphs[0] + ) ); expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); await assertIsInSidechainBlock('activateIdentityCall', res); @@ -580,11 +659,12 @@ describe('Test Identity (direct invocation)', function () { } }); } - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new PolkadotSigner(context.substrateWallet.alice), identityActivatedEvents, idGraphHashResults, - 3 + 4 ); }); @@ -592,7 +672,7 @@ describe('Test Identity (direct invocation)', function () { const idGraphGetter = await createSignedTrustedGetterIdGraph( context.api, new PolkadotSigner(context.substrateWallet.alice), - aliceSubject + aliceSubstrateIdentity ); const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); @@ -612,7 +692,7 @@ describe('Test Identity (direct invocation)', function () { console.debug('active ✅'); } - await assertIdGraphHash(context, new PolkadotSigner(context.substrateWallet.alice), idGraph); + await assertIdGraphHash(context, teeShieldingKey, aliceSubstrateIdentity, idGraph); }); step('check idgraph from sidechain storage before setting identity network', async function () { @@ -620,7 +700,7 @@ describe('Test Identity (direct invocation)', function () { const idGraphGetter = await createSignedTrustedGetterIdGraph( context.api, new PolkadotSigner(context.substrateWallet.alice), - aliceSubject + aliceSubstrateIdentity ); const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); @@ -630,7 +710,7 @@ describe('Test Identity (direct invocation)', function () { }); step('setting identity network(alice)', async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const eveSubstrateIdentity = await buildIdentityHelper( u8aToHex(context.substrateWallet.eve.addressRaw), @@ -642,7 +722,7 @@ describe('Test Identity (direct invocation)', function () { const identityNetworksSetEvents: any[] = []; const idGraphHashResults: any[] = []; - let expectedIdGraphs: [LitentryPrimitivesIdentity, boolean][][] = [[[eveSubstrateIdentity, true]]]; + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [[[eveSubstrateIdentity, true]]]; const eventsPromise = subscribeToEventsWithExtHash(requestIdentifier, context); // we set the network to ['Litentry', 'Kusama'] @@ -651,7 +731,7 @@ describe('Test Identity (direct invocation)', function () { context.mrEnclave, context.api.createType('Index', nonce), new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, + aliceSubstrateIdentity, eveSubstrateIdentity.toHex(), context.api.createType('Vec', ['Litentry', 'Kusama']).toHex(), context.api.createType('Option', aesKey).toHex(), @@ -660,7 +740,14 @@ describe('Test Identity (direct invocation)', function () { const res = await sendRequestFromTrustedCall(context, teeShieldingKey, setIdentityNetworksCall); idGraphHashResults.push( - assertIdGraphMutationResult(context, res, 'ActivateIdentityResult', expectedIdGraphs[0]) + await assertIdGraphMutationResult( + context, + teeShieldingKey, + aliceSubstrateIdentity, + res, + 'ActivateIdentityResult', + expectedIdGraphs[0] + ) ); expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); await assertIsInSidechainBlock('setIdentityNetworksCall', res); @@ -671,7 +758,8 @@ describe('Test Identity (direct invocation)', function () { identityNetworksSetEvents.push(event); } }); - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new PolkadotSigner(context.substrateWallet.alice), identityNetworksSetEvents, idGraphHashResults, @@ -684,7 +772,7 @@ describe('Test Identity (direct invocation)', function () { const idGraphGetter = await createSignedTrustedGetterIdGraph( context.api, new PolkadotSigner(context.substrateWallet.alice), - aliceSubject + aliceSubstrateIdentity ); const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); @@ -695,11 +783,11 @@ describe('Test Identity (direct invocation)', function () { 'idGraph should be changed after setting network' ); - await assertIdGraphHash(context, new PolkadotSigner(context.substrateWallet.alice), idGraph); + await assertIdGraphHash(context, teeShieldingKey, aliceSubstrateIdentity, idGraph); }); step('setting incompatible identity network(alice)', async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const eveSubstrateIdentity = await buildIdentityHelper( u8aToHex(context.substrateWallet.eve.addressRaw), @@ -716,7 +804,7 @@ describe('Test Identity (direct invocation)', function () { context.api.createType('Index', nonce), new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, + aliceSubstrateIdentity, eveSubstrateIdentity.toHex(), context.api.createType('Vec', ['BSC', 'Ethereum']).toHex(), context.api.createType('Option', aesKey).toHex(), @@ -729,7 +817,7 @@ describe('Test Identity (direct invocation)', function () { assert.isTrue(v.isDispatch, `expected Dispatch, received ${v.type} instead`); assert.equal( v.asDispatch.toString(), - ' error: Module(ModuleError { index: 6, error: [5, 0, 0, 0], message: Some("WrongWeb3NetworkTypes") })' + ' error: Module(ModuleError { index: 6, error: [4, 0, 0, 0], message: Some("WrongWeb3NetworkTypes") })' ); }, res @@ -743,7 +831,7 @@ describe('Test Identity (direct invocation)', function () { const idGraphGetter = await createSignedTrustedGetterIdGraph( context.api, new PolkadotSigner(context.substrateWallet.alice), - aliceSubject + aliceSubstrateIdentity ); const res = await sendRequestFromGetter(context, teeShieldingKey, idGraphGetter); const idGraph = decodeIdGraph(context.sidechainRegistry, res.value); @@ -754,52 +842,135 @@ describe('Test Identity (direct invocation)', function () { 'idGraph should not be changed after setting incompatible network' ); }); - step('deactivating prime identity is disallowed', async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + + step('deactivate prime identity', async function () { + // deactivating prime identity should be possible and create the IDGraph if one doesn't exist already + const bobSubstrateIdentity = await buildIdentityFromKeypair( + new PolkadotSigner(context.substrateWallet.bob), + context + ); + + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, bobSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; - // nonce should be plus 1 why? - const nonce = getNextNonce() + 1; + const deactivateIdentityRequestParams: { + nonce: number; + identity: CorePrimitivesIdentity; + }[] = []; + + deactivateIdentityRequestParams.push({ + nonce: getNextNonce(), + identity: bobSubstrateIdentity, + }); + + const identityDeactivatedEvents: any[] = []; + const idGraphHashResults: any[] = []; + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [[[bobSubstrateIdentity, false]]]; + + for (const { nonce, identity } of deactivateIdentityRequestParams) { + const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; + const eventsPromise = subscribeToEventsWithExtHash(requestIdentifier, context); + const deactivateIdentityCall = await createSignedTrustedCallDeactivateIdentity( + context.api, + context.mrEnclave, + context.api.createType('Index', nonce), + new PolkadotSigner(context.substrateWallet.bob), + bobSubstrateIdentity, + identity.toHex(), + context.api.createType('Option', aesKey).toHex(), + requestIdentifier + ); + + const res = await sendRequestFromTrustedCall(context, teeShieldingKey, deactivateIdentityCall); + idGraphHashResults.push( + await assertIdGraphMutationResult( + context, + teeShieldingKey, + bobSubstrateIdentity, + res, + 'DeactivateIdentityResult', + expectedIdGraphs[0] + ) + ); + expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); + await assertIsInSidechainBlock('deactivateIdentityCall', res); + + const events = (await eventsPromise).map(({ event }) => event); + events.forEach((event) => { + if (context.api.events.identityManagement.DeactivateIdentityFailed.is(event)) { + assert.fail(JSON.stringify(event.toHuman(), null, 4)); + } + if (context.api.events.identityManagement.IdentityDeactivated.is(event)) { + identityDeactivatedEvents.push(event); + } + }); + } + await assertIdGraphMutationEvent( + context, + new PolkadotSigner(context.substrateWallet.bob), + identityDeactivatedEvents, + idGraphHashResults, + 1 + ); + }); - // prime identity - const substratePrimeIdentity = await buildIdentityHelper( - u8aToHex(context.substrateWallet.alice.addressRaw), + step('setting identity networks for prime identity)', async function () { + const charlieSubstrateIdentity = await buildIdentityHelper( + u8aToHex(context.substrateWallet.charlie.addressRaw), 'Substrate', context ); + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, charlieSubstrateIdentity)).toNumber(); + const getNextNonce = () => currentNonce++; + const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; + const nonce = getNextNonce(); + + const identityNetworksSetEvents: any[] = []; + const idGraphHashResults: any[] = []; + let expectedIdGraphs: [CorePrimitivesIdentity, boolean][][] = [[[charlieSubstrateIdentity, true]]]; + const eventsPromise = subscribeToEventsWithExtHash(requestIdentifier, context); - const deactivateIdentityCall = await createSignedTrustedCallDeactivateIdentity( + // we set the network to ['Litentry', 'Kusama'] + const setIdentityNetworksCall = await createSignedTrustedCallSetIdentityNetworks( context.api, context.mrEnclave, context.api.createType('Index', nonce), - new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, - substratePrimeIdentity.toHex(), + new PolkadotSigner(context.substrateWallet.charlie), + charlieSubstrateIdentity, + charlieSubstrateIdentity.toHex(), + context.api.createType('Vec', ['Litentry', 'Kusama']).toHex(), context.api.createType('Option', aesKey).toHex(), requestIdentifier ); - const res = await sendRequestFromTrustedCall(context, teeShieldingKey, deactivateIdentityCall); - assert.isTrue(res.do_watch.isFalse); - assert.isTrue(res.status.asTrustedOperationStatus[0].isInvalid); - assertWorkerError( + const res = await sendRequestFromTrustedCall(context, teeShieldingKey, setIdentityNetworksCall); + idGraphHashResults.push( + await assertIdGraphMutationResult( + context, + teeShieldingKey, + charlieSubstrateIdentity, + res, + 'ActivateIdentityResult', + expectedIdGraphs[0] + ) + ); + expectedIdGraphs = expectedIdGraphs.slice(1, expectedIdGraphs.length); + await assertIsInSidechainBlock('setIdentityNetworksCall', res); + + const events = (await eventsPromise).map(({ event }) => event); + events.forEach((event) => { + if (context.api.events.identityManagement.IdentityNetworksSet.is(event)) { + identityNetworksSetEvents.push(event); + } + }); + await assertIdGraphMutationEvent( context, - (v) => { - assert.isTrue( - v.isDeactivateIdentityFailed, - `expected DeactivateIdentityFailed, received ${v.type} instead` - ); - assert.isTrue( - v.asDeactivateIdentityFailed.isStfError, - `expected StfError, received ${v.asDeactivateIdentityFailed.type} instead` - ); - assert.equal(u8aToString(v.asDeactivateIdentityFailed.asStfError), 'DeactivatePrimeIdentityDisallowed'); - }, - res + new PolkadotSigner(context.substrateWallet.charlie), + identityNetworksSetEvents, + idGraphHashResults, + 1 ); - const events = await eventsPromise; - await assertFailedEvent(context, events, 'DeactivateIdentityFailed', 'DeactivatePrimeIdentityDisallowed'); }); }); diff --git a/tee-worker/ts-tests/integration-tests/di_vc.test.ts b/tee-worker/ts-tests/integration-tests/di_vc.test.ts index 8c61b055ed..9403bafbd8 100644 --- a/tee-worker/ts-tests/integration-tests/di_vc.test.ts +++ b/tee-worker/ts-tests/integration-tests/di_vc.test.ts @@ -1,40 +1,149 @@ import { randomBytes, KeyObject } from 'crypto'; import { step } from 'mocha-steps'; import { assert } from 'chai'; +import { u8aToHex, bufferToU8a } from '@polkadot/util'; import { buildIdentityFromKeypair, initIntegrationTestContext, PolkadotSigner } from './common/utils'; import { assertIsInSidechainBlock, assertVc } from './common/utils/assertion'; import { getSidechainNonce, + createSignedTrustedCallLinkIdentity, getTeeShieldingKey, sendRequestFromTrustedCall, createSignedTrustedCallRequestVc, } from './common/di-utils'; // @fixme move to a better place +import { buildIdentityHelper, buildValidations } from './common/utils'; import type { IntegrationTestContext } from './common/common-types'; import { aesKey } from './common/call'; -import { LitentryPrimitivesIdentity } from 'sidechain-api'; +import { CorePrimitivesIdentity } from 'parachain-api'; import { subscribeToEventsWithExtHash } from './common/transactions'; import { defaultAssertions, unconfiguredAssertions } from './common/utils/vc-helper'; +import { LitentryValidationData, Web3Network } from 'parachain-api'; +import { Vec } from '@polkadot/types'; describe('Test Vc (direct invocation)', function () { let context: IntegrationTestContext = undefined as any; let teeShieldingKey: KeyObject = undefined as any; - let aliceSubject: LitentryPrimitivesIdentity = undefined as any; + let aliceSubstrateIdentity: CorePrimitivesIdentity = undefined as any; + // Alice links: + // - a `mock_user` twitter + // - alice's evm identity + // - alice's bitcoin identity] + // + // We need this linking to not have empty eligible identities for any vc request + const linkIdentityRequestParams: { + nonce: number; + identity: CorePrimitivesIdentity; + validation: LitentryValidationData; + networks: Vec; + }[] = []; this.timeout(6000000); before(async () => { context = await initIntegrationTestContext( process.env.WORKER_ENDPOINT!, // @fixme evil assertion; centralize env access - process.env.NODE_ENDPOINT!, // @fixme evil assertion; centralize env access - 0 + process.env.NODE_ENDPOINT! // @fixme evil assertion; centralize env access ); teeShieldingKey = await getTeeShieldingKey(context); - aliceSubject = await buildIdentityFromKeypair(new PolkadotSigner(context.substrateWallet.alice), context); + aliceSubstrateIdentity = await buildIdentityFromKeypair( + new PolkadotSigner(context.substrateWallet.alice), + context + ); + }); + + step('linking identities (alice)', async function () { + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); + const getNextNonce = () => currentNonce++; + + const twitterNonce = getNextNonce(); + const twitterIdentity = await buildIdentityHelper('mock_user', 'Twitter', context); + const [twitterValidation] = await buildValidations( + context, + [aliceSubstrateIdentity], + [twitterIdentity], + twitterNonce, + 'twitter' + ); + const twitterNetworks = context.api.createType('Vec', []) as unknown as Vec; // @fixme #1878 + linkIdentityRequestParams.push({ + nonce: twitterNonce, + identity: twitterIdentity, + validation: twitterValidation, + networks: twitterNetworks, + }); + + const evmNonce = getNextNonce(); + const evmIdentity = await buildIdentityHelper(context.ethersWallet.alice.address, 'Evm', context); + const [evmValidation] = await buildValidations( + context, + [aliceSubstrateIdentity], + [evmIdentity], + evmNonce, + 'ethereum', + undefined, + [context.ethersWallet.alice] + ); + const evmNetworks = context.api.createType('Vec', [ + 'Ethereum', + 'Bsc', + ]) as unknown as Vec; // @fixme #1878 + linkIdentityRequestParams.push({ + nonce: evmNonce, + identity: evmIdentity, + validation: evmValidation, + networks: evmNetworks, + }); + + const bitcoinNonce = getNextNonce(); + const bitcoinIdentity = await buildIdentityHelper( + u8aToHex(bufferToU8a(context.bitcoinWallet.alice.toPublicKey().toBuffer())), + 'Bitcoin', + context + ); + console.log('bitcoin id: ', bitcoinIdentity.toHuman()); + const [bitcoinValidation] = await buildValidations( + context, + [aliceSubstrateIdentity], + [bitcoinIdentity], + bitcoinNonce, + 'bitcoin', + undefined, + undefined, + context.bitcoinWallet.alice + ); + const bitcoinNetworks = context.api.createType('Vec', [ + 'BitcoinP2tr', + ]) as unknown as Vec; // @fixme #1878 + linkIdentityRequestParams.push({ + nonce: bitcoinNonce, + identity: bitcoinIdentity, + validation: bitcoinValidation, + networks: bitcoinNetworks, + }); + + for (const { nonce, identity, validation, networks } of linkIdentityRequestParams) { + const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; + const linkIdentityCall = await createSignedTrustedCallLinkIdentity( + context.api, + context.mrEnclave, + context.api.createType('Index', nonce), + new PolkadotSigner(context.substrateWallet.alice), + aliceSubstrateIdentity, + identity.toHex(), + validation.toHex(), + networks.toHex(), + context.api.createType('Option', aesKey).toHex(), + requestIdentifier + ); + + const res = await sendRequestFromTrustedCall(context, teeShieldingKey, linkIdentityCall); + await assertIsInSidechainBlock('linkIdentityCall', res); + } }); defaultAssertions.forEach(({ description, assertion }) => { step(`request vc ${Object.keys(assertion)[0]} (alice)`, async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const nonce = getNextNonce(); const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; @@ -46,7 +155,7 @@ describe('Test Vc (direct invocation)', function () { context.mrEnclave, context.api.createType('Index', nonce), new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, + aliceSubstrateIdentity, context.api.createType('Assertion', assertion).toHex(), context.api.createType('Option', aesKey).toHex(), requestIdentifier @@ -64,30 +173,30 @@ describe('Test Vc (direct invocation)', function () { 1, `vcIssuedEvents.length != 1, please check the ${Object.keys(assertion)[0]} call` ); - await assertVc(context, aliceSubject, res.value); + await assertVc(context, aliceSubstrateIdentity, res.value); }); }); unconfiguredAssertions.forEach(({ description, assertion }) => { it(`request vc ${Object.keys(assertion)[0]} (alice)`, async function () { - let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubject)).toNumber(); + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, aliceSubstrateIdentity)).toNumber(); const getNextNonce = () => currentNonce++; const nonce = getNextNonce(); const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; console.log(`request vc ${Object.keys(assertion)[0]} for Alice ... Assertion description: ${description}`); - const eventsPromise = subscribeToEventsWithExtHash(requestIdentifier, context); + subscribeToEventsWithExtHash(requestIdentifier, context); const requestVcCall = await createSignedTrustedCallRequestVc( context.api, context.mrEnclave, context.api.createType('Index', nonce), new PolkadotSigner(context.substrateWallet.alice), - aliceSubject, + aliceSubstrateIdentity, context.api.createType('Assertion', assertion).toHex(), context.api.createType('Option', aesKey).toHex(), requestIdentifier ); - const res = await sendRequestFromTrustedCall(context, teeShieldingKey, requestVcCall); + await sendRequestFromTrustedCall(context, teeShieldingKey, requestVcCall); // pending test this.skip(); }); diff --git a/tee-worker/ts-tests/integration-tests/ii_batch.test.ts b/tee-worker/ts-tests/integration-tests/ii_batch.test.ts index c39cd08cf0..c7522fcfea 100644 --- a/tee-worker/ts-tests/integration-tests/ii_batch.test.ts +++ b/tee-worker/ts-tests/integration-tests/ii_batch.test.ts @@ -9,18 +9,17 @@ import { } from './common/utils'; import { step } from 'mocha-steps'; import { sendTxsWithUtility } from './common/transactions'; -import { generateWeb3Wallets, assertIdGraphMutation, assertIdentityDeactivated } from './common/utils'; +import { generateWeb3Wallets, assertIdGraphMutationEvent, assertIdentityDeactivated } from './common/utils'; import { ethers } from 'ethers'; -import type { LitentryPrimitivesIdentity } from 'sidechain-api'; -import type { LitentryValidationData, Web3Network } from 'parachain-api'; +import type { LitentryValidationData, Web3Network, CorePrimitivesIdentity } from 'parachain-api'; import { Vec } from '@polkadot/types'; -describeLitentry('Test Batch Utility', 0, (context) => { - let identities: LitentryPrimitivesIdentity[] = []; +describeLitentry('Test Batch Utility', (context) => { + let identities: CorePrimitivesIdentity[] = []; let validations: LitentryValidationData[] = []; let evmSigners: ethers.Wallet[] = []; const we3networks: Web3Network[][] = []; - const signerIdentities: LitentryPrimitivesIdentity[] = []; + const signerIdentities: CorePrimitivesIdentity[] = []; step('generate web3 wallets', async function () { const web3Wallets = await generateWeb3Wallets(3); @@ -31,14 +30,17 @@ describeLitentry('Test Batch Utility', 0, (context) => { step('batch test: link identities', async function () { const defaultNetworks = context.api.createType('Vec', ['Ethereum']); - const aliceSubject = await buildIdentityFromKeypair(new PolkadotSigner(context.substrateWallet.alice), context); + const aliceSubstrateIdentity = await buildIdentityFromKeypair( + new PolkadotSigner(context.substrateWallet.alice), + context + ); for (let index = 0; index < evmSigners.length; index++) { const signer = evmSigners[index]; const evmIdentity = await buildIdentityHelper(signer.address, 'Evm', context); identities.push(evmIdentity); we3networks.push(defaultNetworks as unknown as Vec); // @fixme #1878 - signerIdentities.push(aliceSubject); + signerIdentities.push(aliceSubstrateIdentity); } const evmValidations = await buildValidations( @@ -65,7 +67,8 @@ describeLitentry('Test Batch Utility', 0, (context) => { ]); const identityLinkedEvents = events.filter((e) => context.api.events.identityManagement.IdentityLinked.is(e)); - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new PolkadotSigner(context.substrateWallet.alice), identityLinkedEvents, undefined, @@ -83,7 +86,7 @@ describeLitentry('Test Batch Utility', 0, (context) => { ['IdentityDeactivated'] ); - await assertIdentityDeactivated(context.substrateWallet.alice, deactivatedEvents); + await assertIdentityDeactivated(context, new PolkadotSigner(context.substrateWallet.alice), deactivatedEvents); }); step('batch test: deactivate error identities', async function () { diff --git a/tee-worker/ts-tests/integration-tests/ii_identity.test.ts b/tee-worker/ts-tests/integration-tests/ii_identity.test.ts index 4cf1c05cc3..281a8c9b63 100644 --- a/tee-worker/ts-tests/integration-tests/ii_identity.test.ts +++ b/tee-worker/ts-tests/integration-tests/ii_identity.test.ts @@ -9,15 +9,14 @@ import { assertIdentityDeactivated, buildIdentityFromKeypair, assertIdentityActivated, - assertIdGraphMutation, + assertIdGraphMutationEvent, PolkadotSigner, } from './common/utils'; import { u8aConcat, u8aToHex, u8aToU8a, stringToU8a } from '@polkadot/util'; import { step } from 'mocha-steps'; import { assert } from 'chai'; import { sendTxsWithUtility } from './common/transactions'; -import type { LitentryPrimitivesIdentity } from 'sidechain-api'; -import type { LitentryValidationData, Web3Network } from 'parachain-api'; +import type { LitentryValidationData, Web3Network, CorePrimitivesIdentity } from 'parachain-api'; import type { IntegrationTestContext } from './common/common-types'; import type { HexString } from '@polkadot/util/types'; import { ethers } from 'ethers'; @@ -51,12 +50,12 @@ async function getNonce(base58mrEnclave: string, workerAddr: string, context: In return nonce; } -describeLitentry('Test Identity', 0, (context) => { +describeLitentry('Test Identity', (context) => { // random wrong msg const wrongMsg = '0x693d9131808e7a8574c7ea5eb7813bdf356223263e61fa8fe2ee8e434508bc75'; let signatureSubstrate; - let eveIdentities: LitentryPrimitivesIdentity[] = []; - let charlieIdentities: LitentryPrimitivesIdentity[] = []; + let eveIdentities: CorePrimitivesIdentity[] = []; + let charlieIdentities: CorePrimitivesIdentity[] = []; let eveValidations: LitentryValidationData[] = []; let bobValidations: LitentryValidationData[] = []; let web3networks: Web3Network[][] = []; @@ -94,11 +93,14 @@ describeLitentry('Test Identity', 0, (context) => { eveIdentities = [evmIdentity, eveSubstrateIdentity, twitterIdentity]; charlieIdentities = [charlieSubstrateIdentity]; - const aliceSubject = await buildIdentityFromKeypair(new PolkadotSigner(context.substrateWallet.alice), context); + const aliceSubstrateIdentity = await buildIdentityFromKeypair( + new PolkadotSigner(context.substrateWallet.alice), + context + ); const evmValidations = await buildValidations( context, - [aliceSubject], + [aliceSubstrateIdentity], [evmIdentity], nonce, 'ethereum', @@ -108,7 +110,7 @@ describeLitentry('Test Identity', 0, (context) => { const eveSubstrateValidations = await buildValidations( context, - [aliceSubject], + [aliceSubstrateIdentity], [eveSubstrateIdentity], nonce + 1, 'substrate', @@ -117,7 +119,7 @@ describeLitentry('Test Identity', 0, (context) => { const twitterValidations = await buildValidations( context, - [aliceSubject], + [aliceSubstrateIdentity], [twitterIdentity], nonce + 2, 'twitter' @@ -155,7 +157,8 @@ describeLitentry('Test Identity', 0, (context) => { context.api.events.identityManagement.IdentityLinked.is(e) ); - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new PolkadotSigner(context.substrateWallet.alice), identityLinkedEvents, undefined, @@ -174,10 +177,13 @@ describeLitentry('Test Identity', 0, (context) => { }, }, }; - const bobSubject = await buildIdentityFromKeypair(new PolkadotSigner(context.substrateWallet.bob), context); + const bobSubstrateIdentity = await buildIdentityFromKeypair( + new PolkadotSigner(context.substrateWallet.bob), + context + ); nonce = await getNonce(base58mrEnclave, workerAddress, context); console.log('nonce for step link identities', nonce); - const msg = generateVerificationMessage(context, bobSubject, charlieSubstrateIdentity, nonce); + const msg = generateVerificationMessage(context, bobSubstrateIdentity, charlieSubstrateIdentity, nonce); console.log('post verification msg to substrate: ', msg); substrateExtensionValidationData.Web3Validation.Substrate.message = msg; // sign the wrapped version as in polkadot-extension @@ -214,7 +220,8 @@ describeLitentry('Test Identity', 0, (context) => { ); identityLinkedEvents = bobRespEvents.filter((e) => context.api.events.identityManagement.IdentityLinked.is(e)); - await assertIdGraphMutation( + await assertIdGraphMutationEvent( + context, new PolkadotSigner(context.substrateWallet.bob), identityLinkedEvents, undefined, @@ -224,10 +231,19 @@ describeLitentry('Test Identity', 0, (context) => { step('check IDGraph after LinkIdentity', async function () { const twitterIdentity = await buildIdentityHelper('mock_user', 'Twitter', context); - const identityHex = context.sidechainRegistry.createType('LitentryPrimitivesIdentity', twitterIdentity).toHex(); - const aliceSubject = await buildIdentityFromKeypair(new PolkadotSigner(context.substrateWallet.alice), context); + const identityHex = context.api.createType('CorePrimitivesIdentity', twitterIdentity).toHex(); + const aliceSubstrateIdentity = await buildIdentityFromKeypair( + new PolkadotSigner(context.substrateWallet.alice), + context + ); - const respIdGraph = await checkIdGraph(context, 'IdentityManagement', 'IDGraphs', aliceSubject, identityHex); + const respIdGraph = await checkIdGraph( + context, + 'IdentityManagement', + 'IDGraphs', + aliceSubstrateIdentity, + identityHex + ); assert.isTrue(respIdGraph.linkBlock.toNumber() > 0, 'linkBlock should be greater than 0'); assert.isTrue(respIdGraph.status.isActive, 'status should be active'); }); @@ -299,7 +315,10 @@ describeLitentry('Test Identity', 0, (context) => { step('link already linked identity', async function () { const twitterIdentity = await buildIdentityHelper('mock_user', 'Twitter', context); - const aliceSubject = await buildIdentityFromKeypair(new PolkadotSigner(context.substrateWallet.alice), context); + const aliceSubstrateIdentity = await buildIdentityFromKeypair( + new PolkadotSigner(context.substrateWallet.alice), + context + ); const aliceIdentities = [twitterIdentity]; @@ -307,7 +326,7 @@ describeLitentry('Test Identity', 0, (context) => { console.log('nonce for step link already linked identity', nonce); const aliceTwitterValidations = await buildValidations( context, - [aliceSubject], + [aliceSubstrateIdentity], [twitterIdentity], nonce, 'twitter', @@ -371,10 +390,10 @@ describeLitentry('Test Identity', 0, (context) => { ); // Alice check identity - assertIdentityDeactivated(context.substrateWallet.alice, aliceDeactivatedEvents); + assertIdentityDeactivated(context, new PolkadotSigner(context.substrateWallet.alice), aliceDeactivatedEvents); // Bob check identity - assertIdentityDeactivated(context.substrateWallet.bob, bobDeactivatedEvents); + assertIdentityDeactivated(context, new PolkadotSigner(context.substrateWallet.bob), bobDeactivatedEvents); }); step('check IDGraph after deactivateIdentity', async function () { @@ -398,32 +417,7 @@ describeLitentry('Test Identity', 0, (context) => { ['IdentityActivated'] ); // Alice check identity - await assertIdentityActivated(context, context.substrateWallet.alice, aliceActivatedEvents); - }); - - step('deactivate prime identity is disallowed', async function () { - // deactivate prime identity - const substratePrimeIdentity = await buildIdentityHelper( - u8aToHex(context.substrateWallet.alice.addressRaw), - 'Substrate', - context - ); - - const primeTxs = await buildIdentityTxs( - context, - context.substrateWallet.alice, - [substratePrimeIdentity], - 'deactivateIdentity' - ); - const primeEvents = await sendTxsWithUtility( - context, - context.substrateWallet.alice, - primeTxs, - 'identityManagement', - ['DeactivateIdentityFailed'] - ); - - await checkErrorDetail(primeEvents, 'DeactivatePrimeIdentityDisallowed'); + await assertIdentityActivated(context, new PolkadotSigner(context.substrateWallet.alice), aliceActivatedEvents); }); step('deactivate error identities', async function () { @@ -446,7 +440,8 @@ describeLitentry('Test Identity', 0, (context) => { await checkErrorDetail(aliceDeactivatedEvents, 'IdentityNotExist'); - // deactivate a wrong identity (alice) for charlie + // deactivate a idneity for charlie, who is already linked to another IDGraph + // so creation of charlie's IDGraph should fail const charlieDeactivateTxs = await buildIdentityTxs( context, context.substrateWallet.charlie, @@ -461,7 +456,7 @@ describeLitentry('Test Identity', 0, (context) => { ['DeactivateIdentityFailed'] ); - await checkErrorDetail(charlieDeactivateEvents, 'IdentityNotExist'); + await checkErrorDetail(charlieDeactivateEvents, 'IdentityAlreadyLinked'); }); step('exceeding IDGraph limit not allowed', async function () { diff --git a/tee-worker/ts-tests/integration-tests/ii_vc.test.ts b/tee-worker/ts-tests/integration-tests/ii_vc.test.ts index 7114565fd6..d4b78418bb 100644 --- a/tee-worker/ts-tests/integration-tests/ii_vc.test.ts +++ b/tee-worker/ts-tests/integration-tests/ii_vc.test.ts @@ -11,22 +11,11 @@ const allAssertions = [ { A1: 'A1', }, - { - A2: 'A2', - }, - - { - A3: ['A3', 'A3', 'A3'], - }, - - { - A4: '10', - }, ]; // It doesn't make much difference test A1 only vs test A1 - A11, one VC type is enough. // So only use A1 to trigger the wrong event -describeLitentry('VC ii test', 0, async (context) => { +describeLitentry('VC ii test', async (context) => { const indexList: HexString[] = []; step('Request VC', async () => { // request all vc diff --git a/tee-worker/ts-tests/integration-tests/package.json b/tee-worker/ts-tests/integration-tests/package.json index a4fd1b74c1..40ecbaaf8f 100644 --- a/tee-worker/ts-tests/integration-tests/package.json +++ b/tee-worker/ts-tests/integration-tests/package.json @@ -11,6 +11,8 @@ "test-di-substrate-identity:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_substrate_identity.test.ts'", "test-di-evm-identity:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_evm_identity.test.ts'", "test-di-evm-identity:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_evm_identity.test.ts'", + "test-di-bitcoin-identity:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_bitcoin_identity.test.ts'", + "test-di-bitcoin-identity:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_bitcoin_identity.test.ts'", "test-di-vc:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_vc.test.ts'", "test-di-vc:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_vc.test.ts'", "test-resuming-worker:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'resuming_worker.test.ts'", @@ -24,34 +26,36 @@ "@noble/ed25519": "^1.7.3", "@polkadot/api": "^10.9.1", "@polkadot/api-augment": "^10.9.1", - "@polkadot/api-derive": "^10.9.1", "@polkadot/api-base": "^10.9.1", + "@polkadot/api-derive": "^10.9.1", "@polkadot/keyring": "^12.2.1", + "@polkadot/rpc-core": "^10.9.1", "@polkadot/types": "^10.9.1", "@polkadot/types-augment": "^10.9.1", + "@polkadot/types-codec": "^10.9.1", "@polkadot/types-create": "^10.9.1", "@polkadot/types-known": "^10.9.1", "@polkadot/types-support": "^10.9.1", "@polkadot/util": "^12.5.1", "@polkadot/util-crypto": "^12.5.1", - "@polkadot/types-codec": "^10.9.1", - "@polkadot/rpc-core": "^10.9.1", "add": "^2.0.6", "ajv": "^8.12.0", + "bitcore-lib": "^10.0.21", "chai": "^4.3.6", "colors": "^1.4.0", "js-base64": "^3.7.5", "micro-base58": "^0.5.1", "mocha": "^10.1.0", "mocha-steps": "^1.3.0", + "parachain-api": "file:../../client-api/parachain-api", "scale-ts": "^0.2.11", + "sidechain-api": "file:../../client-api/sidechain-api", "websocket-as-promised": "^2.0.1", - "ws": "^8.8.1", - "parachain-api": "file:../../client-api/parachain-api", - "sidechain-api": "file:../../client-api/sidechain-api" + "ws": "^8.8.1" }, "devDependencies": { "@ethersproject/providers": "^5.7.2", + "@types/bitcore-lib": "^0.15.1", "@types/chai": "^4.3.3", "@types/mocha": "^10.0.0", "@types/node": "^20.4.4", diff --git a/tee-worker/ts-tests/pnpm-lock.yaml b/tee-worker/ts-tests/pnpm-lock.yaml index 53a61d3a2b..f6605b0ca5 100644 --- a/tee-worker/ts-tests/pnpm-lock.yaml +++ b/tee-worker/ts-tests/pnpm-lock.yaml @@ -61,6 +61,9 @@ importers: ajv: specifier: ^8.12.0 version: 8.12.0 + bitcore-lib: + specifier: ^10.0.21 + version: 10.0.21 chai: specifier: ^4.3.6 version: 4.3.10 @@ -98,6 +101,9 @@ importers: '@ethersproject/providers': specifier: ^5.7.2 version: 5.7.2 + '@types/bitcore-lib': + specifier: ^0.15.1 + version: 0.15.6 '@types/chai': specifier: ^4.3.3 version: 4.3.6 @@ -1135,6 +1141,12 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true + /@types/bitcore-lib@0.15.6: + resolution: {integrity: sha512-CtKDBgSBubPXZ0wFeCiUCSdzH+cuy6nFya3FboOqf44evi+OmkQPqEg3ASMpmPDYE8vkcxV302Iu8lZqCjYieg==} + dependencies: + '@types/node': 20.7.1 + dev: true + /@types/bn.js@5.1.2: resolution: {integrity: sha512-dkpZu0szUtn9UXTmw+e0AJFd4D2XAxDnsCLdc05SfqpqzPEBft8eQr8uaFitfo/dUUOZERaLec2hHMG87A4Dxg==} dependencies: @@ -1405,17 +1417,58 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + /base-x@3.0.9: + resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /bech32@1.1.4: resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} dev: true + /bech32@2.0.0: + resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} + dev: false + + /bigi@1.4.2: + resolution: {integrity: sha512-ddkU+dFIuEIW8lE7ZwdIAf2UPoM90eaprg5m3YXAVVTmKlqV/9BX4A2M8BOK2yOq6/VgZFVhK6QAxJebhlbhzw==} + dev: false + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} + /bip-schnorr@0.6.4: + resolution: {integrity: sha512-dNKw7Lea8B0wMIN4OjEmOk/Z5qUGqoPDY0P2QttLqGk1hmDPytLWW8PR5Pb6Vxy6CprcdEgfJpOjUu+ONQveyg==} + engines: {node: '>=8.0.0'} + dependencies: + bigi: 1.4.2 + ecurve: 1.0.6 + js-sha256: 0.9.0 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + dev: false + + /bitcore-lib@10.0.21: + resolution: {integrity: sha512-oYB1BrHjeRHqdrZ+j/cVm+M0XGxRDDjDiX4wP9QWQGAZkvS6DQGeNIndwS7m7BA18H76PJRhBM+DrSV2medpXg==} + dependencies: + bech32: 2.0.0 + bip-schnorr: 0.6.4 + bn.js: 4.11.8 + bs58: 4.0.1 + buffer-compare: 1.1.1 + elliptic: 6.5.4 + inherits: 2.0.1 + lodash: 4.17.21 + dev: false + + /bn.js@4.11.8: + resolution: {integrity: sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==} + dev: false + /bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} - dev: true /bn.js@5.2.1: resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} @@ -1439,11 +1492,20 @@ packages: /brorand@1.1.0: resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - dev: true /browser-stdout@1.3.1: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + /bs58@4.0.1: + resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + dependencies: + base-x: 3.0.9 + dev: false + + /buffer-compare@1.1.1: + resolution: {integrity: sha512-O6NvNiHZMd3mlIeMDjP6t/gPG75OqGPeiRZXoMQZJ6iy9GofCls4Ijs5YkPZZwoysizLiedhticmdyx/GyHghA==} + dev: false + /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: @@ -1633,6 +1695,13 @@ packages: engines: {node: '>=12'} dev: true + /ecurve@1.0.6: + resolution: {integrity: sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w==} + dependencies: + bigi: 1.4.2 + safe-buffer: 5.2.1 + dev: false + /elliptic@6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} dependencies: @@ -1643,7 +1712,6 @@ packages: inherits: 2.0.4 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: true /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2136,7 +2204,6 @@ packages: dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: true /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} @@ -2148,7 +2215,6 @@ packages: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: true /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} @@ -2174,6 +2240,10 @@ packages: once: 1.4.0 wrappy: 1.0.2 + /inherits@2.0.1: + resolution: {integrity: sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==} + dev: false + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2322,6 +2392,10 @@ packages: resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==} dev: false + /js-sha256@0.9.0: + resolution: {integrity: sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==} + dev: false + /js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} dev: true @@ -2422,11 +2496,9 @@ packages: /minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - dev: true /minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - dev: true /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} diff --git a/tee-worker/ts-tests/stress/src/litentry-api.ts b/tee-worker/ts-tests/stress/src/litentry-api.ts index 8852b98a6c..af3b425aed 100644 --- a/tee-worker/ts-tests/stress/src/litentry-api.ts +++ b/tee-worker/ts-tests/stress/src/litentry-api.ts @@ -11,11 +11,7 @@ import { } from '@polkadot/util'; import { blake2AsHex } from '@polkadot/util-crypto'; import crypto, { KeyObject, createPublicKey } from 'crypto'; -import { - LitentryPrimitivesIdentity, - TypeRegistry as SidechainTypeRegistry, - Metadata as SidechainMetadata, -} from 'sidechain-api'; +import { TypeRegistry as SidechainTypeRegistry, Metadata as SidechainMetadata } from 'sidechain-api'; import { Bytes, Codec, @@ -25,15 +21,16 @@ import { WorkerRpcReturnValue, LitentryValidationData, Assertion, + CorePrimitivesIdentity, + TrustedCallSigned, + PublicGetter, } from 'parachain-api'; import WebSocketAsPromised from 'websocket-as-promised'; import { Index } from '@polkadot/types/interfaces'; import { Option, u32, u8, Vector } from 'scale-ts'; -import { TrustedCallSigned } from 'parachain-api'; import WsWebSocket from 'ws'; import type { HexString } from '@polkadot/util/types'; import { ethers } from 'ethers'; -import { PublicGetter } from 'parachain-api'; async function logLine(log: WritableStream, message: string): Promise { const writer = log.getWriter(); @@ -69,13 +66,12 @@ function encryptWithAes(key: string, nonce: Uint8Array, cleartext: Buffer): HexS function generateVerificationMessage( parachainApi: ParachainApiPromise, - sidechainRegistry: SidechainTypeRegistry, - signer: LitentryPrimitivesIdentity, - identity: LitentryPrimitivesIdentity, + signer: CorePrimitivesIdentity, + identity: CorePrimitivesIdentity, sidechainNonce: number ): HexString { - const encodedIdentity = sidechainRegistry.createType('LitentryPrimitivesIdentity', identity).toU8a(); - const encodedWho = sidechainRegistry.createType('LitentryPrimitivesIdentity', signer).toU8a(); + const encodedIdentity = parachainApi.createType('CorePrimitivesIdentity', identity).toU8a(); + const encodedWho = parachainApi.createType('CorePrimitivesIdentity', signer).toU8a(); const encodedSidechainNonce = parachainApi.createType('Index', sidechainNonce); const msg = Buffer.concat([encodedSidechainNonce.toU8a(), encodedWho, encodedIdentity]); return blake2AsHex(msg, 256); @@ -84,18 +80,12 @@ function generateVerificationMessage( export async function buildValidation( parachainApi: ParachainApiPromise, sidechainRegistry: SidechainTypeRegistry, - signerIdentity: LitentryPrimitivesIdentity, - identity: LitentryPrimitivesIdentity, + signerIdentity: CorePrimitivesIdentity, + identity: CorePrimitivesIdentity, startingSidechainNonce: number, signer: Wallet ): Promise { - const message = generateVerificationMessage( - parachainApi, - sidechainRegistry, - signerIdentity, - identity, - startingSidechainNonce - ); + const message = generateVerificationMessage(parachainApi, signerIdentity, identity, startingSidechainNonce); return parachainApi.createType('LitentryValidationData', { Web3Validation: @@ -121,17 +111,14 @@ export async function buildValidation( export async function buildIdentityFromWallet( wallet: Wallet, - sidechainRegistry: SidechainTypeRegistry -): Promise { + api: ParachainApiPromise +): Promise { if (wallet.type === 'evm') { const identity = { Evm: wallet.wallet.address, }; - return sidechainRegistry.createType( - 'LitentryPrimitivesIdentity', - identity - ) as unknown as LitentryPrimitivesIdentity; + return api.createType('CorePrimitivesIdentity', identity) as unknown as CorePrimitivesIdentity; } const { keyringPair } = wallet; @@ -155,10 +142,7 @@ export async function buildIdentityFromWallet( [type]: address, }; - return sidechainRegistry.createType( - 'LitentryPrimitivesIdentity', - identity - ) as unknown as LitentryPrimitivesIdentity; + return api.createType('CorePrimitivesIdentity', identity) as unknown as CorePrimitivesIdentity; } export function decodeRpcBytesAsString(value: Bytes): string { @@ -264,7 +248,7 @@ export const createSignedTrustedGetter = ( export function createSignedTrustedGetterUserShieldingKey( parachainApi: ParachainApiPromise, signer: KeyringPair, - subject: LitentryPrimitivesIdentity + subject: CorePrimitivesIdentity ) { const getterSigned = createSignedTrustedGetter( parachainApi, @@ -343,7 +327,7 @@ export const getSidechainNonce = async ( parachainApi: ParachainApiPromise, mrenclave: string, teeShieldingKey: KeyObject, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, log: WritableStream ): Promise => { const getterPublic = createPublicGetter(parachainApi, ['nonce', '(LitentryIdentity)'], subject.toHuman()); @@ -499,7 +483,7 @@ export function createSignedTrustedCallSetUserShieldingKey( mrenclave: string, nonce: Codec, signer: Wallet, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, key: string, hash: string, withWrappedBytes = false @@ -520,7 +504,7 @@ export function createSignedTrustedCallLinkIdentity( mrenclave: string, nonce: Codec, signer: Wallet, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, identity: string, validationData: string, web3networks: string, @@ -545,8 +529,8 @@ export function createSignedTrustedCallDeactivateIdentity( mrenclave: string, nonce: Codec, signer: Wallet, - subject: LitentryPrimitivesIdentity, - identity: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, + identity: CorePrimitivesIdentity, hash: string ) { return createSignedTrustedCall( @@ -564,8 +548,8 @@ export function createSignedTrustedCallActivateIdentity( mrenclave: string, nonce: Codec, signer: Wallet, - subject: LitentryPrimitivesIdentity, - identity: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, + identity: CorePrimitivesIdentity, hash: string ) { return createSignedTrustedCall( @@ -583,7 +567,7 @@ export function createSignedTrustedCallRequestVc( mrenclave: string, nonce: Codec, signer: Wallet, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, assertion: Assertion, key: string, hash: string diff --git a/tee-worker/ts-tests/stress/src/steps.ts b/tee-worker/ts-tests/stress/src/steps.ts index 5a51491a27..2bc98fa59b 100644 --- a/tee-worker/ts-tests/stress/src/steps.ts +++ b/tee-worker/ts-tests/stress/src/steps.ts @@ -1,4 +1,4 @@ -import { LitentryPrimitivesIdentity, TypeRegistry as SidechainTypeRegistry } from 'sidechain-api'; +import { TypeRegistry as SidechainTypeRegistry } from 'sidechain-api'; import { Wallet, buildIdentityFromWallet, @@ -12,7 +12,7 @@ import { subscribeToEventsWithExtHash, } from './litentry-api'; import WebSocketAsPromised from 'websocket-as-promised'; -import { ApiPromise as ParachainApiPromise } from 'parachain-api'; +import { ApiPromise as ParachainApiPromise, CorePrimitivesIdentity } from 'parachain-api'; import crypto, { randomBytes } from 'crypto'; import { Index } from '@polkadot/types/interfaces'; import { Measurement, Runner } from './measurement'; @@ -29,7 +29,7 @@ export async function setShieldingKey( teeShieldingKey: crypto.KeyObject, userShieldingKey: string, nonce: Index, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, log: WritableStream ): Promise { const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; @@ -74,7 +74,7 @@ export async function linkIdentity( mrEnclave: string, teeShieldingKey: crypto.KeyObject, nonce: Index, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, log: WritableStream ): Promise { const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; @@ -127,7 +127,7 @@ export async function requestVc1( mrEnclave: string, teeShieldingKey: crypto.KeyObject, nonce: Index, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, log: WritableStream ): Promise { const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; @@ -162,7 +162,7 @@ export async function requestVc4( mrEnclave: string, teeShieldingKey: crypto.KeyObject, nonce: Index, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, log: WritableStream ): Promise { const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; @@ -199,7 +199,7 @@ export async function deactivateIdentity( teeShieldingKey: crypto.KeyObject, userShieldingKey: string, nonce: Index, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, log: WritableStream ): Promise { const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; @@ -242,7 +242,7 @@ export async function activateIdentity( mrEnclave: string, teeShieldingKey: crypto.KeyObject, nonce: Index, - subject: LitentryPrimitivesIdentity, + subject: CorePrimitivesIdentity, log: WritableStream ): Promise { const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; diff --git a/tee-worker/ts-tests/stress/src/user-session.ts b/tee-worker/ts-tests/stress/src/user-session.ts index 5e64794584..f776c834e3 100644 --- a/tee-worker/ts-tests/stress/src/user-session.ts +++ b/tee-worker/ts-tests/stress/src/user-session.ts @@ -1,4 +1,4 @@ -import { LitentryPrimitivesIdentity } from 'sidechain-api'; +import { CorePrimitivesIdentity } from 'parachain-api'; import { Index } from '@polkadot/types/interfaces'; import { buildIdentityFromWallet, getSidechainNonce, Wallet, Api } from './litentry-api'; import { Runner } from './measurement'; @@ -7,7 +7,7 @@ import { setShieldingKey } from './steps'; export type UserSession = { primary: Wallet; userShieldingKey: `0x${string}`; - subject: LitentryPrimitivesIdentity; + subject: CorePrimitivesIdentity; nextNonce: () => Index; }; export async function newUserSession( @@ -17,7 +17,7 @@ export async function newUserSession( log: WritableStream, runner: Runner ): Promise { - const subject = await buildIdentityFromWallet(primary, api.sidechainRegistry); + const subject = await buildIdentityFromWallet(primary, api.parachainApi); const initialNonce = await getSidechainNonce( api.teeWorker, api.parachainApi, diff --git a/ts-tests/bridge/contracts/Bridge.json b/ts-tests/common/abi/bridge/Bridge.json similarity index 100% rename from ts-tests/bridge/contracts/Bridge.json rename to ts-tests/common/abi/bridge/Bridge.json diff --git a/ts-tests/bridge/contracts/ERC20Handler.json b/ts-tests/common/abi/bridge/ERC20Handler.json similarity index 100% rename from ts-tests/bridge/contracts/ERC20Handler.json rename to ts-tests/common/abi/bridge/ERC20Handler.json diff --git a/ts-tests/bridge/contracts/ERC721Handler.json b/ts-tests/common/abi/bridge/ERC721Handler.json similarity index 100% rename from ts-tests/bridge/contracts/ERC721Handler.json rename to ts-tests/common/abi/bridge/ERC721Handler.json diff --git a/ts-tests/bridge/contracts/GenericHandler.json b/ts-tests/common/abi/bridge/GenericHandler.json similarity index 100% rename from ts-tests/bridge/contracts/GenericHandler.json rename to ts-tests/common/abi/bridge/GenericHandler.json diff --git a/ts-tests/bridge/contracts/MintableERC20.json b/ts-tests/common/abi/bridge/MintableERC20.json similarity index 100% rename from ts-tests/bridge/contracts/MintableERC20.json rename to ts-tests/common/abi/bridge/MintableERC20.json diff --git a/ts-tests/bridge/contracts/README.md b/ts-tests/common/abi/bridge/README.md similarity index 100% rename from ts-tests/bridge/contracts/README.md rename to ts-tests/common/abi/bridge/README.md diff --git a/ts-tests/common/abi/precompile/Bridge.json b/ts-tests/common/abi/precompile/Bridge.json new file mode 100644 index 0000000000..d5ab21c488 --- /dev/null +++ b/ts-tests/common/abi/precompile/Bridge.json @@ -0,0 +1,25 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "receipt", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "dest_id", + "type": "uint8" + } + ], + "name": "transferNative", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/ts-tests/common/abi/precompile/README.md b/ts-tests/common/abi/precompile/README.md new file mode 100644 index 0000000000..3f0b65430a --- /dev/null +++ b/ts-tests/common/abi/precompile/README.md @@ -0,0 +1,12 @@ +# EVM Contracts JSON + +This folder contains Ethereum Virtual Machine (EVM) contracts converted to JSON format using Remix. + +## What is Remix? + +[Remix](https://remix.ethereum.org/) is an open-source web and desktop application that helps developers write, test, and deploy smart contracts on the Ethereum blockchain. It provides a user-friendly interface for Solidity development and offers features such as a built-in code editor, debugger, and compiler. + +## Included Contracts + +- [ParachainStaking](https://www.notion.so/web3builders/Parachain-Precompile-Contract-0c34929e5f16408084446dcf3dd36006?pvs=4#53cff6e661e84c4fb300bbb5e9f68033) +- [BridgeTransfer](https://www.notion.so/web3builders/Parachain-Precompile-Contract-0c34929e5f16408084446dcf3dd36006?pvs=4#1f82e990425345b59b7cdbba50bb2c49) diff --git a/ts-tests/common/abi/precompile/Staking.json b/ts-tests/common/abi/precompile/Staking.json new file mode 100644 index 0000000000..488464b8e5 --- /dev/null +++ b/ts-tests/common/abi/precompile/Staking.json @@ -0,0 +1,165 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "candidate", + "type": "bytes32" + } + ], + "name": "cancelDelegationRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "candidate", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "delegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "candidate", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "autoCompound", + "type": "uint8" + } + ], + "name": "delegateWithAutoCompound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegator", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "candidate", + "type": "bytes32" + } + ], + "name": "delegationRequestIsPending", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "candidate", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "more", + "type": "uint256" + } + ], + "name": "delegatorBondMore", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegator", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "candidate", + "type": "bytes32" + } + ], + "name": "executeDelegationRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "candidate", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "less", + "type": "uint256" + } + ], + "name": "scheduleDelegatorBondLess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "candidate", + "type": "bytes32" + } + ], + "name": "scheduleRevokeDelegation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "candidate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "value", + "type": "uint8" + } + ], + "name": "setAutoCompound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/ts-tests/bridge/keys/0x148FfB2074A9e59eD58142822b3eB3fcBffb0cd7.key b/ts-tests/common/keys/0x148FfB2074A9e59eD58142822b3eB3fcBffb0cd7.key similarity index 100% rename from ts-tests/bridge/keys/0x148FfB2074A9e59eD58142822b3eB3fcBffb0cd7.key rename to ts-tests/common/keys/0x148FfB2074A9e59eD58142822b3eB3fcBffb0cd7.key diff --git a/ts-tests/bridge/keys/0x24962717f8fA5BA3b931bACaF9ac03924EB475a0.key b/ts-tests/common/keys/0x24962717f8fA5BA3b931bACaF9ac03924EB475a0.key similarity index 100% rename from ts-tests/bridge/keys/0x24962717f8fA5BA3b931bACaF9ac03924EB475a0.key rename to ts-tests/common/keys/0x24962717f8fA5BA3b931bACaF9ac03924EB475a0.key diff --git a/ts-tests/bridge/keys/0x4CEEf6139f00F9F4535Ad19640Ff7A0137708485.key b/ts-tests/common/keys/0x4CEEf6139f00F9F4535Ad19640Ff7A0137708485.key similarity index 100% rename from ts-tests/bridge/keys/0x4CEEf6139f00F9F4535Ad19640Ff7A0137708485.key rename to ts-tests/common/keys/0x4CEEf6139f00F9F4535Ad19640Ff7A0137708485.key diff --git a/ts-tests/bridge/keys/0x8e0a907331554AF72563Bd8D43051C2E64Be5d35.key b/ts-tests/common/keys/0x8e0a907331554AF72563Bd8D43051C2E64Be5d35.key similarity index 100% rename from ts-tests/bridge/keys/0x8e0a907331554AF72563Bd8D43051C2E64Be5d35.key rename to ts-tests/common/keys/0x8e0a907331554AF72563Bd8D43051C2E64Be5d35.key diff --git a/ts-tests/bridge/keys/0xff93B45308FD417dF303D6515aB04D9e89a750Ca.key b/ts-tests/common/keys/0xff93B45308FD417dF303D6515aB04D9e89a750Ca.key similarity index 100% rename from ts-tests/bridge/keys/0xff93B45308FD417dF303D6515aB04D9e89a750Ca.key rename to ts-tests/common/keys/0xff93B45308FD417dF303D6515aB04D9e89a750Ca.key diff --git a/ts-tests/bridge/keys/5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy.key b/ts-tests/common/keys/5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy.key similarity index 100% rename from ts-tests/bridge/keys/5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy.key rename to ts-tests/common/keys/5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy.key diff --git a/ts-tests/bridge/keys/5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty.key b/ts-tests/common/keys/5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty.key similarity index 100% rename from ts-tests/bridge/keys/5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty.key rename to ts-tests/common/keys/5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty.key diff --git a/ts-tests/bridge/keys/5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y.key b/ts-tests/common/keys/5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y.key similarity index 100% rename from ts-tests/bridge/keys/5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y.key rename to ts-tests/common/keys/5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y.key diff --git a/ts-tests/bridge/keys/5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY.key b/ts-tests/common/keys/5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY.key similarity index 100% rename from ts-tests/bridge/keys/5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY.key rename to ts-tests/common/keys/5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY.key diff --git a/ts-tests/bridge/keys/5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw.key b/ts-tests/common/keys/5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw.key similarity index 100% rename from ts-tests/bridge/keys/5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw.key rename to ts-tests/common/keys/5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw.key diff --git a/ts-tests/tests/register-parathread.ts b/ts-tests/common/setup/register-parathread.ts similarity index 96% rename from ts-tests/tests/register-parathread.ts rename to ts-tests/common/setup/register-parathread.ts index 81d1b2f86f..01de1dc613 100644 --- a/ts-tests/tests/register-parathread.ts +++ b/ts-tests/common/setup/register-parathread.ts @@ -3,7 +3,7 @@ import '@polkadot/api-augment'; import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; import { TypeRegistry } from '@polkadot/types/create'; import { Bytes } from '@polkadot/types'; -import { loadConfig, signAndSend } from './utils'; +import { loadConfig, signAndSend } from '../utils'; async function registerParathread(api: ApiPromise, config: any) { // Get keyring of Alice, who is also the sudo in dev chain spec diff --git a/ts-tests/tests/setup-bridge.ts b/ts-tests/common/setup/setup-bridge.ts similarity index 96% rename from ts-tests/tests/setup-bridge.ts rename to ts-tests/common/setup/setup-bridge.ts index 1978d9fd3a..75fbded4b6 100644 --- a/ts-tests/tests/setup-bridge.ts +++ b/ts-tests/common/setup/setup-bridge.ts @@ -5,15 +5,15 @@ import { Contract, ethers, Wallet } from 'ethers'; import { BN } from '@polkadot/util'; import fs from 'fs'; import { spawn } from 'child_process'; -import { initApiPromise, loadConfig, ParachainConfig, signAndSend, sleep, sudoWrapper } from './utils'; +import { initApiPromise, loadConfig, ParachainConfig, signAndSend, sleep, sudoWrapper } from '../utils'; import { toWei } from 'web3-utils'; const path = require('path'); -const BridgeContract = require('../bridge/contracts/Bridge.json'); -const ERC20HandlerContract = require('../bridge/contracts/ERC20Handler.json'); -const ERC721HandlerContract = require('../bridge/contracts/ERC721Handler.json'); -const GenericHandlerContract = require('../bridge/contracts/GenericHandler.json'); -const ERC20Contract = require('../bridge/contracts/MintableERC20.json'); +const BridgeContract = require('../abi/bridge/Bridge.json'); +const ERC20HandlerContract = require('../abi/bridge/ERC20Handler.json'); +const ERC721HandlerContract = require('../abi/bridge/ERC721Handler.json'); +const GenericHandlerContract = require('../abi/bridge/GenericHandler.json'); +const ERC20Contract = require('../abi/bridge/MintableERC20.json'); class EthConfig { wallets!: { alice: Wallet; bob: Wallet; charlie: Wallet; dave: Wallet; eve: Wallet }; @@ -236,7 +236,7 @@ async function startChainBridge( log: string ) { require('dotenv').config(); - const dataDir = './bridge/data'; + const dataDir = './common/data'; if (!fs.existsSync(dataDir)) { fs.mkdirSync(dataDir, { recursive: true }); } @@ -255,10 +255,11 @@ async function startChainBridge( config ); const logging = fs.createWriteStream(log, { flags: 'w+' }); + const lsProcess = spawn( // `${process.env.GOPATH}/bin/chainbridge`, bridgePath, - ['--verbosity', 'trace', '--blockstore', dataDir, '--config', config, '--keystore', './bridge/keys'], + ['--verbosity', 'trace', '--blockstore', dataDir, '--config', config, '--keystore', './common/keys'], { env: { STAGE: 'dev' } } ); lsProcess.stdout.pipe(logging); @@ -300,6 +301,7 @@ export function describeCrossChainTransfer( const parachainConfig = await initApiPromise(config); const provider = new ethers.providers.JsonRpcProvider(config.eth_endpoint); + const wallets = { alice: new ethers.Wallet(generateTestKeys().alice, provider), bob: new ethers.Wallet(generateTestKeys().bob, provider), @@ -307,6 +309,7 @@ export function describeCrossChainTransfer( dave: new ethers.Wallet(generateTestKeys().dave, provider), eve: new ethers.Wallet(generateTestKeys().eve, provider), }; + const { bridge, erc20Handler, erc721Handler, genericHandler, erc20 } = await deployBridgeContracts( wallets.alice ); @@ -336,7 +339,7 @@ export function describeCrossChainTransfer( ethConfig.wallets.bob.address, parachainConfig.bob.address, config.bridge_path, - './bridge/bob.json', + './common/bob.json', '/tmp/parachain_dev/bob.log' ); await sleep(5); diff --git a/ts-tests/tests/setup-enclave.ts b/ts-tests/common/setup/setup-enclave.ts similarity index 81% rename from ts-tests/tests/setup-enclave.ts rename to ts-tests/common/setup/setup-enclave.ts index ae1d2203f3..36faf3db7b 100644 --- a/ts-tests/tests/setup-enclave.ts +++ b/ts-tests/common/setup/setup-enclave.ts @@ -1,9 +1,6 @@ -import fs from 'fs'; import '@polkadot/api-augment'; import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; -import { TypeRegistry } from '@polkadot/types/create'; -import { Bytes } from '@polkadot/types'; -import { loadConfig, signAndSend } from './utils'; +import { loadConfig, signAndSend } from '../utils'; import { hexToU8a } from '@polkadot/util'; const mrenclave = process.argv[2]; @@ -14,7 +11,7 @@ async function setAliceAsAdmin(api: ApiPromise, config: any) { const keyring = new Keyring({ type: 'sr25519' }); const alice = keyring.addFromUri('//Alice'); - const tx = api.tx.sudo.sudo(api.tx.teerex.setAdmin('esqZdrqhgH8zy1wqYh1aLKoRyoRWLFbX9M62eKfaTAoK67pJ5')); + const tx = api.tx.sudo.sudo(api.tx.teerex.setAdmin('esqZdrqhgH8zy1wqYh1aLKoRyoRWLFbX9M62eKfaTAoK67pJ5')); console.log(`Setting Alice as Admin for Teerex`); return signAndSend(tx, alice); @@ -23,11 +20,11 @@ async function setAliceAsAdmin(api: ApiPromise, config: any) { async function updateScheduledEnclave(api: ApiPromise, config: any) { const keyring = new Keyring({ type: 'sr25519' }); const alice = keyring.addFromUri('//Alice'); - + const tx = api.tx.teerex.updateScheduledEnclave(block, hexToU8a(`0x${mrenclave}`)); - console.log("Schedule Enclave Extrinsic sent"); - return signAndSend(tx, alice) + console.log('Schedule Enclave Extrinsic sent'); + return signAndSend(tx, alice); } (async () => { @@ -41,11 +38,10 @@ async function updateScheduledEnclave(api: ApiPromise, config: any) { await setAliceAsAdmin(api, config); await updateScheduledEnclave(api, config); - + await api.disconnect(); provider.on('disconnected', () => { console.log('Disconnect from relaychain'); process.exit(0); }); })(); - diff --git a/ts-tests/tests/upgrade-parathread.ts b/ts-tests/common/setup/upgrade-parathread.ts similarity index 97% rename from ts-tests/tests/upgrade-parathread.ts rename to ts-tests/common/setup/upgrade-parathread.ts index 2e9b6d1122..a794c0656d 100644 --- a/ts-tests/tests/upgrade-parathread.ts +++ b/ts-tests/common/setup/upgrade-parathread.ts @@ -1,7 +1,7 @@ import '@polkadot/api-augment'; import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; import type { ISubmittableResult } from '@polkadot/types/types'; -import { loadConfig } from './utils'; +import { loadConfig } from '../utils'; // upgrade the parathread to parachain by forcibly leasing a certain period // can be used to extend the leasing period if it's already in onboarding process diff --git a/ts-tests/tests/compile.ts b/ts-tests/common/utils/compile.ts similarity index 74% rename from ts-tests/tests/compile.ts rename to ts-tests/common/utils/compile.ts index ac6f42ad67..e1fdeede77 100644 --- a/ts-tests/tests/compile.ts +++ b/ts-tests/common/utils/compile.ts @@ -25,22 +25,21 @@ contract Hello { const input = { language: 'Solidity', sources: { - 'hello.sol': { - content: source - } + 'hello.sol': { + content: source, + }, }, settings: { - outputSelection: { - '*': { - '*': ['*'] - } - }, - // evmVersion: "byzantium", - } - }; + outputSelection: { + '*': { + '*': ['*'], + }, + }, + // evmVersion: "byzantium", + }, +}; const result = JSON.parse(solcWrapper.compile(JSON.stringify(input))); if (!result.contracts) { - console.log(result.errors); + console.log(result.errors); } export const compiled: any = result.contracts['hello.sol']['Hello']; - diff --git a/ts-tests/common/utils/config.ts b/ts-tests/common/utils/config.ts new file mode 100644 index 0000000000..256f9152de --- /dev/null +++ b/ts-tests/common/utils/config.ts @@ -0,0 +1,14 @@ +export function loadConfig() { + require('dotenv').config(); + switch (process.env.NODE_ENV) { + case 'local': + return require('../../config.local.json'); + case 'test': + case 'ci': + return require('../../config.ci.json'); + case 'staging': + return require('../../config.staging.json'); + default: + throw new Error(`Invalid NODE_ENV: ${process.env.NODE_ENV}`); + } +} diff --git a/ts-tests/common/utils/function.ts b/ts-tests/common/utils/function.ts new file mode 100644 index 0000000000..c94ede409f --- /dev/null +++ b/ts-tests/common/utils/function.ts @@ -0,0 +1,74 @@ +import { AddressOrPair, ApiTypes, SubmittableExtrinsic } from '@polkadot/api/types'; +import { ApiPromise } from '@polkadot/api'; +import { FrameSystemEventRecord } from '@polkadot/types/lookup'; +export function sleep(secs: number) { + return new Promise((resolve) => { + setTimeout(resolve, secs * 1000); + }); +} + +export function signAndSend(tx: SubmittableExtrinsic, account: AddressOrPair) { + return new Promise<{ block: string }>(async (resolve, reject) => { + await tx.signAndSend(account, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log(`Transaction included at blockHash ${result.status.asInBlock}`); + } else if (result.status.isFinalized) { + console.log(`Transaction finalized at blockHash ${result.status.asFinalized}`); + resolve({ + block: result.status.asFinalized.toString(), + }); + } else if (result.status.isInvalid) { + reject(`Transaction is ${result.status}`); + } + }); + }); +} + +// After removing the sudo module, we use `EnsureRootOrHalfCouncil` instead of `Sudo`, +// and there are only two council members in litmus-dev/rococo-dev/litentry-dev. +// So only `propose` is required, no vote. +// +// TODO: support to send the `vote extrinsic`, if the number of council members is greater than 2. +export async function sudoWrapper(api: ApiPromise, tx: SubmittableExtrinsic) { + const chain = (await api.rpc.system.chain()).toString().toLowerCase(); + if (chain == 'litmus-dev') { + const threshold = api.createType('Compact', 1); + const call = api.createType('Call', tx); + return api.tx.council.propose(threshold, call, api.createType('Compact', tx.length)); + } else { + return api.tx.sudo.sudo(tx); + } +} + +export const subscribeToEvents = async ( + section: string, + method: string, + api: ApiPromise +): Promise => { + return new Promise((resolve, reject) => { + let blocksToScan = 15; + const unsubscribe = api.rpc.chain.subscribeNewHeads(async (blockHeader) => { + const shiftedApi = await api.at(blockHeader.hash); + + const allBlockEvents = await shiftedApi.query.system.events(); + const allExtrinsicEvents = allBlockEvents.filter(({ phase }) => phase.isApplyExtrinsic); + + const matchingEvent = allExtrinsicEvents.filter(({ event, phase }) => { + return event.section === section && event.method === method; + }); + + if (matchingEvent.length == 0) { + blocksToScan -= 1; + if (blocksToScan < 1) { + reject(new Error(`timed out listening for event ${section}.${method}`)); + (await unsubscribe)(); + } + return; + } + + resolve(matchingEvent); + (await unsubscribe)(); + }); + }); +}; diff --git a/ts-tests/common/utils/index.ts b/ts-tests/common/utils/index.ts new file mode 100644 index 0000000000..537f5e77e5 --- /dev/null +++ b/ts-tests/common/utils/index.ts @@ -0,0 +1,3 @@ +export * from './config'; +export * from './integration-setup'; +export * from './function'; diff --git a/ts-tests/tests/utils.ts b/ts-tests/common/utils/integration-setup.ts similarity index 59% rename from ts-tests/tests/utils.ts rename to ts-tests/common/utils/integration-setup.ts index 52cc919c51..099dc6cd37 100644 --- a/ts-tests/tests/utils.ts +++ b/ts-tests/common/utils/integration-setup.ts @@ -1,10 +1,8 @@ import 'mocha'; - import '@polkadot/api-augment'; import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; -import { AddressOrPair, ApiTypes, SubmittableExtrinsic } from '@polkadot/api/types'; import { KeyringPair } from '@polkadot/keyring/types'; -import { BN } from '@polkadot/util'; +import { loadConfig } from './config'; export class ParachainConfig { api!: ApiPromise; @@ -15,27 +13,6 @@ export class ParachainConfig { ferdie!: KeyringPair; } -export function loadConfig() { - require('dotenv').config(); - switch (process.env.NODE_ENV) { - case 'local': - return require('../config.local.json') - case 'test': - case 'ci': - return require('../config.ci.json'); - case 'staging': - return require('../config.staging.json'); - default: - throw new Error(`Invalid NODE_ENV: ${process.env.NODE_ENV}`); - } -} - -export function sleep(secs: number) { - return new Promise((resolve) => { - setTimeout(resolve, secs * 1000); - }); -} - export async function initApiPromise(config: any): Promise { console.log(`Initiating the API (ignore message "Unable to resolve type B..." and "Unknown types found...")`); // Provider is set for parachain node @@ -76,40 +53,6 @@ export async function initApiPromise(config: any): Promise { return { api, parachain, alice, bob, eve, ferdie }; } -export function signAndSend(tx: SubmittableExtrinsic, account: AddressOrPair) { - return new Promise<{ block: string }>(async (resolve, reject) => { - await tx.signAndSend(account, (result) => { - console.log(`Current status is ${result.status}`); - if (result.status.isInBlock) { - console.log(`Transaction included at blockHash ${result.status.asInBlock}`); - } else if (result.status.isFinalized) { - console.log(`Transaction finalized at blockHash ${result.status.asFinalized}`); - resolve({ - block: result.status.asFinalized.toString(), - }); - } else if (result.status.isInvalid) { - reject(`Transaction is ${result.status}`); - } - }); - }); -} - -// After removing the sudo module, we use `EnsureRootOrHalfCouncil` instead of `Sudo`, -// and there are only two council members in litmus-dev/rococo-dev/litentry-dev. -// So only `propose` is required, no vote. -// -// TODO: support to send the `vote extrinsic`, if the number of council members is greater than 2. -export async function sudoWrapper(api: ApiPromise, tx: SubmittableExtrinsic) { - const chain = (await api.rpc.system.chain()).toString().toLowerCase(); - if (chain == 'litmus-dev') { - const threshold = api.createType('Compact', 1); - const call = api.createType('Call', tx); - return api.tx.council.propose(threshold, call, api.createType('Compact', tx.length)); - } else { - return api.tx.sudo.sudo(tx); - } -} - export function describeLitentry(title: string, specFilename: string, cb: (context: ParachainConfig) => void) { describe(title, function () { // Set timeout to 6000 seconds (Because of 50-blocks delay of rococo, so called "training wheels") diff --git a/ts-tests/global.d.ts b/ts-tests/global.d.ts new file mode 100644 index 0000000000..905f98a29b --- /dev/null +++ b/ts-tests/global.d.ts @@ -0,0 +1 @@ +declare module 'solc'; diff --git a/ts-tests/tests/base-filter.test.ts b/ts-tests/integration-tests/base-filter.test.ts similarity index 96% rename from ts-tests/tests/base-filter.test.ts rename to ts-tests/integration-tests/base-filter.test.ts index e3bcba7b0b..5bc277d386 100644 --- a/ts-tests/tests/base-filter.test.ts +++ b/ts-tests/integration-tests/base-filter.test.ts @@ -1,7 +1,6 @@ import { expect } from 'chai'; import { step } from 'mocha-steps'; -import { assert } from 'chai'; -import { signAndSend, describeLitentry, loadConfig, sleep } from './utils'; +import { signAndSend, describeLitentry } from '../common/utils'; describeLitentry('Test Base Filter', ``, (context) => { console.log(`Test Base Filter`); diff --git a/ts-tests/tests/bridge.test.ts b/ts-tests/integration-tests/bridge.test.ts similarity index 98% rename from ts-tests/tests/bridge.test.ts rename to ts-tests/integration-tests/bridge.test.ts index ff803199f8..2a0844ef6b 100644 --- a/ts-tests/tests/bridge.test.ts +++ b/ts-tests/integration-tests/bridge.test.ts @@ -1,7 +1,7 @@ -import { createERCDepositData, describeCrossChainTransfer } from './setup-bridge'; +import { createERCDepositData, describeCrossChainTransfer } from '../common/setup/setup-bridge'; import { step } from 'mocha-steps'; import { toHex } from 'web3-utils'; -import { signAndSend, sleep } from './utils'; +import { signAndSend, sleep } from '../common/utils'; import { assert } from 'chai'; import { BigNumber, ethers } from 'ethers'; @@ -15,8 +15,11 @@ describeCrossChainTransfer('Test Cross-chain Transfer', ``, (context) => { // substrate native token // const destResourceId = "0x00000000000000000000000000000063a7e2be78898ba83824b0c0cc8dfb6001" const destResourceId = context.parachainConfig.api.consts.bridgeTransfer.nativeTokenResourceId.toHex(); + const depositAmount = toHex(BigNumber.from('100,000,000,000,000,000,000'.replace(/,/g, '')).toString()); let destinationChainID = parseInt(context.parachainConfig.api.consts.chainBridge.bridgeChainId.toString()); + console.log(destinationChainID); + //FERDIE key command: polkadot key inspect //Ferdie const destinationRecipientAddress = '0x1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c'; @@ -64,7 +67,7 @@ describeCrossChainTransfer('Test Cross-chain Transfer', ``, (context) => { context.ethConfig.erc20Handler.address ); const fee = await context.parachainConfig.api.query.chainBridge.bridgeFee(0); - const Bridge = require('../bridge/contracts/Bridge.json'); + const Bridge = require('../common/abi/bridge/Bridge.json'); const inter = new ethers.utils.Interface(Bridge.abi); await signAndSend( context.parachainConfig.api.tx.bridgeTransfer.transferNative( diff --git a/ts-tests/tests/evm-contract.test.ts b/ts-tests/integration-tests/evm-contract.test.ts similarity index 97% rename from ts-tests/tests/evm-contract.test.ts rename to ts-tests/integration-tests/evm-contract.test.ts index 9242688678..4811b9cb94 100644 --- a/ts-tests/tests/evm-contract.test.ts +++ b/ts-tests/integration-tests/evm-contract.test.ts @@ -1,14 +1,15 @@ import { assert, expect } from 'chai'; import { step } from 'mocha-steps'; -import { signAndSend, describeLitentry, loadConfig, sleep } from './utils'; -import { evmToAddress } from '@polkadot/util-crypto' +import { signAndSend, describeLitentry, loadConfig, sleep } from '../common/utils'; +import { evmToAddress } from '@polkadot/util-crypto'; import Web3 from 'web3'; -import { compiled } from './compile'; +import { compiled } from '../common/utils/compile'; describeLitentry('Test EVM Module Contract', ``, (context) => { console.log(`Test EVM Module Contract`); + const config = loadConfig(); step('Transfer Value from Eve to EVM external account', async function () { // In case evm is not enabled in Normal Mode, switch back to filterMode, after test. @@ -63,7 +64,7 @@ describeLitentry('Test EVM Module Contract', ``, (context) => { // If a substrate account using pallet_evm to trigger evm transaction, // it will bump 2 for nonce (one for substrate extrinsic, one for evm). - // +1 nonce for original substrate account, plus another 1 nonce for original substrate account's truncated evm address's mapped susbtrate account. + // +1 nonce for original substrate account, plus another 1 nonce for original substrate account's truncated evm address's mapped susbtrate account. expect(eveCurrentNonce.toNumber()).to.equal(eveInitNonce.toNumber() + 1); expect(evmAccountCurrentBalance.free.toBigInt()).to.equal( evmAccountInitBalance.free.toBigInt() + BigInt(value) @@ -96,7 +97,7 @@ describeLitentry('Test EVM Module Contract', ``, (context) => { const bytecode = compiled.evm.bytecode.object; const abi = compiled.abi; // Create Web3 instance - const web3 = new Web3('http://localhost:9944'); + const web3 = new Web3(config.parachain_ws); // Create deploy function const deploy = async (accountFrom: any) => { diff --git a/ts-tests/tests/evm-transfer.test.ts b/ts-tests/integration-tests/evm-transfer.test.ts similarity index 97% rename from ts-tests/tests/evm-transfer.test.ts rename to ts-tests/integration-tests/evm-transfer.test.ts index d464a5db33..b7a5d7fe52 100644 --- a/ts-tests/tests/evm-transfer.test.ts +++ b/ts-tests/integration-tests/evm-transfer.test.ts @@ -1,15 +1,15 @@ import { assert, expect } from 'chai'; import { step } from 'mocha-steps'; -import { signAndSend, describeLitentry } from './utils'; +import { signAndSend, describeLitentry, loadConfig } from '../common/utils'; import { hexToU8a, u8aToHex } from '@polkadot/util'; import { createPair, encodeAddress } from '@polkadot/keyring'; -import { evmToAddress } from '@polkadot/util-crypto' +import { evmToAddress } from '@polkadot/util-crypto'; import Web3 from 'web3'; describeLitentry('Test EVM Module Transfer', ``, (context) => { console.log(`Test EVM Module Transfer`); - + const config = loadConfig(); step('Transfer Value from Eve to EVM external account', async function () { // In case evm is not enabled in Normal Mode, switch back to filterMode, after test. // We do not test mode in initialization since ts-test concerns filter function too. @@ -75,7 +75,7 @@ describeLitentry('Test EVM Module Transfer', ``, (context) => { // If a substrate account using pallet_evm to trigger evm transaction, // it will bump 2 for nonce (one for substrate extrinsic, one for evm). - // +1 nonce for original substrate account, plus another 1 nonce for original substrate account's truncated evm address's mapped susbtrate account. + // +1 nonce for original substrate account, plus another 1 nonce for original substrate account's truncated evm address's mapped susbtrate account. expect(eveCurrentNonce.toNumber()).to.equal(eveInitNonce.toNumber() + 1); expect(evmAccountCurrentBalance.free.toBigInt()).to.equal( evmAccountInitBalance.free.toBigInt() + BigInt(value) @@ -116,7 +116,7 @@ describeLitentry('Test EVM Module Transfer', ``, (context) => { console.log(`evmAccount Balance: ${evmAccountInitBalance}`); // Create Web3 instance - const web3 = new Web3('http://localhost:9944'); + const web3 = new Web3(config.parachain_ws); let value = 100000000000; // ExistentialDeposit = 100 000 000 000 (0x174876E800) diff --git a/ts-tests/integration-tests/precompile-contract.test.ts b/ts-tests/integration-tests/precompile-contract.test.ts new file mode 100644 index 0000000000..bb3b9d65d5 --- /dev/null +++ b/ts-tests/integration-tests/precompile-contract.test.ts @@ -0,0 +1,346 @@ +import { expect } from 'chai'; +import { step } from 'mocha-steps'; +import { AbiItem } from 'web3-utils'; +import { signAndSend, describeLitentry, loadConfig, subscribeToEvents } from '../common/utils'; +import Web3 from 'web3'; +import precompileStakingContractAbi from '../common/abi/precompile/Staking.json'; +import precompileBridgeContractAbi from '../common/abi/precompile/Bridge.json'; +const BN = require('bn.js'); +import { mnemonicGenerate, mnemonicToMiniSecret, evmToAddress } from '@polkadot/util-crypto'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { HexString } from '@polkadot/util/types'; + +const toBigNumber = (int: number) => int * 1e12; +const bn1e12 = new BN(10).pow(new BN(12)).mul(new BN(1)); + +describeLitentry('Test Parachain Precompile Contract', ``, (context) => { + const config = loadConfig(); + + const precompileStakingContractAddress = '0x000000000000000000000000000000000000502d'; + const precompileBridgeContractAddress = '0x000000000000000000000000000000000000503d'; + const evmAccountRaw = { + privateKey: '0x01ab6e801c06e59ca97a14fc0a1978b27fa366fc87450e0b65459dd3515b7391', + address: '0xaaafB3972B05630fCceE866eC69CdADd9baC2771', + mappedAddress: evmToAddress('0xaaafB3972B05630fCceE866eC69CdADd9baC2771', 31), + publicKey: '0x93eac2793cb6d9e837b0f8da1a63dbc0db2ca848c05cbe66db139157922f78f9', + }; + + // candidate: collator address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY + // transform to bytes32(public key) reference:https://polkadot.subscan.io/tools/format_transform?input=5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY&type=All + const collatorPublicKey = '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'; + + const web3 = new Web3(config.parachain_ws); + + const precompileStakingContract = new web3.eth.Contract( + precompileStakingContractAbi as AbiItem[], + precompileStakingContractAddress + ); + const precompileBridgeContract = new web3.eth.Contract( + precompileBridgeContractAbi as AbiItem[], + precompileBridgeContractAddress + ); + + const executeTransaction = async (delegateTransaction: any, contractAddress: HexString, label = '') => { + console.log(`=== Executing ${label} ===`); + + // estimate gas doesn't work + // const gas = await delegateTransaction.estimateGas(); + // console.log("gas", gas); + + const transaction = await web3.eth.accounts.signTransaction( + { + to: contractAddress, + data: delegateTransaction.encodeABI(), + gas: 1000000, + }, + evmAccountRaw.privateKey + ); + + return await web3.eth.sendSignedTransaction(transaction.rawTransaction!); + }; + + const printBalance = (label: string, bl: any) => { + console.log(label, 'free', bl.free.toNumber() / 1e12, 'reserved', bl.reserved.toNumber() / 1e12); + }; + + // this function makes two transactions first one from token owner substrate account to owner evm mapped account + // and then it transfers to recepient + const transferTokens = async (from: KeyringPair, to: any) => { + const aliceEVMMappedAccount = from.publicKey.slice(0, 20); // pretend to be evm + console.log(`alice address: ${from.publicKey}`); + console.log(`aliceEVMMappedAccount: ${aliceEVMMappedAccount}`); + + let aliceMappedSustrateAccount = evmToAddress(aliceEVMMappedAccount, 31); + + console.log('transfer from Alice to alice EMV'); + + // Deposit money into substrate account's truncated EVM address's mapping substrate account + const tx_init = context.api.tx.balances.transfer(aliceMappedSustrateAccount, 70 * 1e12); + await signAndSend(tx_init, context.alice); + + // 25000 is min_gas_price setup + const tx = context.api.tx.evm.call( + aliceEVMMappedAccount, // evm like + to.address, // evm like + '0x', + toBigNumber(65), + 1000000, + 25000, + null, + null, + [] + ); + let block = await context.api.rpc.chain.getBlock(); + console.log(`evm call await before: ${block.block.header.number}`); + await signAndSend(tx, from); + let temp = await context.api.rpc.chain.getBlock(); + console.log(`evm call await end: ${temp.block.header.number}`); + }; + + const isPendingRequest = async () => + await precompileStakingContract.methods + .delegationRequestIsPending(evmAccountRaw.publicKey, collatorPublicKey) + .call(); + + const collatorDetails = async () => { + const response = await context.api.query.parachainStaking.autoCompoundingDelegations(collatorPublicKey); + const collators = response.toJSON() as { address: string; value: number }[]; + return collators[0]; + }; + + step('Address with not sufficient amount of tokens', async function () { + // Create valid Substrate-compatible seed from mnemonic + const randomSeed = mnemonicToMiniSecret(mnemonicGenerate()); + const secretKey = Buffer.from(randomSeed).toString('hex'); + + const delegateWithAutoCompound = precompileStakingContract.methods.delegateWithAutoCompound( + collatorPublicKey, + toBigNumber(60), + 1 + ); + + try { + await web3.eth.accounts.signTransaction( + { + to: precompileStakingContractAddress, + data: delegateWithAutoCompound.encodeABI(), + gas: await delegateWithAutoCompound.estimateGas(), + }, + secretKey + ); + expect(true).to.eq(false); // test should fail here + } catch (e) { + expect(e).to.be.instanceof(Error); + } + }); + + // To see full params types for the interfaces, check notion page: https://web3builders.notion.site/Parachain-Precompile-Contract-0c34929e5f16408084446dcf3dd36006 + step('Test precompile staking contract', async function () { + console.time('Test precompile staking contract'); + const filterMode = (await context.api.query.extrinsicFilter.mode()).toHuman(); + if ('Test' !== filterMode) { + let extrinsic = context.api.tx.sudo.sudo(context.api.tx.extrinsicFilter.setMode('Test')); + let temp = await context.api.rpc.chain.getBlock(); + console.log(`setMode await Before: ${temp.block.header.number}`); + await signAndSend(extrinsic, context.alice); + temp = await context.api.rpc.chain.getBlock(); + console.log(`setMode await end: ${temp.block.header.number}`); + } + let balance = (await context.api.query.system.account(evmAccountRaw.mappedAddress)).data; + printBalance('initial balance', balance); + + // top up LITs if not sufficient amount for staking or they are not reserved (require: 50 LITs minimum) + if (balance.free.toNumber() < toBigNumber(60) && balance.reserved.toNumber() === 0) { + console.log('transferring more tokens'); + + await transferTokens(context.alice, evmAccountRaw); + + balance = (await context.api.query.system.account(evmAccountRaw.mappedAddress)).data; + printBalance('balance after transferring', balance); + } + + //// TESTS + const autoCompoundPercent = 20; + + // delegateWithAutoCompound(collator, amount, percent) + const delegateWithAutoCompound = precompileStakingContract.methods.delegateWithAutoCompound( + collatorPublicKey, + toBigNumber(60), + autoCompoundPercent + ); + + let afterDelegateBalance = balance; + // skip test if already delegated + if (balance.reserved.toNumber() === 0) { + await executeTransaction( + delegateWithAutoCompound, + precompileStakingContractAddress, + 'delegateWithAutoCompound' + ); + afterDelegateBalance = (await context.api.query.system.account(evmAccountRaw.mappedAddress)).data; + + expect(balance.free.toNumber() - toBigNumber(60)).to.closeTo( + afterDelegateBalance.free.toNumber(), + toBigNumber(1) + ); + expect(afterDelegateBalance.reserved.toNumber()).to.eq(toBigNumber(60)); + const collator = await collatorDetails(); + expect(collator.value).to.eq(autoCompoundPercent); + } + + // delegatorBondMore(collator, amount) + const delegatorBondMore = precompileStakingContract.methods.delegatorBondMore( + collatorPublicKey, + toBigNumber(1) + ); + await executeTransaction(delegatorBondMore, precompileStakingContractAddress, 'delegatorBondMore'); + + const { data: balanceAfterBondMore } = await context.api.query.system.account(evmAccountRaw.mappedAddress); + expect(balanceAfterBondMore.free.toNumber()).to.closeTo( + balanceAfterBondMore.free.toNumber() - toBigNumber(1), + toBigNumber(1) + ); + expect(balanceAfterBondMore.reserved.toNumber()).to.eq( + afterDelegateBalance.reserved.toNumber() + toBigNumber(1) + ); + + // setAutoCompound(collator, percent); + const setAutoCompound = precompileStakingContract.methods.setAutoCompound( + collatorPublicKey, + autoCompoundPercent + 5 + ); + await executeTransaction(setAutoCompound, precompileStakingContractAddress, 'setAutoCompound'); + const collatorAfterCompound = await collatorDetails(); + expect(collatorAfterCompound.value).to.eq(autoCompoundPercent + 5); + + // scheduleDelegatorBondLess(collator, amount) + expect(await isPendingRequest()).to.be.false; + const scheduleDelegatorBondLess = precompileStakingContract.methods.scheduleDelegatorBondLess( + collatorPublicKey, + toBigNumber(5) + ); + await executeTransaction( + scheduleDelegatorBondLess, + precompileStakingContractAddress, + 'scheduleDelegatorBondLess' + ); + expect(await isPendingRequest()).to.be.true; + + // cancelDelegationRequest(collator) + const cancelDelegationRequest = precompileStakingContract.methods.cancelDelegationRequest(collatorPublicKey); + expect(await isPendingRequest()).to.be.true; + await executeTransaction(cancelDelegationRequest, precompileStakingContractAddress, 'cancelDelegationRequest'); + expect(await isPendingRequest()).to.be.false; + + // testing bond less + execution + await executeTransaction( + scheduleDelegatorBondLess, + precompileStakingContractAddress, + 'scheduleDelegatorBondLess again to test execution' + ); + expect(await isPendingRequest()).to.be.true; + + console.log('Waiting 2 blocks before execute delegation request'); + await context.api.rpc.chain.getBlock(); + await context.api.rpc.chain.getBlock(); + + // executeDelegationRequest(delegator, collator); + const executeDelegationRequest = precompileStakingContract.methods.executeDelegationRequest( + evmAccountRaw.publicKey, + collatorPublicKey + ); + await executeTransaction( + executeDelegationRequest, + precompileStakingContractAddress, + 'executeDelegationRequest' + ); + const { data: balanceAfterBondLess } = await context.api.query.system.account(evmAccountRaw.mappedAddress); + expect(balanceAfterBondLess.free.toNumber()).to.closeTo( + balanceAfterBondMore.free.toNumber() + toBigNumber(5), + toBigNumber(1) + ); + expect(balanceAfterBondLess.reserved.toNumber()).to.eq( + balanceAfterBondMore.reserved.toNumber() - toBigNumber(5) + ); + + // testing revoke delegation + execute + // scheduleRevokeDelegation(collator); + const scheduleRevokeDelegation = precompileStakingContract.methods.scheduleRevokeDelegation(collatorPublicKey); + await executeTransaction( + scheduleRevokeDelegation, + precompileStakingContractAddress, + 'scheduleRevokeDelegation' + ); + + console.log('Waiting 2 blocks before execute delegation request'); + await context.api.rpc.chain.getBlock(); + await context.api.rpc.chain.getBlock(); + + await executeTransaction( + executeDelegationRequest, + precompileStakingContractAddress, + 'executeDelegationRequest' + ); + const { data: balanceAfterRevoke } = await context.api.query.system.account(evmAccountRaw.mappedAddress); + expect(balanceAfterRevoke.free.toNumber()).to.closeTo(balance.free.toNumber(), toBigNumber(1)); + expect(balanceAfterRevoke.reserved.toNumber()).to.eq(0); + + // delegate(collator, amount); + const delegate = precompileStakingContract.methods.delegate(collatorPublicKey, toBigNumber(57)); + await executeTransaction(delegate, precompileStakingContractAddress, 'delegate'); + const { data: balanceAfterDelegate } = await context.api.query.system.account(evmAccountRaw.mappedAddress); + expect(balanceAfterDelegate.reserved.toNumber()).to.eq(toBigNumber(57)); + + // In case evm is not enabled in Normal Mode, switch back to filterMode, after test. + let extrinsic = context.api.tx.sudo.sudo(context.api.tx.extrinsicFilter.setMode(filterMode)); + await signAndSend(extrinsic, context.alice); + + console.timeEnd('Test precompile staking contract'); + }); + step('Test precompile bridge contract', async function () { + console.time('Test precompile bridge contract'); + const dest_address = '0xaaafb3972b05630fccee866ec69cdadd9bac2772'; // random address + let balance = (await context.api.query.system.account(evmAccountRaw.mappedAddress)).data; + if (balance.free.toNumber() < toBigNumber(0.01)) { + await transferTokens(context.alice, evmAccountRaw); + expect(balance.free.toNumber()).to.gt(toBigNumber(0.01)); + } + + // update chain bridge fee + const updateFeeTx = context.api.tx.sudo.sudo(context.api.tx.chainBridge.updateFee(0, bn1e12 / 1000)); + await signAndSend(updateFeeTx, context.alice); + + const bridge_fee = await context.api.query.chainBridge.bridgeFee(0); + expect(bridge_fee.toString()).to.eq((bn1e12 / 1000).toString()); + + // set chainId to whitelist + const whitelistChainTx = context.api.tx.sudo.sudo(context.api.tx.chainBridge.whitelistChain(0)); + await signAndSend(whitelistChainTx, context.alice); + + // The above two steps are necessary, otherwise the contract transaction will be reverted. + // transfer native token + const transferNativeTx = precompileBridgeContract.methods.transferNative( + bn1e12 / 100, // 0.01 LIT + dest_address, + 0 + ); + + const res = await executeTransaction(transferNativeTx, precompileBridgeContractAddress, 'transferNative'); + expect(res.status).to.eq(true); + + const eventsPromise = subscribeToEvents('chainBridge', 'FungibleTransfer', context.api); + const events = (await eventsPromise).map(({ event }) => event); + + expect(events.length).to.eq(1); + const event_data = events[0].toHuman().data! as Array; + + // FungibleTransfer(BridgeChainId, DepositNonce, ResourceId, u128, Vec) + expect(event_data[0]).to.eq('0'); + const destResourceId = context.api.consts.bridgeTransfer.nativeTokenResourceId.toHex(); + expect(event_data[2]).to.eq(destResourceId); + expect(event_data[3]).to.eq((bn1e12 / 100 - bn1e12 / 1000).toLocaleString()); + expect(event_data[4]).to.eq(dest_address); + + console.timeEnd('Test precompile bridge contract'); + }); +}); diff --git a/ts-tests/tests/runtime-upgrade.test.ts b/ts-tests/integration-tests/runtime-upgrade.test.ts similarity index 97% rename from ts-tests/tests/runtime-upgrade.test.ts rename to ts-tests/integration-tests/runtime-upgrade.test.ts index 252a66cf36..2c6019c8e2 100644 --- a/ts-tests/tests/runtime-upgrade.test.ts +++ b/ts-tests/integration-tests/runtime-upgrade.test.ts @@ -1,7 +1,7 @@ import { blake2AsHex } from '@polkadot/util-crypto'; import * as fs from 'fs'; import { Keyring, ApiPromise, WsProvider } from '@polkadot/api'; -import { describeLitentry } from './utils'; +import { describeLitentry } from '../common/utils/integration-setup'; import '@polkadot/wasm-crypto/initOnlyAsm'; import * as path from 'path'; import { expect } from 'chai'; @@ -21,7 +21,7 @@ async function runtimeUpgrade(api: ApiPromise, wasm: string) { // authorize and enact the upgrade await api.tx.sudo - .sudo(api.tx.parachainSystem.authorizeUpgrade(blake2AsHex(wasm))) + .sudo(api.tx.parachainSystem.authorizeUpgrade(blake2AsHex(wasm), true)) .signAndSend(alice, { nonce: -1 }); console.log('Submitted authorizeUpgrade'); await api.tx.parachainSystem.enactAuthorizedUpgrade(wasm).signAndSend(alice, { nonce: -1 }); diff --git a/ts-tests/tests/transaction-fee.test.ts b/ts-tests/integration-tests/transaction-fee.test.ts similarity index 99% rename from ts-tests/tests/transaction-fee.test.ts rename to ts-tests/integration-tests/transaction-fee.test.ts index 57e0e12c0b..d6eca16117 100644 --- a/ts-tests/tests/transaction-fee.test.ts +++ b/ts-tests/integration-tests/transaction-fee.test.ts @@ -2,7 +2,7 @@ import { u8aConcat } from '@polkadot/util'; import { expect } from 'chai'; import { step } from 'mocha-steps'; -import { signAndSend, describeLitentry, loadConfig, sleep } from './utils'; +import { signAndSend, describeLitentry, loadConfig, sleep } from '../common/utils'; describeLitentry('Test Transaction Fee', ``, (context) => { console.log(`Test Transaction Fee`); diff --git a/ts-tests/tests/transfer.test.ts b/ts-tests/integration-tests/transfer.test.ts similarity index 96% rename from ts-tests/tests/transfer.test.ts rename to ts-tests/integration-tests/transfer.test.ts index cb7082875a..4537f7feb2 100644 --- a/ts-tests/tests/transfer.test.ts +++ b/ts-tests/integration-tests/transfer.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { step } from 'mocha-steps'; -import { signAndSend, describeLitentry, loadConfig, sleep } from './utils'; +import { signAndSend, describeLitentry } from '../common/utils'; describeLitentry('Test Balance Transfer', ``, (context) => { console.log(`Test Balance Transfer`); diff --git a/ts-tests/package.json b/ts-tests/package.json index 3af0758c10..ca9bdb873a 100644 --- a/ts-tests/package.json +++ b/ts-tests/package.json @@ -4,19 +4,20 @@ "description": "This is a set of integration tests for litentry runtime, using TypeScript.", "main": "index.js", "directories": { - "test": "tests" + "test": "integration-tests" }, "scripts": { - "register-parathread": "pnpm exec ts-node tests/register-parathread.ts", - "upgrade-parathread": "pnpm exec ts-node tests/upgrade-parathread.ts", - "setup-enclave": "pnpm exec ts-node tests/setup-enclave.ts", - "test-filter": "pnpm exec mocha --exit --sort -r ts-node/register 'tests/base-filter.test.ts'", - "test-bridge": "pnpm exec mocha --exit --sort -r ts-node/register 'tests/bridge.test.ts'", - "test-evm-transfer": "pnpm exec mocha --exit --sort -r ts-node/register 'tests/evm-transfer.test.ts'", - "test-evm-contract": "pnpm exec mocha --exit --sort -r ts-node/register 'tests/evm-contract.test.ts'", - "test-runtime-upgrade": "pnpm exec mocha --exit --sort -r ts-node/register 'tests/runtime-upgrade.test.ts'", - "test-all": "pnpm exec mocha --exit --sort -r ts-node/register 'tests/**/*.test.ts'", - "format": "pnpm exec prettier --write 'tests/**/*.test.ts'" + "register-parathread": "pnpm exec ts-node common/setup/register-parathread.ts", + "upgrade-parathread": "pnpm exec ts-node common/setup/upgrade-parathread.ts", + "setup-enclave": "pnpm exec ts-node common/setup/setup-enclave.ts", + "test-filter": "pnpm exec mocha --exit --sort -r ts-node/register 'integration-tests/base-filter.test.ts'", + "test-bridge": "pnpm exec mocha --exit --sort -r ts-node/register 'integration-tests/bridge.test.ts'", + "test-evm-transfer": "pnpm exec mocha --exit --sort -r ts-node/register 'integration-tests/evm-transfer.test.ts'", + "test-evm-contract": "pnpm exec mocha --exit --sort -r ts-node/register 'integration-tests/evm-contract.test.ts'", + "test-runtime-upgrade": "pnpm exec mocha --exit --sort -r ts-node/register 'integration-tests/runtime-upgrade.test.ts'", + "test-all": "pnpm exec mocha --exit --sort -r ts-node/register 'integration-tests/**/*.test.ts'", + "test-precompile-contract": "pnpm exec mocha --exit --sort -r ts-node/register 'integration-tests/precompile-contract.test.ts'", + "format": "pnpm exec prettier --write '**.ts'" }, "author": "Litentry Dev", "license": "ISC", diff --git a/ts-tests/tests/global.d.ts b/ts-tests/tests/global.d.ts deleted file mode 100644 index 54ae6ea73f..0000000000 --- a/ts-tests/tests/global.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'solc'; \ No newline at end of file diff --git a/ts-tests/tsconfig.json b/ts-tests/tsconfig.json index f6ba8eb1e7..7543742807 100644 --- a/ts-tests/tsconfig.json +++ b/ts-tests/tsconfig.json @@ -2,11 +2,12 @@ "extends": "./node_modules/gts/tsconfig-google.json", "compilerOptions": { "esModuleInterop": true, + "resolveJsonModule": true, "target": "es5", "lib": ["es2019"], "rootDir": ".", "outDir": "build" }, - "include": ["tests/**/*.ts", "tests/**/*.d.ts"], + "include": ["ts-tests/**/*.ts", "ts-tests/**/*.d.ts"], "exclude": ["node_modules"] }