#![cfg(feature = "runtime-benchmarks")] use super::*; use frame_benchmarking::v2::*; use frame_system::RawOrigin; use ghost_helpers::networks::NetworkDataBuilder; fn prepare_pallet(authorities_len: usize) -> ( PendingDkgAuthorities, BlockNumberFor>, NetworkCurve, T::AuthorityId, AuthIndex ) { let network_curve = NetworkCurve::Secp256k1; let authority = T::AuthorityId::generate_pair(None); let authorities = vec![authority.clone(); authorities_len]; let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone()) .expect("more than the maximum number of keys provided"); QualificationAuthorities::::insert(network_curve, bounded_authorities); let network_id = NetworkIdOf::::default(); let network_data = NetworkDataBuilder::default() .with_network_type(NetworkType::Evm) .with_network_curve(NetworkCurve::Secp256k1) .build(); T::NetworkDataHandler::register(network_id, network_data) .expect("network should be valid for insertion"); let mut dkg_state = PendingDkgAuthorities::default(); dkg_state.new_dkg(authorities_len); (dkg_state, network_curve, authority, 0) } #[benchmarks(where T: Config)] mod benchmarks { use super::*; #[benchmark] fn register_round0_package() -> Result<(), BenchmarkError> { let dummy_hash = ExodusHash::repeat_byte(69); let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(1); dkg_state.next_phase(); QualificationDkgState::::insert(&network_curve, dkg_state); let round0_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(0) .with_dkg_round0(dummy_hash); let dkg_package = DkgPackage::Round0(round0_package); let signature = authority .sign(&dkg_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, dkg_package, signature); let expected_hash = Round0Packages::::get(&network_curve, authority_index); assert_eq!(expected_hash, dummy_hash); Ok(()) } #[benchmark] fn register_round1_package( c: Linear<4, { T::MaxAuthorities::get() }> ) -> Result<(), BenchmarkError> { let max_signers = c as AuthIndex; let min_signers = get_byzantium_threshold(max_signers); let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(max_signers as usize); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.insert_index(authority_index); QualificationDkgState::::insert(&network_curve, dkg_state); let mut rng = Pallet::::get_offchain_rng(authority_index); let (_, public_package) = network_curve.dkg_part1( authority_index, max_signers, min_signers, &mut rng, ).expect("DKG part1 should work"); let package_hash = ExodusHash::from(blake2_256(&public_package)); Round0Packages::::insert(network_curve, authority_index, package_hash); let round1_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(0) .with_dkg_round1(public_package) .expect("Round1 package should be valid"); let dkg_package = DkgPackage::Round1(round1_package); let signature = authority .sign(&dkg_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, dkg_package, signature); let participants = Round1Packages::::get(&network_curve); assert!(participants.contains(authority_index)); Ok(()) } #[benchmark] fn register_encrypted_round2_packages( c: Linear<1, { T::MaxAuthorities::get() / 8 }> ) -> Result<(), BenchmarkError> { let max_signers = (c * 8) as AuthIndex; let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(max_signers as usize); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); (0..max_signers).for_each(|i| { dkg_state.insert_index(i); }); let padded_participants = dkg_state .highest_bit::() .map(|bit_pos| bit_pos.saturating_add(1)) .unwrap_or_default(); QualificationDkgState::::insert(&network_curve, dkg_state); let bitmask_len = padded_participants.div_ceil(8); let blob_len = round2_encrypted_package_size( padded_participants, network_curve.scalar_bytes_len(), network_curve.header_bytes_len(), ); let mut bitmask = vec![0u8; bitmask_len]; let blob = vec![0u8; blob_len]; (0..max_signers).for_each(|i| { if i != authority_index { let byte_index = (i / 8) as usize; let bit_index = (i % 8) as u8; bitmask[byte_index] |= 1 << bit_index; } }); let encryption_bundle = BundledPackages { bitmask, blob }; let round2_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_round2(encryption_bundle) .expect("Round2 package should be valid"); let dkg_package = DkgPackage::Round2(round2_package); let signature = authority .sign(&dkg_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, dkg_package, signature); let participants = Round2Packages::::get(&network_curve); assert!(participants.contains(authority_index)); Ok(()) } #[benchmark] fn register_round3_complaints( c: Linear<1, { T::MaxAuthorities::get() / 8 }> ) -> Result<(), BenchmarkError> { let max_signers = (c * 8) as AuthIndex; let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(max_signers as usize); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); (0..max_signers).for_each(|i| { dkg_state.insert_index(i); }); QualificationDkgState::::insert(&network_curve, dkg_state); let bitvec_len = (max_signers + 7) / 8; let mut accused_indexes = vec![0u8; bitvec_len as usize]; (0..max_signers).for_each(|i| { if i != authority_index { let byte_index = (i / 8) as usize; let bit_index = (i % 8) as u8; accused_indexes[byte_index] |= 1 << bit_index; } }); let round3_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_round3(accused_indexes) .expect("Round3 package should be valid"); let dkg_package = DkgPackage::Round3(round3_package); let signature = authority .sign(&dkg_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, dkg_package, signature); let complaints = Complaints::::get(&network_curve); let my_complaints = complaints.get(&authority_index) .expect("Comlpaints should be registered"); assert_eq!(my_complaints.count_ones::(), max_signers - 1); Ok(()) } #[benchmark] fn register_round4_justifications( c: Linear<1, { T::MaxAuthorities::get() / 8 }> ) -> Result<(), BenchmarkError> { let max_signers = (c * 8) as AuthIndex; let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(max_signers as usize); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); (0..max_signers).for_each(|i| { dkg_state.insert_index(i); }); let padded_participants = dkg_state .highest_bit::() .map(|bit_pos| bit_pos.saturating_add(1)) .unwrap_or_default(); QualificationDkgState::::insert(&network_curve, dkg_state); let bitmask_len = padded_participants.div_ceil(8); let blob_len = round2_package_size( padded_participants, network_curve.scalar_bytes_len(), network_curve.header_bytes_len(), ); let mut bitmask = vec![0u8; bitmask_len]; let blob = vec![0u8; blob_len]; (0..max_signers).for_each(|i| { if i != authority_index { let byte_index = (i / 8) as usize; let bit_index = (i % 8) as u8; bitmask[byte_index] |= 1 << bit_index; } }); let justification_bundle = BundledPackages { bitmask, blob }; let round4_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_round4(justification_bundle) .expect("Round4 package should be valid"); let dkg_package = DkgPackage::Round4(round4_package); let signature = authority .sign(&dkg_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, dkg_package, signature); let justifications = Justifications::::get(&network_curve); let my_justifications = justifications.get(&authority_index) .expect("Justifications should be registered"); assert_eq!(my_justifications.count_ones::(), max_signers - 1); Ok(()) } #[benchmark] fn register_round5_verifying_package() -> Result<(), BenchmarkError> { let max_signers = 4 as AuthIndex; let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(max_signers as usize); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); (0..max_signers).for_each(|i| { dkg_state.insert_index(i); }); let dkg_index = dkg_state.get_dkg_index(); QualificationDkgState::::insert(&network_curve, dkg_state); let dummy_merkle_root = ExodusHash::repeat_byte(69); let dummy_verifying_key = vec![69u8; network_curve.element_bytes_len()]; let round5_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_round5(dummy_verifying_key.clone(), dummy_merkle_root) .expect("Round5 package should be valid"); let dkg_package = DkgPackage::Round5(round5_package); let signature = authority .sign(&dkg_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, dkg_package, signature); let bounded_verifying_key = BoundedVec::>::try_from( dummy_verifying_key, ).expect("Bounded verifying key should be correct"); let metadata_hashes = [ SubstrateBlake2Hasher::hash(dummy_merkle_root.as_ref()), SubstrateBlake2Hasher::hash(bounded_verifying_key.as_ref()), ]; let verifying_hash = sequential_hash::(metadata_hashes); let verification_key = (network_curve, dkg_index, verifying_hash); let participants = Verifications::::get(verification_key); assert!(participants.contains(authority_index)); let consensus = VerifyingKeyConsensus::::get(&network_curve, dkg_index); assert!(consensus.participants.contains(authority_index)); assert_eq!(consensus.participants.count_ones::(), 1); assert_eq!(consensus.merkle_root, dummy_merkle_root); assert_eq!(consensus.element_bytes, bounded_verifying_key); Ok(()) } #[benchmark] fn register_round6_public_share_package( c: Linear<1, { T::MaxAuthorities::get().next_power_of_two().trailing_zeros() }> ) -> Result<(), BenchmarkError> { let computed_max_signers = (1u32 << c) as AuthIndex; let constant_max_signers = T::MaxAuthorities::get() as AuthIndex; let max_signers = sp_std::cmp::min(computed_max_signers, constant_max_signers); let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(max_signers as usize); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); dkg_state.next_phase(); (0..max_signers).for_each(|i| { dkg_state.insert_index(i); }); let dkg_index = dkg_state.get_dkg_index(); QualificationDkgState::::insert(&network_curve, dkg_state); let dummy_value = vec![69u8; network_curve.element_bytes_len()]; let (dummy_merkle_root, dummy_verifying_share, dummy_merkle_proof) = with_ciphersuite!(network_curve, |f| { let mut dummy_values = BTreeMap::new(); for i in 0..max_signers { let non_zero_index = i.saturating_add(1); let identifier: frost_core::Identifier = frost_core::Identifier::::try_from(non_zero_index) .expect("Identifier should be created"); dummy_values.insert(identifier, dummy_value.clone()); } let merkle_tree = NetworkCurve::build_merkle_tree( &dummy_values, |value: &Vec| Ok(value.clone()), ).expect("Merkle tree for verifying shares should be valid"); let merkle_root = merkle_tree.last().copied().unwrap(); let (verifying_share, merkle_proof) = NetworkCurve::generate_merkle_proof_from_tree( &dummy_values, &merkle_tree, authority_index, |value: &Vec| Ok(value.clone()) ).expect("Merkle tree proof should be valid"); (merkle_root, verifying_share, merkle_proof) }); VerifyingKeyConsensus::::mutate(&network_curve, &dkg_index, |consensus| { consensus.participants.insert(authority_index); consensus.merkle_root = dummy_merkle_root; }); let round6_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_round6(dummy_verifying_share.clone(), dummy_merkle_proof) .expect("Round6 package should be valid"); let dkg_package = DkgPackage::Round6(round6_package); let signature = authority .sign(&dkg_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, dkg_package, signature); let participants = VerifyingSharesParticipants::::get(&network_curve, dkg_index); assert!(participants.contains(authority_index)); let bundled_verifying_share = BoundedVec::>::try_from( dummy_verifying_share, ).expect("Verifying share should be bundled"); let verifying_share_key = (network_curve, dkg_index, authority_index); assert_eq!( VerifyingShares::::get(verifying_share_key).unwrap(), bundled_verifying_share, ); Ok(()) } #[benchmark] fn register_nonce_commitment() -> Result<(), BenchmarkError> { let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(1); loop { if !dkg_state.is_dkg_pending() { break; } dkg_state.next_phase(); } dkg_state.insert_index(authority_index); let exodus_session = CurrentExodus::::get(); let dummy_bytes = EvmBytes32::repeat_byte(69); let dummy_nonce = vec![0u8; network_curve.element_bytes_len()]; let request = ExodusRequest::evm_rotation(exodus_session, dummy_bytes, 0); let authorities = QualificationAuthorities::::get(&network_curve); let active_dkg: ActivatedState = (&dkg_state).into(); QualificationDkgState::::insert(&network_curve, dkg_state); ActiveDkgAuthorities::::insert(&network_curve, active_dkg); ActiveAuthorities::::insert(&network_curve, authorities); ExodusRequests::::insert(network_curve, exodus_session, request); let nonce_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_exodus_nonce_commitment( exodus_session, dummy_nonce.clone(), dummy_nonce.clone(), ) .expect("Nonce commitment package should be valid"); let exodus_package = ExodusPackage::NonceCommitment(nonce_package); let signature = authority .sign(&exodus_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, exodus_package, signature); let roast_session = RoastSessions::::get(&exodus_session, authority_index) .expect("ROAST session should exist after nonce commitment"); let roast_state = RoastSessionStates::::get(&exodus_session, roast_session); assert!(roast_state.nonce_committers.contains(authority_index)); assert!(!roast_state.group_committers.contains(authority_index)); assert!(!roast_state.partial_signers.contains(authority_index)); let registered_nonces = NonceCommitments::::get( (exodus_session, roast_session), authority_index, ).expect("Nonces should be registered during nonce commitment"); assert_eq!(registered_nonces.hiding, dummy_nonce.clone()); assert_eq!(registered_nonces.binding, dummy_nonce); Ok(()) } #[benchmark] fn register_group_commitment() -> Result<(), BenchmarkError> { let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(1); loop { if !dkg_state.is_dkg_pending() { break; } dkg_state.next_phase(); } dkg_state.insert_index(authority_index); let exodus_session = CurrentExodus::::get(); let dummy_bytes = EvmBytes32::repeat_byte(69); let dummy_merkle_root = ExodusHash::repeat_byte(69); let dummy_group_commitment = vec![0u8; network_curve.element_bytes_len()]; let request = ExodusRequest::evm_rotation(exodus_session, dummy_bytes, 0); let authorities = QualificationAuthorities::::get(&network_curve); let active_dkg: ActivatedState = (&dkg_state).into(); QualificationDkgState::::insert(&network_curve, dkg_state); ActiveDkgAuthorities::::insert(&network_curve, active_dkg); ActiveAuthorities::::insert(&network_curve, authorities); ExodusRequests::::insert(network_curve, exodus_session, request); let mut roast_state = RoastSessionState::>::default(); roast_state.status = RoastStatus::GroupCommitments; roast_state.nonce_committers.insert(authority_index); let roast_session = 3; RoastSessions::::insert(&exodus_session, authority_index, roast_session); RoastSessionStates::::insert(exodus_session, roast_session, roast_state); let group_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_exodus_group_commitment( exodus_session, dummy_group_commitment.clone(), dummy_bytes, ) .expect("Group commitment package should be valid"); let exodus_package = ExodusPackage::GroupCommitment(group_package); let signature = authority .sign(&exodus_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, exodus_package, signature); let bounded_group_commitment = BoundedVec::>::try_from( dummy_group_commitment, ).expect("Group commtiment should be valid"); let roast_state = RoastSessionStates::::get(&exodus_session, roast_session); assert!(roast_state.nonce_committers.contains(authority_index)); assert!(roast_state.group_committers.contains(authority_index)); let consensus = GroupCommitmentsConsensus::::get(exodus_session, roast_session); assert!(consensus.participants.contains(authority_index)); assert_eq!(&consensus.element_bytes, &bounded_group_commitment); assert_eq!(consensus.merkle_root, dummy_merkle_root); let group_commitment_key = (exodus_session, roast_session, dummy_merkle_root, bounded_group_commitment); let participants = GroupCommitters::::get(group_commitment_key); assert!(participants.contains(authority_index)); Ok(()) } #[benchmark] fn register_signature_share() -> Result<(), BenchmarkError> { let max_signers = T::MaxAuthorities::get(); let threshold = get_byzantium_threshold(max_signers); let (mut dkg_state, network_curve, authority, authority_index) = prepare_pallet::(max_signers as usize); loop { if !dkg_state.is_dkg_pending() { break; } dkg_state.next_phase(); } dkg_state.insert_index(authority_index); let dkg_index = dkg_state.get_dkg_index(); let exodus_session = CurrentExodus::::get(); let mut dummy_element = vec![0u8; network_curve.element_bytes_len()]; dummy_element[0] = 0x02; let g_x_coords = [ 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98 ]; dummy_element[1..33].copy_from_slice(&g_x_coords); let dummy_bytes = EvmBytes32::repeat_byte(69); let request = ExodusRequest::evm_rotation(exodus_session, dummy_bytes, 0); let mut dummy_scalar = vec![0u8; network_curve.scalar_bytes_len()]; dummy_scalar[network_curve.scalar_bytes_len() - 1] = 1; let dummy_signature_share = dummy_scalar.clone(); let dummy_binding_factor = dummy_scalar.clone(); let dummy_group_commitment = dummy_element.clone(); let (dummy_merkle_root, dummy_merkle_proof) = with_ciphersuite!(network_curve, |f| { let mut dummy_values = BTreeMap::new(); for i in 0..threshold { let non_zero_index = (i as AuthIndex).saturating_add(1); let identifier: frost_core::Identifier = frost_core::Identifier::::try_from(non_zero_index) .expect("Identifier should be created"); dummy_values.insert(identifier, dummy_binding_factor.clone()); } let merkle_tree = NetworkCurve::build_merkle_tree( &dummy_values, |value: &Vec| Ok(value.clone()), ).expect("Merkle tree for binding factor should be valid"); let merkle_root = merkle_tree.last().copied().unwrap(); let (_, merkle_proof) = NetworkCurve::generate_merkle_proof_from_tree( &dummy_values, &merkle_tree, authority_index, |value: &Vec| Ok(value.clone()) ).expect("Merkle tree proof should be valid"); (merkle_root, merkle_proof) }); let authorities = QualificationAuthorities::::get(&network_curve); let active_dkg: ActivatedState = (&dkg_state).into(); let active_dkg_index = active_dkg.get_dkg_index(); QualificationDkgState::::insert(&network_curve, dkg_state); ActiveDkgAuthorities::::insert(&network_curve, active_dkg); ActiveAuthorities::::insert(&network_curve, authorities); ExodusRequests::::insert(network_curve, exodus_session, request); let mut roast_state = RoastSessionState::>::default(); roast_state.status = RoastStatus::PartialSignatures; roast_state.nonce_committers.insert(authority_index); roast_state.group_committers.insert(authority_index); (0..threshold).for_each(|i| { if i as AuthIndex == authority_index { return; } roast_state.partial_signers.insert(i); }); let roast_session = 3; RoastSessions::::insert(&exodus_session, authority_index, roast_session); RoastSessionStates::::insert(exodus_session, roast_session, roast_state); let bounded_group_commitment = BoundedVec::try_from(dummy_group_commitment).unwrap(); let dummy_verifying_key = BoundedVec::try_from(dummy_element.clone()).unwrap(); let dummy_verifying_share = BoundedVec::try_from(dummy_element.clone()).unwrap(); let dummy_nonce = ExodusSeparatedNonce::new( BoundedVec::try_from(dummy_element.clone()).unwrap(), BoundedVec::try_from(dummy_element.clone()).unwrap(), ); ActiveVerifyingKey::::insert(&network_curve, dummy_verifying_key); VerifyingShares::::insert((&network_curve, active_dkg_index, authority_index), dummy_verifying_share); NonceCommitments::::insert((&exodus_session, &roast_session), authority_index, dummy_nonce); GroupCommitmentsConsensus::::mutate(&exodus_session, &roast_session, |consensus| { consensus.element_bytes = bounded_group_commitment.clone(); consensus.merkle_root = dummy_merkle_root; (0..threshold).for_each(|i| { consensus.participants.insert(i); }); }); let share_package = PackageContext::default() .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_exodus_signature_share( exodus_session, dummy_signature_share.clone(), dummy_merkle_proof, dummy_binding_factor ) .expect("Signature share package should be valid"); let exodus_package = ExodusPackage::SignatureShare(share_package); let signature = authority .sign(&exodus_package.encode()) .expect("Signature should be created"); #[extrinsic_call] _(RawOrigin::None, exodus_package, signature); let maybe_roast_session = RoastSessions::::get(&exodus_session, authority_index); assert!(maybe_roast_session.is_none()); let scalar_exists = SignatureScalars::::contains_key(&exodus_session, &roast_session); let consensus_exists = GroupCommitmentsConsensus::::contains_key(&exodus_session, &roast_session); let committers_exists = GroupCommitters::::contains_key(( &exodus_session, &roast_session, &dummy_merkle_root, &bounded_group_commitment, )); assert!(!scalar_exists); assert!(!consensus_exists); assert!(!committers_exists); assert!(!ExodusRequests::::contains_key(&network_curve, &exodus_session)); assert!(ExodusSignedRotations::::contains_key((&exodus_session, NetworkType::Evm), dkg_index)); Ok(()) } impl_benchmark_test_suite!( Pallet, crate::mock::new_test_ext_benchmarks(), crate::mock::Runtime ); }