@@ -6,10 +6,10 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface
66import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol " ;
77
88import {ReferenceModularAccount} from "../../src/account/ReferenceModularAccount.sol " ;
9-
109import {HookConfigLib} from "../../src/helpers/HookConfigLib.sol " ;
1110import {ModuleEntity, ModuleEntityLib} from "../../src/helpers/ModuleEntityLib.sol " ;
1211import {SparseCalldataSegmentLib} from "../../src/helpers/SparseCalldataSegmentLib.sol " ;
12+ import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol " ;
1313
1414import {Counter} from "../mocks/Counter.sol " ;
1515import {MockAccessControlHookModule} from "../mocks/modules/MockAccessControlHookModule.sol " ;
@@ -22,6 +22,10 @@ contract PerHookDataTest is CustomValidationTestBase {
2222
2323 Counter internal _counter;
2424
25+ uint32 internal constant _VALIDATION_ENTITY_ID = 0 ;
26+ uint32 internal constant _PRE_HOOK_ENTITY_ID_1 = 0 ;
27+ uint32 internal constant _PRE_HOOK_ENTITY_ID_2 = 1 ;
28+
2529 function setUp () public {
2630 _counter = new Counter ();
2731
@@ -127,7 +131,58 @@ contract PerHookDataTest is CustomValidationTestBase {
127131 entryPoint.handleOps (userOps, beneficiary);
128132 }
129133
130- // todo: index out of order failure case with 2 pre hooks
134+ function test_passAccessControl_twoHooks_userOp () public {
135+ _installSecondPreHook ();
136+
137+ assertEq (_counter.number (), 0 );
138+
139+ (PackedUserOperation memory userOp , bytes32 userOpHash ) = _getCounterUserOP ();
140+
141+ (uint8 v , bytes32 r , bytes32 s ) = vm.sign (owner1Key, userOpHash.toEthSignedMessageHash ());
142+
143+ PreValidationHookData[] memory preValidationHookData = new PreValidationHookData [](2 );
144+ preValidationHookData[0 ] = PreValidationHookData ({index: 0 , validationData: abi.encodePacked (_counter)});
145+ preValidationHookData[1 ] = PreValidationHookData ({index: 1 , validationData: abi.encodePacked (_counter)});
146+
147+ userOp.signature = _encodeSignature (
148+ _signerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked (r, s, v)
149+ );
150+
151+ PackedUserOperation[] memory userOps = new PackedUserOperation [](1 );
152+ userOps[0 ] = userOp;
153+
154+ entryPoint.handleOps (userOps, beneficiary);
155+
156+ assertEq (_counter.number (), 1 );
157+ }
158+
159+ function test_failAccessControl_indexOutOfOrder_userOp () public {
160+ _installSecondPreHook ();
161+
162+ (PackedUserOperation memory userOp , bytes32 userOpHash ) = _getCounterUserOP ();
163+ (uint8 v , bytes32 r , bytes32 s ) = vm.sign (owner1Key, userOpHash.toEthSignedMessageHash ());
164+
165+ PreValidationHookData[] memory preValidationHookData = new PreValidationHookData [](3 );
166+ preValidationHookData[0 ] = PreValidationHookData ({index: 0 , validationData: abi.encodePacked (_counter)});
167+ preValidationHookData[1 ] = PreValidationHookData ({index: 0 , validationData: abi.encodePacked (_counter)});
168+
169+ userOp.signature = _encodeSignature (
170+ _signerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked (r, s, v)
171+ );
172+
173+ PackedUserOperation[] memory userOps = new PackedUserOperation [](1 );
174+ userOps[0 ] = userOp;
175+
176+ vm.expectRevert (
177+ abi.encodeWithSelector (
178+ IEntryPoint.FailedOpWithRevert.selector ,
179+ 0 ,
180+ "AA23 reverted " ,
181+ abi.encodeWithSelector (SparseCalldataSegmentLib.SegmentOutOfOrder.selector )
182+ )
183+ );
184+ entryPoint.handleOps (userOps, beneficiary);
185+ }
131186
132187 function test_failAccessControl_badTarget_userOp () public {
133188 PackedUserOperation memory userOp = PackedUserOperation ({
@@ -248,7 +303,7 @@ contract PerHookDataTest is CustomValidationTestBase {
248303 abi.encodeWithSelector (
249304 ReferenceModularAccount.PreRuntimeValidationHookFailed.selector ,
250305 _accessControlHookModule,
251- uint32 (MockAccessControlHookModule.EntityId.PRE_VALIDATION_HOOK) ,
306+ _PRE_HOOK_ENTITY_ID_1 ,
252307 abi.encodeWithSignature ("Error(string) " , "Proof doesn't match target " )
253308 )
254309 );
@@ -266,7 +321,7 @@ contract PerHookDataTest is CustomValidationTestBase {
266321 abi.encodeWithSelector (
267322 ReferenceModularAccount.PreRuntimeValidationHookFailed.selector ,
268323 _accessControlHookModule,
269- uint32 (MockAccessControlHookModule.EntityId.PRE_VALIDATION_HOOK) ,
324+ _PRE_HOOK_ENTITY_ID_1 ,
270325 abi.encodeWithSignature ("Error(string) " , "Proof doesn't match target " )
271326 )
272327 );
@@ -295,7 +350,42 @@ contract PerHookDataTest is CustomValidationTestBase {
295350 );
296351 }
297352
298- //todo: index out of order failure case with 2 pre hooks
353+ function test_passAccessControl_twoHooks_runtime () public {
354+ _installSecondPreHook ();
355+
356+ assertEq (_counter.number (), 0 );
357+
358+ PreValidationHookData[] memory preValidationHookData = new PreValidationHookData [](2 );
359+ preValidationHookData[0 ] = PreValidationHookData ({index: 0 , validationData: abi.encodePacked (_counter)});
360+ preValidationHookData[1 ] = PreValidationHookData ({index: 1 , validationData: abi.encodePacked (_counter)});
361+
362+ vm.prank (owner1);
363+ account1.executeWithAuthorization (
364+ abi.encodeCall (
365+ ReferenceModularAccount.execute, (address (_counter), 0 wei, abi.encodeCall (Counter.increment, ()))
366+ ),
367+ _encodeSignature (_signerValidation, GLOBAL_VALIDATION, preValidationHookData, "" )
368+ );
369+
370+ assertEq (_counter.number (), 1 );
371+ }
372+
373+ function test_failAccessControl_indexOutOfOrder_runtime () public {
374+ _installSecondPreHook ();
375+
376+ PreValidationHookData[] memory preValidationHookData = new PreValidationHookData [](3 );
377+ preValidationHookData[0 ] = PreValidationHookData ({index: 0 , validationData: abi.encodePacked (_counter)});
378+ preValidationHookData[1 ] = PreValidationHookData ({index: 0 , validationData: abi.encodePacked (_counter)});
379+
380+ vm.prank (owner1);
381+ vm.expectRevert (abi.encodeWithSelector (SparseCalldataSegmentLib.SegmentOutOfOrder.selector ));
382+ account1.executeWithAuthorization (
383+ abi.encodeCall (
384+ ReferenceModularAccount.execute, (address (_counter), 0 wei, abi.encodeCall (Counter.increment, ()))
385+ ),
386+ _encodeSignature (_signerValidation, GLOBAL_VALIDATION, preValidationHookData, "" )
387+ );
388+ }
299389
300390 function test_failAccessControl_badTarget_runtime () public {
301391 PreValidationHookData[] memory preValidationHookData = new PreValidationHookData [](1 );
@@ -306,7 +396,7 @@ contract PerHookDataTest is CustomValidationTestBase {
306396 abi.encodeWithSelector (
307397 ReferenceModularAccount.PreRuntimeValidationHookFailed.selector ,
308398 _accessControlHookModule,
309- uint32 (MockAccessControlHookModule.EntityId.PRE_VALIDATION_HOOK) ,
399+ _PRE_HOOK_ENTITY_ID_1 ,
310400 abi.encodeWithSignature ("Error(string) " , "Target not allowed " )
311401 )
312402 );
@@ -394,6 +484,19 @@ contract PerHookDataTest is CustomValidationTestBase {
394484 account1.isValidSignature (messageHash, _encode1271Signature (_signerValidation, abi.encodePacked (r, s, v)));
395485 }
396486
487+ function _installSecondPreHook () internal {
488+ // depends on the ability of `installValidation` to append hooks
489+ bytes [] memory hooks = new bytes [](1 );
490+ hooks[0 ] = abi.encodePacked (
491+ HookConfigLib.packValidationHook (address (_accessControlHookModule), _PRE_HOOK_ENTITY_ID_2),
492+ abi.encode (_PRE_HOOK_ENTITY_ID_2, _counter)
493+ );
494+ vm.prank (address (entryPoint));
495+ account1.installValidation (
496+ ValidationConfigLib.pack (_signerValidation, true , false ), new bytes4 [](0 ), "" , hooks
497+ );
498+ }
499+
397500 function _getCounterUserOP () internal view returns (PackedUserOperation memory , bytes32 ) {
398501 PackedUserOperation memory userOp = PackedUserOperation ({
399502 sender: address (account1),
@@ -424,13 +527,11 @@ contract PerHookDataTest is CustomValidationTestBase {
424527 {
425528 bytes [] memory hooks = new bytes [](1 );
426529 hooks[0 ] = abi.encodePacked (
427- HookConfigLib.packValidationHook (
428- address (_accessControlHookModule), uint32 (MockAccessControlHookModule.EntityId.PRE_VALIDATION_HOOK)
429- ),
430- abi.encode (_counter)
530+ HookConfigLib.packValidationHook (address (_accessControlHookModule), _PRE_HOOK_ENTITY_ID_1),
531+ abi.encode (_PRE_HOOK_ENTITY_ID_1, _counter)
431532 );
432533 // patched to also work during SMA tests by differentiating the validation
433- _signerValidation = ModuleEntityLib.pack (address (singleSignerValidationModule), type ( uint32 ).max - 1 );
434- return (_signerValidation, true , true , new bytes4 [](0 ), abi.encode (type ( uint32 ).max - 1 , owner1), hooks);
534+ _signerValidation = ModuleEntityLib.pack (address (singleSignerValidationModule), _VALIDATION_ENTITY_ID );
535+ return (_signerValidation, true , true , new bytes4 [](0 ), abi.encode (_VALIDATION_ENTITY_ID , owner1), hooks);
435536 }
436537}
0 commit comments