From 25350a522c80ac50f201d12bf58337b89d8c30ca Mon Sep 17 00:00:00 2001 From: gui Date: Thu, 8 Aug 2024 16:29:35 +0900 Subject: [PATCH 01/10] Make hex2array static and checked at compile time --- .../frame/metadata-hash-extension/src/lib.rs | 15 +- .../metadata-hash-extension/src/utils.rs | 143 ++++++++++++++++++ 2 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 substrate/frame/metadata-hash-extension/src/utils.rs diff --git a/substrate/frame/metadata-hash-extension/src/lib.rs b/substrate/frame/metadata-hash-extension/src/lib.rs index 8a1c26abddea..de59a6961f70 100644 --- a/substrate/frame/metadata-hash-extension/src/lib.rs +++ b/substrate/frame/metadata-hash-extension/src/lib.rs @@ -50,6 +50,7 @@ use sp_runtime::{ #[cfg(test)] mod tests; +mod utils; /// The mode of [`CheckMetadataHash`]. #[derive(Decode, Encode, PartialEq, Debug, TypeInfo, Clone, Copy, Eq)] @@ -68,12 +69,19 @@ enum MetadataHash { Custom([u8; 32]), } +const INVALID_ENV_MSG: &str = "Invalid environment variable `RUNTIME_METADATA_HASH`, it must be a 32 \ + bytes hexadecimal string, optionally prefixed with `0x`."; + +static RUNTIME_METADATA: Option<[u8; 32]> = match option_env!("RUNTIME_METADATA_HASH") { + Some(hex) => Some(utils::hex_str_to_32_bytes_panic(hex, INVALID_ENV_MSG)), + None => None, +}; + impl MetadataHash { /// Returns the metadata hash. fn hash(&self) -> Option<[u8; 32]> { match self { - Self::FetchFromEnv => - option_env!("RUNTIME_METADATA_HASH").map(array_bytes::hex2array_unchecked), + Self::FetchFromEnv => RUNTIME_METADATA, Self::Custom(hash) => Some(*hash), } } @@ -152,6 +160,9 @@ impl TransactionExtensionBase for CheckMetadataHash Ok(signed) } + fn weight() -> Weight { + Weight::default() + } } impl TransactionExtension for CheckMetadataHash { type Val = (); diff --git a/substrate/frame/metadata-hash-extension/src/utils.rs b/substrate/frame/metadata-hash-extension/src/utils.rs new file mode 100644 index 000000000000..2341cacabd08 --- /dev/null +++ b/substrate/frame/metadata-hash-extension/src/utils.rs @@ -0,0 +1,143 @@ +// 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. + +/// Function to convert hex string to Option<[u8; 32]> in a const context +/// panics if the input is not valid. +pub const fn hex_str_to_32_bytes_panic(hex_str: &str, msg: &str) -> [u8; 32] { + let len = hex_str.len(); + + let start = if len == 64 { + 0 + } else if len == 66 && hex_str.as_bytes()[0] == b'0' && hex_str.as_bytes()[1] == b'x' { + 2 + } else { + panic!("{}", msg) + }; + + let mut bytes = [0u8; 32]; + let mut i = 0; + + while i < 32 { + let high = from_hex_digit_panic(hex_str.as_bytes()[start + i * 2], msg); + let low = from_hex_digit_panic(hex_str.as_bytes()[start + i * 2 + 1], msg); + bytes[i] = (high << 4) | low; + i += 1; + } + + bytes +} + +// Helper function to convert a single hex character to a byte +const fn from_hex_digit_panic(digit: u8, msg: &str) -> u8 { + match digit { + b'0'..=b'9' => digit - b'0', + b'a'..=b'f' => digit - b'a' + 10, + b'A'..=b'F' => digit - b'A' + 10, + _ => panic!("{}", msg), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_from_hex_digit_panic() { + assert_eq!(from_hex_digit_panic(b'0', ""), 0); + assert_eq!(from_hex_digit_panic(b'1', ""), 1); + assert_eq!(from_hex_digit_panic(b'2', ""), 2); + assert_eq!(from_hex_digit_panic(b'3', ""), 3); + assert_eq!(from_hex_digit_panic(b'4', ""), 4); + assert_eq!(from_hex_digit_panic(b'5', ""), 5); + assert_eq!(from_hex_digit_panic(b'6', ""), 6); + assert_eq!(from_hex_digit_panic(b'7', ""), 7); + assert_eq!(from_hex_digit_panic(b'8', ""), 8); + assert_eq!(from_hex_digit_panic(b'9', ""), 9); + assert_eq!(from_hex_digit_panic(b'a', ""), 10); + assert_eq!(from_hex_digit_panic(b'b', ""), 11); + assert_eq!(from_hex_digit_panic(b'c', ""), 12); + assert_eq!(from_hex_digit_panic(b'd', ""), 13); + assert_eq!(from_hex_digit_panic(b'e', ""), 14); + assert_eq!(from_hex_digit_panic(b'f', ""), 15); + assert_eq!(from_hex_digit_panic(b'A', ""), 10); + assert_eq!(from_hex_digit_panic(b'B', ""), 11); + assert_eq!(from_hex_digit_panic(b'C', ""), 12); + assert_eq!(from_hex_digit_panic(b'D', ""), 13); + assert_eq!(from_hex_digit_panic(b'E', ""), 14); + assert_eq!(from_hex_digit_panic(b'F', ""), 15); + } + + #[test] + #[should_panic(expected = "TEST1")] + fn test_from_hex_digit_panic_should_panic() { + from_hex_digit_panic(b'g', "TEST1"); + } + + #[test] + fn test_hex_str_to_32_bytes_panic() { + assert_eq!( + hex_str_to_32_bytes_panic( + "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "" + ), + [ + 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, + 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, + 0x90, 0xab, 0xcd, 0xef, + ] + ); + assert_eq!( + hex_str_to_32_bytes_panic( + "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "" + ), + [ + 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, + 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, + 0x90, 0xab, 0xcd, 0xef, + ] + ); + assert_eq!( + hex_str_to_32_bytes_panic( + "0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", + "" + ), + [ + 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, + 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, + 0x90, 0xab, 0xcd, 0xef, + ] + ); + assert_eq!( + hex_str_to_32_bytes_panic( + "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", + "" + ), + [ + 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, + 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, + 0x90, 0xab, 0xcd, 0xef, + ] + ); + } + + #[test] + #[should_panic(expected = "TEST")] + fn test_hex_str_to_32_bytes_panic_should_panic() { + hex_str_to_32_bytes_panic("0x1234", "TEST"); + } +} From 8144c61c3263fad4fab5e8b6c5640bf3341d4533 Mon Sep 17 00:00:00 2001 From: gui Date: Thu, 8 Aug 2024 17:35:39 +0900 Subject: [PATCH 02/10] no breaking change --- .../frame/metadata-hash-extension/src/lib.rs | 7 +- .../metadata-hash-extension/src/utils.rs | 99 +++++++++---------- 2 files changed, 49 insertions(+), 57 deletions(-) diff --git a/substrate/frame/metadata-hash-extension/src/lib.rs b/substrate/frame/metadata-hash-extension/src/lib.rs index de59a6961f70..90814242adb8 100644 --- a/substrate/frame/metadata-hash-extension/src/lib.rs +++ b/substrate/frame/metadata-hash-extension/src/lib.rs @@ -39,7 +39,7 @@ extern crate alloc; extern crate self as frame_metadata_hash_extension; use codec::{Decode, Encode}; -use frame_support::DebugNoBound; +use frame_support::{pallet_prelude::Weight, DebugNoBound}; use frame_system::Config; use scale_info::TypeInfo; use sp_runtime::{ @@ -69,11 +69,8 @@ enum MetadataHash { Custom([u8; 32]), } -const INVALID_ENV_MSG: &str = "Invalid environment variable `RUNTIME_METADATA_HASH`, it must be a 32 \ - bytes hexadecimal string, optionally prefixed with `0x`."; - static RUNTIME_METADATA: Option<[u8; 32]> = match option_env!("RUNTIME_METADATA_HASH") { - Some(hex) => Some(utils::hex_str_to_32_bytes_panic(hex, INVALID_ENV_MSG)), + Some(hex) => utils::hex_str_to_32_bytes_panic(hex), None => None, }; diff --git a/substrate/frame/metadata-hash-extension/src/utils.rs b/substrate/frame/metadata-hash-extension/src/utils.rs index 2341cacabd08..78ca5ca200c3 100644 --- a/substrate/frame/metadata-hash-extension/src/utils.rs +++ b/substrate/frame/metadata-hash-extension/src/utils.rs @@ -15,9 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Function to convert hex string to Option<[u8; 32]> in a const context -/// panics if the input is not valid. -pub const fn hex_str_to_32_bytes_panic(hex_str: &str, msg: &str) -> [u8; 32] { +/// Function to convert hex string to Option<[u8; 32]> in a const context. +/// Returns `None` if fails to decode. +pub const fn hex_str_to_32_bytes_panic(hex_str: &str) -> Option<[u8; 32]> { let len = hex_str.len(); let start = if len == 64 { @@ -25,29 +25,32 @@ pub const fn hex_str_to_32_bytes_panic(hex_str: &str, msg: &str) -> [u8; 32] { } else if len == 66 && hex_str.as_bytes()[0] == b'0' && hex_str.as_bytes()[1] == b'x' { 2 } else { - panic!("{}", msg) + return None; }; let mut bytes = [0u8; 32]; let mut i = 0; while i < 32 { - let high = from_hex_digit_panic(hex_str.as_bytes()[start + i * 2], msg); - let low = from_hex_digit_panic(hex_str.as_bytes()[start + i * 2 + 1], msg); + let high = from_hex_digit_panic(hex_str.as_bytes()[start + i * 2]); + let low = from_hex_digit_panic(hex_str.as_bytes()[start + i * 2 + 1]); + + let (Some(high), Some(low)) = (high, low) else { return None }; + bytes[i] = (high << 4) | low; i += 1; } - bytes + Some(bytes) } // Helper function to convert a single hex character to a byte -const fn from_hex_digit_panic(digit: u8, msg: &str) -> u8 { +const fn from_hex_digit_panic(digit: u8) -> Option { match digit { - b'0'..=b'9' => digit - b'0', - b'a'..=b'f' => digit - b'a' + 10, - b'A'..=b'F' => digit - b'A' + 10, - _ => panic!("{}", msg), + b'0'..=b'9' => Some(digit - b'0'), + b'a'..=b'f' => Some(digit - b'a' + 10), + b'A'..=b'F' => Some(digit - b'A' + 10), + _ => None, } } @@ -57,34 +60,30 @@ mod tests { #[test] fn test_from_hex_digit_panic() { - assert_eq!(from_hex_digit_panic(b'0', ""), 0); - assert_eq!(from_hex_digit_panic(b'1', ""), 1); - assert_eq!(from_hex_digit_panic(b'2', ""), 2); - assert_eq!(from_hex_digit_panic(b'3', ""), 3); - assert_eq!(from_hex_digit_panic(b'4', ""), 4); - assert_eq!(from_hex_digit_panic(b'5', ""), 5); - assert_eq!(from_hex_digit_panic(b'6', ""), 6); - assert_eq!(from_hex_digit_panic(b'7', ""), 7); - assert_eq!(from_hex_digit_panic(b'8', ""), 8); - assert_eq!(from_hex_digit_panic(b'9', ""), 9); - assert_eq!(from_hex_digit_panic(b'a', ""), 10); - assert_eq!(from_hex_digit_panic(b'b', ""), 11); - assert_eq!(from_hex_digit_panic(b'c', ""), 12); - assert_eq!(from_hex_digit_panic(b'd', ""), 13); - assert_eq!(from_hex_digit_panic(b'e', ""), 14); - assert_eq!(from_hex_digit_panic(b'f', ""), 15); - assert_eq!(from_hex_digit_panic(b'A', ""), 10); - assert_eq!(from_hex_digit_panic(b'B', ""), 11); - assert_eq!(from_hex_digit_panic(b'C', ""), 12); - assert_eq!(from_hex_digit_panic(b'D', ""), 13); - assert_eq!(from_hex_digit_panic(b'E', ""), 14); - assert_eq!(from_hex_digit_panic(b'F', ""), 15); - } + assert_eq!(from_hex_digit_panic(b'0').unwrap(), 0); + assert_eq!(from_hex_digit_panic(b'1').unwrap(), 1); + assert_eq!(from_hex_digit_panic(b'2').unwrap(), 2); + assert_eq!(from_hex_digit_panic(b'3').unwrap(), 3); + assert_eq!(from_hex_digit_panic(b'4').unwrap(), 4); + assert_eq!(from_hex_digit_panic(b'5').unwrap(), 5); + assert_eq!(from_hex_digit_panic(b'6').unwrap(), 6); + assert_eq!(from_hex_digit_panic(b'7').unwrap(), 7); + assert_eq!(from_hex_digit_panic(b'8').unwrap(), 8); + assert_eq!(from_hex_digit_panic(b'9').unwrap(), 9); + assert_eq!(from_hex_digit_panic(b'a').unwrap(), 10); + assert_eq!(from_hex_digit_panic(b'b').unwrap(), 11); + assert_eq!(from_hex_digit_panic(b'c').unwrap(), 12); + assert_eq!(from_hex_digit_panic(b'd').unwrap(), 13); + assert_eq!(from_hex_digit_panic(b'e').unwrap(), 14); + assert_eq!(from_hex_digit_panic(b'f').unwrap(), 15); + assert_eq!(from_hex_digit_panic(b'A').unwrap(), 10); + assert_eq!(from_hex_digit_panic(b'B').unwrap(), 11); + assert_eq!(from_hex_digit_panic(b'C').unwrap(), 12); + assert_eq!(from_hex_digit_panic(b'D').unwrap(), 13); + assert_eq!(from_hex_digit_panic(b'E').unwrap(), 14); + assert_eq!(from_hex_digit_panic(b'F').unwrap(), 15); - #[test] - #[should_panic(expected = "TEST1")] - fn test_from_hex_digit_panic_should_panic() { - from_hex_digit_panic(b'g', "TEST1"); + assert_eq!(from_hex_digit_panic(b'g'), None); } #[test] @@ -92,8 +91,8 @@ mod tests { assert_eq!( hex_str_to_32_bytes_panic( "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", - "" - ), + ) + .unwrap(), [ 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, @@ -103,8 +102,8 @@ mod tests { assert_eq!( hex_str_to_32_bytes_panic( "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", - "" - ), + ) + .unwrap(), [ 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, @@ -114,8 +113,8 @@ mod tests { assert_eq!( hex_str_to_32_bytes_panic( "0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", - "" - ), + ) + .unwrap(), [ 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, @@ -125,19 +124,15 @@ mod tests { assert_eq!( hex_str_to_32_bytes_panic( "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", - "" - ), + ) + .unwrap(), [ 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, ] ); - } - #[test] - #[should_panic(expected = "TEST")] - fn test_hex_str_to_32_bytes_panic_should_panic() { - hex_str_to_32_bytes_panic("0x1234", "TEST"); + assert_eq!(hex_str_to_32_bytes_panic("0x1234"), None); } } From edffdafaae14648761d203f11d9bf52d68a61363 Mon Sep 17 00:00:00 2001 From: gui Date: Sat, 10 Aug 2024 09:27:50 +0900 Subject: [PATCH 03/10] fix naming --- .../frame/metadata-hash-extension/src/lib.rs | 2 +- .../metadata-hash-extension/src/utils.rs | 68 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/substrate/frame/metadata-hash-extension/src/lib.rs b/substrate/frame/metadata-hash-extension/src/lib.rs index 90814242adb8..99a7cbacec58 100644 --- a/substrate/frame/metadata-hash-extension/src/lib.rs +++ b/substrate/frame/metadata-hash-extension/src/lib.rs @@ -70,7 +70,7 @@ enum MetadataHash { } static RUNTIME_METADATA: Option<[u8; 32]> = match option_env!("RUNTIME_METADATA_HASH") { - Some(hex) => utils::hex_str_to_32_bytes_panic(hex), + Some(hex) => utils::hex_str_to_32_bytes(hex), None => None, }; diff --git a/substrate/frame/metadata-hash-extension/src/utils.rs b/substrate/frame/metadata-hash-extension/src/utils.rs index 78ca5ca200c3..d75aedb283df 100644 --- a/substrate/frame/metadata-hash-extension/src/utils.rs +++ b/substrate/frame/metadata-hash-extension/src/utils.rs @@ -17,7 +17,7 @@ /// Function to convert hex string to Option<[u8; 32]> in a const context. /// Returns `None` if fails to decode. -pub const fn hex_str_to_32_bytes_panic(hex_str: &str) -> Option<[u8; 32]> { +pub const fn hex_str_to_32_bytes(hex_str: &str) -> Option<[u8; 32]> { let len = hex_str.len(); let start = if len == 64 { @@ -32,8 +32,8 @@ pub const fn hex_str_to_32_bytes_panic(hex_str: &str) -> Option<[u8; 32]> { let mut i = 0; while i < 32 { - let high = from_hex_digit_panic(hex_str.as_bytes()[start + i * 2]); - let low = from_hex_digit_panic(hex_str.as_bytes()[start + i * 2 + 1]); + let high = from_hex_digit(hex_str.as_bytes()[start + i * 2]); + let low = from_hex_digit(hex_str.as_bytes()[start + i * 2 + 1]); let (Some(high), Some(low)) = (high, low) else { return None }; @@ -45,7 +45,7 @@ pub const fn hex_str_to_32_bytes_panic(hex_str: &str) -> Option<[u8; 32]> { } // Helper function to convert a single hex character to a byte -const fn from_hex_digit_panic(digit: u8) -> Option { +const fn from_hex_digit(digit: u8) -> Option { match digit { b'0'..=b'9' => Some(digit - b'0'), b'a'..=b'f' => Some(digit - b'a' + 10), @@ -59,37 +59,37 @@ mod tests { use super::*; #[test] - fn test_from_hex_digit_panic() { - assert_eq!(from_hex_digit_panic(b'0').unwrap(), 0); - assert_eq!(from_hex_digit_panic(b'1').unwrap(), 1); - assert_eq!(from_hex_digit_panic(b'2').unwrap(), 2); - assert_eq!(from_hex_digit_panic(b'3').unwrap(), 3); - assert_eq!(from_hex_digit_panic(b'4').unwrap(), 4); - assert_eq!(from_hex_digit_panic(b'5').unwrap(), 5); - assert_eq!(from_hex_digit_panic(b'6').unwrap(), 6); - assert_eq!(from_hex_digit_panic(b'7').unwrap(), 7); - assert_eq!(from_hex_digit_panic(b'8').unwrap(), 8); - assert_eq!(from_hex_digit_panic(b'9').unwrap(), 9); - assert_eq!(from_hex_digit_panic(b'a').unwrap(), 10); - assert_eq!(from_hex_digit_panic(b'b').unwrap(), 11); - assert_eq!(from_hex_digit_panic(b'c').unwrap(), 12); - assert_eq!(from_hex_digit_panic(b'd').unwrap(), 13); - assert_eq!(from_hex_digit_panic(b'e').unwrap(), 14); - assert_eq!(from_hex_digit_panic(b'f').unwrap(), 15); - assert_eq!(from_hex_digit_panic(b'A').unwrap(), 10); - assert_eq!(from_hex_digit_panic(b'B').unwrap(), 11); - assert_eq!(from_hex_digit_panic(b'C').unwrap(), 12); - assert_eq!(from_hex_digit_panic(b'D').unwrap(), 13); - assert_eq!(from_hex_digit_panic(b'E').unwrap(), 14); - assert_eq!(from_hex_digit_panic(b'F').unwrap(), 15); + fn test_from_hex_digit() { + assert_eq!(from_hex_digit(b'0').unwrap(), 0); + assert_eq!(from_hex_digit(b'1').unwrap(), 1); + assert_eq!(from_hex_digit(b'2').unwrap(), 2); + assert_eq!(from_hex_digit(b'3').unwrap(), 3); + assert_eq!(from_hex_digit(b'4').unwrap(), 4); + assert_eq!(from_hex_digit(b'5').unwrap(), 5); + assert_eq!(from_hex_digit(b'6').unwrap(), 6); + assert_eq!(from_hex_digit(b'7').unwrap(), 7); + assert_eq!(from_hex_digit(b'8').unwrap(), 8); + assert_eq!(from_hex_digit(b'9').unwrap(), 9); + assert_eq!(from_hex_digit(b'a').unwrap(), 10); + assert_eq!(from_hex_digit(b'b').unwrap(), 11); + assert_eq!(from_hex_digit(b'c').unwrap(), 12); + assert_eq!(from_hex_digit(b'd').unwrap(), 13); + assert_eq!(from_hex_digit(b'e').unwrap(), 14); + assert_eq!(from_hex_digit(b'f').unwrap(), 15); + assert_eq!(from_hex_digit(b'A').unwrap(), 10); + assert_eq!(from_hex_digit(b'B').unwrap(), 11); + assert_eq!(from_hex_digit(b'C').unwrap(), 12); + assert_eq!(from_hex_digit(b'D').unwrap(), 13); + assert_eq!(from_hex_digit(b'E').unwrap(), 14); + assert_eq!(from_hex_digit(b'F').unwrap(), 15); - assert_eq!(from_hex_digit_panic(b'g'), None); + assert_eq!(from_hex_digit(b'g'), None); } #[test] - fn test_hex_str_to_32_bytes_panic() { + fn test_hex_str_to_32_bytes() { assert_eq!( - hex_str_to_32_bytes_panic( + hex_str_to_32_bytes( "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", ) .unwrap(), @@ -100,7 +100,7 @@ mod tests { ] ); assert_eq!( - hex_str_to_32_bytes_panic( + hex_str_to_32_bytes( "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", ) .unwrap(), @@ -111,7 +111,7 @@ mod tests { ] ); assert_eq!( - hex_str_to_32_bytes_panic( + hex_str_to_32_bytes( "0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", ) .unwrap(), @@ -122,7 +122,7 @@ mod tests { ] ); assert_eq!( - hex_str_to_32_bytes_panic( + hex_str_to_32_bytes( "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", ) .unwrap(), @@ -133,6 +133,6 @@ mod tests { ] ); - assert_eq!(hex_str_to_32_bytes_panic("0x1234"), None); + assert_eq!(hex_str_to_32_bytes("0x1234"), None); } } From 20a74ea1013ef42547fdf4e05fad0e9af5f430c9 Mon Sep 17 00:00:00 2001 From: gui Date: Sat, 10 Aug 2024 10:46:40 +0900 Subject: [PATCH 04/10] use const for 32 --- .../frame/metadata-hash-extension/src/utils.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/substrate/frame/metadata-hash-extension/src/utils.rs b/substrate/frame/metadata-hash-extension/src/utils.rs index d75aedb283df..ecbe133a2391 100644 --- a/substrate/frame/metadata-hash-extension/src/utils.rs +++ b/substrate/frame/metadata-hash-extension/src/utils.rs @@ -15,23 +15,28 @@ // See the License for the specific language governing permissions and // limitations under the License. +const HASH_LEN: usize = 32; + /// Function to convert hex string to Option<[u8; 32]> in a const context. /// Returns `None` if fails to decode. -pub const fn hex_str_to_32_bytes(hex_str: &str) -> Option<[u8; 32]> { +pub const fn hex_str_to_32_bytes(hex_str: &str) -> Option<[u8; HASH_LEN]> { let len = hex_str.len(); - let start = if len == 64 { + let start = if len == HASH_LEN * 2 { 0 - } else if len == 66 && hex_str.as_bytes()[0] == b'0' && hex_str.as_bytes()[1] == b'x' { + } else if len == HASH_LEN * 2 + 2 && + hex_str.as_bytes()[0] == b'0' && + hex_str.as_bytes()[1] == b'x' + { 2 } else { return None; }; - let mut bytes = [0u8; 32]; + let mut bytes = [0u8; HASH_LEN]; let mut i = 0; - while i < 32 { + while i < HASH_LEN { let high = from_hex_digit(hex_str.as_bytes()[start + i * 2]); let low = from_hex_digit(hex_str.as_bytes()[start + i * 2 + 1]); From eea48ded11fda240252756626701468d126ba34d Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Thu, 15 Aug 2024 20:45:32 +0900 Subject: [PATCH 05/10] Update substrate/frame/metadata-hash-extension/src/lib.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- substrate/frame/metadata-hash-extension/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/metadata-hash-extension/src/lib.rs b/substrate/frame/metadata-hash-extension/src/lib.rs index 99a7cbacec58..4a75e00e73f7 100644 --- a/substrate/frame/metadata-hash-extension/src/lib.rs +++ b/substrate/frame/metadata-hash-extension/src/lib.rs @@ -69,7 +69,7 @@ enum MetadataHash { Custom([u8; 32]), } -static RUNTIME_METADATA: Option<[u8; 32]> = match option_env!("RUNTIME_METADATA_HASH") { +const RUNTIME_METADATA: Option<[u8; 32]> = match option_env!("RUNTIME_METADATA_HASH") { Some(hex) => utils::hex_str_to_32_bytes(hex), None => None, }; From 19923a1141bd36983b6b37b723e07536cdaf9954 Mon Sep 17 00:00:00 2001 From: gui Date: Fri, 16 Aug 2024 08:10:20 +0900 Subject: [PATCH 06/10] use const-hex --- Cargo.lock | 1 + Cargo.toml | 1 + .../frame/metadata-hash-extension/Cargo.toml | 2 + .../frame/metadata-hash-extension/src/lib.rs | 11 +- .../metadata-hash-extension/src/utils.rs | 143 ------------------ 5 files changed, 11 insertions(+), 147 deletions(-) delete mode 100644 substrate/frame/metadata-hash-extension/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index c6bd6ddf0126..cd4c772945a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5973,6 +5973,7 @@ name = "frame-metadata-hash-extension" version = "0.1.0" dependencies = [ "array-bytes", + "const-hex", "docify", "frame-metadata", "frame-support", diff --git a/Cargo.toml b/Cargo.toml index 7ae7c3bd1811..f6794de9e458 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -668,6 +668,7 @@ codec = { version = "3.6.12", default-features = false, package = "parity-scale- collectives-westend-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend" } collectives-westend-runtime = { path = "cumulus/parachains/runtimes/collectives/collectives-westend" } color-eyre = { version = "0.6.1", default-features = false } +const-hex = { version = "1.10.0", default-features = false } color-print = { version = "0.3.4" } colored = { version = "2.0.4" } comfy-table = { version = "7.1.0", default-features = false } diff --git a/substrate/frame/metadata-hash-extension/Cargo.toml b/substrate/frame/metadata-hash-extension/Cargo.toml index 10d90bba0911..f1934fed9ada 100644 --- a/substrate/frame/metadata-hash-extension/Cargo.toml +++ b/substrate/frame/metadata-hash-extension/Cargo.toml @@ -17,6 +17,7 @@ frame-support = { workspace = true } frame-system = { workspace = true } log = { workspace = true } docify = { workspace = true } +const-hex = { workspace = true } [dev-dependencies] substrate-wasm-builder = { features = ["metadata-hash"], workspace = true, default-features = true } @@ -36,4 +37,5 @@ std = [ "log/std", "scale-info/std", "sp-runtime/std", + "const-hex/std", ] diff --git a/substrate/frame/metadata-hash-extension/src/lib.rs b/substrate/frame/metadata-hash-extension/src/lib.rs index 4a75e00e73f7..de9e5d617e59 100644 --- a/substrate/frame/metadata-hash-extension/src/lib.rs +++ b/substrate/frame/metadata-hash-extension/src/lib.rs @@ -50,7 +50,6 @@ use sp_runtime::{ #[cfg(test)] mod tests; -mod utils; /// The mode of [`CheckMetadataHash`]. #[derive(Decode, Encode, PartialEq, Debug, TypeInfo, Clone, Copy, Eq)] @@ -69,9 +68,13 @@ enum MetadataHash { Custom([u8; 32]), } -const RUNTIME_METADATA: Option<[u8; 32]> = match option_env!("RUNTIME_METADATA_HASH") { - Some(hex) => utils::hex_str_to_32_bytes(hex), - None => None, +const RUNTIME_METADATA: Option<[u8; 32]> = if let Some(hex) = option_env!("RUNTIME_METADATA_HASH") { + match const_hex::const_decode_to_array(hex.as_bytes()) { + Ok(hex) => Some(hex), + Err(_) => None, + } +} else { + None }; impl MetadataHash { diff --git a/substrate/frame/metadata-hash-extension/src/utils.rs b/substrate/frame/metadata-hash-extension/src/utils.rs deleted file mode 100644 index ecbe133a2391..000000000000 --- a/substrate/frame/metadata-hash-extension/src/utils.rs +++ /dev/null @@ -1,143 +0,0 @@ -// 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. - -const HASH_LEN: usize = 32; - -/// Function to convert hex string to Option<[u8; 32]> in a const context. -/// Returns `None` if fails to decode. -pub const fn hex_str_to_32_bytes(hex_str: &str) -> Option<[u8; HASH_LEN]> { - let len = hex_str.len(); - - let start = if len == HASH_LEN * 2 { - 0 - } else if len == HASH_LEN * 2 + 2 && - hex_str.as_bytes()[0] == b'0' && - hex_str.as_bytes()[1] == b'x' - { - 2 - } else { - return None; - }; - - let mut bytes = [0u8; HASH_LEN]; - let mut i = 0; - - while i < HASH_LEN { - let high = from_hex_digit(hex_str.as_bytes()[start + i * 2]); - let low = from_hex_digit(hex_str.as_bytes()[start + i * 2 + 1]); - - let (Some(high), Some(low)) = (high, low) else { return None }; - - bytes[i] = (high << 4) | low; - i += 1; - } - - Some(bytes) -} - -// Helper function to convert a single hex character to a byte -const fn from_hex_digit(digit: u8) -> Option { - match digit { - b'0'..=b'9' => Some(digit - b'0'), - b'a'..=b'f' => Some(digit - b'a' + 10), - b'A'..=b'F' => Some(digit - b'A' + 10), - _ => None, - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_from_hex_digit() { - assert_eq!(from_hex_digit(b'0').unwrap(), 0); - assert_eq!(from_hex_digit(b'1').unwrap(), 1); - assert_eq!(from_hex_digit(b'2').unwrap(), 2); - assert_eq!(from_hex_digit(b'3').unwrap(), 3); - assert_eq!(from_hex_digit(b'4').unwrap(), 4); - assert_eq!(from_hex_digit(b'5').unwrap(), 5); - assert_eq!(from_hex_digit(b'6').unwrap(), 6); - assert_eq!(from_hex_digit(b'7').unwrap(), 7); - assert_eq!(from_hex_digit(b'8').unwrap(), 8); - assert_eq!(from_hex_digit(b'9').unwrap(), 9); - assert_eq!(from_hex_digit(b'a').unwrap(), 10); - assert_eq!(from_hex_digit(b'b').unwrap(), 11); - assert_eq!(from_hex_digit(b'c').unwrap(), 12); - assert_eq!(from_hex_digit(b'd').unwrap(), 13); - assert_eq!(from_hex_digit(b'e').unwrap(), 14); - assert_eq!(from_hex_digit(b'f').unwrap(), 15); - assert_eq!(from_hex_digit(b'A').unwrap(), 10); - assert_eq!(from_hex_digit(b'B').unwrap(), 11); - assert_eq!(from_hex_digit(b'C').unwrap(), 12); - assert_eq!(from_hex_digit(b'D').unwrap(), 13); - assert_eq!(from_hex_digit(b'E').unwrap(), 14); - assert_eq!(from_hex_digit(b'F').unwrap(), 15); - - assert_eq!(from_hex_digit(b'g'), None); - } - - #[test] - fn test_hex_str_to_32_bytes() { - assert_eq!( - hex_str_to_32_bytes( - "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", - ) - .unwrap(), - [ - 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, - 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, - 0x90, 0xab, 0xcd, 0xef, - ] - ); - assert_eq!( - hex_str_to_32_bytes( - "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", - ) - .unwrap(), - [ - 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, - 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, - 0x90, 0xab, 0xcd, 0xef, - ] - ); - assert_eq!( - hex_str_to_32_bytes( - "0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", - ) - .unwrap(), - [ - 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, - 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, - 0x90, 0xab, 0xcd, 0xef, - ] - ); - assert_eq!( - hex_str_to_32_bytes( - "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF", - ) - .unwrap(), - [ - 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, - 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, - 0x90, 0xab, 0xcd, 0xef, - ] - ); - - assert_eq!(hex_str_to_32_bytes("0x1234"), None); - } -} From 456d6832da99eeeeeabd95921614f44326c8afb5 Mon Sep 17 00:00:00 2001 From: gui Date: Fri, 16 Aug 2024 08:24:09 +0900 Subject: [PATCH 07/10] fmt --- Cargo.toml | 2 +- substrate/frame/metadata-hash-extension/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f6794de9e458..95199eda899c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -668,11 +668,11 @@ codec = { version = "3.6.12", default-features = false, package = "parity-scale- collectives-westend-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend" } collectives-westend-runtime = { path = "cumulus/parachains/runtimes/collectives/collectives-westend" } color-eyre = { version = "0.6.1", default-features = false } -const-hex = { version = "1.10.0", default-features = false } color-print = { version = "0.3.4" } colored = { version = "2.0.4" } comfy-table = { version = "7.1.0", default-features = false } console = { version = "0.15.8" } +const-hex = { version = "1.10.0", default-features = false } contracts-rococo-runtime = { path = "cumulus/parachains/runtimes/contracts/contracts-rococo" } coretime-rococo-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo" } coretime-rococo-runtime = { path = "cumulus/parachains/runtimes/coretime/coretime-rococo" } diff --git a/substrate/frame/metadata-hash-extension/Cargo.toml b/substrate/frame/metadata-hash-extension/Cargo.toml index f1934fed9ada..bca2c3ffb198 100644 --- a/substrate/frame/metadata-hash-extension/Cargo.toml +++ b/substrate/frame/metadata-hash-extension/Cargo.toml @@ -32,10 +32,10 @@ sp-tracing = { workspace = true, default-features = true } default = ["std"] std = [ "codec/std", + "const-hex/std", "frame-support/std", "frame-system/std", "log/std", "scale-info/std", "sp-runtime/std", - "const-hex/std", ] From 8ffd8a91b995a78882b2935ad1c8ae87565137d3 Mon Sep 17 00:00:00 2001 From: gui Date: Thu, 29 Aug 2024 09:02:55 +0900 Subject: [PATCH 08/10] panic on invalid environment variable --- substrate/frame/metadata-hash-extension/src/lib.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/substrate/frame/metadata-hash-extension/src/lib.rs b/substrate/frame/metadata-hash-extension/src/lib.rs index de9e5d617e59..ff9d3cec5930 100644 --- a/substrate/frame/metadata-hash-extension/src/lib.rs +++ b/substrate/frame/metadata-hash-extension/src/lib.rs @@ -71,7 +71,9 @@ enum MetadataHash { const RUNTIME_METADATA: Option<[u8; 32]> = if let Some(hex) = option_env!("RUNTIME_METADATA_HASH") { match const_hex::const_decode_to_array(hex.as_bytes()) { Ok(hex) => Some(hex), - Err(_) => None, + Err(_) => panic!("Invalid RUNTIME_METADATA_HASH environment variable: it must be a 32 \ + bytes value in hexadecimal: e.g. 0x1234567890abcdef1234567890ABCDEF. Upper case or \ + lower case, 0x prefix is optional."), } } else { None @@ -160,13 +162,16 @@ impl TransactionExtensionBase for CheckMetadataHash Ok(signed) } - fn weight() -> Weight { - Weight::default() - } } impl TransactionExtension for CheckMetadataHash { type Val = (); type Pre = (); + fn weight(&self, _: &T::RuntimeCall) -> Weight { + // The weight is the weight of implicit, it consists of a few match operation, it is + // negligible. + Weight::zero() + } + impl_tx_ext_default!(T::RuntimeCall; validate prepare); } From 7bd871e55dcf57c01a78054f27ae28336ba68660 Mon Sep 17 00:00:00 2001 From: gui Date: Sat, 31 Aug 2024 08:26:32 +0900 Subject: [PATCH 09/10] fix example --- substrate/frame/metadata-hash-extension/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/metadata-hash-extension/src/lib.rs b/substrate/frame/metadata-hash-extension/src/lib.rs index ff9d3cec5930..104b56ef5d14 100644 --- a/substrate/frame/metadata-hash-extension/src/lib.rs +++ b/substrate/frame/metadata-hash-extension/src/lib.rs @@ -72,8 +72,8 @@ const RUNTIME_METADATA: Option<[u8; 32]> = if let Some(hex) = option_env!("RUNTI match const_hex::const_decode_to_array(hex.as_bytes()) { Ok(hex) => Some(hex), Err(_) => panic!("Invalid RUNTIME_METADATA_HASH environment variable: it must be a 32 \ - bytes value in hexadecimal: e.g. 0x1234567890abcdef1234567890ABCDEF. Upper case or \ - lower case, 0x prefix is optional."), + bytes value in hexadecimal: e.g. 0x123ABCabd...123ABCabc. Upper case or lower case, \ + 0x prefix is optional."), } } else { None From 6d94fa43b50889930f066c34e35b94a126536882 Mon Sep 17 00:00:00 2001 From: gui Date: Wed, 2 Oct 2024 15:33:03 +0200 Subject: [PATCH 10/10] prdoc --- prdoc/pr_5277.prdoc | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 prdoc/pr_5277.prdoc diff --git a/prdoc/pr_5277.prdoc b/prdoc/pr_5277.prdoc new file mode 100644 index 000000000000..187d79c4b3a0 --- /dev/null +++ b/prdoc/pr_5277.prdoc @@ -0,0 +1,11 @@ +title: Improve weight for `CheckMetadataHash` transaction extension + +doc: + - audience: Runtime Dev + description: | + This PR improves the weight for the `CheckMetadataHash` transaction extension. + The compilation now panics if the optional compile-time environment variable `RUNTIME_METADATA_HASH` contains an invalid value. + +crates: +- name: frame-metadata-hash-extension + bump: minor