@@ -6,6 +6,7 @@ use super::*;
66use crate :: block_tree:: { prune_receipt, BlockTreeNode } ;
77use crate :: bundle_storage_fund:: refund_storage_fee;
88use crate :: domain_registry:: { into_domain_config, DomainConfigParams } ;
9+ use crate :: runtime_registry:: DomainRuntimeUpgradeEntry ;
910use crate :: staking:: {
1011 do_convert_previous_epoch_withdrawal, do_mark_operators_as_slashed, do_reward_operators,
1112 Error as StakingError , OperatorConfig , OperatorStatus , WithdrawStake ,
@@ -15,7 +16,8 @@ use crate::staking_epoch::{
1516 operator_take_reward_tax_and_stake,
1617} ;
1718use crate :: {
18- DomainBlockNumberFor , Pallet as Domains , RawOrigin as DomainOrigin , MAX_NOMINATORS_TO_SLASH ,
19+ DomainBlockNumberFor , ExecutionReceiptOf , Pallet as Domains , RawOrigin as DomainOrigin ,
20+ MAX_NOMINATORS_TO_SLASH ,
1921} ;
2022#[ cfg( not( feature = "std" ) ) ]
2123use alloc:: borrow:: ToOwned ;
@@ -26,15 +28,22 @@ use frame_support::assert_ok;
2628use frame_support:: traits:: fungible:: { Inspect , Mutate } ;
2729use frame_support:: traits:: Hooks ;
2830use frame_system:: { Pallet as System , RawOrigin } ;
31+ use hex_literal:: hex;
32+ use sp_consensus_slots:: Slot ;
2933use sp_core:: crypto:: { Ss58Codec , UncheckedFrom } ;
34+ use sp_core:: sr25519:: vrf:: { VrfPreOutput , VrfProof , VrfSignature } ;
35+ use sp_core:: H256 ;
36+ use sp_domains:: merkle_tree:: MerkleTree ;
3037use sp_domains:: {
31- dummy_opaque_bundle, DomainId , ExecutionReceipt , OperatorAllowList , OperatorId ,
32- OperatorPublicKey , OperatorRewardSource , OperatorSignature , PermissionedActionAllowedBy ,
33- ProofOfElection , RuntimeType , SealedSingletonReceipt , SingletonReceipt ,
38+ dummy_opaque_bundle, BundleHeader , DomainId , ExecutionReceipt , OpaqueBundle , OperatorAllowList ,
39+ OperatorId , OperatorPublicKey , OperatorRewardSource , OperatorSignature ,
40+ PermissionedActionAllowedBy , ProofOfElection , RuntimeType , SealedBundleHeader ,
41+ SealedSingletonReceipt , SingletonReceipt , EMPTY_EXTRINSIC_ROOT ,
3442} ;
3543use sp_domains_fraud_proof:: fraud_proof:: FraudProof ;
3644use sp_runtime:: traits:: { CheckedAdd , One , Zero } ;
3745use sp_std:: collections:: btree_set:: BTreeSet ;
46+ use subspace_core_primitives:: pot:: PotOutput ;
3847
3948const SEED : u32 = 0 ;
4049const MAX_NOMINATORS_TO_SLASH_WITHOUT_OPERATOR : u32 = MAX_NOMINATORS_TO_SLASH - 1 ;
@@ -894,6 +903,133 @@ mod benchmarks {
894903 assert_eq ! ( Domains :: <T >:: head_receipt_number( domain_id) , 1u32 . into( ) ) ;
895904 }
896905
906+ #[ benchmark]
907+ fn validate_submit_bundle ( ) {
908+ let domain_id = register_domain :: < T > ( ) ;
909+
910+ // Use `Alice` as signing key
911+ let signing_key =
912+ OperatorPublicKey :: from_ss58check ( "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" )
913+ . unwrap ( ) ;
914+ let ( _, operator_id) = register_operator_with_key :: < T > (
915+ domain_id,
916+ 1u32 ,
917+ signing_key,
918+ T :: MinNominatorStake :: get ( ) ,
919+ ) ;
920+ do_finalize_domain_current_epoch :: < T > ( domain_id)
921+ . expect ( "finalize domain staking should success" ) ;
922+
923+ let proof_of_election = mock_constant_proof_of_election ( domain_id, operator_id) ;
924+ let receipt = mock_constant_receipt :: < T > ( domain_id) ;
925+
926+ // Instead of inserting consensus hash into `ConsensusBlockHash` for the receipt check
927+ // insert `DomainRuntimeUpgradeRecords` to routed to a worse path
928+ DomainRuntimeUpgradeRecords :: < T > :: mutate (
929+ Domains :: < T > :: runtime_id ( domain_id) . unwrap ( ) ,
930+ |upgrade_record| {
931+ upgrade_record. insert (
932+ receipt. consensus_block_number ,
933+ DomainRuntimeUpgradeEntry {
934+ at_hash : receipt. consensus_block_hash ,
935+ reference_count : 1 ,
936+ } ,
937+ )
938+ } ,
939+ ) ;
940+
941+ let header = BundleHeader {
942+ proof_of_election,
943+ receipt,
944+ estimated_bundle_weight : Default :: default ( ) ,
945+ bundle_extrinsics_root : EMPTY_EXTRINSIC_ROOT . into ( ) ,
946+ } ;
947+
948+ // Hardcoded signature of the constant bundle header, signed by `Alice`
949+ // NOTE: we can't sign in no-std because it requires randomness
950+ let signature = OperatorSignature :: unchecked_from ( [
951+ 212 , 250 , 46 , 171 , 239 , 93 , 105 , 105 , 36 , 78 , 32 , 229 , 166 , 253 , 168 , 142 , 109 , 123 ,
952+ 213 , 159 , 210 , 106 , 192 , 62 , 54 , 82 , 64 , 64 , 19 , 27 , 136 , 33 , 19 , 241 , 58 , 116 , 252 ,
953+ 133 , 147 , 129 , 32 , 182 , 201 , 18 , 47 , 80 , 117 , 124 , 136 , 186 , 168 , 15 , 193 , 71 , 236 ,
954+ 201 , 155 , 176 , 188 , 254 , 114 , 173 , 96 , 134 ,
955+ ] ) ;
956+
957+ let opaque_bundle = OpaqueBundle {
958+ sealed_header : SealedBundleHeader :: new ( header, signature) ,
959+ extrinsics : Vec :: new ( ) ,
960+ } ;
961+
962+ #[ block]
963+ {
964+ assert_ok ! ( Domains :: <T >:: validate_submit_bundle( & opaque_bundle, true ) ) ;
965+ }
966+ }
967+
968+ #[ benchmark]
969+ fn validate_singleton_receipt ( ) {
970+ let domain_id = register_domain :: < T > ( ) ;
971+
972+ // Use `Alice` as signing key
973+ let signing_key =
974+ OperatorPublicKey :: from_ss58check ( "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" )
975+ . unwrap ( ) ;
976+ let ( _, operator_id) = register_operator_with_key :: < T > (
977+ domain_id,
978+ 1u32 ,
979+ signing_key,
980+ T :: MinNominatorStake :: get ( ) ,
981+ ) ;
982+ do_finalize_domain_current_epoch :: < T > ( domain_id)
983+ . expect ( "finalize domain staking should success" ) ;
984+
985+ let proof_of_election = mock_constant_proof_of_election ( domain_id, operator_id) ;
986+ let receipt = mock_constant_receipt :: < T > ( domain_id) ;
987+
988+ // Instead of inserting consensus hash into `ConsensusBlockHash` for the receipt check
989+ // insert `DomainRuntimeUpgradeRecords` to routed to a worse path
990+ DomainRuntimeUpgradeRecords :: < T > :: mutate (
991+ Domains :: < T > :: runtime_id ( domain_id) . unwrap ( ) ,
992+ |upgrade_record| {
993+ upgrade_record. insert (
994+ receipt. consensus_block_number ,
995+ DomainRuntimeUpgradeEntry {
996+ at_hash : receipt. consensus_block_hash ,
997+ reference_count : 1 ,
998+ } ,
999+ )
1000+ } ,
1001+ ) ;
1002+
1003+ let singleton_receipt: SingletonReceipt < _ , _ , T :: DomainHeader , _ > = SingletonReceipt {
1004+ proof_of_election,
1005+ receipt,
1006+ } ;
1007+
1008+ // Hardcoded signature of the constant singleton receipt, signed by `Alice`
1009+ // NOTE: we can't sign in no-std because it requires randomness
1010+ let signature = OperatorSignature :: unchecked_from ( [
1011+ 10 , 180 , 139 , 94 , 205 , 225 , 15 , 19 , 141 , 141 , 133 , 23 , 32 , 66 , 177 , 60 , 131 , 89 , 91 ,
1012+ 110 , 161 , 218 , 6 , 228 , 214 , 118 , 106 , 108 , 217 , 36 , 108 , 40 , 85 , 150 , 165 , 177 , 40 , 9 ,
1013+ 98 , 82 , 203 , 27 , 32 , 98 , 122 , 123 , 78 , 221 , 229 , 50 , 118 , 153 , 61 , 111 , 95 , 51 , 130 ,
1014+ 195 , 94 , 212 , 225 , 14 , 184 , 141 ,
1015+ ] ) ;
1016+
1017+ let sealed_singleton_receipt = SealedSingletonReceipt {
1018+ singleton_receipt,
1019+ signature,
1020+ } ;
1021+
1022+ HeadDomainNumber :: < T > :: set ( domain_id, 10u32 . into ( ) ) ;
1023+
1024+ #[ block]
1025+ {
1026+ assert_ok ! ( Domains :: <T >:: validate_singleton_receipt(
1027+ & sealed_singleton_receipt,
1028+ true
1029+ ) ) ;
1030+ }
1031+ }
1032+
8971033 fn register_runtime < T : Config > ( ) -> RuntimeId {
8981034 let genesis_storage = include_bytes ! ( "../res/evm-domain-genesis-storage" ) . to_vec ( ) ;
8991035 let runtime_id = NextRuntimeId :: < T > :: get ( ) ;
@@ -961,28 +1097,43 @@ mod benchmarks {
9611097 operator_seed : u32 ,
9621098 minimum_nominator_stake : BalanceOf < T > ,
9631099 ) -> ( T :: AccountId , OperatorId ) {
964- let operator_account = account ( "operator" , operator_seed, SEED ) ;
965- T :: Currency :: set_balance (
966- & operator_account,
967- T :: MinOperatorStake :: get ( ) + T :: MinNominatorStake :: get ( ) ,
968- ) ;
969-
9701100 let key = {
9711101 let mut k = [ 0u8 ; 32 ] ;
9721102 ( k[ ..4 ] ) . copy_from_slice ( & operator_seed. to_be_bytes ( ) [ ..] ) ;
9731103 k
9741104 } ;
1105+ let signing_key = OperatorPublicKey :: unchecked_from ( key) ;
1106+ register_operator_with_key :: < T > (
1107+ domain_id,
1108+ operator_seed,
1109+ signing_key,
1110+ minimum_nominator_stake,
1111+ )
1112+ }
1113+
1114+ fn register_operator_with_key < T : Config > (
1115+ domain_id : DomainId ,
1116+ operator_seed : u32 ,
1117+ signing_key : OperatorPublicKey ,
1118+ minimum_nominator_stake : BalanceOf < T > ,
1119+ ) -> ( T :: AccountId , OperatorId ) {
1120+ let operator_account = account ( "operator" , operator_seed, SEED ) ;
1121+ T :: Currency :: set_balance (
1122+ & operator_account,
1123+ T :: MinOperatorStake :: get ( ) * 100u32 . into ( ) + T :: MinNominatorStake :: get ( ) ,
1124+ ) ;
1125+
9751126 let operator_id = NextOperatorId :: < T > :: get ( ) ;
9761127 let operator_config = OperatorConfig {
977- signing_key : OperatorPublicKey :: unchecked_from ( key ) ,
1128+ signing_key,
9781129 minimum_nominator_stake,
9791130 nomination_tax : Default :: default ( ) ,
9801131 } ;
9811132
9821133 assert_ok ! ( crate :: do_register_operator:: <T >(
9831134 operator_account. clone( ) ,
9841135 domain_id,
985- T :: MinOperatorStake :: get( ) ,
1136+ T :: MinOperatorStake :: get( ) * 50u32 . into ( ) ,
9861137 operator_config. clone( ) ,
9871138 ) ) ;
9881139 assert_eq ! (
@@ -995,6 +1146,94 @@ mod benchmarks {
9951146 ( operator_account, operator_id)
9961147 }
9971148
1149+ // Return a mock `proof_of_election` which should be a constant value, otherwise,
1150+ // it won't match with the hardcoded signature
1151+ fn mock_constant_proof_of_election (
1152+ domain_id : DomainId ,
1153+ operator_id : OperatorId ,
1154+ ) -> ProofOfElection {
1155+ let ( proof_of_time, slot) = ( PotOutput :: default ( ) , Slot :: from ( 1 ) ) ;
1156+
1157+ // VRF signature generated by signing:
1158+ // ```
1159+ // let global_challenge = proof_of_time
1160+ // .derive_global_randomness()
1161+ // .derive_global_challenge(slot.into());
1162+ // let vrf_sign_data = make_transcript(domain_id, &global_challenge).into_sign_data();
1163+ // ```
1164+ // with the key `Alice`
1165+ let vrf_signature = VrfSignature {
1166+ pre_output : VrfPreOutput (
1167+ schnorrkel:: vrf:: VRFPreOut :: from_bytes ( & [
1168+ 248 , 47 , 99 , 253 , 224 , 36 , 127 , 251 , 30 , 132 , 220 , 112 , 51 , 251 , 195 , 246 , 140 ,
1169+ 97 , 153 , 49 , 166 , 36 , 114 , 142 , 73 , 214 , 185 , 156 , 2 , 142 , 180 , 57 ,
1170+ ] )
1171+ . unwrap ( ) ,
1172+ ) ,
1173+ proof : VrfProof (
1174+ schnorrkel:: vrf:: VRFProof :: from_bytes ( & [
1175+ 28 , 138 , 214 , 43 , 79 , 128 , 75 , 106 , 98 , 232 , 188 , 139 , 101 , 206 , 174 , 146 , 138 ,
1176+ 210 , 101 , 72 , 184 , 227 , 115 , 72 , 37 , 246 , 182 , 247 , 102 , 34 , 11 , 3 , 22 , 106 ,
1177+ 116 , 209 , 34 , 220 , 216 , 20 , 93 , 101 , 182 , 130 , 15 , 71 , 73 , 27 , 51 , 126 , 100 ,
1178+ 43 , 80 , 253 , 101 , 132 , 222 , 234 , 196 , 167 , 19 , 126 , 16 , 8 ,
1179+ ] )
1180+ . unwrap ( ) ,
1181+ ) ,
1182+ } ;
1183+ ProofOfElection {
1184+ domain_id,
1185+ slot_number : slot. into ( ) ,
1186+ proof_of_time,
1187+ vrf_signature,
1188+ operator_id,
1189+ }
1190+ }
1191+
1192+ // Return a mock `receipt` which should be a constant value, otherwise, it won't match
1193+ // with the hardcoded signature
1194+ fn mock_constant_receipt < T : Config > ( domain_id : DomainId ) -> ExecutionReceiptOf < T > {
1195+ // The genesis ER will changed as the runtime code changed, thus using a mock genesis
1196+ // ER hash to ensure the return ER is constant
1197+ let mock_genesis_er_hash = H256 :: from_slice (
1198+ hex ! ( "5207cc85cfd1f53e11f4b9e85bf2d0a4f33e24d0f0f18b818b935a6aa47d3930" ) . as_slice ( ) ,
1199+ ) ;
1200+ BlockTree :: < T > :: insert :: < _ , DomainBlockNumberFor < T > , <T as Config >:: DomainHash > (
1201+ domain_id,
1202+ Zero :: zero ( ) ,
1203+ mock_genesis_er_hash. into ( ) ,
1204+ ) ;
1205+
1206+ let trace: Vec < <T as Config >:: DomainHash > = vec ! [
1207+ H256 :: repeat_byte( 1 ) . into( ) ,
1208+ H256 :: repeat_byte( 2 ) . into( ) ,
1209+ H256 :: repeat_byte( 3 ) . into( ) ,
1210+ ] ;
1211+ let execution_trace_root = {
1212+ let trace: Vec < _ > = trace
1213+ . iter ( )
1214+ . map ( |t| t. encode ( ) . try_into ( ) . unwrap ( ) )
1215+ . collect ( ) ;
1216+ MerkleTree :: from_leaves ( trace. as_slice ( ) )
1217+ . root ( )
1218+ . unwrap ( )
1219+ . into ( )
1220+ } ;
1221+ ExecutionReceipt {
1222+ domain_block_number : One :: one ( ) ,
1223+ domain_block_hash : H256 :: repeat_byte ( 7 ) . into ( ) ,
1224+ domain_block_extrinsic_root : EMPTY_EXTRINSIC_ROOT . into ( ) ,
1225+ parent_domain_block_receipt_hash : mock_genesis_er_hash. into ( ) ,
1226+ consensus_block_number : One :: one ( ) ,
1227+ consensus_block_hash : H256 :: repeat_byte ( 9 ) . into ( ) ,
1228+ inboxed_bundles : vec ! [ ] ,
1229+ final_state_root : trace[ 2 ] ,
1230+ execution_trace : trace,
1231+ execution_trace_root,
1232+ block_fees : Default :: default ( ) ,
1233+ transfers : Default :: default ( ) ,
1234+ }
1235+ }
1236+
9981237 fn run_to_block < T : Config > ( block_number : BlockNumberFor < T > , parent_hash : T :: Hash ) {
9991238 if let Some ( parent_block_number) = block_number. checked_sub ( & One :: one ( ) ) {
10001239 Domains :: < T > :: on_finalize ( parent_block_number) ;
0 commit comments