diff --git a/prdoc/pr_10861.prdoc b/prdoc/pr_10861.prdoc new file mode 100644 index 0000000000000..7fcdb09dddd72 --- /dev/null +++ b/prdoc/pr_10861.prdoc @@ -0,0 +1,10 @@ +title: '[pallet-revive] weight charge in `sr25519_verify` and `ecdsa_to_eth_address` + precompiles' +doc: +- audience: Runtime Dev + description: I couldn't see where the weight is charged in those builtin pre-compiles + and a quick test indicated that there's no charges implemented. Assuming those + are compute-heavy functions, the missing weight charges seem like a DoS vector. +crates: +- name: pallet-revive + bump: patch diff --git a/substrate/frame/revive/src/precompiles/builtin/system.rs b/substrate/frame/revive/src/precompiles/builtin/system.rs index e465fcbfadf12..896abe5c55bd6 100644 --- a/substrate/frame/revive/src/precompiles/builtin/system.rs +++ b/substrate/frame/revive/src/precompiles/builtin/system.rs @@ -100,6 +100,22 @@ impl BuiltinPrecompile for System { env.terminate_caller(&h160).map_err(Error::try_to_revert::)?; Ok(Vec::new()) }, + ISystemCalls::sr25519Verify(ISystem::sr25519VerifyCall { + signature, + message, + publicKey, + }) => { + env.frame_meter_mut() + .charge_weight_token(RuntimeCosts::Sr25519Verify(message.len() as _))?; + let ok = env.sr25519_verify(signature, message, publicKey); + Ok(ok.abi_encode()) + }, + ISystemCalls::ecdsaToEthAddress(ISystem::ecdsaToEthAddressCall { publicKey }) => { + env.frame_meter_mut().charge_weight_token(RuntimeCosts::EcdsaToEthAddress)?; + let address = + env.ecdsa_to_eth_address(publicKey).map_err(Error::try_to_revert::)?; + Ok(address.abi_encode()) + }, } } } @@ -110,14 +126,19 @@ mod tests { use crate::{ address::AddressMapper, call_builder::{caller_funding, CallSetup}, + metering::Token, pallet, precompiles::{ alloy::sol_types::{sol_data::Bytes, SolType}, tests::run_test_vectors, BuiltinPrecompile, }, + test_utils::ALICE, tests::{ExtBuilder, Test}, + vm::RuntimeCosts, }; + + use alloy_core::primitives::FixedBytes; use codec::Decode; use frame_support::traits::fungible::Mutate; @@ -190,4 +211,94 @@ mod tests { ); }) } + + #[test] + fn sr25519_verify() { + use crate::precompiles::alloy::sol_types::sol_data::Bool; + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 100_000_000_000); + + let mut call_setup = CallSetup::::default(); + let (mut ext, _) = call_setup.ext(); + + let mut call_with = |message: &[u8; 11]| { + // Alice's signature for "hello world" + #[rustfmt::skip] + let signature: [u8; 64] = [ + 184, 49, 74, 238, 78, 165, 102, 252, 22, 92, 156, 176, 124, 118, 168, 116, 247, + 99, 0, 94, 2, 45, 9, 170, 73, 222, 182, 74, 60, 32, 75, 64, 98, 174, 69, 55, 83, + 85, 180, 98, 208, 75, 231, 57, 205, 62, 4, 105, 26, 136, 172, 17, 123, 99, 90, 255, + 228, 54, 115, 63, 30, 207, 205, 131, + ]; + + // Alice's public key + #[rustfmt::skip] + let public_key: [u8; 32] = [ + 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, + 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, + ]; + + let weight_before = ext.frame_meter().weight_consumed(); + + let input = ISystem::ISystemCalls::sr25519Verify(ISystem::sr25519VerifyCall { + signature, + message: (*message).into(), + publicKey: public_key.into(), + }); + let result = + >::call(&>::MATCHER.base_address(), &input, &mut ext) + .unwrap(); + + let weight_used = ext.frame_meter().weight_consumed() - weight_before; + assert!(weight_used.ref_time() > 0, "sr25519_verify should charge weight"); + assert_eq!( + weight_used, + Token::::weight(&RuntimeCosts::Sr25519Verify(message.len() as u32)), + "sr25519_verify should charge the expected weight" + ); + result + }; + let result = Bool::abi_decode(&call_with(&b"hello world")).expect("decoding failed"); + assert!(result); + let result = Bool::abi_decode(&call_with(&b"hello worlD")).expect("decoding failed"); + assert!(!result); + }); + } + + #[test] + fn ecdsa_to_eth_address() { + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 100_000_000_000); + + let mut call_setup = CallSetup::::default(); + let (mut ext, _) = call_setup.ext(); + + let pubkey_compressed = array_bytes::hex2array_unchecked( + "028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd91", + ); + + let weight_before = ext.frame_meter().weight_consumed(); + + let input = ISystem::ISystemCalls::ecdsaToEthAddress(ISystem::ecdsaToEthAddressCall { + publicKey: pubkey_compressed, + }); + let result = + >::call(&>::MATCHER.base_address(), &input, &mut ext) + .unwrap(); + + let expected: FixedBytes<20> = array_bytes::hex2array_unchecked::<_, 20>( + "09231da7b19A016f9e576d23B16277062F4d46A8", + ) + .into(); + assert_eq!(result, expected.abi_encode()); + + let weight_used = ext.frame_meter().weight_consumed() - weight_before; + assert!(weight_used.ref_time() > 0, "ecdsa_to_eth_address should charge weight"); + assert_eq!( + weight_used, + Token::::weight(&RuntimeCosts::EcdsaToEthAddress), + "ecdsa_to_eth_address should charge the expected weight" + ); + }); + } }