From ae224f73aec9526e372222de50093ffc501de626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lo=C3=AFs?= <c@elo.tf> Date: Fri, 21 Mar 2025 19:10:59 +0100 Subject: [PATCH] Resolve "Perf: use host function to verify legacy revocation document" (nodes/rust/duniter-v2s!320) * check metadata with our own subxt fork * rustfmt * use host function for sig verification of legacy revoke document --- Cargo.lock | 3 +- Cargo.toml | 3 -- pallets/distance/src/mock.rs | 36 ++++++++++++++++++++++- pallets/duniter-wot/src/mock.rs | 43 +++++++++++++++++++++++++++- pallets/duniter-wot/src/tests.rs | 5 ++-- pallets/identity/Cargo.toml | 1 - pallets/identity/src/lib.rs | 22 +++++++++----- pallets/identity/src/mock.rs | 1 + pallets/quota/src/mock.rs | 1 + runtime/common/src/pallets_config.rs | 1 + scripts/check_metadata.sh | 2 +- 11 files changed, 99 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8f42cc06..92c7b383b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -9058,7 +9058,6 @@ dependencies = [ "base64 0.22.1", "bs58", "duniter-primitives", - "ed25519-dalek", "frame-benchmarking", "frame-support", "frame-system", diff --git a/Cargo.toml b/Cargo.toml index 54ee0162e..44e1a7c9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,6 @@ base64 = { version = "0.22.1", default-features = false } countmap = { version = "0.2.0", default-features = false } ctrlc = { version = "3.4.4", default-features = false } cucumber = { version = "0.20.2", default-features = false } -ed25519-dalek = { version = "2.1.1", default-features = false } env_logger = { version = "0.11.3", default-features = false } notify = { version = "6.1.1", default-features = false } portpicker = { version = "0.1.1", default-features = false } @@ -248,8 +247,6 @@ cranelift-wasm = { opt-level = 3 } crc32fast = { opt-level = 3 } crossbeam-deque = { opt-level = 3 } crypto-mac = { opt-level = 3 } -curve25519-dalek = { opt-level = 3 } -ed25519-dalek = { opt-level = 3 } futures-channel = { opt-level = 3 } hashbrown = { opt-level = 3 } hash-db = { opt-level = 3 } diff --git a/pallets/distance/src/mock.rs b/pallets/distance/src/mock.rs index e804df741..06b89976b 100644 --- a/pallets/distance/src/mock.rs +++ b/pallets/distance/src/mock.rs @@ -27,7 +27,7 @@ use sp_core::{ConstU32, H256}; use sp_runtime::{ impl_opaque_keys, key_types::DUMMY, - testing::{TestSignature, UintAuthorityId}, + testing::{TestSignature as SubtrateTestSignature, UintAuthorityId}, traits::{BlakeTwo256, ConvertInto, IdentityLookup, IsMember, OpaqueKeys}, BuildStorage, KeyTypeId, Perbill, }; @@ -36,6 +36,39 @@ type Balance = u64; type Block = frame_system::mocking::MockBlock<Test>; pub type AccountId = u64; +pub struct AccountId32Mock(u64); +impl From<AccountId32Mock> for u64 { + fn from(account: AccountId32Mock) -> u64 { + account.0 + } +} +impl From<[u8; 32]> for AccountId32Mock { + fn from(bytes: [u8; 32]) -> Self { + Self(u64::from_be_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + ])) + } +} + +/// Test signature that impl From<ed25519::Signature> (required to compile pallet identity) +#[derive(Clone, codec::Decode, Debug, Eq, codec::Encode, PartialEq, scale_info::TypeInfo)] +pub struct TestSignature(SubtrateTestSignature); +impl From<sp_core::ed25519::Signature> for TestSignature { + fn from(_: sp_core::ed25519::Signature) -> Self { + // Implementation here only to satisfy traits bounds at compilation + // This convertion should not be used inside pallet distance tests + unimplemented!() + } +} + +impl sp_runtime::traits::Verify for TestSignature { + type Signer = UintAuthorityId; + + fn verify<L: sp_runtime::traits::Lazy<[u8]>>(&self, msg: L, signer: &u64) -> bool { + <SubtrateTestSignature as sp_runtime::traits::Verify>::verify::<L>(&self.0, msg, signer) + } +} + impl_opaque_keys! { pub struct MockSessionKeys { pub dummy: UintAuthorityId, @@ -217,6 +250,7 @@ impl pallet_identity::traits::IdtyNameValidator for IdtyNameValidatorTestImpl { } impl pallet_identity::Config for Test { + type AccountId32 = AccountId32Mock; type AccountLinker = (); type AutorevocationPeriod = AutorevocationPeriod; type ChangeOwnerKeyPeriod = ChangeOwnerKeyPeriod; diff --git a/pallets/duniter-wot/src/mock.rs b/pallets/duniter-wot/src/mock.rs index a3c550632..e246133a0 100644 --- a/pallets/duniter-wot/src/mock.rs +++ b/pallets/duniter-wot/src/mock.rs @@ -20,7 +20,7 @@ use frame_support::{derive_impl, parameter_types, traits::Everything}; use frame_system as system; use sp_core::H256; use sp_runtime::{ - testing::{TestSignature, UintAuthorityId}, + testing::{TestSignature as SubtrateTestSignature, UintAuthorityId}, traits::{BlakeTwo256, IdentityLookup}, BuildStorage, }; @@ -30,6 +30,46 @@ use std::collections::BTreeMap; type AccountId = u64; type Block = frame_system::mocking::MockBlock<Test>; +pub struct AccountId32Mock(u64); +impl From<AccountId32Mock> for u64 { + fn from(account: AccountId32Mock) -> u64 { + account.0 + } +} +impl From<[u8; 32]> for AccountId32Mock { + fn from(bytes: [u8; 32]) -> Self { + Self(u64::from_be_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + ])) + } +} + +/// Test signature that impl From<ed25519::Signature> (required to compile pallet identity) +#[derive(Clone, codec::Decode, Debug, Eq, codec::Encode, PartialEq, scale_info::TypeInfo)] +pub struct TestSignature(SubtrateTestSignature); + +impl TestSignature { + pub fn new(signer: u64, message: Vec<u8>) -> Self { + Self(SubtrateTestSignature(signer, message)) + } +} + +impl From<sp_core::ed25519::Signature> for TestSignature { + fn from(_: sp_core::ed25519::Signature) -> Self { + // Implementation here only to satisfy traits bounds at compilation + // This convertion should not be used inside pallet distance tests + unimplemented!() + } +} + +impl sp_runtime::traits::Verify for TestSignature { + type Signer = UintAuthorityId; + + fn verify<L: sp_runtime::traits::Lazy<[u8]>>(&self, msg: L, signer: &u64) -> bool { + <SubtrateTestSignature as sp_runtime::traits::Verify>::verify::<L>(&self.0, msg, signer) + } +} + // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( pub enum Test { @@ -96,6 +136,7 @@ impl pallet_identity::traits::IdtyNameValidator for IdtyNameValidatorTestImpl { } impl pallet_identity::Config for Test { + type AccountId32 = AccountId32Mock; type AccountLinker = (); type AutorevocationPeriod = AutorevocationPeriod; type ChangeOwnerKeyPeriod = ChangeOwnerKeyPeriod; diff --git a/pallets/duniter-wot/src/tests.rs b/pallets/duniter-wot/src/tests.rs index a6baf65e2..6db6cfe80 100644 --- a/pallets/duniter-wot/src/tests.rs +++ b/pallets/duniter-wot/src/tests.rs @@ -21,7 +21,6 @@ use pallet_identity::{ IdtyName, IdtyStatus, RevocationPayload, RevocationReason, REVOCATION_PAYLOAD_PREFIX, }; use pallet_membership::MembershipRemovalReason; -use sp_runtime::testing::TestSignature; /// test that genesis builder creates the good number of identities /// and good identity and certification metadate @@ -186,7 +185,7 @@ fn test_revoke_idty() { RuntimeOrigin::signed(1), 1, 1, - TestSignature( + TestSignature::new( 1, ( REVOCATION_PAYLOAD_PREFIX, @@ -211,7 +210,7 @@ fn test_revoke_idty() { RuntimeOrigin::signed(42), 2, 2, - TestSignature( + TestSignature::new( 2, ( REVOCATION_PAYLOAD_PREFIX, diff --git a/pallets/identity/Cargo.toml b/pallets/identity/Cargo.toml index f86d0f991..13eb8edf3 100644 --- a/pallets/identity/Cargo.toml +++ b/pallets/identity/Cargo.toml @@ -46,7 +46,6 @@ base64 = { workspace = true } bs58 = { workspace = true } codec = { workspace = true, features = ["derive"] } duniter-primitives = { workspace = true } -ed25519-dalek = { workspace = true } frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 64f47fe83..0e00c7e7a 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -103,6 +103,9 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The way to deserialize accound id from 32 raw bytes + type AccountId32: From<[u8; 32]> + Into<Self::AccountId>; + /// The period during which the owner can confirm the new identity. #[pallet::constant] type ConfirmPeriod: Get<BlockNumberFor<Self>>; @@ -173,7 +176,9 @@ pub mod pallet { type Signer: IdentifyAccount<AccountId = Self::AccountId>; /// Signature type for payload verification. - type Signature: Parameter + Verify<Signer = Self::Signer>; + type Signature: Parameter + + Verify<Signer = Self::Signer> + + From<sp_core::ed25519::Signature>; /// The overarching event type. type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; @@ -637,10 +642,15 @@ pub mod pallet { ) .into_array_const::<32>() .map_err(|_| Error::<T>::InvalidLegacyRevocationFormat)?; - ed25519_dalek::VerifyingKey::from_bytes(&issuer) - .map_err(|_| Error::<T>::InvalidLegacyRevocationFormat)? - .verify_strict(document, &ed25519_dalek::Signature::from_bytes(&signature)) - .map_err(|_| Error::<T>::InvalidSignature)?; + + // Verify signature + let revocation_key = T::AccountId32::from(issuer).into(); + let signature = T::Signature::from(sp_core::ed25519::Signature::from(signature)); + ensure!( + signature.verify(document, &revocation_key), + Error::<T>::InvalidSignature + ); + let username = line_username .get(14..) .ok_or(Error::<T>::InvalidLegacyRevocationFormat)?; @@ -657,8 +667,6 @@ pub mod pallet { IdtyStatus::Revoked => Err(Error::<T>::AlreadyRevoked), }?; - let revocation_key = T::AccountId::decode(&mut &issuer[..]).unwrap(); - ensure!( if let Some((ref old_owner_key, last_change)) = idty_value.old_owner_key { // old owner key can also revoke the identity until the period expired diff --git a/pallets/identity/src/mock.rs b/pallets/identity/src/mock.rs index 70758bc81..de48f0a2e 100644 --- a/pallets/identity/src/mock.rs +++ b/pallets/identity/src/mock.rs @@ -89,6 +89,7 @@ impl pallet_identity::traits::IdtyNameValidator for IdtyNameValidatorTestImpl { } impl pallet_identity::Config for Test { + type AccountId32 = AccountId; type AccountLinker = (); type AutorevocationPeriod = AutorevocationPeriod; type ChangeOwnerKeyPeriod = ChangeOwnerKeyPeriod; diff --git a/pallets/quota/src/mock.rs b/pallets/quota/src/mock.rs index f39b053df..dcfd30f77 100644 --- a/pallets/quota/src/mock.rs +++ b/pallets/quota/src/mock.rs @@ -133,6 +133,7 @@ impl pallet_identity::traits::IdtyNameValidator for IdtyNameValidatorTestImpl { } } impl pallet_identity::Config for Test { + type AccountId32 = AccountId; type AccountLinker = (); type AutorevocationPeriod = AutorevocationPeriod; type ChangeOwnerKeyPeriod = ChangeOwnerKeyPeriod; diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index 04fa45d06..c3b6e728f 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -432,6 +432,7 @@ macro_rules! pallets_config { pub const ValidationPeriod: BlockNumber = 2 * MONTHS; } impl pallet_identity::Config for Runtime { + type AccountId32 = AccountId; type AccountLinker = Account; type AutorevocationPeriod = AutorevocationPeriod; type ChangeOwnerKeyPeriod = ChangeOwnerKeyPeriod; diff --git a/scripts/check_metadata.sh b/scripts/check_metadata.sh index 69bd92b6f..053b92c18 100755 --- a/scripts/check_metadata.sh +++ b/scripts/check_metadata.sh @@ -1,6 +1,6 @@ #!/bin/sh -cargo install subxt-cli +cargo install subxt-cli --git https://github.com/duniter/subxt --branch subxt-v0.38.0-duniter-substrate-v1.17.0 cargo build target/debug/duniter --dev& sleep 20 -- GitLab