diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3637c56 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,84 @@ +name: Lint, check, clippy and test + +on: + push: + branches: [ main ] + pull_request: + types: [ opened, synchronize, reopened, ready_for_review ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check formatting + run: cargo fmt --all -- --check + + check: + needs: lint + runs-on: ubuntu-latest + steps: + - name: Setup Ubuntu dependencies + shell: bash + run: sudo apt update && sudo apt install -y protobuf-compiler + + - uses: actions/checkout@v4 + + - name: Rust Cache + uses: Swatinem/rust-cache@v2.7.3 + with: + cache-on-failure: true + cache-all-crates: true + key: check + + - name: Check Build + run: | + cargo check --release --locked --all-features --workspace + + clippy: + needs: lint + runs-on: ubuntu-latest + permissions: + checks: write + env: + SKIP_WASM_BUILD: 1 + steps: + - name: Setup Ubuntu dependencies + shell: bash + run: sudo apt update && sudo apt install -y protobuf-compiler + + - uses: actions/checkout@v4 + + - name: Rust Cache + uses: Swatinem/rust-cache@v2.7.3 + with: + cache-on-failure: true + cache-all-crates: true + key: check + + - name: Annotate with Clippy warnings + uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --release --locked --all-features --workspace + + test: + needs: lint + runs-on: ubuntu-latest + steps: + - name: Setup Ubuntu dependencies + shell: bash + run: sudo apt update && sudo apt install -y protobuf-compiler + + - uses: actions/checkout@v4 + + - name: Rust Cache + uses: Swatinem/rust-cache@v2.7.3 + with: + cache-on-failure: true + cache-all-crates: true + key: test + + - name: Run tests + run: cargo test --release --locked --all-features --workspace diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml new file mode 100644 index 0000000..047030c --- /dev/null +++ b/.github/workflows/lint-pr.yml @@ -0,0 +1,20 @@ +name: "Lint PR" + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +permissions: + pull-requests: read + +jobs: + lint: + name: Validate PR title for conventional commit compliance + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 73fab07..66e0450 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +.vscode \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c50c609..3bafdee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,18 +14,18 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -65,11 +65,60 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "approx" @@ -91,7 +140,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -328,7 +377,7 @@ dependencies = [ "ark-ff", "ark-serialize", "ark-std", - "ark-transcript", + "ark-transcript 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=e9782f9)", "digest 0.10.7", "getrandom_or_panic", "zeroize", @@ -381,6 +430,19 @@ dependencies = [ "sha3", ] +[[package]] +name = "ark-transcript" +version = "0.0.2" +source = "git+https://github.com/w3f/ring-vrf#0fef8266d851932ad25d6b41bc4b34d834d1e11d" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "digest 0.10.7", + "rand_core", + "sha3", +] + [[package]] name = "array-bytes" version = "6.2.3" @@ -389,9 +451,9 @@ checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293" [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -407,13 +469,13 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -424,17 +486,17 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -585,9 +647,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" [[package]] name = "byteorder" @@ -597,15 +659,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.1.15" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "shlex", ] @@ -625,10 +687,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#665f5f51af5734c7b6d90b985dd6861d4c5b4752" +source = "git+https://github.com/w3f/ring-proof#31658d1f5b88e106c969557c36c821aff46b2236" dependencies = [ "ark-ec", "ark-ff", @@ -637,8 +705,7 @@ dependencies = [ "ark-std", "fflonk", "getrandom_or_panic", - "merlin", - "rand_chacha", + "rand_core", ] [[package]] @@ -687,9 +754,9 @@ checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -782,7 +849,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -823,7 +890,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -834,7 +901,7 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -869,7 +936,7 @@ dependencies = [ "ark-secret-scalar", "ark-serialize", "ark-std", - "ark-transcript", + "ark-transcript 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=e9782f9)", "arrayvec", "zeroize", ] @@ -895,7 +962,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.76", + "syn 2.0.77", "termcolor", "toml", "walkdir", @@ -1025,7 +1092,30 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", ] [[package]] @@ -1052,7 +1142,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1071,6 +1161,29 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "fc-pallet-pass" +version = "1.0.0" +dependencies = [ + "env_logger", + "fc-traits-authn", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-babe", + "pallet-balances", + "pallet-scheduler", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0)", +] + [[package]] name = "fc-pallet-referenda-tracks" version = "1.0.0" @@ -1109,6 +1222,26 @@ dependencies = [ "sp-std 14.0.0 (git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0)", ] +[[package]] +name = "fc-traits-authn" +version = "0.1.0" +dependencies = [ + "fc-traits-authn-proc", + "frame-support", + "impl-trait-for-tuples", + "parity-scale-codec", + "paste", + "scale-info", +] + +[[package]] +name = "fc-traits-authn-proc" +version = "0.1.0" +dependencies = [ + "quote", + "syn 2.0.77", +] + [[package]] name = "fc-traits-gas-tank" version = "0.1.0" @@ -1292,7 +1425,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1304,7 +1437,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1314,7 +1447,7 @@ source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1409,7 +1542,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1476,9 +1609,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "group" @@ -1573,6 +1706,12 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "impl-codec" version = "0.6.0" @@ -1623,9 +1762,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -1640,6 +1779,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -1781,7 +1926,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1795,7 +1940,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1806,7 +1951,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1817,7 +1962,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1868,11 +2013,11 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -1899,7 +2044,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2018,6 +2163,44 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "pallet-authorship" +version = "28.0.0" +source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0#b788ee63a311984787af4d37d37c14dcff1c18c3" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0)", +] + +[[package]] +name = "pallet-babe" +version = "28.0.0" +source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0#b788ee63a311984787af4d37d37c14dcff1c18c3" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-babe", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 14.0.0 (git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0)", +] + [[package]] name = "pallet-balances" version = "28.0.0" @@ -2105,6 +2288,48 @@ dependencies = [ "sp-weights", ] +[[package]] +name = "pallet-session" +version = "28.0.0" +source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0#b788ee63a311984787af4d37d37c14dcff1c18c3" +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-state-machine", + "sp-std 14.0.0 (git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0)", + "sp-trie", +] + +[[package]] +name = "pallet-timestamp" +version = "27.0.0" +source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0#b788ee63a311984787af4d37d37c14dcff1c18c3" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0)", + "sp-storage 19.0.0 (git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0)", + "sp-timestamp", +] + [[package]] name = "pallet-transaction-payment" version = "28.0.0" @@ -2279,7 +2504,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2289,7 +2514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2314,7 +2539,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2371,7 +2596,7 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2456,9 +2681,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags 2.6.0", ] @@ -2480,7 +2705,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2540,18 +2765,18 @@ dependencies = [ [[package]] name = "ring" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#665f5f51af5734c7b6d90b985dd6861d4c5b4752" +source = "git+https://github.com/w3f/ring-proof#31658d1f5b88e106c969557c36c821aff46b2236" dependencies = [ "ark-ec", "ark-ff", "ark-poly", "ark-serialize", "ark-std", + "ark-transcript 0.0.2 (git+https://github.com/w3f/ring-vrf)", "arrayvec", "blake2", "common", "fflonk", - "merlin", ] [[package]] @@ -2717,9 +2942,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -2735,20 +2960,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -2901,7 +3126,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2950,6 +3175,35 @@ dependencies = [ "sp-crypto-ec-utils", ] +[[package]] +name = "sp-consensus-babe" +version = "0.32.0" +source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0#b788ee63a311984787af4d37d37c14dcff1c18c3" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-slots" +version = "0.32.0" +source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0#b788ee63a311984787af4d37d37c14dcff1c18c3" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-timestamp", +] + [[package]] name = "sp-core" version = "28.0.0" @@ -3000,7 +3254,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#da6541030f72da5a8c5a60c2ab2dd2c2c136b471" +source = "git+https://github.com/paritytech/polkadot-sdk#5a431470d2a4155a4bb00ea6ac07d7f9707f8e82" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -3037,7 +3291,7 @@ source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto dependencies = [ "quote", "sp-crypto-hashing", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3047,17 +3301,17 @@ source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#da6541030f72da5a8c5a60c2ab2dd2c2c136b471" +source = "git+https://github.com/paritytech/polkadot-sdk#5a431470d2a4155a4bb00ea6ac07d7f9707f8e82" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3073,7 +3327,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#da6541030f72da5a8c5a60c2ab2dd2c2c136b471" +source = "git+https://github.com/paritytech/polkadot-sdk#5a431470d2a4155a4bb00ea6ac07d7f9707f8e82" dependencies = [ "environmental", "parity-scale-codec", @@ -3209,7 +3463,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#da6541030f72da5a8c5a60c2ab2dd2c2c136b471" +source = "git+https://github.com/paritytech/polkadot-sdk#5a431470d2a4155a4bb00ea6ac07d7f9707f8e82" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -3235,20 +3489,34 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#da6541030f72da5a8c5a60c2ab2dd2c2c136b471" +source = "git+https://github.com/paritytech/polkadot-sdk#5a431470d2a4155a4bb00ea6ac07d7f9707f8e82" dependencies = [ "Inflector", "expander", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", +] + +[[package]] +name = "sp-session" +version = "27.0.0" +source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0#b788ee63a311984787af4d37d37c14dcff1c18c3" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-staking", ] [[package]] @@ -3292,7 +3560,7 @@ source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#da6541030f72da5a8c5a60c2ab2dd2c2c136b471" +source = "git+https://github.com/paritytech/polkadot-sdk#5a431470d2a4155a4bb00ea6ac07d7f9707f8e82" [[package]] name = "sp-storage" @@ -3309,7 +3577,7 @@ dependencies = [ [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#da6541030f72da5a8c5a60c2ab2dd2c2c136b471" +source = "git+https://github.com/paritytech/polkadot-sdk#5a431470d2a4155a4bb00ea6ac07d7f9707f8e82" dependencies = [ "impl-serde", "parity-scale-codec", @@ -3318,6 +3586,18 @@ dependencies = [ "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", ] +[[package]] +name = "sp-timestamp" +version = "26.0.0" +source = "git+https://github.com/virto-network/polkadot-sdk?branch=release-virto-v1.13.0#b788ee63a311984787af4d37d37c14dcff1c18c3" +dependencies = [ + "async-trait", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "thiserror", +] + [[package]] name = "sp-tracing" version = "16.0.0" @@ -3332,7 +3612,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#da6541030f72da5a8c5a60c2ab2dd2c2c136b471" +source = "git+https://github.com/paritytech/polkadot-sdk#5a431470d2a4155a4bb00ea6ac07d7f9707f8e82" dependencies = [ "parity-scale-codec", "tracing", @@ -3388,7 +3668,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3404,7 +3684,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#da6541030f72da5a8c5a60c2ab2dd2c2c136b471" +source = "git+https://github.com/paritytech/polkadot-sdk#5a431470d2a4155a4bb00ea6ac07d7f9707f8e82" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -3438,9 +3718,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.47.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4743ce898933fbff7bbf414f497c459a782d496269644b3d650a398ae6a487ba" +checksum = "43fce22ed1df64d04b262351c8f9d5c6da4f76f79f25ad15529792f893fad25d" dependencies = [ "Inflector", "num-format", @@ -3488,9 +3768,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.76" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -3529,7 +3809,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3620,9 +3900,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", "serde", @@ -3650,7 +3930,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3752,9 +4032,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" @@ -3767,9 +4047,15 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" @@ -3855,7 +4141,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -3864,6 +4150,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -3973,7 +4268,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3993,5 +4288,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] diff --git a/Cargo.toml b/Cargo.toml index fbba9a2..9bd7b28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,41 +5,52 @@ license = "GPL-3.0-only" repository = "https://github.com/virto-network/frame-contrib.git" [workspace.dependencies] -codec = {package = "parity-scale-codec", version = "3.6.12", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = [ "derive", -]} -scale-info = {version = "2.11.3", default-features = false, features = [ +] } +env_logger = { version = "0.11.3" } +impl-trait-for-tuples = { version = "0.2.2" } +log = { version = "0.4.21" } +scale-info = { version = "2.11.3", default-features = false, features = [ "derive", -]} -serde = {version = "1.0.203"} +] } +serde = { version = "1.0.203" } +syn = { version = "2" } +quote = { version = "1" } -fc-traits-gas-tank = {path = "./traits/gas-tank", default-features = false} -fc-traits-tracks = {path = "./traits/tracks", default-features = false} +fc-traits-authn = { path = "./traits/authn", default-features = false } +fc-traits-authn-proc = { path = "./traits/authn/proc", default-features = false } +fc-traits-gas-tank = { path = "./traits/gas-tank", default-features = false } +fc-traits-tracks = { path = "./traits/tracks", default-features = false } -frame-benchmarking = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -frame-support = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -frame-system = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} +frame-benchmarking = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +frame-support = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +frame-system = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } -sp-core = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -sp-io = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -sp-runtime = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -sp-std = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} +sp-core = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +sp-io = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +sp-runtime = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +sp-std = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } -pallet-balances = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -pallet-nfts = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -pallet-preimage = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -pallet-referenda = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -pallet-scheduler = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -pallet-transaction-payment = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} -pallet-utility = {git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false} +pallet-babe = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +pallet-balances = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +pallet-nfts = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +pallet-preimage = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +pallet-referenda = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +pallet-scheduler = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +pallet-timestamp = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +pallet-transaction-payment = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } +pallet-utility = { git = "https://github.com/virto-network/polkadot-sdk", branch = "release-virto-v1.13.0", default-features = false } [workspace] members = [ "pallets/gas-transaction-payment", "pallets/referenda-tracks", "pallets/template", + "pallets/pass", + "traits/authn", + "traits/authn/proc", "traits/gas-tank", "traits/memberships", "traits/tracks", ] -resolver = "2" diff --git a/pallets/gas-transaction-payment/Cargo.toml b/pallets/gas-transaction-payment/Cargo.toml index 21540d6..8950324 100644 --- a/pallets/gas-transaction-payment/Cargo.toml +++ b/pallets/gas-transaction-payment/Cargo.toml @@ -26,6 +26,14 @@ sp-io = {workspace = true} [features] default = ["std"] +runtime-benchmarks=[ + "fc-traits-gas-tank/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] std = [ "codec/std", "fc-traits-gas-tank/std", diff --git a/pallets/gas-transaction-payment/src/extensions.rs b/pallets/gas-transaction-payment/src/extensions.rs index 8e2d093..088649a 100644 --- a/pallets/gas-transaction-payment/src/extensions.rs +++ b/pallets/gas-transaction-payment/src/extensions.rs @@ -99,7 +99,7 @@ where info: &DispatchInfoOf, len: usize, ) -> frame_support::pallet_prelude::TransactionValidity { - if let Some(_) = T::GasBurner::check_available_gas(who, &info.weight) { + if T::GasBurner::check_available_gas(who, &info.weight).is_some() { Ok(ValidTransaction::default()) } else { self.0.validate(who, call, info, len) @@ -115,13 +115,9 @@ where ) -> Result<(), frame_support::pallet_prelude::TransactionValidityError> { if let Some((who, pre)) = pre { match pre { - Pre::Inner(inner_pre) => S::post_dispatch( - Some(inner_pre), - &info.clone().into(), - post_info, - len, - result, - )?, + Pre::Inner(inner_pre) => { + S::post_dispatch(Some(inner_pre), info, post_info, len, result)? + } Pre::Burner(expected_remaining) => { let used_gas = post_info.actual_weight.unwrap_or(info.weight); let should_burn_gas = post_info.pays_fee == Pays::Yes; diff --git a/pallets/pass/Cargo.toml b/pallets/pass/Cargo.toml new file mode 100644 index 0000000..e4a57a5 --- /dev/null +++ b/pallets/pass/Cargo.toml @@ -0,0 +1,72 @@ +[package] +authors.workspace = true +description = "Allows dispatching calls on behalf of a keyless account using an authenticator that resolves alternative signing methods." +edition.workspace = true +license.workspace = true +name = "fc-pallet-pass" +repository.workspace = true +version = "1.0.0" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = {workspace = true} +fc-traits-authn = {workspace = true} +frame-benchmarking = {workspace = true, optional = true} +frame-support = {workspace = true} +frame-system = {workspace = true} +log = {workspace = true} +scale-info = {workspace = true} +serde = {workspace = true, optional = true} +sp-core = {workspace = true} +sp-io = {workspace = true} +sp-runtime = {workspace = true} +sp-std = {workspace = true} + +[dev-dependencies] +env_logger = {workspace = true} +pallet-babe = {workspace = true} +pallet-balances = {workspace = true} +pallet-scheduler = {workspace = true} +pallet-timestamp = {workspace = true} + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-babe/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "fc-traits-authn/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-babe/std", + "pallet-balances/std", + "pallet-scheduler/std", + "pallet-timestamp/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-babe/try-runtime", + "pallet-balances/try-runtime", + "pallet-scheduler/try-runtime", + "pallet-timestamp/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/pass/src/benchmarking.rs b/pallets/pass/src/benchmarking.rs new file mode 100644 index 0000000..7772ce8 --- /dev/null +++ b/pallets/pass/src/benchmarking.rs @@ -0,0 +1,59 @@ +use super::*; +use crate::Pallet; +use frame_benchmarking::v2::*; +use frame_support::traits::OriginTrait; +use sp_runtime::traits::Hash; + +type RuntimeEventFor = >::RuntimeEvent; + +fn assert_has_event, I: 'static>(generic_event: RuntimeEventFor) { + frame_system::Pallet::::assert_has_event(generic_event.into()); +} + +#[allow(dead_code)] +fn setup_signers() -> (T::AccountId, T::AccountId) { + ( + frame_benchmarking::account("signer", 0, 0), + frame_benchmarking::account("signer", 1, 0), + ) +} + +fn hash(b: &[u8]) -> HashedUserId +where + T::Hash: Into, +{ + T::Hashing::hash(b).into() +} + +#[instance_benchmarks( +where + OriginFor: From>, + T::Hash: Into, + RuntimeEventFor: From>, +)] +mod benchmarks { + use super::*; + + #[benchmark] + pub fn register() -> Result<(), BenchmarkError> { + // Setup code + let origin = T::BenchmarkHelper::register_origin(); + let user_id = hash::(b"my-account"); + let account_id = Pallet::::account_id_for(user_id)?; + let device_id = [0u8; 32]; + + #[extrinsic_call] + _( + origin.into_caller(), + user_id, + T::BenchmarkHelper::device_attestation(device_id), + ); + + // Verification code + assert_has_event::(Event::Registered { who: account_id }.into()); + + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/pass/src/lib.rs b/pallets/pass/src/lib.rs new file mode 100644 index 0000000..e191006 --- /dev/null +++ b/pallets/pass/src/lib.rs @@ -0,0 +1,317 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +//! # Pallet Pass +//! +//! > TODO: Update with [spec](https://hackmd.io/@pandres95/pallet-pass) document once complete + +use fc_traits_authn::{ + util::AuthorityFromPalletId, Authenticator, DeviceChallengeResponse, DeviceId, HashedUserId, + UserAuthenticator, UserChallengeResponse, +}; +use frame_support::{ + pallet_prelude::*, + traits::{ + fungible::{Inspect, Mutate}, + EnsureOriginWithArg, + }, + PalletId, +}; +use frame_system::{pallet_prelude::*, RawOrigin}; +use sp_runtime::{ + traits::{Dispatchable, TrailingZeroInput}, + DispatchResult, +}; +use sp_std::fmt::Debug; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; +mod types; + +pub mod weights; +pub use pallet::*; +pub use types::*; +pub use weights::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + + type RuntimeCall: Parameter + + Dispatchable + + Debug + + From> + + From>; + + type Currency: Inspect + Mutate; + + type WeightInfo: WeightInfo; + + type Authenticator: Authenticator>; + + type PalletsOrigin: From>; + + #[pallet::constant] + type PalletId: Get; + + /// The maximum duration of a session + #[pallet::constant] + type MaxSessionDuration: Get>; + + type RegisterOrigin: EnsureOriginWithArg< + Self::RuntimeOrigin, + HashedUserId, + Success = Option>, + >; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; + } + + #[pallet::pallet] + pub struct Pallet(_); + + // Storage + #[pallet::storage] + pub type Devices, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Blake2_128Concat, + DeviceId, + DeviceOf, + >; + + #[pallet::storage] + pub type Sessions, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, T::AccountId, (T::AccountId, BlockNumberFor)>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + Registered { + who: T::AccountId, + }, + AddedDevice { + who: T::AccountId, + device_id: DeviceId, + }, + SessionCreated { + session_key: T::AccountId, + until: BlockNumberFor, + }, + } + + #[pallet::error] + pub enum Error { + AccountAlreadyRegistered, + AccountNotFound, + CredentialInvalid, + DeviceAttestationInvalid, + DeviceNotFound, + SessionExpired, + SessionNotFound, + } + + #[pallet::call(weight(>::WeightInfo))] + impl, I: 'static> Pallet { + /// Register an account + #[pallet::call_index(0)] + pub fn register( + origin: OriginFor, + user: HashedUserId, + attestation: DeviceAttestationOf, + ) -> DispatchResult { + let maybe_deposit_info = T::RegisterOrigin::ensure_origin(origin, &user)?; + let account_id = Self::account_id_for(user)?; + ensure!( + !Self::account_exists(&account_id), + Error::::AccountAlreadyRegistered + ); + + if let Some(deposit_info) = maybe_deposit_info { + Self::charge_register_deposit(deposit_info)?; + } + Self::create_account(&account_id)?; + Self::deposit_event(Event::::Registered { + who: account_id.clone(), + }); + + Self::do_add_device(&account_id, attestation) + } + + #[pallet::call_index(3)] + pub fn authenticate( + origin: OriginFor, + device_id: DeviceId, + credential: CredentialOf, + duration: Option>, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let account_id = Self::account_id_for(credential.user_id())?; + ensure!( + Self::account_exists(&account_id), + Error::::AccountNotFound + ); + + let device = Devices::::get(&account_id, device_id) + .ok_or(Error::::DeviceNotFound)?; + device + .verify_user(&credential) + .ok_or(Error::::CredentialInvalid)?; + + Self::do_add_session(&who, &account_id, duration); + Ok(()) + } + + /// Call to claim an Account: It assumes the account is initialized + /// (because an active account is required to authenticate first of all). + #[pallet::call_index(4)] + pub fn add_device( + origin: OriginFor, + attestation: DeviceAttestationOf, + ) -> DispatchResult { + let who = Self::ensure_signer_is_valid_session(origin)?; + Self::do_add_device(&who, attestation) + } + + #[pallet::call_index(5)] + pub fn dispatch( + origin: OriginFor, + call: Box>, + maybe_credential: Option<(DeviceId, CredentialOf)>, + maybe_next_session_key: Option, + ) -> DispatchResult { + let account_id = if let Some((device_id, credential)) = maybe_credential { + let account_id = Self::account_id_for(credential.user_id())?; + Self::do_authenticate(credential, device_id)?; + account_id + } else { + Self::ensure_signer_is_valid_session(origin)? + }; + + if let Some(next_session_key) = maybe_next_session_key { + Self::do_add_session( + &next_session_key, + &account_id, + Some(T::MaxSessionDuration::get()), + ); + } + + // Re-dispatch the call on behalf of the caller. + let res = call.dispatch(RawOrigin::Signed(account_id).into()); + // Turn the result from the `dispatch` into our expected `DispatchResult` type. + res.map(|_| ()).map_err(|e| e.error) + } + } +} + +impl, I: 'static> Pallet { + pub fn account_id_for(user: HashedUserId) -> Result { + let account_id: T::AccountId = T::AccountId::decode(&mut TrailingZeroInput::new(&user)) + .map_err(|_| Error::::AccountNotFound)?; + Ok(account_id) + } + + pub(crate) fn account_exists(who: &T::AccountId) -> bool { + frame_system::Pallet::::account_exists(who) + } + + #[allow(dead_code)] + pub(crate) fn charge_register_deposit( + (source, amount, dest): DepositInformation, + ) -> DispatchResult { + T::Currency::transfer( + &source, + &dest, + amount, + frame_support::traits::tokens::Preservation::Expendable, + ) + .map(|_| ()) + } + + pub(crate) fn create_account(who: &T::AccountId) -> DispatchResult { + ensure!( + frame_system::Pallet::::inc_providers(who) == frame_system::IncRefStatus::Created, + Error::::AccountAlreadyRegistered + ); + Ok(()) + } + + pub(crate) fn do_add_device( + who: &T::AccountId, + attestation: DeviceAttestationOf, + ) -> DispatchResult { + let device_id = attestation.device_id(); + let device = T::Authenticator::verify_device(attestation.clone()) + .ok_or(Error::::DeviceAttestationInvalid)?; + + Devices::::insert(who, device_id, device); + Self::deposit_event(Event::::AddedDevice { + who: who.clone(), + device_id: *device_id, + }); + + Ok(()) + } + + pub(crate) fn ensure_signer_is_valid_session( + origin: OriginFor, + ) -> Result { + let who = ensure_signed(origin)?; + + let (account_id, until) = + Sessions::::get(&who).ok_or(Error::::SessionNotFound)?; + if frame_system::Pallet::::block_number() > until { + Sessions::::remove(who); + return Err(Error::::SessionExpired.into()); + } + + Ok(account_id) + } + + pub(crate) fn do_authenticate( + credential: CredentialOf, + device_id: DeviceId, + ) -> Result { + let account_id = Self::account_id_for(credential.user_id())?; + ensure!( + Self::account_exists(&account_id), + Error::::AccountNotFound + ); + let device = + Devices::::get(&account_id, device_id).ok_or(Error::::DeviceNotFound)?; + device + .verify_user(&credential) + .ok_or(Error::::CredentialInvalid)?; + Ok(account_id) + } + + pub(crate) fn do_add_session( + session_key: &T::AccountId, + account_id: &T::AccountId, + duration: Option>, + ) { + let block_number = frame_system::Pallet::::block_number(); + let session_duration = duration + .unwrap_or(T::MaxSessionDuration::get()) + .min(T::MaxSessionDuration::get()); + let until = block_number + session_duration; + + Sessions::::insert(session_key.clone(), (account_id.clone(), until)); + + Self::deposit_event(Event::::SessionCreated { + session_key: session_key.clone(), + until, + }); + } +} diff --git a/pallets/pass/src/mock.rs b/pallets/pass/src/mock.rs new file mode 100644 index 0000000..5b3c86d --- /dev/null +++ b/pallets/pass/src/mock.rs @@ -0,0 +1,161 @@ +//! Test environment for pallet pass. + +use crate::{self as pallet_pass, Config}; +pub use authenticators::*; +use codec::{Decode, Encode, MaxEncodedLen}; +use fc_traits_authn::{composite_authenticators, util::AuthorityFromPalletId, Challenger}; +use frame_support::{ + derive_impl, parameter_types, + traits::{ConstU32, ConstU64, EitherOf, EqualPrivilegeOnly, OnInitialize}, + weights::Weight, + DebugNoBound, EqNoBound, PalletId, +}; +use frame_system::{pallet_prelude::OriginFor, EnsureRoot, EnsureRootWithSuccess}; +use scale_info::TypeInfo; +use sp_core::{blake2_256, H256}; +use sp_io::TestExternalities; +use sp_runtime::{ + traits::{IdentifyAccount, IdentityLookup, Verify}, + MultiSignature, +}; + +mod authenticators; + +pub type Block = frame_system::mocking::MockBlock; + +pub type AccountPublic = ::Signer; +pub type AccountId = ::AccountId; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + Scheduler: pallet_scheduler, + Pass: pallet_pass, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type AccountData = pallet_balances::AccountData; +} + +impl pallet_balances::Config for Test { + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type MaxLocks = ConstU32<10>; + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); +} + +parameter_types! { + pub MaxScheduledPerBlock: u32 = u32::MAX; + pub MaximumWeight: Weight = Weight::MAX; +} + +impl pallet_scheduler::Config for Test { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = MaxScheduledPerBlock; + type MaximumWeight = MaximumWeight; + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = (); + type WeightInfo = (); +} + +parameter_types! { + pub const RootAccount: AccountId = AccountId::new([0u8; 32]); + pub PassPalletId: PalletId = PalletId(*b"py/pass_"); + pub RootDoesNotPay: Option> = None; +} + +composite_authenticators! { + pub Pass> { + authenticator_a::Authenticator, + AuthenticatorB, + }; +} + +impl Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Authenticator = PassAuthenticator; + type RegisterOrigin = EitherOf< + // Root does not pay + EnsureRootWithSuccess, + // Anyone else pays + pallet_pass::EnsureSignedPays, RootAccount>, + >; + type RuntimeCall = RuntimeCall; + type PalletId = PassPalletId; + type PalletsOrigin = OriginCaller; + type MaxSessionDuration = ConstU64<10>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = BenchmarkHelper; +} + +#[cfg(feature = "runtime-benchmarks")] +use pallet_pass::{CredentialOf, DeviceAttestationOf}; + +#[cfg(feature = "runtime-benchmarks")] +pub struct BenchmarkHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_pass::BenchmarkHelper for BenchmarkHelper { + fn register_origin() -> OriginFor { + RuntimeOrigin::root() + } + + fn device_attestation(device_id: DeviceId) -> DeviceAttestationOf { + PassDeviceAttestation::AuthenticatorAAuthenticator(authenticator_a::DeviceAttestation { + device_id, + challenge: authenticator_a::Authenticator::generate(&()), + }) + } + + fn credential(user_id: HashedUserId) -> CredentialOf { + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id, + challenge: authenticator_a::Authenticator::generate(&()), + }) + } +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut ext = TestExternalities::new(Default::default()); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} + +pub fn run_to(n: u64) { + while System::block_number() < n { + next_block(); + } +} + +pub fn next_block() { + System::reset_events(); + System::set_block_number(System::block_number() + 1); + log::info!("Starting block {:?}", System::block_number()); + Scheduler::on_initialize(System::block_number()); +} diff --git a/pallets/pass/src/mock/authenticators.rs b/pallets/pass/src/mock/authenticators.rs new file mode 100644 index 0000000..d87152d --- /dev/null +++ b/pallets/pass/src/mock/authenticators.rs @@ -0,0 +1,209 @@ +use super::*; +use fc_traits_authn::{util::AuthorityFromPalletId, *}; +use frame_support::traits::Randomness; +use sp_core::Get; + +pub use authenticator_b::AuthenticatorB; + +pub struct RandomnessFromBlockNumber; +impl Randomness for RandomnessFromBlockNumber { + fn random(subject: &[u8]) -> (H256, u64) { + let block_number = System::block_number(); + let block_number_as_bytes = block_number.to_le_bytes(); + ( + H256(blake2_256( + &vec![block_number_as_bytes.to_vec(), subject.to_vec()].concat()[..], + )), + block_number, + ) + } +} + +pub(self) type PassAuthorityId = AuthorityFromPalletId; + +pub mod authenticator_a { + use super::{Authenticator as TAuthenticator, *}; + + pub struct Authenticator; + + #[derive(TypeInfo, DebugNoBound, EqNoBound, PartialEq, Clone, Encode, Decode)] + pub struct DeviceAttestation { + pub(crate) device_id: DeviceId, + pub(crate) challenge: Challenge, + } + + #[derive(TypeInfo, Encode, Decode, MaxEncodedLen)] + pub struct Device { + pub(crate) device_id: DeviceId, + } + + #[derive( + TypeInfo, DebugNoBound, EqNoBound, PartialEq, Clone, Encode, Decode, MaxEncodedLen, + )] + pub struct Credential { + pub(crate) user_id: HashedUserId, + pub(crate) challenge: Challenge, + } + + impl TAuthenticator for Authenticator { + type Authority = PassAuthorityId; + type Challenger = Self; + type DeviceAttestation = DeviceAttestation; + type Device = Device; + + fn unpack_device(attestation: Self::DeviceAttestation) -> Self::Device { + Device { + device_id: attestation.device_id, + } + } + } + + impl Challenger for Authenticator { + type Context = (); + + fn generate(_: &Self::Context) -> Challenge { + let (hash, _) = RandomnessFromBlockNumber::random_seed(); + hash.0 + } + } + + impl UserAuthenticator for Device { + type Authority = PassAuthorityId; + type Challenger = Authenticator; + type Credential = Credential; + + fn device_id(&self) -> &DeviceId { + &self.device_id + } + } + + impl DeviceChallengeResponse<()> for DeviceAttestation { + fn is_valid(&self) -> bool { + true + } + + fn used_challenge(&self) -> ((), Challenge) { + ((), self.challenge) + } + + fn authority(&self) -> AuthorityId { + PassAuthorityId::get() + } + + fn device_id(&self) -> &DeviceId { + &self.device_id + } + } + + impl UserChallengeResponse<()> for Credential { + fn is_valid(&self) -> bool { + true + } + + fn used_challenge(&self) -> ((), Challenge) { + ((), self.challenge) + } + + fn authority(&self) -> AuthorityId { + PassAuthorityId::get() + } + + fn user_id(&self) -> HashedUserId { + self.user_id + } + } +} + +pub mod authenticator_b { + use super::*; + + pub struct AuthenticatorB; + + #[derive(TypeInfo, DebugNoBound, EqNoBound, PartialEq, Clone, Encode, Decode)] + pub struct DeviceAttestation { + pub(crate) device_id: DeviceId, + pub(crate) challenge: Challenge, + } + + #[derive(TypeInfo, Encode, Decode, MaxEncodedLen)] + pub struct Device { + pub(crate) device_id: DeviceId, + } + + #[derive( + TypeInfo, DebugNoBound, EqNoBound, PartialEq, Clone, Encode, Decode, MaxEncodedLen, + )] + pub struct Credential { + pub(crate) device_id: DeviceId, + pub(crate) user_id: HashedUserId, + pub(crate) challenge: Challenge, + } + + impl Authenticator for AuthenticatorB { + type Authority = PassAuthorityId; + type Challenger = Self; + type DeviceAttestation = DeviceAttestation; + type Device = Device; + + fn unpack_device(attestation: Self::DeviceAttestation) -> Self::Device { + Device { + device_id: attestation.device_id, + } + } + } + + impl Challenger for AuthenticatorB { + type Context = DeviceId; + + fn generate(context: &Self::Context) -> Challenge { + let (hash, _) = RandomnessFromBlockNumber::random(context); + hash.0 + } + } + + impl UserAuthenticator for Device { + type Authority = PassAuthorityId; + type Challenger = AuthenticatorB; + type Credential = Credential; + + fn device_id(&self) -> &DeviceId { + &self.device_id + } + } + + impl DeviceChallengeResponse for DeviceAttestation { + fn is_valid(&self) -> bool { + true + } + + fn used_challenge(&self) -> (DeviceId, Challenge) { + (self.device_id, self.challenge) + } + + fn authority(&self) -> AuthorityId { + PassAuthorityId::get() + } + + fn device_id(&self) -> &DeviceId { + &self.device_id + } + } + + impl UserChallengeResponse for Credential { + fn is_valid(&self) -> bool { + true + } + + fn used_challenge(&self) -> (DeviceId, Challenge) { + (self.device_id, self.challenge) + } + + fn authority(&self) -> AuthorityId { + PassAuthorityId::get() + } + + fn user_id(&self) -> HashedUserId { + self.user_id + } + } +} diff --git a/pallets/pass/src/tests.rs b/pallets/pass/src/tests.rs new file mode 100644 index 0000000..2bfd23d --- /dev/null +++ b/pallets/pass/src/tests.rs @@ -0,0 +1,541 @@ +//! Tests for pass pallet. +use super::{Error, Event}; +use crate::mock::*; + +use fc_traits_authn::{Challenger, HashedUserId}; +use frame_support::{assert_noop, assert_ok, parameter_types, traits::fungible::Mutate}; +use sp_core::Hasher; +use sp_runtime::ArithmeticError; + +const SIGNER: AccountId = AccountId::new([0u8; 32]); +const OTHER: AccountId = AccountId::new([1u8; 32]); + +const THE_DEVICE: fc_traits_authn::DeviceId = [0u8; 32]; +const OTHER_DEVICE: fc_traits_authn::DeviceId = [1u8; 32]; + +parameter_types! { + pub AccountNameA: HashedUserId = ::Hashing::hash( + &*b"@account.a:example.org" + ).0; + pub AccountNameB: HashedUserId = ::Hashing::hash( + &*b"@account.b:example.org" + ).0; +} + +mod register { + use super::*; + + #[test] + fn fail_if_already_registered() { + new_test_ext().execute_with(|| { + let account_id = + Pass::account_id_for(AccountNameA::get()).expect("account exists; qed"); + assert_ok!(Pass::create_account(&account_id)); + + assert_noop!( + Pass::register( + RuntimeOrigin::signed(SIGNER), + AccountNameA::get(), + PassDeviceAttestation::AuthenticatorAAuthenticator( + authenticator_a::DeviceAttestation { + device_id: THE_DEVICE, + challenge: authenticator_a::Authenticator::generate(&()), + } + ), + ), + Error::::AccountAlreadyRegistered + ); + }); + } + + #[test] + fn register_deposit_logic_works() { + new_test_ext().execute_with(|| { + assert_ok!(Pass::register( + RuntimeOrigin::root(), + AccountNameA::get(), + PassDeviceAttestation::AuthenticatorAAuthenticator( + authenticator_a::DeviceAttestation { + device_id: THE_DEVICE, + challenge: authenticator_a::Authenticator::generate(&()), + } + ), + )); + }); + + new_test_ext().execute_with(|| { + assert_noop!( + Pass::register( + RuntimeOrigin::signed(SIGNER), + AccountNameA::get(), + PassDeviceAttestation::AuthenticatorAAuthenticator( + authenticator_a::DeviceAttestation { + device_id: THE_DEVICE, + challenge: authenticator_a::Authenticator::generate(&()), + } + ), + ), + ArithmeticError::Underflow, + ); + }); + } + + #[test] + fn fail_if_attestation_is_invalid() { + new_test_ext().execute_with(|| { + assert_ok!(Balances::mint_into(&SIGNER, 2)); + + assert_noop!( + Pass::register( + RuntimeOrigin::signed(SIGNER), + AccountNameB::get(), + PassDeviceAttestation::AuthenticatorB(authenticator_b::DeviceAttestation { + device_id: THE_DEVICE, + challenge: AuthenticatorB::generate(&OTHER_DEVICE), + }), + ), + Error::::DeviceAttestationInvalid + ); + }); + } + + #[test] + fn it_works() { + new_test_ext().execute_with(|| { + assert_ok!(Balances::mint_into(&SIGNER, 2)); + + let account_id = + Pass::account_id_for(AccountNameA::get()).expect("account exists; qed"); + + assert_ok!(Pass::register( + RuntimeOrigin::signed(SIGNER), + AccountNameA::get(), + PassDeviceAttestation::AuthenticatorAAuthenticator( + authenticator_a::DeviceAttestation { + device_id: THE_DEVICE, + challenge: authenticator_a::Authenticator::generate(&()), + } + ), + )); + + System::assert_has_event( + Event::::Registered { + who: account_id.clone(), + } + .into(), + ); + System::assert_has_event( + Event::::AddedDevice { + who: account_id, + device_id: THE_DEVICE, + } + .into(), + ); + }); + } +} + +fn prepare(user_id: HashedUserId) -> sp_io::TestExternalities { + let mut t = new_test_ext(); + t.execute_with(|| { + assert_ok!(Balances::mint_into(&SIGNER, 2)); + assert_ok!(Pass::register( + RuntimeOrigin::signed(SIGNER), + user_id, + PassDeviceAttestation::AuthenticatorAAuthenticator( + authenticator_a::DeviceAttestation { + device_id: THE_DEVICE, + challenge: authenticator_a::Authenticator::generate(&()), + } + ), + )); + }); + t +} + +const DURATION: u64 = 10; + +mod authenticate { + use super::*; + + #[test] + fn fail_if_cannot_find_account() { + prepare(AccountNameA::get()).execute_with(|| { + assert_noop!( + Pass::authenticate( + RuntimeOrigin::signed(OTHER), + THE_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameB::get(), + challenge: authenticator_a::Authenticator::generate(&()), + }), + Some(DURATION), + ), + Error::::AccountNotFound + ); + }); + } + + #[test] + fn fail_if_cannot_find_device() { + prepare(AccountNameA::get()).execute_with(|| { + assert_noop!( + Pass::authenticate( + RuntimeOrigin::signed(OTHER), + OTHER_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameA::get(), + challenge: authenticator_a::Authenticator::generate(&()), + }), + Some(DURATION), + ), + Error::::DeviceNotFound + ); + }); + } + + #[test] + fn fail_if_attestation_is_invalid() { + new_test_ext().execute_with(|| { + assert_ok!(Balances::mint_into(&SIGNER, 2)); + + assert_ok!(Pass::register( + RuntimeOrigin::signed(SIGNER), + AccountNameA::get(), + PassDeviceAttestation::AuthenticatorB(authenticator_b::DeviceAttestation { + device_id: THE_DEVICE, + challenge: AuthenticatorB::generate(&THE_DEVICE), + }), + )); + + assert_noop!( + Pass::authenticate( + RuntimeOrigin::signed(OTHER), + THE_DEVICE, + PassCredential::AuthenticatorB(authenticator_b::Credential { + user_id: AccountNameA::get(), + device_id: THE_DEVICE, + challenge: AuthenticatorB::generate(&OTHER_DEVICE), + }), + Some(DURATION), + ), + Error::::CredentialInvalid + ); + }); + } + + #[test] + fn it_works() { + prepare(AccountNameA::get()).execute_with(|| { + let block_number = System::block_number(); + + assert_ok!(Pass::authenticate( + RuntimeOrigin::signed(OTHER), + THE_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameA::get(), + challenge: authenticator_a::Authenticator::generate(&()), + }), + Some(DURATION), + )); + + System::assert_has_event( + Event::::SessionCreated { + session_key: OTHER, + until: block_number + DURATION, + } + .into(), + ); + }); + } +} + +mod add_device { + use super::*; + + fn prepare() -> sp_io::TestExternalities { + let mut t = super::prepare(AccountNameA::get()); + t.execute_with(|| { + assert_ok!(Pass::authenticate( + RuntimeOrigin::signed(SIGNER), + THE_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameA::get(), + challenge: authenticator_a::Authenticator::generate(&()), + }), + Some(DURATION), + )); + }); + t + } + + #[test] + fn fail_if_not_signed_by_session_key() { + prepare().execute_with(|| { + assert_noop!( + Pass::add_device( + RuntimeOrigin::signed(OTHER), + PassDeviceAttestation::AuthenticatorAAuthenticator( + authenticator_a::DeviceAttestation { + device_id: OTHER_DEVICE, + challenge: authenticator_a::Authenticator::generate(&()), + } + ), + ), + Error::::SessionNotFound + ); + }); + } + + #[test] + fn it_works() { + prepare().execute_with(|| { + let who = Pass::account_id_for(AccountNameA::get()).expect("account exists; qed"); + + assert_ok!(Pass::add_device( + RuntimeOrigin::signed(SIGNER), + PassDeviceAttestation::AuthenticatorAAuthenticator( + authenticator_a::DeviceAttestation { + device_id: OTHER_DEVICE, + challenge: authenticator_a::Authenticator::generate(&()), + } + ), + ),); + + System::assert_has_event( + Event::::AddedDevice { + who, + device_id: OTHER_DEVICE, + } + .into(), + ); + }); + } +} + +mod dispatch { + use super::*; + + parameter_types! { + pub Call: Box = Box::new(RuntimeCall::System(frame_system::Call::remark_with_event { + remark: b"Hello, world".to_vec() + })); + pub CallEvent: RuntimeEvent = frame_system::Event::Remarked { + sender: Pass::account_id_for(AccountNameA::get()).expect("account exists; qed"), + hash: ::Hashing::hash(&*b"Hello, world"), + }.into(); + } + + fn prepare() -> sp_io::TestExternalities { + let mut t = super::prepare(AccountNameA::get()); + t.execute_with(|| { + assert_ok!(Pass::authenticate( + RuntimeOrigin::signed(SIGNER), + THE_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameA::get(), + challenge: authenticator_a::Authenticator::generate(&()), + }), + Some(DURATION), + )); + }); + t + } + + #[test] + fn fail_without_credentials_if_not_signed_by_session_key() { + prepare().execute_with(|| { + assert_noop!( + Pass::dispatch(RuntimeOrigin::signed(OTHER), Call::get(), None, None), + Error::::SessionNotFound + ); + }); + } + + #[test] + fn without_credentials_it_works() { + prepare().execute_with(|| { + assert_ok!(Pass::dispatch( + RuntimeOrigin::signed(SIGNER), + Call::get(), + None, + None + )); + + System::assert_has_event(CallEvent::get()); + }); + } + + #[test] + fn fail_with_credentials_if_account_not_found() { + prepare().execute_with(|| { + assert_noop!( + Pass::dispatch( + RuntimeOrigin::signed(OTHER), + Call::get(), + Some(( + OTHER_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameB::get(), + challenge: authenticator_a::Authenticator::generate(&()) + }) + )), + None + ), + Error::::AccountNotFound + ); + }); + } + + #[test] + fn fail_with_credentials_if_device_not_found() { + prepare().execute_with(|| { + assert_noop!( + Pass::dispatch( + RuntimeOrigin::signed(OTHER), + Call::get(), + Some(( + OTHER_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameA::get(), + challenge: authenticator_a::Authenticator::generate(&()) + }) + )), + None + ), + Error::::DeviceNotFound + ); + }); + } + + #[test] + fn fail_with_credentials_if_credential_invalid() { + prepare().execute_with(|| { + // On block 1 + let challenge = authenticator_a::Authenticator::generate(&()); + + // On block 3 + run_to(3); + + assert_noop!( + Pass::dispatch( + RuntimeOrigin::signed(OTHER), + Call::get(), + Some(( + THE_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameA::get(), + challenge, + }) + )), + None, + ), + Error::::CredentialInvalid + ); + + let challenge = authenticator_a::Authenticator::generate(&()); + assert_ok!(Pass::dispatch( + RuntimeOrigin::signed(OTHER), + Call::get(), + Some(( + THE_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameA::get(), + challenge, + }) + )), + None, + )); + }); + } + + #[test] + fn with_credentials_it_works() { + prepare().execute_with(|| { + assert_ok!(Pass::dispatch( + RuntimeOrigin::signed(OTHER), + Call::get(), + Some(( + THE_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameA::get(), + challenge: authenticator_a::Authenticator::generate(&()) + }) + )), + None + )); + + System::assert_has_event(CallEvent::get()); + }); + } + + #[test] + fn with_new_session_key_it_creates_a_session() { + prepare().execute_with(|| { + let block_number = System::block_number(); + + assert_ok!(Pass::dispatch( + RuntimeOrigin::signed(OTHER), + Call::get(), + Some(( + THE_DEVICE, + PassCredential::AuthenticatorAAuthenticator(authenticator_a::Credential { + user_id: AccountNameA::get(), + challenge: authenticator_a::Authenticator::generate(&()) + }) + )), + Some(OTHER) + )); + + System::assert_has_event( + Event::SessionCreated { + session_key: OTHER, + until: block_number + DURATION, + } + .into(), + ); + }); + } + + #[test] + fn session_duration_is_met() { + prepare().execute_with(|| { + assert_ok!(Pass::dispatch( + RuntimeOrigin::signed(SIGNER), + Call::get(), + None, + None, + )); + + run_to(9); + + assert_ok!(Pass::dispatch( + RuntimeOrigin::signed(SIGNER), + Call::get(), + None, + Some(OTHER), + )); + + run_to(12); + + assert_noop!( + Pass::dispatch(RuntimeOrigin::signed(SIGNER), Call::get(), None, None,), + Error::::SessionExpired + ); + + assert_ok!(Pass::dispatch( + RuntimeOrigin::signed(OTHER), + Call::get(), + None, + None, + )); + + run_to(20); + + assert_noop!( + Pass::dispatch(RuntimeOrigin::signed(OTHER), Call::get(), None, None,), + Error::::SessionExpired + ); + }); + } +} diff --git a/pallets/pass/src/types.rs b/pallets/pass/src/types.rs new file mode 100644 index 0000000..f3926f3 --- /dev/null +++ b/pallets/pass/src/types.rs @@ -0,0 +1,49 @@ +use crate::Config; +use frame_support::traits::{fungible::Inspect, MapSuccess}; +use frame_system::{pallet_prelude::OriginFor, EnsureSigned}; +use sp_core::TypedGet; +use sp_runtime::{morph_types, traits::StaticLookup}; + +// pub type HashedUserId = ::Hash; +type AccountIdOf = ::AccountId; +pub type ContextOf = +<<>::Authenticator as fc_traits_authn::Authenticator>::Challenger as fc_traits_authn::Challenger>::Context; +pub type DeviceOf = + <>::Authenticator as fc_traits_authn::Authenticator>::Device; +pub type CredentialOf = as fc_traits_authn::UserAuthenticator>::Credential; +pub type DeviceAttestationOf = + <>::Authenticator as fc_traits_authn::Authenticator>::DeviceAttestation; +pub type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +pub type BalanceOf = + <>::Currency as Inspect<::AccountId>>::Balance; +pub type DepositInformation = ( + ::AccountId, + BalanceOf, + ::AccountId, +); + +morph_types! { + pub type PaymentForCreate< + AccountId, + GetAmount: TypedGet, + GetReceiver: TypedGet + >: Morph = |sender: AccountId| -> Option<(AccountId, GetAmount::Type, GetReceiver::Type)> { + Some((sender, GetAmount::get(), GetReceiver::get())) + }; +} + +pub type EnsureSignedPays = + MapSuccess>, PaymentForCreate, Amount, Beneficiary>>; + +#[cfg(feature = "runtime-benchmarks")] +use fc_traits_authn::HashedUserId; +#[cfg(feature = "runtime-benchmarks")] +pub trait BenchmarkHelper +where + T: Config, + I: 'static, +{ + fn register_origin() -> OriginFor; + fn device_attestation(device_id: fc_traits_authn::DeviceId) -> DeviceAttestationOf; + fn credential(user_id: HashedUserId) -> CredentialOf; +} diff --git a/pallets/pass/src/weights.rs b/pallets/pass/src/weights.rs new file mode 100644 index 0000000..e5d4e29 --- /dev/null +++ b/pallets/pass/src/weights.rs @@ -0,0 +1,156 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_remark. +pub trait WeightInfo { + fn register() -> Weight; + fn claim() -> Weight; + fn unreserve_uninitialized_account() -> Weight; + fn authenticate() -> Weight; + fn add_device() -> Weight; + fn dispatch() -> Weight; +} + +/// Weights for pallet_remark using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// The range of component `l` is `[1, 1048576]`. + fn register() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn claim() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn unreserve_uninitialized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn authenticate() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn add_device() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// The range of component `l` is `[1, 1048576]`. + fn register() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn claim() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn unreserve_uninitialized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(0, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(0, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn authenticate() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(0, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(0, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn add_device() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(0, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(0, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(0, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(0, 0)) + } +} diff --git a/pallets/referenda-tracks/src/benchmarking.rs b/pallets/referenda-tracks/src/benchmarking.rs index 837b437..27edf40 100644 --- a/pallets/referenda-tracks/src/benchmarking.rs +++ b/pallets/referenda-tracks/src/benchmarking.rs @@ -17,8 +17,6 @@ //! Benchmarks for remarks pallet -#![cfg(feature = "runtime-benchmarks")] - use super::*; use crate::{Event, OriginToTrackId, Pallet as ReferendaTracks, Tracks, TracksIds}; use frame_benchmarking::v2::*; @@ -29,124 +27,128 @@ use sp_runtime::{str_array as s, traits::AtLeast32Bit, Perbill}; type AccountIdOf = ::AccountId; type BalanceOf = - <>::Currency as frame_support::traits::Currency< - AccountIdOf, - >>::Balance; + <>::Currency as frame_support::traits::Currency< + AccountIdOf, + >>::Balance; fn assert_last_event, I: 'static>(generic_event: >::RuntimeEvent) { - frame_system::Pallet::::assert_last_event(generic_event.into()); + frame_system::Pallet::::assert_last_event(generic_event.into()); } fn track_info_of() -> TrackInfoOf where - T: pallet_referenda::Config, - BalanceOf: AtLeast32Bit, + T: pallet_referenda::Config, + BalanceOf: AtLeast32Bit, { - TrackInfo { - name: s("Test Track"), - max_deciding: 1, - decision_deposit: 0u32.into(), - prepare_period: 10u32.into(), - decision_period: 100u32.into(), - confirm_period: 10u32.into(), - min_enactment_period: 2u32.into(), - min_approval: Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(50), - ceil: Perbill::from_percent(100), - }, - min_support: Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(0), - ceil: Perbill::from_percent(50), - }, - } + TrackInfo { + name: s("Test Track"), + max_deciding: 1, + decision_deposit: 0u32.into(), + prepare_period: 10u32.into(), + decision_period: 100u32.into(), + confirm_period: 10u32.into(), + min_enactment_period: 2u32.into(), + min_approval: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(50), + ceil: Perbill::from_percent(100), + }, + min_support: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(0), + ceil: Perbill::from_percent(50), + }, + } } fn max_tracks, I: 'static>() -> u32 { - T::MaxTracks::get() + T::MaxTracks::get() } fn max_track_id, I: 'static>() -> TrackIdOf { - T::BenchmarkHelper::track_id(max_tracks::()) + T::BenchmarkHelper::track_id(max_tracks::()) } fn prepare_tracks, I: 'static>(full: bool) { - let ids = (0..max_tracks::() - 1) - .map(|x| T::BenchmarkHelper::track_id(x)) - .collect::>>(); - let track = track_info_of::(); - let origin: PalletsOriginOf = RawOrigin::Signed(whitelisted_caller()).into(); - - TracksIds::::mutate(|tracks_ids| { - *tracks_ids = BoundedVec::truncate_from(ids.clone()); - }); - ids.iter().for_each(|id| { - Tracks::::insert(id.clone(), track.clone()); - OriginToTrackId::::insert(origin.clone(), id.clone()); - }); - - if full { - ReferendaTracks::::insert( - RawOrigin::Root.into(), - max_track_id::(), - track, - origin, - ) - .expect("inserts last track"); - } + let ids = (0..max_tracks::() - 1) + .map(|x| T::BenchmarkHelper::track_id(x)) + .collect::>>(); + let track = track_info_of::(); + let origin: PalletsOriginOf = RawOrigin::Signed(whitelisted_caller()).into(); + + TracksIds::::mutate(|tracks_ids| { + *tracks_ids = BoundedVec::truncate_from(ids.clone()); + }); + ids.iter().for_each(|id| { + Tracks::::insert(id, track.clone()); + OriginToTrackId::::insert(origin.clone(), id); + }); + + if full { + ReferendaTracks::::insert( + RawOrigin::Root.into(), + max_track_id::(), + track, + origin, + ) + .expect("inserts last track"); + } } #[instance_benchmarks] mod benchmarks { - use super::*; + use super::*; - #[benchmark] - pub fn insert() { - // Setup code - prepare_tracks::(false); + #[benchmark] + pub fn insert() { + // Setup code + prepare_tracks::(false); - let id = max_track_id::(); - let track = track_info_of::(); - let origin: PalletsOriginOf = RawOrigin::Signed(whitelisted_caller()).into(); + let id = max_track_id::(); + let track = track_info_of::(); + let origin: PalletsOriginOf = RawOrigin::Signed(whitelisted_caller()).into(); - #[extrinsic_call] - _(RawOrigin::Root, id, track, origin); + #[extrinsic_call] + _(RawOrigin::Root, id, track, origin); - // Verification code - assert_last_event::(Event::Created { id }.into()); - } + // Verification code + assert_last_event::(Event::Created { id }.into()); + } - #[benchmark] - pub fn update() { - // Setup code - prepare_tracks::(true); + #[benchmark] + pub fn update() { + // Setup code + prepare_tracks::(true); - let id = max_track_id::(); - let caller = whitelisted_caller(); - let track = track_info_of::(); + let id = max_track_id::(); + let caller = whitelisted_caller(); + let track = track_info_of::(); - #[extrinsic_call] - _(RawOrigin::Signed(caller), id, track); + #[extrinsic_call] + _(RawOrigin::Signed(caller), id, track); - // Verification code - assert_last_event::(Event::Updated { id }.into()); - } + // Verification code + assert_last_event::(Event::Updated { id }.into()); + } - #[benchmark] - pub fn remove() { - // Setup code - prepare_tracks::(true); + #[benchmark] + pub fn remove() { + // Setup code + prepare_tracks::(true); - let id = max_track_id::(); - let origin = RawOrigin::Signed(whitelisted_caller()).into(); + let id = max_track_id::(); + let origin = RawOrigin::Signed(whitelisted_caller()).into(); - #[extrinsic_call] - _(RawOrigin::Root, id, origin); + #[extrinsic_call] + _(RawOrigin::Root, id, origin); - // Verification code - assert_last_event::(Event::Removed { id }.into()); - } + // Verification code + assert_last_event::(Event::Removed { id }.into()); + } - impl_benchmark_test_suite!(ReferendaTracks, crate::mock::new_test_ext(None), crate::mock::Test); + impl_benchmark_test_suite!( + ReferendaTracks, + crate::mock::new_test_ext(None), + crate::mock::Test + ); } diff --git a/pallets/referenda-tracks/src/impls.rs b/pallets/referenda-tracks/src/impls.rs index 25d5439..cf39e1a 100644 --- a/pallets/referenda-tracks/src/impls.rs +++ b/pallets/referenda-tracks/src/impls.rs @@ -38,7 +38,7 @@ impl, I> fc_traits_tracks::MutateTracks, BlockNumbe origin: Self::RuntimeOrigin, ) -> DispatchResult { ensure!( - Tracks::::get(id) == None, + Tracks::::get(id).is_none(), Error::::TrackIdAlreadyExisting ); @@ -93,6 +93,6 @@ impl, I> fc_traits_tracks::MutateTracks, BlockNumbe })?; Self::deposit_event(Event::Removed { id }); - Ok(().into()) + Ok(()) } } diff --git a/pallets/referenda-tracks/src/lib.rs b/pallets/referenda-tracks/src/lib.rs index 24b67d8..02b1bb4 100644 --- a/pallets/referenda-tracks/src/lib.rs +++ b/pallets/referenda-tracks/src/lib.rs @@ -83,7 +83,7 @@ pub mod pallet { type TrackId: Parameter + Member + Copy + MaxEncodedLen + Ord; type MaxTracks: Get; - /// + type WeightInfo: WeightInfo; #[cfg(feature = "runtime-benchmarks")] diff --git a/pallets/referenda-tracks/src/tests.rs b/pallets/referenda-tracks/src/tests.rs index a801999..a1c832b 100644 --- a/pallets/referenda-tracks/src/tests.rs +++ b/pallets/referenda-tracks/src/tests.rs @@ -25,201 +25,201 @@ use pallet_referenda::TrackInfo; use sp_runtime::{str_array as s, traits::BadOrigin, Perbill}; const TRACK: pallet_referenda::TrackInfoOf = TrackInfo { - name: s("Test Track"), - max_deciding: 1, - decision_deposit: 0, - prepare_period: 10, - decision_period: 100, - confirm_period: 10, - min_enactment_period: 2, - min_approval: pallet_referenda::Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(50), - ceil: Perbill::from_percent(100), - }, - min_support: pallet_referenda::Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(0), - ceil: Perbill::from_percent(50), - }, + name: s("Test Track"), + max_deciding: 1, + decision_deposit: 0, + prepare_period: 10, + decision_period: 100, + confirm_period: 10, + min_enactment_period: 2, + min_approval: pallet_referenda::Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(50), + ceil: Perbill::from_percent(100), + }, + min_support: pallet_referenda::Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(0), + ceil: Perbill::from_percent(50), + }, }; const ORIGIN_SIGNED_1: OriginCaller = OriginCaller::system(RawOrigin::Signed(1)); const ORIGIN_SIGNED_2: OriginCaller = OriginCaller::system(RawOrigin::Signed(2)); mod insert { - use super::*; - - #[test] - fn fails_if_incorrect_origin() { - new_test_ext(None).execute_with(|| { - assert_noop!( - ReferendaTracks::::insert( - RuntimeOrigin::signed(1), - 1, - TRACK, - ORIGIN_SIGNED_1 - ), - BadOrigin - ); - }); - } - - #[test] - fn it_works() { - new_test_ext(None).execute_with(|| { - System::set_block_number(1); - - assert_ok!(ReferendaTracks::::insert( - RuntimeOrigin::root(), - 1, - TRACK, - ORIGIN_SIGNED_1 - )); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Tracks(Event::Created { id: 1 }), - topics: vec![], - }], - ); - - assert_eq!(Tracks::::get(1), Some(TRACK)); - }); - } - - #[test] - fn it_fails_if_inserting_an_already_existing_track() { - new_test_ext(None).execute_with(|| { - assert_ok!(ReferendaTracks::::insert( - RuntimeOrigin::root(), - 1, - TRACK, - ORIGIN_SIGNED_1 - )); - - assert_noop!( - ReferendaTracks::::insert( - RuntimeOrigin::root(), - 1, - TRACK, - ORIGIN_SIGNED_2 - ), - Error::::TrackIdAlreadyExisting - ); - }); - } - - #[test] - fn fails_if_exceeds_max_tracks() { - new_test_ext(None).execute_with(|| { - for i in 0..MaxTracks::get() { - let origin_signed = OriginCaller::system(RawOrigin::Signed(i as u64)); - assert_ok!(ReferendaTracks::::insert( - RuntimeOrigin::root(), - i, - TRACK, - origin_signed, - )); - } - - let origin_signed_n = OriginCaller::system(RawOrigin::Signed(MaxTracks::get() as u64)); - assert_noop!( - ReferendaTracks::::insert( - RuntimeOrigin::root(), - MaxTracks::get(), - TRACK, - origin_signed_n - ), - Error::::MaxTracksExceeded - ); - }); - } + use super::*; + + #[test] + fn fails_if_incorrect_origin() { + new_test_ext(None).execute_with(|| { + assert_noop!( + ReferendaTracks::::insert( + RuntimeOrigin::signed(1), + 1, + TRACK, + ORIGIN_SIGNED_1 + ), + BadOrigin + ); + }); + } + + #[test] + fn it_works() { + new_test_ext(None).execute_with(|| { + System::set_block_number(1); + + assert_ok!(ReferendaTracks::::insert( + RuntimeOrigin::root(), + 1, + TRACK, + ORIGIN_SIGNED_1 + )); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Tracks(Event::Created { id: 1 }), + topics: vec![], + }], + ); + + assert_eq!(Tracks::::get(1), Some(TRACK)); + }); + } + + #[test] + fn it_fails_if_inserting_an_already_existing_track() { + new_test_ext(None).execute_with(|| { + assert_ok!(ReferendaTracks::::insert( + RuntimeOrigin::root(), + 1, + TRACK, + ORIGIN_SIGNED_1 + )); + + assert_noop!( + ReferendaTracks::::insert( + RuntimeOrigin::root(), + 1, + TRACK, + ORIGIN_SIGNED_2 + ), + Error::::TrackIdAlreadyExisting + ); + }); + } + + #[test] + fn fails_if_exceeds_max_tracks() { + new_test_ext(None).execute_with(|| { + for i in 0..MaxTracks::get() { + let origin_signed = OriginCaller::system(RawOrigin::Signed(i as u64)); + assert_ok!(ReferendaTracks::::insert( + RuntimeOrigin::root(), + i, + TRACK, + origin_signed, + )); + } + + let origin_signed_n = OriginCaller::system(RawOrigin::Signed(MaxTracks::get() as u64)); + assert_noop!( + ReferendaTracks::::insert( + RuntimeOrigin::root(), + MaxTracks::get(), + TRACK, + origin_signed_n + ), + Error::::MaxTracksExceeded + ); + }); + } } mod update { - use super::*; - - #[test] - fn fails_if_incorrect_origin() { - new_test_ext(None).execute_with(|| { - assert_noop!( - ReferendaTracks::::update(RuntimeOrigin::signed(1), 1, TRACK), - BadOrigin - ); - }); - } - - #[test] - fn it_works() { - new_test_ext(Some(vec![(1, TRACK, ORIGIN_SIGNED_1)])).execute_with(|| { - let mut track = TRACK.clone(); - track.max_deciding = 2; - - assert_ok!(ReferendaTracks::::update( - RuntimeOrigin::signed(1), - 1, - track.clone() - )); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Tracks(Event::Updated { id: 1 }), - topics: vec![], - }], - ); - - assert_eq!(Tracks::::get(1), Some(track)); - }); - } + use super::*; + + #[test] + fn fails_if_incorrect_origin() { + new_test_ext(None).execute_with(|| { + assert_noop!( + ReferendaTracks::::update(RuntimeOrigin::signed(1), 1, TRACK), + BadOrigin + ); + }); + } + + #[test] + fn it_works() { + new_test_ext(Some(vec![(1, TRACK, ORIGIN_SIGNED_1)])).execute_with(|| { + let mut track = TRACK.clone(); + track.max_deciding = 2; + + assert_ok!(ReferendaTracks::::update( + RuntimeOrigin::signed(1), + 1, + track.clone() + )); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Tracks(Event::Updated { id: 1 }), + topics: vec![], + }], + ); + + assert_eq!(Tracks::::get(1), Some(track)); + }); + } } mod remove { - use super::*; - - #[test] - fn fails_if_incorrect_origin() { - new_test_ext(None).execute_with(|| { - assert_noop!( - ReferendaTracks::::remove(RuntimeOrigin::signed(1), 1, ORIGIN_SIGNED_1), - BadOrigin - ); - }); - } - - #[test] - fn fails_if_non_existing() { - new_test_ext(None).execute_with(|| { - assert_noop!( - ReferendaTracks::::remove(RuntimeOrigin::root(), 1, ORIGIN_SIGNED_1), - Error::::TrackIdNotFound, - ); - }); - } - - #[test] - fn it_works() { - new_test_ext(Some(vec![(1, TRACK, ORIGIN_SIGNED_1)])).execute_with(|| { - assert_ok!(ReferendaTracks::::remove( - RuntimeOrigin::root(), - 1, - ORIGIN_SIGNED_1 - )); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Tracks(Event::Removed { id: 1 }), - topics: vec![], - }], - ); - - assert_eq!(Tracks::::get(1), None); - }); - } + use super::*; + + #[test] + fn fails_if_incorrect_origin() { + new_test_ext(None).execute_with(|| { + assert_noop!( + ReferendaTracks::::remove(RuntimeOrigin::signed(1), 1, ORIGIN_SIGNED_1), + BadOrigin + ); + }); + } + + #[test] + fn fails_if_non_existing() { + new_test_ext(None).execute_with(|| { + assert_noop!( + ReferendaTracks::::remove(RuntimeOrigin::root(), 1, ORIGIN_SIGNED_1), + Error::::TrackIdNotFound, + ); + }); + } + + #[test] + fn it_works() { + new_test_ext(Some(vec![(1, TRACK, ORIGIN_SIGNED_1)])).execute_with(|| { + assert_ok!(ReferendaTracks::::remove( + RuntimeOrigin::root(), + 1, + ORIGIN_SIGNED_1 + )); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Tracks(Event::Removed { id: 1 }), + topics: vec![], + }], + ); + + assert_eq!(Tracks::::get(1), None); + }); + } } diff --git a/pallets/template/src/benchmarking.rs b/pallets/template/src/benchmarking.rs index 3cc8d35..c13403f 100644 --- a/pallets/template/src/benchmarking.rs +++ b/pallets/template/src/benchmarking.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "runtime-benchmarks")] - use super::*; use crate::Pallet; use frame_benchmarking::v2::*; diff --git a/traits/authn/Cargo.toml b/traits/authn/Cargo.toml new file mode 100644 index 0000000..56333ca --- /dev/null +++ b/traits/authn/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "fc-traits-authn" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +paste = "1.0" +codec = { workspace = true } +frame-support = { workspace = true } +impl-trait-for-tuples = { workspace = true } +scale-info = { workspace = true } +fc-traits-authn-proc = { workspace = true } + +[features] +default = ["std"] +std = ["codec/std", "scale-info/std", "frame-support/std"] diff --git a/traits/authn/proc/Cargo.toml b/traits/authn/proc/Cargo.toml new file mode 100644 index 0000000..a93c142 --- /dev/null +++ b/traits/authn/proc/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "fc-traits-authn-proc" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true + +[lib] +proc-macro = true + +[dependencies] +syn = { workspace = true } +quote = { workspace = true } diff --git a/traits/authn/proc/src/lib.rs b/traits/authn/proc/src/lib.rs new file mode 100644 index 0000000..e8c7666 --- /dev/null +++ b/traits/authn/proc/src/lib.rs @@ -0,0 +1,301 @@ +use proc_macro::{Span, TokenStream}; +use quote::{format_ident, quote}; +use syn::parse::{Parse, ParseStream}; +use syn::spanned::Spanned; +use syn::{ + braced, parse_macro_input, AngleBracketedGenericArguments, Error, GenericArgument, Ident, Path, + Result, Token, Type, TypePath, Visibility, +}; + +struct AuthMacroInput { + vis: Visibility, + name: Ident, + authority: Path, + authenticators: Vec<(Ident, Path)>, +} + +impl Parse for AuthMacroInput { + fn parse(input: ParseStream) -> Result { + let vis = input.parse()?; + let name: Path = input.parse()?; + + if name.leading_colon.is_some() || name.segments.len() != 1 { + return Err(Error::new(name.span(), "Expected a name, not a path")); + } + + let iden = &name.segments[0].ident; + let (name, authority) = + match &name.segments[0].arguments { + syn::PathArguments::AngleBracketed(AngleBracketedGenericArguments { + args, .. + }) if args.len() == 1 => match &args[0] { + GenericArgument::Type(Type::Path(TypePath { path, .. })) => { + (iden.clone(), path.clone()) + } + _ => { + return Err(Error::new( + name.span(), + "Expected the authority type to be a well-defined path.", + )) + } + }, + _ => { + return Err(Error::new( + name.span(), + "A single parameter for the authority type is expected", + )) + } + }; + + let content; + braced!(content in input); + let mut authenticators: Vec<_> = vec![]; + + fn capitalize_first_letter(s: &str) -> String { + let mut c = s.chars(); + match c.next() { + None => String::new(), + Some(first) => first.to_uppercase().collect::() + c.as_str(), + } + } + + for p in content + .parse_terminated(Path::parse, Token![,])? + .into_iter() + { + let id = p + .clone() + .segments + .into_iter() + .map(|s| { + s.ident + .to_string() + .split("_") + .map(capitalize_first_letter) + .collect::>() + .concat() + }) + .collect::>() + .concat(); + let id = Ident::new(&id, Span::call_site().into()); + + authenticators.push((id.clone(), p)); + } + + if input.peek(Token![;]) { + let _: Token![;] = input.parse()?; + } + + Ok(AuthMacroInput { + vis, + name, + authority, + authenticators, + }) + } +} + +#[proc_macro] +pub fn composite_authenticator(input: TokenStream) -> TokenStream { + let AuthMacroInput { + vis, + name, + authority, + authenticators, + } = parse_macro_input!(input as AuthMacroInput); + + // Dynamically create identifiers based on `name` + let auth_struct = format_ident!("{}Authenticator", name); + let device_attestation = format_ident!("{}DeviceAttestation", name); + let device = format_ident!("{}Device", name); + let credential = format_ident!("{}Credential", name); + + // Generate enum variants for authenticators + let auth_variants = authenticators + .clone() + .into_iter() + .map(|(id, path)| { + quote! { + #id(<#path as Authenticator>::DeviceAttestation) + } + }) + .collect::>(); + + let device_variants = authenticators + .clone() + .into_iter() + .map(|(id, path)| { + quote! { + #id(<#path as Authenticator>::Device) + } + }) + .collect::>(); + + let credential_variants = authenticators + .clone() + .into_iter() + .map(|(id, path)| { + quote! { + #id(<<#path as Authenticator>::Device as UserAuthenticator>::Credential) + } + }) + .collect::>(); + + let match_attestations = authenticators.clone().into_iter().map(|(id, p)| { + quote! { + #device_attestation::#id(attestation) => { + #device::#id(#p::verify_device(attestation)?) + } + } + }); + + let match_device_id_from_attestation = authenticators.clone().into_iter().map(|(id, _)| { + quote! { + #device_attestation::#id(attestation) => attestation.device_id() + } + }); + + let match_device_id_from_device = authenticators.clone().into_iter().map(|(id, _)| { + quote! { + #device::#id(device) => device.device_id() + } + }); + + let match_credentials = authenticators.clone().into_iter().map(|(id, _)| { + quote! { + ( + #device::#id(device), + #credential::#id(credential), + ) => device.verify_user(credential) + } + }); + let match_user_id = authenticators.clone().into_iter().map(|(id, _)| { + quote! { + #credential::#id(credential) => credential.user_id() + } + }); + + // Generate the full struct and impl code + let expanded = quote! { + use fc_traits_authn::composite_prelude::*; + + #vis struct #auth_struct; + + impl Authenticator for #auth_struct { + type Authority = #authority; + type Challenger = Self; + type DeviceAttestation = #device_attestation; + type Device = #device; + + fn verify_device(attestation: Self::DeviceAttestation) -> Option { + Some(match attestation { + #(#match_attestations),* + }) + } + + fn unpack_device(_: Self::DeviceAttestation) -> Self::Device { + unimplemented!("This method should not be called, instead call inner `unpack_device` on each authenticator") + } + } + + impl Challenger for #auth_struct { + type Context = (); + + fn generate(_: &Self::Context) -> Challenge { + unimplemented!( + "This method should not be called, instead call inner `generate` on each challenger" + ) + } + } + + #[derive(TypeInfo, DebugNoBound, EqNoBound, PartialEq, Clone, Encode, Decode)] + pub enum #device_attestation { + #(#auth_variants),* + } + + impl DeviceChallengeResponse<()> for #device_attestation { + fn is_valid(&self) -> bool { + unimplemented!( + "This method should not be called, instead call inner `is_valid` on each attestation" + ) + } + + fn used_challenge(&self) -> ((), Challenge) { + unimplemented!( + "This method should not be called, instead call inner `used_challenge` on each attestation" + ) + } + + fn authority(&self) -> AuthorityId { + unimplemented!( + "This method should not be called, instead call inner `authority` on each attestation" + ) + } + + fn device_id(&self) -> &DeviceId { + match self { + #(#match_device_id_from_attestation),* + } + } + } + + + #[derive(TypeInfo, Encode, Decode, MaxEncodedLen)] + pub enum #device { + #(#device_variants),* + } + + impl UserAuthenticator for #device { + type Authority = #authority; + type Challenger = #auth_struct; + type Credential = #credential; + + fn verify_user(&self, credential: &Self::Credential) -> Option<()> { + match (self, credential) { + #(#match_credentials),*, + _ => None, + } + } + + fn device_id(&self) -> &DeviceId { + match self { + #(#match_device_id_from_device),* + } + } + } + + + #[derive(TypeInfo, DebugNoBound, EqNoBound, PartialEq, Clone, Encode, Decode, MaxEncodedLen)] + pub enum #credential { + #(#credential_variants),* + } + + impl UserChallengeResponse<()> for #credential { + fn is_valid(&self) -> bool { + unimplemented!( + "This method should not be called, instead call inner `is_valid` on each credential" + ) + } + + fn used_challenge(&self) -> ((), Challenge) { + unimplemented!( + "This method should not be called, instead call inner `used_challenge` on each credential" + ) + } + + fn authority(&self) -> AuthorityId { + unimplemented!( + "This method should not be called, instead call inner `authority` on each credential" + ) + } + + fn user_id(&self) -> HashedUserId { + match self { + #(#match_user_id),* + } + } + } + }; + + TokenStream::from(expanded) +} diff --git a/traits/authn/src/lib.rs b/traits/authn/src/lib.rs new file mode 100644 index 0000000..2527171 --- /dev/null +++ b/traits/authn/src/lib.rs @@ -0,0 +1,112 @@ +use codec::{FullCodec, MaxEncodedLen}; +use frame_support::{traits::Get, Parameter}; +use scale_info::TypeInfo; + +pub mod util; + +pub use fc_traits_authn_proc::composite_authenticator; + +pub mod composite_prelude { + pub use crate::{ + Authenticator, AuthorityId, Challenge, Challenger, DeviceChallengeResponse, DeviceId, + HashedUserId, UserAuthenticator, UserChallengeResponse, + }; + pub use frame_support::traits::Get; +} + +#[macro_export] +macro_rules! composite_authenticators { + // Match a single composite authenticator with the format: + // pub CompositePassA { AuthA, AuthB }; + ($( + pub $name:path { + $($auth:path),* $(,)? + }; + )*) => { + $( + $crate::composite_authenticator!( + pub $name { + $($auth),* + } + ); + )* + } +} + +// A reasonabily sized secure challenge +const CHALLENGE_SIZE: usize = 32; +pub type Challenge = [u8; CHALLENGE_SIZE]; +type CxOf = ::Context; + +pub type DeviceId = [u8; 32]; +pub type AuthorityId = [u8; 32]; +pub type HashedUserId = [u8; 32]; + +/// Given some context it deterministically generates a "challenge" used by authenticators +pub trait Challenger { + type Context; + + fn generate(cx: &Self::Context) -> Challenge; + + /// Ensure that given the context produces the same challenge + fn check_challenge(cx: &Self::Context, challenge: &[u8]) -> Option<()> { + Self::generate(cx).eq(challenge).then_some(()) + } +} + +/// Authenticator is used to verify authentication devices that in turn are used to verify users +pub trait Authenticator { + type Authority: Get; + type Challenger: Challenger; + type DeviceAttestation: DeviceChallengeResponse>; + type Device: UserAuthenticator; + + fn verify_device(attestation: Self::DeviceAttestation) -> Option { + attestation + .authority() + .eq(&Self::Authority::get()) + .then_some(())?; + let (cx, challenge) = attestation.used_challenge(); + Self::Challenger::check_challenge(&cx, &challenge)?; + attestation.is_valid().then_some(())?; + Some(Self::unpack_device(attestation)) + } + + /// Extract device information from the attestation payload + fn unpack_device(attestation: Self::DeviceAttestation) -> Self::Device; +} + +/// A device capable of verifying a user provided credential +pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo { + type Authority: Get; + type Challenger: Challenger; + type Credential: UserChallengeResponse>; + + fn verify_user(&self, credential: &Self::Credential) -> Option<()> { + credential + .authority() + .eq(&Self::Authority::get()) + .then_some(())?; + let (cx, challenge) = credential.used_challenge(); + Self::Challenger::check_challenge(&cx, &challenge)?; + credential.is_valid().then_some(()) + } + + fn device_id(&self) -> &DeviceId; +} + +/// A response to a challenge for creating a new authentication device +pub trait DeviceChallengeResponse: Parameter { + fn is_valid(&self) -> bool; + fn used_challenge(&self) -> (Cx, Challenge); + fn authority(&self) -> AuthorityId; + fn device_id(&self) -> &DeviceId; +} + +/// A response to a challenge for identifying a user +pub trait UserChallengeResponse: Parameter { + fn is_valid(&self) -> bool; + fn used_challenge(&self) -> (Cx, Challenge); + fn authority(&self) -> AuthorityId; + fn user_id(&self) -> HashedUserId; +} diff --git a/traits/authn/src/util.rs b/traits/authn/src/util.rs new file mode 100644 index 0000000..3538ae4 --- /dev/null +++ b/traits/authn/src/util.rs @@ -0,0 +1,190 @@ +use std::marker::PhantomData; + +use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use frame_support::{sp_runtime::traits::TrailingZeroInput, traits::Get, PalletId}; +use scale_info::TypeInfo; + +use crate::{ + Authenticator, AuthorityId, Challenger, CxOf, DeviceChallengeResponse, DeviceId, + UserAuthenticator, UserChallengeResponse, +}; + +type ChallengerOf = ::Challenger; + +pub struct AuthorityFromPalletId(PhantomData); + +impl> Get for AuthorityFromPalletId { + fn get() -> AuthorityId { + Decode::decode(&mut TrailingZeroInput::new(&Id::get().0)) + .expect("Size of PalletId is less than 32 bytes; qed") + } +} + +/// Convenient auto-implemtator of the Authenticator trait +pub struct Auth(PhantomData<(Dev, Att)>); + +impl Authenticator for Auth +where + Att: DeviceChallengeResponse>>, + Dev: UserAuthenticator + From, +{ + type Authority = ::Authority; + type Challenger = ChallengerOf; + type DeviceAttestation = Att; + type Device = Dev; + + fn unpack_device(attestation: Self::DeviceAttestation) -> Self::Device { + attestation.into() + } +} + +/// Convenient auto-implemtator of the UserAuthenticator trait +#[derive(Encode, Decode, TypeInfo)] +#[scale_info(skip_type_params(A, Ch, Cred))] +pub struct Dev(T, PhantomData<(A, Ch, Cred)>); + +impl UserAuthenticator for Dev +where + T: AsRef + FullCodec + MaxEncodedLen + TypeInfo + 'static, + A: Get + 'static, + Ch: Challenger + 'static, + Cred: UserChallengeResponse + 'static, +{ + type Authority = A; + type Challenger = Ch; + type Credential = Cred; + + fn device_id(&self) -> &DeviceId { + self.0.as_ref() + } +} + +impl MaxEncodedLen for Dev { + fn max_encoded_len() -> usize { + T::max_encoded_len() + } +} + +// TODO implement here +mod pass_key { + use codec::{Decode, Encode}; + use scale_info::TypeInfo; + + use super::{Auth, Dev}; + use crate::{DeviceChallengeResponse, DeviceId, UserChallengeResponse}; + + #[allow(dead_code)] + pub type PassKey = Dev<(), A, (), PassKeyAssertion>; + #[allow(dead_code)] + pub type PassKeyManager = Auth, PassKeyAttestation>; + + #[derive(Clone, Debug, Decode, Encode, TypeInfo, PartialEq, Eq)] + pub struct PassKeyAttestation; + + impl DeviceChallengeResponse for PassKeyAttestation { + fn is_valid(&self) -> bool { + todo!() + } + + fn used_challenge(&self) -> (Cx, crate::Challenge) { + todo!() + } + + fn authority(&self) -> crate::AuthorityId { + todo!() + } + + fn device_id(&self) -> &DeviceId { + todo!() + } + } + + #[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, Eq)] + pub struct PassKeyAssertion; + + impl UserChallengeResponse for PassKeyAssertion { + fn is_valid(&self) -> bool { + todo!() + } + + fn used_challenge(&self) -> (Cx, crate::Challenge) { + todo!() + } + + fn authority(&self) -> crate::AuthorityId { + todo!() + } + + fn user_id(&self) -> crate::HashedUserId { + todo!() + } + } +} + +mod dummy { + use frame_support::{parameter_types, sp_runtime::str_array as s}; + + use crate::{ + AuthorityId, Challenger, DeviceChallengeResponse, DeviceId, HashedUserId, + UserChallengeResponse, + }; + + use super::{Auth, Dev}; + + type DummyAttestation = bool; + type DummyCredential = bool; + type DummyChallenger = u8; + type DummyCx = ::Context; + + parameter_types! { + const DummyAuthority: AuthorityId = s("dummy_authority"); + } + pub const DUMMY_DEV: DeviceId = s("dummy_device"); + pub const DUMMY_USER: HashedUserId = s("dummy_user_hash"); + + impl Challenger for DummyChallenger { + type Context = Self; + fn generate(cx: &Self::Context) -> crate::Challenge { + [*cx; 32] + } + } + + #[allow(dead_code)] + pub type DummyDev = Dev; + #[allow(dead_code)] + pub type Dummy = Auth; + + impl DeviceChallengeResponse for DummyAttestation { + fn device_id(&self) -> &DeviceId { + &DUMMY_DEV + } + + fn is_valid(&self) -> bool { + *self + } + fn used_challenge(&self) -> (DummyCx, crate::Challenge) { + (0, [0; 32]) + } + fn authority(&self) -> crate::AuthorityId { + DummyAuthority::get() + } + } + + impl UserChallengeResponse for DummyCredential { + fn user_id(&self) -> crate::HashedUserId { + DUMMY_USER + } + + fn is_valid(&self) -> bool { + *self + } + + fn used_challenge(&self) -> (DummyCx, crate::Challenge) { + (0, [0; 32]) + } + + fn authority(&self) -> crate::AuthorityId { + DummyAuthority::get() + } + } +} diff --git a/traits/gas-tank/Cargo.toml b/traits/gas-tank/Cargo.toml index 67a3d26..c49d421 100644 --- a/traits/gas-tank/Cargo.toml +++ b/traits/gas-tank/Cargo.toml @@ -20,6 +20,13 @@ sp-io.workspace = true [features] default = ["std"] +runtime-benchmarks=[ + "pallet-balances/runtime-benchmarks", + "pallet-nfts/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] std = [ "codec/std", "pallet-balances/std", diff --git a/traits/memberships/Cargo.toml b/traits/memberships/Cargo.toml index 55425f1..fdf1c3d 100644 --- a/traits/memberships/Cargo.toml +++ b/traits/memberships/Cargo.toml @@ -1,32 +1,39 @@ [package] - name = "fc-traits-memberships" - version = "0.1.0" - authors.workspace = true - edition.workspace = true - repository.workspace = true - license.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +name = "fc-traits-memberships" +repository.workspace = true +version = "0.1.0" [dependencies] - codec = { workspace = true } - scale-info = { workspace = true } - frame-support = { workspace = true } +codec = {workspace = true} +frame-support = {workspace = true} +scale-info = {workspace = true} [dev-dependencies] - frame-system = { workspace = true } - pallet-balances = { workspace = true } - pallet-nfts = { workspace = true } - sp-io = { workspace = true } - sp-runtime = { workspace = true } +frame-system = {workspace = true} +pallet-balances = {workspace = true} +pallet-nfts = {workspace = true} +sp-io = {workspace = true} +sp-runtime = {workspace = true} [features] - default = ["std"] - std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "pallet-nfts/std", - "scale-info/std", - "sp-io/std", - "sp-runtime/std", - ] +default = ["std"] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-nfts/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-nfts/std", + "scale-info/std", + "sp-io/std", + "sp-runtime/std", +] diff --git a/traits/memberships/src/lib.rs b/traits/memberships/src/lib.rs index f0de079..25d6e77 100644 --- a/traits/memberships/src/lib.rs +++ b/traits/memberships/src/lib.rs @@ -95,7 +95,7 @@ impl GenericRank { Self(self.0.saturating_add(n.get()).min(Self::MAX.0)) } pub fn demote_by(self, n: NonZeroU8) -> Self { - Self(self.0.saturating_sub(n.get()).max(Self::MIN.0)) + Self(self.0.saturating_sub(n.get())) } } impl From for u8 {