From ade30281951d9957e93800a18cd6d7daa3bfdf78 Mon Sep 17 00:00:00 2001 From: Ayden Brewer Date: Wed, 15 Nov 2023 13:35:54 -0800 Subject: [PATCH] Identity system for Subnets and Root-validators (#191) * Start identity pallet * Rename identity to registry, import some FRAME types for identity fields * Add IdentityUpdated event * Finish set_identity and clear_identity extrinsic implementations * Add types to handle storage of identities on-chain * Remove unused import * Add benchmarking and WeightInfo for pallet_registry * Add is_subnet_owner util function, implement can_register to only allow identities for root validators and subnet owners * Use is_hotkey_registered_on_network over get_axon_info for CanRegisterIdentity * Allow specification of hotkey-coldkey pair when registering identity * Add Raw33 -> Raw64 fields for larger identity fields * Fix errors caused by Raw type bounds at 32 * Fix set_identity using wrong address when inserting data into storage * Update clear_identity to allow for identified argument --- .maintain/frame-weight-template.hbs | 121 +++++++ Cargo.lock | 168 ++++++---- pallets/registry/Cargo.toml | 50 +++ pallets/registry/scripts/benchmark.sh | 9 + pallets/registry/src/benchmarking.rs | 67 ++++ pallets/registry/src/lib.rs | 156 +++++++++ pallets/registry/src/mock.rs | 56 ++++ pallets/registry/src/tests.rs | 4 + pallets/registry/src/types.rs | 457 ++++++++++++++++++++++++++ pallets/registry/src/weights.rs | 87 +++++ pallets/subtensor/src/utils.rs | 4 + runtime/Cargo.toml | 5 + runtime/src/lib.rs | 41 ++- 13 files changed, 1164 insertions(+), 61 deletions(-) create mode 100644 .maintain/frame-weight-template.hbs create mode 100644 pallets/registry/Cargo.toml create mode 100755 pallets/registry/scripts/benchmark.sh create mode 100644 pallets/registry/src/benchmarking.rs create mode 100644 pallets/registry/src/lib.rs create mode 100644 pallets/registry/src/mock.rs create mode 100644 pallets/registry/src/tests.rs create mode 100644 pallets/registry/src/types.rs create mode 100644 pallets/registry/src/weights.rs diff --git a/.maintain/frame-weight-template.hbs b/.maintain/frame-weight-template.hbs new file mode 100644 index 000000000..5e837b247 --- /dev/null +++ b/.maintain/frame-weight-template.hbs @@ -0,0 +1,121 @@ +{{header}} +//! Autogenerated weights for `{{pallet}}` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} +//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` +//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}` +//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}` + +// Executed Command: +{{#each args as |arg|}} +// {{arg}} +{{/each}} + +#![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}}`. +pub trait WeightInfo { + {{#each benchmarks as |benchmark|}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{c.name}}: u32, {{/each~}} + ) -> Weight; + {{/each}} +} + +/// Weights for `{{pallet}}` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +{{#if (eq pallet "frame_system")}} +impl WeightInfo for SubstrateWeight { +{{else}} +impl WeightInfo for SubstrateWeight { +{{/if}} + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 44a5a8602..b5a6def3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,7 +261,7 @@ checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "synstructure", ] @@ -273,7 +273,7 @@ checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "synstructure", ] @@ -285,7 +285,7 @@ checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -332,7 +332,7 @@ checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -855,7 +855,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1270,7 +1270,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 1.0.107", ] [[package]] @@ -1287,7 +1287,7 @@ checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1311,7 +1311,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 1.0.107", ] [[package]] @@ -1322,7 +1322,7 @@ checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1348,7 +1348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" dependencies = [ "data-encoding", - "syn", + "syn 1.0.107", ] [[package]] @@ -1398,7 +1398,7 @@ checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1419,7 +1419,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1429,7 +1429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" dependencies = [ "derive_builder_core", - "syn", + "syn 1.0.107", ] [[package]] @@ -1440,7 +1440,7 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1527,7 +1527,7 @@ checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1566,7 +1566,7 @@ checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -1661,7 +1661,27 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 1.0.107", +] + +[[package]] +name = "enumflags2" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", ] [[package]] @@ -2070,7 +2090,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -2082,7 +2102,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -2092,7 +2112,7 @@ source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.3 dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -2237,7 +2257,7 @@ checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -2801,7 +2821,7 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -3011,7 +3031,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -3447,7 +3467,7 @@ checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" dependencies = [ "heck", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -3910,7 +3930,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -3969,7 +3989,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "synstructure", ] @@ -4017,7 +4037,7 @@ checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -4202,6 +4222,7 @@ dependencies = [ "pallet-membership", "pallet-multisig", "pallet-preimage", + "pallet-registry", "pallet-scheduler", "pallet-subtensor", "pallet-sudo", @@ -4570,6 +4591,22 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-registry" +version = "4.0.0-dev" +dependencies = [ + "enumflags2", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" @@ -4776,7 +4813,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -4810,7 +4847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ "proc-macro2", - "syn", + "syn 1.0.107", "synstructure", ] @@ -4958,7 +4995,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -4999,7 +5036,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -5139,7 +5176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" dependencies = [ "proc-macro2", - "syn", + "syn 1.0.107", ] [[package]] @@ -5187,7 +5224,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "version_check", ] @@ -5204,9 +5241,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -5245,7 +5282,7 @@ checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -5275,7 +5312,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn", + "syn 1.0.107", "tempfile", "which", ] @@ -5303,7 +5340,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -5362,9 +5399,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -5545,7 +5582,7 @@ checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -5952,7 +5989,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -6752,7 +6789,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -6833,7 +6870,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -7050,7 +7087,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -7083,7 +7120,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -7290,7 +7327,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -7499,7 +7536,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing", - "syn", + "syn 1.0.107", ] [[package]] @@ -7518,7 +7555,7 @@ source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.3 dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -7706,7 +7743,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -7873,7 +7910,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -7973,7 +8010,7 @@ dependencies = [ "memchr", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -8001,7 +8038,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 1.0.107", ] [[package]] @@ -8166,6 +8203,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -8174,7 +8222,7 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "unicode-xid", ] @@ -8257,7 +8305,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -8413,7 +8461,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -8525,7 +8573,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -8953,7 +9001,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-shared", ] @@ -8987,7 +9035,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9845,7 +9893,7 @@ checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "synstructure", ] diff --git a/pallets/registry/Cargo.toml b/pallets/registry/Cargo.toml new file mode 100644 index 000000000..432a459ea --- /dev/null +++ b/pallets/registry/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "pallet-registry" +version = "4.0.0-dev" +description = "Simplified identity system for network participants." +authors = ["Bittensor Nucleus Team"] +homepage = "https://bittensor.com" +edition = "2021" +license = "Unlicense" +publish = false +repository = "https://github.com/opentensor/subtensor" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ + "derive", + "max-encoded-len" +] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.39" } +frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.39" } +frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.39" } +sp-runtime = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.39" } +sp-std = { version = "5.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.39" } + +[dependencies.enumflags2] +version = "0.7.7" + +[dev-dependencies] +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.39" } +sp-io = { version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.39" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-std/std" +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/registry/scripts/benchmark.sh b/pallets/registry/scripts/benchmark.sh new file mode 100755 index 000000000..2bb8fabc8 --- /dev/null +++ b/pallets/registry/scripts/benchmark.sh @@ -0,0 +1,9 @@ +cargo build --release --features runtime-benchmarks +./target/release/node-subtensor benchmark pallet \ + --chain=local \ + --execution=wasm \ + --wasm-execution=compiled \ + --pallet=pallet_registry \ + --extrinsic="*" \ + --output=pallets/registry/src/weights.rs \ + --template=./.maintain/frame-weight-template.hbs \ No newline at end of file diff --git a/pallets/registry/src/benchmarking.rs b/pallets/registry/src/benchmarking.rs new file mode 100644 index 000000000..cf77d827c --- /dev/null +++ b/pallets/registry/src/benchmarking.rs @@ -0,0 +1,67 @@ +//! Benchmarking setup +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Registry; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +use sp_runtime::traits::{StaticLookup, Bounded}; +use frame_support::traits::Get; +use sp_std::mem::size_of; + +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +// This creates an `IdentityInfo` object with `num_fields` extra fields. +// All data is pre-populated with some arbitrary bytes. +fn create_identity_info(num_fields: u32) -> IdentityInfo { + let data = Data::Raw(vec![0; 32].try_into().unwrap()); + + IdentityInfo { + additional: Default::default(), + display: data.clone(), + legal: data.clone(), + web: data.clone(), + riot: data.clone(), + email: data.clone(), + pgp_fingerprint: Some([0; 20]), + image: data.clone(), + twitter: data, + } +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn set_identity() { + // The target user + let caller: T::AccountId = whitelisted_caller(); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), Box::new(create_identity_info::(0))); + + assert_last_event::(Event::::IdentitySet { who: caller }.into()); + } + + #[benchmark] + fn clear_identity() { + // The target user + let caller: T::AccountId = whitelisted_caller(); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + Registry::::set_identity(RawOrigin::Signed(caller.clone()).into(), Box::new(create_identity_info::(0))); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + + assert_last_event::(Event::::IdentityDissolved { who: caller }.into()); + } + + impl_benchmark_test_suite!(Registry, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/registry/src/lib.rs b/pallets/registry/src/lib.rs new file mode 100644 index 000000000..36262d8d5 --- /dev/null +++ b/pallets/registry/src/lib.rs @@ -0,0 +1,156 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub mod types; + +pub use pallet::*; +pub use types::*; +pub use weights::WeightInfo; + +use sp_std::boxed::Box; +use frame_support::traits::Currency; +use sp_runtime::traits::Zero; + +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{pallet_prelude::*, traits::ReservableCurrency}; + use frame_system::{pallet_prelude::*}; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + // Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + // Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + // Currency type that will be used to place deposits on neurons + type Currency: ReservableCurrency + Send + Sync; + + // Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + // Interface to allow other pallets to control who can register identities + type CanRegister: crate::CanRegisterIdentity; + + // Configuration fields + /// Maximum user-configured additional fields + #[pallet::constant] + type MaxAdditionalFields: Get; + + /// The amount held on deposit for a registered identity + #[pallet::constant] + type InitialDeposit: Get>; + + /// The amount held on deposit per additional field for a registered identity. + #[pallet::constant] + type FieldDeposit: Get>; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + IdentitySet {who: T::AccountId}, // Emitted when a user registers an identity + IdentityDissolved {who: T::AccountId}, // Emitted when a user dissolves an identity + } + + #[pallet::error] + pub enum Error { + /// Account attempted to register an identity but doesn't meet the requirements. + CannotRegister, + /// Account passed too many additional fields to their identity + TooManyFields, + /// Account doesn't have a registered identity + NotRegistered + } + + /// Identity data by account + #[pallet::storage] + #[pallet::getter(fn identity_of)] + pub(super) type IdentityOf = StorageMap< + _, + Twox64Concat, + T::AccountId, + Registration, T::MaxAdditionalFields>, + OptionQuery, + >; + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(( + T::WeightInfo::set_identity(), + DispatchClass::Operational + ))] + pub fn set_identity(origin: OriginFor, identified: T::AccountId, info: Box>) -> DispatchResult { + let who = ensure_signed(origin)?; + ensure!(T::CanRegister::can_register(&who, &identified), Error::::CannotRegister); + + let extra_fields = info.additional.len() as u32; + ensure!(extra_fields <= T::MaxAdditionalFields::get(), Error::::TooManyFields); + + let fd = >::from(extra_fields) * T::FieldDeposit::get(); + let mut id = match >::get(&identified) { + Some(mut id) => { + id.info = *info; + id + }, + None => Registration { + info: *info, + deposit: Zero::zero(), + }, + }; + + let old_deposit = id.deposit; + id.deposit = T::InitialDeposit::get() + fd; + if id.deposit > old_deposit { + T::Currency::reserve(&who, id.deposit - old_deposit)?; + } + if old_deposit > id.deposit { + let err_amount = T::Currency::unreserve(&who, old_deposit - id.deposit); + debug_assert!(err_amount.is_zero()); + } + + >::insert(&identified, id); + Self::deposit_event(Event::IdentitySet { who: identified }); + + Ok(().into()) + } + + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::clear_identity())] + pub fn clear_identity(origin: OriginFor, identified: T::AccountId) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + ensure!(T::CanRegister::can_register(&who, &identified), Error::::CannotRegister); + + let id = >::take(&identified).ok_or(Error::::NotRegistered)?; + let deposit = id.total_deposit(); + + let err_amount = T::Currency::unreserve(&who, deposit); + debug_assert!(err_amount.is_zero()); + + Self::deposit_event(Event::IdentityDissolved { who: identified }); + + Ok(().into()) + } + } +} +// Interfaces to interact with other pallets +pub trait CanRegisterIdentity { + fn can_register(who: &AccountId, identified: &AccountId) -> bool; +} + +impl CanRegisterIdentity for () { + fn can_register(_: &A, _: &A) -> bool {false} +} diff --git a/pallets/registry/src/mock.rs b/pallets/registry/src/mock.rs new file mode 100644 index 000000000..fd0f1f3ff --- /dev/null +++ b/pallets/registry/src/mock.rs @@ -0,0 +1,56 @@ +use crate as pallet_template; +use frame_support::traits::{ConstU16, ConstU64}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + TemplateModule: pallet_template, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_registry::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type MaxAdditionalFields = frame_support::traits::ConstU32<16>; + type CanRegisterIdentity = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::::default().build_storage().unwrap().into() +} diff --git a/pallets/registry/src/tests.rs b/pallets/registry/src/tests.rs new file mode 100644 index 000000000..fa2447e34 --- /dev/null +++ b/pallets/registry/src/tests.rs @@ -0,0 +1,4 @@ +use crate::{Error, Event}; +use frame_support::{assert_noop, assert_ok}; + +// Testing \ No newline at end of file diff --git a/pallets/registry/src/types.rs b/pallets/registry/src/types.rs new file mode 100644 index 000000000..72377dc22 --- /dev/null +++ b/pallets/registry/src/types.rs @@ -0,0 +1,457 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use codec::{Decode, Encode, MaxEncodedLen}; +use enumflags2::{bitflags, BitFlags}; +use frame_support::{ + traits::{ConstU32, Get}, + BoundedVec, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, +}; +use scale_info::{ + build::{Fields, Variants}, + meta_type, Path, Type, TypeInfo, TypeParameter, +}; +use sp_runtime::{traits::{Zero, AppendZerosInput}, RuntimeDebug}; +use sp_std::{fmt::Debug, iter::once, ops::Add, prelude::*}; + +/// Either underlying data blob if it is at most 32 bytes, or a hash of it. If the data is greater +/// than 32-bytes then it will be truncated when encoding. +/// +/// Can also be `None`. +#[derive(Clone, Eq, PartialEq, RuntimeDebug, MaxEncodedLen)] +pub enum Data { + /// No data here. + None, + /// The data is stored directly. + Raw(BoundedVec>), + /// Only the Blake2 hash of the data is stored. The preimage of the hash may be retrieved + /// through some hash-lookup service. + BlakeTwo256([u8; 32]), + /// Only the SHA2-256 hash of the data is stored. The preimage of the hash may be retrieved + /// through some hash-lookup service. + Sha256([u8; 32]), + /// Only the Keccak-256 hash of the data is stored. The preimage of the hash may be retrieved + /// through some hash-lookup service. + Keccak256([u8; 32]), + /// Only the SHA3-256 hash of the data is stored. The preimage of the hash may be retrieved + /// through some hash-lookup service. + ShaThree256([u8; 32]), +} + +impl Data { + pub fn is_none(&self) -> bool { + self == &Data::None + } +} + +impl Decode for Data { + fn decode(input: &mut I) -> sp_std::result::Result { + let b = input.read_byte()?; + Ok(match b { + 0 => Data::None, + n @ 1..=65 => { + let mut r: BoundedVec<_, _> = vec![0u8; n as usize - 1] + .try_into() + .expect("bound checked in match arm condition; qed"); + input.read(&mut r[..])?; + Data::Raw(r) + }, + 66 => Data::BlakeTwo256(<[u8; 32]>::decode(input)?), + 67 => Data::Sha256(<[u8; 32]>::decode(input)?), + 68 => Data::Keccak256(<[u8; 32]>::decode(input)?), + 69 => Data::ShaThree256(<[u8; 32]>::decode(input)?), + _ => return Err(codec::Error::from("invalid leading byte")), + }) + } +} + +impl Encode for Data { + fn encode(&self) -> Vec { + match self { + Data::None => vec![0u8; 1], + Data::Raw(ref x) => { + let l = x.len().min(64); + let mut r = vec![l as u8 + 1; l + 1]; + r[1..].copy_from_slice(&x[..l as usize]); + r + }, + Data::BlakeTwo256(ref h) => once(34u8).chain(h.iter().cloned()).collect(), + Data::Sha256(ref h) => once(35u8).chain(h.iter().cloned()).collect(), + Data::Keccak256(ref h) => once(36u8).chain(h.iter().cloned()).collect(), + Data::ShaThree256(ref h) => once(37u8).chain(h.iter().cloned()).collect(), + } + } +} +impl codec::EncodeLike for Data {} + +/// Add a Raw variant with the given index and a fixed sized byte array +macro_rules! data_raw_variants { + ($variants:ident, $(($index:literal, $size:literal)),* ) => { + $variants + $( + .variant(concat!("Raw", stringify!($size)), |v| v + .index($index) + .fields(Fields::unnamed().field(|f| f.ty::<[u8; $size]>())) + ) + )* + } +} + +impl TypeInfo for Data { + type Identity = Self; + + fn type_info() -> Type { + let variants = Variants::new().variant("None", |v| v.index(0)); + + // create a variant for all sizes of Raw data from 0-32 + let variants = data_raw_variants!( + variants, + (1, 0), + (2, 1), + (3, 2), + (4, 3), + (5, 4), + (6, 5), + (7, 6), + (8, 7), + (9, 8), + (10, 9), + (11, 10), + (12, 11), + (13, 12), + (14, 13), + (15, 14), + (16, 15), + (17, 16), + (18, 17), + (19, 18), + (20, 19), + (21, 20), + (22, 21), + (23, 22), + (24, 23), + (25, 24), + (26, 25), + (27, 26), + (28, 27), + (29, 28), + (30, 29), + (31, 30), + (32, 31), + (33, 32), + (34, 33), + (35, 34), + (36, 35), + (37, 36), + (38, 37), + (39, 38), + (40, 39), + (41, 40), + (42, 41), + (43, 42), + (44, 43), + (45, 44), + (46, 45), + (47, 46), + (48, 47), + (49, 48), + (50, 49), + (51, 50), + (52, 51), + (53, 52), + (54, 53), + (55, 54), + (56, 55), + (57, 56), + (58, 57), + (59, 58), + (60, 59), + (61, 60), + (62, 61), + (63, 62), + (64, 63), + (65, 64) + ); + + let variants = variants + .variant("BlakeTwo256", |v| { + v.index(66).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) + }) + .variant("Sha256", |v| { + v.index(67).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) + }) + .variant("Keccak256", |v| { + v.index(68).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) + }) + .variant("ShaThree256", |v| { + v.index(69).fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) + }); + + Type::builder().path(Path::new("Data", module_path!())).variant(variants) + } +} + +impl Default for Data { + fn default() -> Self { + Self::None + } +} + +/// The fields that we use to identify the owner of an account with. Each corresponds to a field +/// in the `IdentityInfo` struct. +#[bitflags] +#[repr(u64)] +#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub enum IdentityField { + Display = 0b0000000000000000000000000000000000000000000000000000000000000001, + Legal = 0b0000000000000000000000000000000000000000000000000000000000000010, + Web = 0b0000000000000000000000000000000000000000000000000000000000000100, + Riot = 0b0000000000000000000000000000000000000000000000000000000000001000, + Email = 0b0000000000000000000000000000000000000000000000000000000000010000, + PgpFingerprint = 0b0000000000000000000000000000000000000000000000000000000000100000, + Image = 0b0000000000000000000000000000000000000000000000000000000001000000, + Twitter = 0b0000000000000000000000000000000000000000000000000000000010000000, +} + +/// Wrapper type for `BitFlags` that implements `Codec`. +#[derive(Clone, Copy, PartialEq, Default, RuntimeDebug)] +pub struct IdentityFields(pub BitFlags); + +impl MaxEncodedLen for IdentityFields { + fn max_encoded_len() -> usize { + u64::max_encoded_len() + } +} + +impl Eq for IdentityFields {} +impl Encode for IdentityFields { + fn using_encoded R>(&self, f: F) -> R { + self.0.bits().using_encoded(f) + } +} +impl Decode for IdentityFields { + fn decode(input: &mut I) -> sp_std::result::Result { + let field = u64::decode(input)?; + Ok(Self(>::from_bits(field as u64).map_err(|_| "invalid value")?)) + } +} +impl TypeInfo for IdentityFields { + type Identity = Self; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("BitFlags", module_path!())) + .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) + .composite(Fields::unnamed().field(|f| f.ty::().type_name("IdentityField"))) + } +} + +/// Information concerning the identity of the controller of an account. +/// +/// NOTE: This should be stored at the end of the storage item to facilitate the addition of extra +/// fields in a backwards compatible way through a specialized `Decode` impl. +#[derive( + CloneNoBound, Encode, Decode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, +)] +#[codec(mel_bound())] +#[cfg_attr(test, derive(frame_support::DefaultNoBound))] +#[scale_info(skip_type_params(FieldLimit))] +pub struct IdentityInfo> { + /// Additional fields of the identity that are not catered for with the struct's explicit + /// fields. + pub additional: BoundedVec<(Data, Data), FieldLimit>, + + /// A reasonable display name for the controller of the account. This should be whatever it is + /// that it is typically known as and should not be confusable with other entities, given + /// reasonable context. + /// + /// Stored as UTF-8. + pub display: Data, + + /// The full legal name in the local jurisdiction of the entity. This might be a bit + /// long-winded. + /// + /// Stored as UTF-8. + pub legal: Data, + + /// A representative website held by the controller of the account. + /// + /// NOTE: `https://` is automatically prepended. + /// + /// Stored as UTF-8. + pub web: Data, + + /// The Riot/Matrix handle held by the controller of the account. + /// + /// Stored as UTF-8. + pub riot: Data, + + /// The email address of the controller of the account. + /// + /// Stored as UTF-8. + pub email: Data, + + /// The PGP/GPG public key of the controller of the account. + pub pgp_fingerprint: Option<[u8; 20]>, + + /// A graphic image representing the controller of the account. Should be a company, + /// organization or project logo or a headshot in the case of a human. + pub image: Data, + + /// The Twitter identity. The leading `@` character may be elided. + pub twitter: Data, +} + +impl> IdentityInfo { + pub(crate) fn fields(&self) -> IdentityFields { + let mut res = >::empty(); + if !self.display.is_none() { + res.insert(IdentityField::Display); + } + if !self.legal.is_none() { + res.insert(IdentityField::Legal); + } + if !self.web.is_none() { + res.insert(IdentityField::Web); + } + if !self.riot.is_none() { + res.insert(IdentityField::Riot); + } + if !self.email.is_none() { + res.insert(IdentityField::Email); + } + if self.pgp_fingerprint.is_some() { + res.insert(IdentityField::PgpFingerprint); + } + if !self.image.is_none() { + res.insert(IdentityField::Image); + } + if !self.twitter.is_none() { + res.insert(IdentityField::Twitter); + } + IdentityFields(res) + } +} + +/// Information concerning the identity of the controller of an account. +/// +/// NOTE: This is stored separately primarily to facilitate the addition of extra fields in a +/// backwards compatible way through a specialized `Decode` impl. +#[derive( + CloneNoBound, Encode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, +)] +#[codec(mel_bound())] +#[scale_info(skip_type_params(MaxAdditionalFields))] +pub struct Registration< + Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq, + MaxAdditionalFields: Get, +> { + /// Amount held on deposit for this information. + pub deposit: Balance, + + /// Information on the identity. + pub info: IdentityInfo, +} + +impl< + Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq + Zero + Add, + MaxAdditionalFields: Get, + > Registration +{ + pub(crate) fn total_deposit(&self) -> Balance { + self.deposit + } +} + +impl< + Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq, + MaxAdditionalFields: Get, + > Decode for Registration +{ + fn decode(input: &mut I) -> sp_std::result::Result { + let (deposit, info) = Decode::decode(&mut AppendZerosInput::new(input))?; + Ok(Self { deposit, info }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn manual_data_type_info() { + let mut registry = scale_info::Registry::new(); + let type_id = registry.register_type(&scale_info::meta_type::()); + let registry: scale_info::PortableRegistry = registry.into(); + let type_info = registry.resolve(type_id.id()).unwrap(); + + let check_type_info = |data: &Data| { + let variant_name = match data { + Data::None => "None".to_string(), + Data::BlakeTwo256(_) => "BlakeTwo256".to_string(), + Data::Sha256(_) => "Sha256".to_string(), + Data::Keccak256(_) => "Keccak256".to_string(), + Data::ShaThree256(_) => "ShaThree256".to_string(), + Data::Raw(bytes) => format!("Raw{}", bytes.len()), + }; + if let scale_info::TypeDef::Variant(variant) = &type_info.type_def() { + let variant = variant + .variants() + .iter() + .find(|v| v.name == variant_name) + .expect(&format!("Expected to find variant {}", variant_name)); + + let field_arr_len = variant + .fields + .first() + .and_then(|f| registry.resolve(f.ty().id())) + .map(|ty| { + if let scale_info::TypeDef::Array(arr) = &ty.type_def() { + arr.len() + } else { + panic!("Should be an array type") + } + }) + .unwrap_or(0); + + let encoded = data.encode(); + assert_eq!(encoded[0], variant.index); + assert_eq!(encoded.len() as u32 - 1, field_arr_len); + } else { + panic!("Should be a variant type") + }; + }; + + let mut data = vec![ + Data::None, + Data::BlakeTwo256(Default::default()), + Data::Sha256(Default::default()), + Data::Keccak256(Default::default()), + Data::ShaThree256(Default::default()), + ]; + + // A Raw instance for all possible sizes of the Raw data + for n in 0..64 { + data.push(Data::Raw(vec![0u8; n as usize].try_into().unwrap())) + } + + for d in data.iter() { + check_type_info(d); + } + } +} \ No newline at end of file diff --git a/pallets/registry/src/weights.rs b/pallets/registry/src/weights.rs new file mode 100644 index 000000000..7850699b5 --- /dev/null +++ b/pallets/registry/src/weights.rs @@ -0,0 +1,87 @@ + +//! Autogenerated weights for `pallet_registry` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-24, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `rustys-MacBook-Pro.local`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("local")`, DB CACHE: `1024` + +// Executed Command: +// ./target/release/node-subtensor +// benchmark +// pallet +// --chain=local +// --execution=wasm +// --wasm-execution=compiled +// --pallet=pallet_registry +// --extrinsic=* +// --output=pallets/registry/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![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_registry`. +pub trait WeightInfo { + fn set_identity() -> Weight; + fn clear_identity() -> Weight; +} + +/// Weights for `pallet_registry` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Registry IdentityOf (r:1 w:1) + /// Proof Skipped: Registry IdentityOf (max_values: None, max_size: None, mode: Measured) + fn set_identity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1024` + // Estimated: `3499` + // Minimum execution time: 41_000_000 picoseconds. + Weight::from_parts(41_000_000, 3499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Registry IdentityOf (r:1 w:1) + /// Proof Skipped: Registry IdentityOf (max_values: None, max_size: None, mode: Measured) + fn clear_identity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1385` + // Estimated: `3860` + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(36_000_000, 3860) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: Registry IdentityOf (r:1 w:1) + /// Proof Skipped: Registry IdentityOf (max_values: None, max_size: None, mode: Measured) + fn set_identity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1024` + // Estimated: `3499` + // Minimum execution time: 41_000_000 picoseconds. + Weight::from_parts(41_000_000, 3499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Registry IdentityOf (r:1 w:1) + /// Proof Skipped: Registry IdentityOf (max_values: None, max_size: None, mode: Measured) + fn clear_identity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1385` + // Estimated: `3860` + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(36_000_000, 3860) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index 74ea53081..ffde203ea 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -1102,4 +1102,8 @@ impl Pallet { Self::deposit_event(Event::SenateRequiredStakePercentSet(required_percent)); Ok(()) } + + pub fn is_subnet_owner(address: &T::AccountId) -> bool { + SubnetOwner::::iter_values().any(|owner| *address == owner) + } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index e2d862b23..a2bb06940 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -67,6 +67,9 @@ pallet-transaction-payment-rpc-runtime-api = { version = "4.0.0-dev", default-fe frame-benchmarking = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", optional = true, branch = "polkadot-v0.9.39" } frame-system-benchmarking = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", optional = true, branch = "polkadot-v0.9.39" } +# Identity registry pallet for registering project info +pallet-registry = { version = "4.0.0-dev", default-features = false, path = "../pallets/registry" } + [build-dependencies] substrate-wasm-builder = { version = "5.0.0-dev", git = "https://github.com/paritytech/substrate.git", optional = true, branch = "polkadot-v0.9.39" } @@ -111,6 +114,7 @@ std = [ "substrate-wasm-builder", "pallet-collective/std", "pallet-membership/std", + "pallet-registry/std" ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -125,6 +129,7 @@ runtime-benchmarks = [ "pallet-subtensor/runtime-benchmarks", "pallet-collective/runtime-benchmarks", "pallet-membership/runtime-benchmarks", + "pallet-registry/runtime-benchmarks" ] try-runtime = [ "frame-try-runtime/try-runtime", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 89a727490..54e66e61f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -15,6 +15,7 @@ use pallet_grandpa::{ use frame_support::pallet_prelude::{DispatchResult, Get}; use frame_system::{EnsureNever, EnsureRoot, RawOrigin}; +use pallet_registry::CanRegisterIdentity; use smallvec::smallvec; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; @@ -553,6 +554,42 @@ impl pallet_preimage::Config for Runtime { type ByteDeposit = PreimageByteDeposit; } +pub struct AllowIdentityReg; + +impl CanRegisterIdentity for AllowIdentityReg { + #[cfg(not(feature = "runtime-benchmarks"))] + fn can_register(address: &AccountId, identified: &AccountId) -> bool { + if address != identified { + return SubtensorModule::coldkey_owns_hotkey(address, identified) && SubtensorModule::is_hotkey_registered_on_network(0, identified); + } else { + return SubtensorModule::is_subnet_owner(address); + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn can_register(_: &AccountId, _: &AccountId) -> bool { + true + } +} + +// Configure registry pallet. +parameter_types! { + pub const MaxAdditionalFields: u32 = 1; + pub const InitialDeposit: Balance = 1_000_000_000; + pub const FieldDeposit: Balance = 100_000_000; +} + +impl pallet_registry::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type CanRegister = AllowIdentityReg; + type WeightInfo = pallet_registry::weights::SubstrateWeight; + + type MaxAdditionalFields = MaxAdditionalFields; + type InitialDeposit = InitialDeposit; + type FieldDeposit = FieldDeposit; +} + // Configure the pallet subtensor. parameter_types! { pub const SubtensorInitialRho: u16 = 10; @@ -666,7 +703,8 @@ construct_runtime!( Sudo: pallet_sudo, Multisig: pallet_multisig, Preimage: pallet_preimage, - Scheduler: pallet_scheduler + Scheduler: pallet_scheduler, + Registry: pallet_registry } ); @@ -715,6 +753,7 @@ mod benches { [pallet_balances, Balances] [pallet_subtensor, SubtensorModule] [pallet_timestamp, Timestamp] + [pallet_registry, Registry] ); }