diff --git a/Cargo.lock b/Cargo.lock index 0ca158505..c649ac2b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4163,6 +4163,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gnark-bn-verifier" +version = "0.1.0" +source = "git+https://github.com/availproject/gnark-bn-verifier?branch=no-std#7cb69460b449f0f130fd716e071ae4d1098b33bd" +dependencies = [ + "anyhow", + "substrate-bn-succinct", +] + [[package]] name = "governor" version = "0.6.3" @@ -7815,6 +7824,8 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "gnark-bn-verifier", + "hex", "hex-literal", "log", "pallet-balances", @@ -7832,6 +7843,7 @@ dependencies = [ "sp-std", "sp1-sdk", "sp1-verifier", + "substrate-bn-succinct", "trie-db 0.24.0", ] diff --git a/pallets/vector/Cargo.toml b/pallets/vector/Cargo.toml index ee358af70..9e706dee5 100644 --- a/pallets/vector/Cargo.toml +++ b/pallets/vector/Cargo.toml @@ -22,6 +22,9 @@ sp-core = { workspace = true, default-features = false } frame-benchmarking = { workspace = true, default-features = false, optional = true } sp1-verifier = { version = "5.0.0", default-features = false } alloy-sol-types = { version = "0.8.12", default-features = false } +hex = { version = "*", default-features = false } +gnark-bn-verifier = { git = "https://github.com/availproject/gnark-bn-verifier", branch = "no-std", default-features = false } +substrate-bn-succinct = { version = "0.6.0-v5.0.0", default-features = false } ark-bn254.workspace = true ark-groth16.workspace = true diff --git a/pallets/vector/pico_vk.bin b/pallets/vector/pico_vk.bin new file mode 100644 index 000000000..cc32ac808 Binary files /dev/null and b/pallets/vector/pico_vk.bin differ diff --git a/pallets/vector/src/lib.rs b/pallets/vector/src/lib.rs index ab64770e2..5d2ae67f7 100644 --- a/pallets/vector/src/lib.rs +++ b/pallets/vector/src/lib.rs @@ -141,6 +141,12 @@ pub mod pallet { SyncCommitteeStartMismatch, /// Mock is not enabled. MockIsNotEnabled, + /// Pico VK cannot be embedded + BadPicoVkKey, + /// Pico proof decoding error. + CannotDecodePicoProof, + /// Pico proof cannot be embedded + BadPicoProof, } #[pallet::event] @@ -192,8 +198,12 @@ pub mod pallet { }, /// Emit new updater. NewUpdater { old: H256, new: H256 }, + /// Emit new updater. + NewPicoUpdater { old: H256, new: H256 }, /// Emit new SP1 verification key. NewSP1VerificationKey { old: H256, new: H256 }, + /// Emit new Pico verification key. + NewPicoVerificationKey { old: H256, new: H256 }, /// Emit when new sync committee is updated. SyncCommitteeHashUpdated { period: u64, hash: H256 }, /// Emit when mocks are enabled or disabled @@ -289,6 +299,11 @@ pub mod pallet { #[pallet::getter(fn updater)] pub type Updater = StorageValue<_, H256, ValueQuery>; + /// Updater that can submit updates + #[pallet::storage] + #[pallet::getter(fn pico_updater)] + pub type PicoUpdater = StorageValue<_, H256, ValueQuery>; + /// Maps from a period to the the sync committee hash. #[pallet::storage] #[pallet::getter(fn sync_committee_hashes)] @@ -299,6 +314,10 @@ pub mod pallet { #[pallet::getter(fn sp1_verification_key)] pub type SP1VerificationKey = StorageValue<_, H256, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn pico_verification_key)] + pub type PicoVerificationKey = StorageValue<_, H256, ValueQuery>; + /// Enable mock functions #[pallet::storage] #[pallet::getter(fn verification_disabled)] @@ -445,6 +464,10 @@ pub mod pallet { Weight::zero() } } + enum UType { + Sp1, + Pico, + } #[pallet::call] impl Pallet @@ -841,8 +864,18 @@ pub mod pallet { proof: ProofInput, public_values: PublicValuesInput, ) -> DispatchResultWithPostInfo { + use substrate_bn_succinct::Fr; let sender: [u8; 32] = ensure_signed(origin)?.into(); - let updater = Updater::::get(); + let pico_updater = PicoUpdater::::get(); + let sp1_updater = Updater::::get(); + let (updater, updater_type) = if H256(sender) == pico_updater { + (pico_updater, UType::Pico) + } else if H256(sender) == sp1_updater { + (sp1_updater, UType::Sp1) + } else { + return Err(Error::::UpdaterMisMatch.into()); + }; + // ensure sender is preconfigured ensure!(H256(sender) == updater, Error::::UpdaterMisMatch); @@ -865,14 +898,44 @@ pub mod pallet { Error::::SyncCommitteeStartMismatch ); - let sp1_vk = SP1VerificationKey::::get(); - let is_valid = Groth16Verifier::verify( - &proof, - &public_values, - &format!("{:?}", sp1_vk), - &GROTH16_VK_BYTES, - ); - ensure!(is_valid.is_ok(), Error::::VerificationFailed); + match updater_type { + UType::Sp1 => { + let sp1_vk = SP1VerificationKey::::get(); + let is_valid = Groth16Verifier::verify( + &proof, + &public_values, + &format!("{:?}", sp1_vk), + &GROTH16_VK_BYTES, + ); + ensure!(is_valid.is_ok(), Error::::VerificationFailed); + }, + UType::Pico => { + let riscv_vk = PicoVerificationKey::::get(); + + let vk_bin = include_bytes!("../pico_vk.bin"); + let vk = gnark_bn_verifier::vk::Groth16VKey::try_from(vk_bin.as_slice()); + ensure!(vk.is_ok(), Error::::BadPicoVkKey); + let vk = vk.unwrap(); + + let groth16_proof = + gnark_bn_verifier::proof::Groth16Proof::try_from(&proof[..]) + .map_err(|_| Error::::BadPicoProof)?; + + let pub_input_hash = hash_public_inputs(public_values.as_slice()); + + let mut vkh: Vec = riscv_vk.0.into(); + vkh.extend(&pub_input_hash); + + let public_inputs = vkh + .chunks(32) + .map(|e| Fr::from_slice(e).unwrap()) + .collect::>(); + + let is_valid = groth16_proof.verify(&vk, public_inputs.as_slice()); + + ensure!(is_valid.is_ok(), Error::::VerificationFailed); + }, + }; Head::::set(new_head); let header = Headers::::get(new_head); @@ -990,7 +1053,16 @@ pub mod pallet { ); let sender: [u8; 32] = ensure_signed(origin)?.into(); - let updater = Updater::::get(); + let pico_updater = PicoUpdater::::get(); + let sp1_updater = Updater::::get(); + let (updater, _updater_type) = if H256(sender) == pico_updater { + (pico_updater, UType::Pico) + } else if H256(sender) == sp1_updater { + (sp1_updater, UType::Sp1) + } else { + return Err(Error::::UpdaterMisMatch.into()); + }; + // ensure sender is preconfigured ensure!(H256(sender) == updater, Error::::UpdaterMisMatch); @@ -1073,6 +1145,32 @@ pub mod pallet { Ok(().into()) } + + #[pallet::call_index(18)] + #[pallet::weight(T::WeightInfo::set_pico_updater())] + pub fn set_pico_updater(origin: OriginFor, updater: H256) -> DispatchResult { + ensure_root(origin)?; + let old = PicoUpdater::::get(); + PicoUpdater::::set(updater); + + Self::deposit_event(Event::::NewPicoUpdater { old, new: updater }); + Ok(()) + } + + #[pallet::call_index(19)] + #[pallet::weight(T::WeightInfo::set_pico_verification_key())] + pub fn set_pico_verification_key(origin: OriginFor, pico_vk: H256) -> DispatchResult { + ensure_root(origin)?; + let old_vk = PicoVerificationKey::::get(); + PicoVerificationKey::::put(pico_vk); + + Self::deposit_event(Event::NewPicoVerificationKey { + old: old_vk, + new: pico_vk, + }); + + Ok(()) + } } impl Pallet { @@ -1347,6 +1445,23 @@ pub mod pallet { } } +pub fn hash_public_inputs(public_inputs: &[u8]) -> [u8; 32] { + hash_public_inputs_with_fn(public_inputs, sp_io::hashing::sha2_256) +} + +pub fn hash_public_inputs_with_fn(public_inputs: &[u8], hasher: F) -> [u8; 32] +where + F: Fn(&[u8]) -> [u8; 32], +{ + let mut result = hasher(public_inputs); + + // The Plonk and Groth16 verifiers operate over a 254 bit field, so we need to zero + // out the first 3 bits. The same logic happens in the SP1 Ethereum verifier contract. + result[0] &= 0x1F; + + result +} + impl ProvidePostInherent for Pallet where [u8; 32]: From, diff --git a/pallets/vector/src/tests.rs b/pallets/vector/src/tests.rs index 03e6bb6fa..130976d01 100644 --- a/pallets/vector/src/tests.rs +++ b/pallets/vector/src/tests.rs @@ -1,3 +1,5 @@ +use std::{fs::File, io::BufReader}; + use crate::{ mock::{ new_test_ext, Balances, Bridge, RuntimeEvent, RuntimeOrigin, System, Test, @@ -7,9 +9,9 @@ use crate::{ storage_utils::MessageStatusEnum, Broadcasters, ConfigurationStorage, Error, Event, ExecutionStateRoots, FunctionIds, FunctionInput, FunctionOutput, FunctionProof, Head, Headers, MessageStatus, MockEnabled, - ProofOutputs, RotateVerificationKey, SP1VerificationKey, SourceChainFrozen, - StepVerificationKey, SyncCommitteeHashes, SyncCommitteePoseidons, Updater, ValidProof, - WhitelistedDomains, + PicoUpdater, PicoVerificationKey, ProofOutputs, RotateVerificationKey, SP1VerificationKey, + SourceChainFrozen, StepVerificationKey, SyncCommitteeHashes, SyncCommitteePoseidons, Updater, + ValidProof, WhitelistedDomains, }; use alloy_sol_types::SolValue; use avail_core::data_proof::Message::FungibleToken; @@ -22,6 +24,7 @@ use frame_support::{ use frame_system::RawOrigin; use hex_literal::hex; use primitive_types::U256; +use serde::{Deserialize, Serialize}; use sp1_sdk::SP1ProofWithPublicValues; use sp_core::{crypto::AccountId32, keccak_256, ByteArray}; use sp_runtime::{testing::H256, traits::BadOrigin}; @@ -30,11 +33,17 @@ const TEST_SENDER_VEC: [u8; 32] = hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"); const TEST_SENDER_ACCOUNT: AccountId32 = AccountId32::new(TEST_SENDER_VEC); +const TEST_PICO_SENDER_VEC: [u8; 32] = + hex!("c43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27c"); + // Generated with SP1 Helios https://github.com/succinctlabs/sp1-helios/blob/main/README.md // cargo prove key —-elf (sp1 helios elf) in SP1 Helios const SP1_VERIFICATION_KEY: [u8; 32] = hex!("003ef077b6a82831a994a12a673901221ca1752080605189930748d0772d5c68"); +const PICO_VERIFICATION_KEY: [u8; 32] = + hex!("0034c0b4c589330096b1141bccb7ba8f6f403f3d46b47c386b404ed9ebc8c5d3"); + pub const PROOF_FILE: &str = "test/proof.bin"; fn get_valid_step_input() -> FunctionInput { @@ -1413,7 +1422,122 @@ fn set_sp1_verification_key_non_root() { } #[test] -fn test_fulfill_successfully() { +fn test_pico_fulfill_successfully() { + fn from_hex<'de, D>(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + let s: String = Deserialize::deserialize(deserializer)?; + hex::decode(s.trim_start_matches("0x")).map_err(serde::de::Error::custom) + } + + fn from_hex2<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error> + where + D: serde::Deserializer<'de>, + { + let s: String = Deserialize::deserialize(deserializer)?; + + Ok(hex::decode(s.trim_start_matches("0x")) + .map_err(serde::de::Error::custom)? + .try_into() + .unwrap()) + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct Input { + pub proof: Vec, + #[serde(rename = "publicValues")] + #[serde(deserialize_with = "from_hex")] + pub public_values: Vec, + #[serde(rename = "riscvVKey")] + #[serde(deserialize_with = "from_hex")] + pub riscv_v_key: Vec, + } + + #[derive(Clone, Debug, Serialize, Deserialize)] + #[serde(transparent)] + pub struct Prooflet(#[serde(deserialize_with = "from_hex2")] pub [u8; 32]); + + new_test_ext().execute_with(|| { + let file = File::open("test/pico_inputs.json").unwrap(); + let reader = BufReader::new(file); + let input: Input = serde_json::from_reader(reader).unwrap(); + + PicoVerificationKey::::set(H256(PICO_VERIFICATION_KEY)); + let _proof_outputs: ProofOutputs = + SolValue::abi_decode(&input.public_values, true).unwrap(); + let slots_per_period = 8192; + let finality_threshold = 342u16; + let last_slot = 7689024u64; + let current_period = last_slot / slots_per_period; + Head::::set(last_slot); + + SyncCommitteeHashes::::set( + current_period, + H256(hex!( + "31512ff842552e59828654c07ac112264508ed9834a0651a783cbd6047a2379e" + )), + ); + + let new_head = 7689056u64; + + ConfigurationStorage::::set(Configuration { + slots_per_period, + finality_threshold, + }); + + PicoUpdater::::set(H256(TEST_PICO_SENDER_VEC)); + + let mut proofs: Vec = vec![]; + for p in input.proof { + proofs.extend(&p.0); + } + let proof = proofs; + + let origin = RuntimeOrigin::signed(TEST_PICO_SENDER_VEC.into()); + let ok = Bridge::fulfill( + origin, + BoundedVec::truncate_from(proof), + BoundedVec::truncate_from(input.public_values), + ); + + assert_ok!(ok); + + let header = Headers::::get(new_head); + assert_eq!( + H256(hex!( + "54b43230b8680d88900ee1a50a5fb98c370d4fb3f650820cdc1554e705c647d9" + )), + header + ); + let execution_state_root = ExecutionStateRoots::::get(new_head); + assert_eq!( + H256(hex!( + "122878128909b776d5eb3aae62052db5e436ae716f296d771468cb53e99fcee6" + )), + execution_state_root + ); + + let sync_committee_hash = SyncCommitteeHashes::::get(new_head / 8192); + assert_eq!( + H256(hex!( + "31512ff842552e59828654c07ac112264508ed9834a0651a783cbd6047a2379e" + )), + sync_committee_hash + ); + + let next_sync_committee_hash = SyncCommitteeHashes::::get((new_head / 8192) + 1); + assert_eq!( + H256(hex!( + "285a236728eb0f7de00632fb29c07165cfa17daa915de3464220b226b3a686b4" + )), + next_sync_committee_hash + ); + }); +} + +#[test] +fn test_sp1_fulfill_successfully() { new_test_ext().execute_with(|| { let sp1_proof_with_public_values = SP1ProofWithPublicValues::load(PROOF_FILE).unwrap(); let proof = sp1_proof_with_public_values.bytes(); diff --git a/pallets/vector/src/weights.rs b/pallets/vector/src/weights.rs index 11b33153a..617866689 100644 --- a/pallets/vector/src/weights.rs +++ b/pallets/vector/src/weights.rs @@ -66,7 +66,9 @@ pub trait WeightInfo { fn set_step_verification_key() -> Weight; fn set_rotate_verification_key() -> Weight; fn set_updater() -> Weight; + fn set_pico_updater() -> Weight; fn set_sp1_verification_key() -> Weight; + fn set_pico_verification_key() -> Weight; fn set_sync_committee_hash() -> Weight; fn fulfill() -> Weight; fn enable_mock() -> Weight; @@ -280,6 +282,17 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: `Vector::PicoUpdater` (r:1 w:1) + /// Proof: `Vector::PicoUpdater` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn set_pico_updater() -> Weight { + // Proof Size summary in bytes: + // Measured: `388` + // Estimated: `1517` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(15_000_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } /// Storage: `Vector::SP1VerificationKey` (r:1 w:1) /// Proof: `Vector::SP1VerificationKey` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) fn set_sp1_verification_key() -> Weight { @@ -291,6 +304,17 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: `Vector::PicoVerificationKey` (r:1 w:1) + /// Proof: `Vector::PicoVerificationKey` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn set_pico_verification_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `357` + // Estimated: `1517` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(8_000_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } /// Storage: `Vector::SyncCommitteeHashes` (r:0 w:1) /// Proof: `Vector::SyncCommitteeHashes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) fn set_sync_committee_hash() -> Weight { @@ -547,6 +571,17 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: `Vector::PicoUpdater` (r:1 w:1) + /// Proof: `Vector::PicoUpdater` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn set_pico_updater() -> Weight { + // Proof Size summary in bytes: + // Measured: `388` + // Estimated: `1517` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(15_000_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } /// Storage: `Vector::SP1VerificationKey` (r:1 w:1) /// Proof: `Vector::SP1VerificationKey` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) fn set_sp1_verification_key() -> Weight { @@ -558,6 +593,17 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: `Vector::PicoVerificationKey` (r:1 w:1) + /// Proof: `Vector::PicoVerificationKey` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn set_pico_verification_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `357` + // Estimated: `1517` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(8_000_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } /// Storage: `Vector::SyncCommitteeHashes` (r:0 w:1) /// Proof: `Vector::SyncCommitteeHashes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) fn set_sync_committee_hash() -> Weight { @@ -605,4 +651,4 @@ impl WeightInfo for () { Weight::from_parts(3_000_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } -} \ No newline at end of file +} diff --git a/pallets/vector/test/pico_inputs.json b/pallets/vector/test/pico_inputs.json new file mode 100644 index 000000000..739cd3414 --- /dev/null +++ b/pallets/vector/test/pico_inputs.json @@ -0,0 +1,14 @@ +{ + "proof": [ + "0x23c168ed21bb8fd15f9a3d5c4a481464d4848b656f20a8e17cb2f8c109679176", + "0x2da25678e1ace95720e2638365b2e9a75884b8a37d61802967d7fce74728e64d", + "0x070497e59728dd8e0953425755a85a29f38fc56e9f5935bfa05012173aede82d", + "0x144f09b331a5207d0a804bb9fd5f94961b2e8749d52c86a969a6f83467c9a226", + "0x1336d21b612faf2fda9dde7273c23f73d622e7eb9a907cc841b5c4bfc9eadb85", + "0x0e806c64ebe1c87f2e3c06693b92716468a85a760bcb6bde683f6aa8ff07abf6", + "0x032e4e63ec8d193626539beb419372b5fead7f926b0121c9dcb4ba18f5c4078b", + "0x25898599ae829c995cf5c14cb4abb73a5bfc893da24b4cced663a65eea057038" + ], + "publicValues": "0x122878128909b776d5eb3aae62052db5e436ae716f296d771468cb53e99fcee654b43230b8680d88900ee1a50a5fb98c370d4fb3f650820cdc1554e705c647d9285a236728eb0f7de00632fb29c07165cfa17daa915de3464220b226b3a686b40000000000000000000000000000000000000000000000000000000000755360c99253edab6fe41e79d49c32ccf9e3ea1c1f64c49bba0e841aee13966eeea146000000000000000000000000000000000000000000000000000000000075534031512ff842552e59828654c07ac112264508ed9834a0651a783cbd6047a2379e31512ff842552e59828654c07ac112264508ed9834a0651a783cbd6047a2379e", + "riscvVKey": "0x0034c0b4c589330096b1141bccb7ba8f6f403f3d46b47c386b404ed9ebc8c5d3" +} diff --git a/runtime/src/weights/pallet_vector.rs b/runtime/src/weights/pallet_vector.rs index 15943ec53..6c4cd1b1f 100644 --- a/runtime/src/weights/pallet_vector.rs +++ b/runtime/src/weights/pallet_vector.rs @@ -267,6 +267,18 @@ impl pallet_vector::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Vector::PicoUpdater` (r:1 w:1) + /// Proof: `Vector::PicoUpdater` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn set_pico_updater() -> Weight { + // Proof Size summary in bytes: + // Measured: `388` + // Estimated: `1517` + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(16_000_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } /// Storage: `Vector::SP1VerificationKey` (r:1 w:1) /// Proof: `Vector::SP1VerificationKey` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) fn set_sp1_verification_key() -> Weight { @@ -279,6 +291,18 @@ impl pallet_vector::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Vector::PicoVerificationKey` (r:1 w:1) + /// Proof: `Vector::PicoVerificationKey` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn set_pico_verification_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `357` + // Estimated: `1517` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(8_000_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } /// Storage: `Vector::SyncCommitteeHashes` (r:0 w:1) /// Proof: `Vector::SyncCommitteeHashes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) fn set_sync_committee_hash() -> Weight {