@@ -49,12 +49,18 @@ void txSkipTest(
4949 Bytes callData ,
5050 boolean provideAccessList ,
5151 DominantCost dominantCostPrediction ,
52+ boolean imposeSenderRecipientCollision ,
5253 TestInfo testInfo ) {
5354 BytecodeRunner bytecodeRunner = BytecodeRunner .of (Bytes .EMPTY );
5455 AccessListEntry accessListEntry =
5556 new AccessListEntry (Address .fromHexString ("0xABCD" ), List .of ());
5657 List <AccessListEntry > accessList = provideAccessList ? List .of (accessListEntry ) : List .of ();
57- bytecodeRunner .run (callData , accessList , chainConfig , testInfo );
58+ if (!imposeSenderRecipientCollision ) {
59+ bytecodeRunner .run (callData , accessList , chainConfig , testInfo );
60+ } else {
61+ bytecodeRunner .runWithImposedSenderRecipientAddressCollision (
62+ callData , accessList , chainConfig , testInfo );
63+ }
5864 // Test blocks contain 4 transactions: 2 system transactions, 1 user transaction (the one we
5965 // created) and 1 noop transaction.
6066 if (isPostPrague (fork )) {
@@ -71,14 +77,20 @@ void trivialCalleeTest(
7177 Bytes callData ,
7278 boolean provideAccessList ,
7379 DominantCost dominantCostPrediction ,
80+ boolean imposeSenderRecipientCollision ,
7481 TestInfo testInfo ) {
7582 BytecodeCompiler program = BytecodeCompiler .newProgram (chainConfig );
7683 program .op (OpCode .STOP );
7784 BytecodeRunner bytecodeRunner = BytecodeRunner .of (program .compile ());
7885 AccessListEntry accessListEntry =
7986 new AccessListEntry (Address .fromHexString ("0xABCD" ), List .of ());
8087 List <AccessListEntry > accessList = provideAccessList ? List .of (accessListEntry ) : List .of ();
81- bytecodeRunner .run (callData , accessList , chainConfig , testInfo );
88+ if (!imposeSenderRecipientCollision ) {
89+ bytecodeRunner .run (callData , accessList , chainConfig , testInfo );
90+ } else {
91+ bytecodeRunner .runWithImposedSenderRecipientAddressCollision (
92+ callData , accessList , chainConfig , testInfo );
93+ }
8294 // Test blocks contain 4 transactions: 2 system transactions, 1 user transaction (the one we
8395 // created) and 1 noop transaction.
8496 if (isPostPrague (fork )) {
@@ -89,6 +101,8 @@ void trivialCalleeTest(
89101 }
90102
91103 static Stream <Arguments > testSource () {
104+ final boolean noSenderRecipientCollision = false ;
105+ final boolean senderRecipientCollision = true ;
92106 /*
93107 Here we change the callData (specifically the length and the CallDataSetting) to make comparingEffectiveRefundsVsFloorCost.result()
94108 become true in UserTransaction.comparingEffectiveRefundToFloorCostComputationRow.
@@ -103,12 +117,14 @@ Here we change the callData (specifically the length and the CallDataSetting) to
103117 Arguments .of (
104118 buildCallData (CallDataSetting .ALL_ZEROS , true , 400 ),
105119 true ,
106- DominantCost .EXECUTION_COST_DOMINATES ));
120+ DominantCost .EXECUTION_COST_DOMINATES ,
121+ noSenderRecipientCollision ));
107122 arguments .add (
108123 Arguments .of (
109124 buildCallData (CallDataSetting .ALL_ZEROS , true , 401 ),
110125 true ,
111- DominantCost .FLOOR_COST_DOMINATES ));
126+ DominantCost .FLOOR_COST_DOMINATES ,
127+ noSenderRecipientCollision ));
112128
113129 // Case ALL_NON_ZEROS_EXCEPT_FOR_FIRST.
114130 // The transaction execution cost (TX_SKIP) is 21000 + 2400 + 16*length.
@@ -118,12 +134,14 @@ Here we change the callData (specifically the length and the CallDataSetting) to
118134 Arguments .of (
119135 buildCallData (CallDataSetting .ALL_NON_ZEROS_EXCEPT_FOR_FIRST , false , 100 ),
120136 true ,
121- DominantCost .EXECUTION_COST_DOMINATES ));
137+ DominantCost .EXECUTION_COST_DOMINATES ,
138+ noSenderRecipientCollision ));
122139 arguments .add (
123140 Arguments .of (
124141 buildCallData (CallDataSetting .ALL_NON_ZEROS_EXCEPT_FOR_FIRST , false , 101 ),
125142 true ,
126- DominantCost .FLOOR_COST_DOMINATES ));
143+ DominantCost .FLOOR_COST_DOMINATES ,
144+ noSenderRecipientCollision ));
127145
128146 // Case ZEROS_AND_NON_ZEROS.
129147 // caveat for simplicity we consider even sizes.
@@ -134,12 +152,28 @@ Here we change the callData (specifically the length and the CallDataSetting) to
134152 Arguments .of (
135153 buildCallData (CallDataSetting .ZEROS_AND_NON_ZEROS , false , 160 ),
136154 true ,
137- DominantCost .EXECUTION_COST_DOMINATES ));
155+ DominantCost .EXECUTION_COST_DOMINATES ,
156+ noSenderRecipientCollision ));
138157 arguments .add (
139158 Arguments .of (
140159 buildCallData (CallDataSetting .ZEROS_AND_NON_ZEROS , false , 162 ),
141160 true ,
142- DominantCost .FLOOR_COST_DOMINATES ));
161+ DominantCost .FLOOR_COST_DOMINATES ,
162+ noSenderRecipientCollision ));
163+
164+ // Case ALL_ZEROS with sender-recipient collision.
165+ // This explores a case which blew up on mainnet, where a transaction had sender == recipient,
166+ // non-empty call
167+ // data, and a bug in the senderAddressCollision() case of TxSkipSection was triggered.
168+ //
169+ // The transaction execution cost (TX_SKIP) is 21000 + 4.
170+ // The floor cost is 21000 + 10.
171+ arguments .add (
172+ Arguments .of (
173+ buildCallData (CallDataSetting .ALL_ZEROS , true , 1 ),
174+ false ,
175+ DominantCost .FLOOR_COST_DOMINATES ,
176+ senderRecipientCollision ));
143177
144178 return arguments .stream ();
145179 }
@@ -152,7 +186,9 @@ enum CallDataSetting {
152186 }
153187
154188 static Bytes buildCallData (CallDataSetting callDataSetting , boolean startsWithZero , int length ) {
155- Preconditions .checkArgument (length > 1 , "length must be at least 2" );
189+ Preconditions .checkArgument (
190+ callDataSetting != CallDataSetting .ZEROS_AND_NON_ZEROS || length > 1 ,
191+ "length must be at least 2" );
156192 return switch (callDataSetting ) {
157193 case ALL_ZEROS -> Bytes .fromHexString ("00" .repeat (length ));
158194 case ZEROS_AND_NON_ZEROS -> Bytes .fromHexString (
0 commit comments