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