mirror of https://github.com/hyperledger/besu
Trigger `OperationTracer` on contexts enter & exit (#5756)
* Trigger `OperationTracer` on contexts enter & exit Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Update CHANGELOG.md Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Spotless Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Daniel's comments Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Ensure `OperationTracer` is not null before calling it Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Move back hook calls into `process` Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Fix @shemnon comments Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * added test for context enter and context exit Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * spotless Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * Trigger `OperationTracer` on contexts enter & exit Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Update CHANGELOG.md Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Spotless Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Daniel's comments Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Ensure `OperationTracer` is not null before calling it Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Move back hook calls into `process` Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * Fix @shemnon comments Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> * added test for context enter and context exit Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * spotless Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * added a test without mocking Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * fixed unit tests Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> --------- Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu> Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> Co-authored-by: Daniel Lehrner <daniel.lehrner@consensys.net>pull/5792/head
parent
cd5998d131
commit
4ecfbb2252
@ -0,0 +1,208 @@ |
||||
/* |
||||
* Copyright Hyperledger Besu Contributors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.evm.processor; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.hyperledger.besu.evm.processor.AbstractMessageProcessorTest.ContextTracer.TRACE_TYPE.CONTEXT_ENTER; |
||||
import static org.hyperledger.besu.evm.processor.AbstractMessageProcessorTest.ContextTracer.TRACE_TYPE.CONTEXT_EXIT; |
||||
import static org.hyperledger.besu.evm.processor.AbstractMessageProcessorTest.ContextTracer.TRACE_TYPE.POST_EXECUTION; |
||||
import static org.hyperledger.besu.evm.processor.AbstractMessageProcessorTest.ContextTracer.TRACE_TYPE.PRE_EXECUTION; |
||||
import static org.mockito.Mockito.never; |
||||
import static org.mockito.Mockito.times; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Wei; |
||||
import org.hyperledger.besu.evm.fluent.EVMExecutor; |
||||
import org.hyperledger.besu.evm.frame.MessageFrame; |
||||
import org.hyperledger.besu.evm.internal.EvmConfiguration; |
||||
import org.hyperledger.besu.evm.operation.Operation; |
||||
import org.hyperledger.besu.evm.tracing.OperationTracer; |
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Deque; |
||||
import java.util.List; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
import org.junit.jupiter.params.ParameterizedTest; |
||||
import org.junit.jupiter.params.provider.ValueSource; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.jupiter.MockitoExtension; |
||||
|
||||
@ExtendWith(MockitoExtension.class) |
||||
abstract class AbstractMessageProcessorTest<T extends AbstractMessageProcessor> { |
||||
|
||||
@Mock MessageFrame messageFrame; |
||||
@Mock OperationTracer operationTracer; |
||||
@Mock Deque<MessageFrame> messageFrameStack; |
||||
@Mock WorldUpdater worldUpdater; |
||||
|
||||
protected abstract T getAbstractMessageProcessor(); |
||||
|
||||
@ParameterizedTest |
||||
@ValueSource(ints = {0, 1}) |
||||
void shouldNotTraceContextIfStackSizeIsZero(final int stackSize) { |
||||
when(messageFrame.getMessageStackSize()).thenReturn(stackSize); |
||||
when(messageFrame.getState()) |
||||
.thenReturn(MessageFrame.State.COMPLETED_SUCCESS, MessageFrame.State.COMPLETED_FAILED); |
||||
when(messageFrame.getMessageFrameStack()).thenReturn(messageFrameStack); |
||||
|
||||
getAbstractMessageProcessor().process(messageFrame, operationTracer); |
||||
|
||||
verify(operationTracer, never()).traceContextEnter(messageFrame); |
||||
verify(operationTracer, never()).traceContextExit(messageFrame); |
||||
} |
||||
|
||||
@ParameterizedTest |
||||
@ValueSource(ints = {2, 3, 5, 15, Integer.MAX_VALUE}) |
||||
void shouldTraceContextIfStackSizeIsGreaterZeroAndSuccess(final int stackSize) { |
||||
when(messageFrame.getMessageStackSize()).thenReturn(stackSize); |
||||
when(messageFrame.getState()).thenReturn(MessageFrame.State.COMPLETED_SUCCESS); |
||||
when(messageFrame.getMessageFrameStack()).thenReturn(messageFrameStack); |
||||
when(messageFrame.getWorldUpdater()).thenReturn(worldUpdater); |
||||
|
||||
getAbstractMessageProcessor().process(messageFrame, operationTracer); |
||||
|
||||
verify(operationTracer, times(1)).traceContextEnter(messageFrame); |
||||
verify(operationTracer, times(1)).traceContextExit(messageFrame); |
||||
} |
||||
|
||||
@ParameterizedTest |
||||
@ValueSource(ints = {2, 3, 5, 15, Integer.MAX_VALUE}) |
||||
void shouldTraceContextIfStackSizeIsGreaterZeroAndFailure(final int stackSize) { |
||||
when(messageFrame.getMessageStackSize()).thenReturn(stackSize); |
||||
when(messageFrame.getState()).thenReturn(MessageFrame.State.COMPLETED_FAILED); |
||||
when(messageFrame.getMessageFrameStack()).thenReturn(messageFrameStack); |
||||
|
||||
getAbstractMessageProcessor().process(messageFrame, operationTracer); |
||||
|
||||
verify(operationTracer, times(1)).traceContextEnter(messageFrame); |
||||
verify(operationTracer, times(1)).traceContextExit(messageFrame); |
||||
} |
||||
|
||||
@Test |
||||
void shouldTraceContextEnterExitForEip3155Test() { |
||||
final EVMExecutor executor = EVMExecutor.shanghai(EvmConfiguration.DEFAULT); |
||||
final ContextTracer contextTracer = new ContextTracer(); |
||||
|
||||
executor.tracer(contextTracer); |
||||
executor.gas(10_000_000_000L); |
||||
|
||||
/* |
||||
The byte code below is taken from https://eips.ethereum.org/EIPS/eip-3155
|
||||
|
||||
It produces the following trace: |
||||
|
||||
0: {"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} |
||||
1: {"pc":2,"op":128,"gas":"0x2540be3fd","gasCost":"0x3","memory":"0x","memSize":0,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"DUP1","error":""} |
||||
2: {"pc":3,"op":83,"gas":"0x2540be3fa","gasCost":"0xc","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"MSTORE8","error":""} |
||||
3: {"pc":4,"op":96,"gas":"0x2540be3ee","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} |
||||
4: {"pc":6,"op":96,"gas":"0x2540be3eb","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} |
||||
5: {"pc":8,"op":85,"gas":"0x2540be3e8","gasCost":"0x4e20","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SSTORE","error":""} |
||||
6: {"pc":9,"op":96,"gas":"0x2540b95c8","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} |
||||
7: {"pc":11,"op":96,"gas":"0x2540b95c5","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} |
||||
8: {"pc":13,"op":96,"gas":"0x2540b95c2","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} |
||||
9: {"pc":15,"op":96,"gas":"0x2540b95bf","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} |
||||
10: {"pc":17,"op":96,"gas":"0x2540b95bc","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} |
||||
11: {"pc":19,"op":90,"gas":"0x2540b95b9","gasCost":"0x2","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"GAS","error":""} |
||||
12: {"pc":20,"op":250,"gas":"0x2540b95b7","gasCost":"0x24abb676c","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2","0x2540b95b7"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"STATICCALL","error":""} |
||||
13: {"pc":21,"op":96,"gas":"0x2540b92a7","gasCost":"0x3","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1"],"returnStack":[],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"PUSH1","error":""} |
||||
14: {"pc":23,"op":243,"gas":"0x2540b92a4","gasCost":"0x0","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1","0x40"],"returnStack":[],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"RETURN","error":""} |
||||
15: {"stateRoot":"2eef130ec61805516c1f050720b520619787704a5dd826a39aeefb850f83acfd", "output":"40","gasUsed":"0x515c","time":350855} |
||||
*/ |
||||
final Bytes codeBytes = |
||||
Bytes.fromHexString("0x604080536040604055604060006040600060025afa6040f3"); |
||||
executor.execute(codeBytes, Bytes.EMPTY, Wei.ZERO, Address.ZERO); |
||||
|
||||
final List<ContextTracer.TRACE_TYPE> expectedTraces = |
||||
Arrays.asList( |
||||
PRE_EXECUTION, // PUSH1
|
||||
POST_EXECUTION, // PUSH1
|
||||
PRE_EXECUTION, // DUP1
|
||||
POST_EXECUTION, // DUP1
|
||||
PRE_EXECUTION, // MSTORE8
|
||||
POST_EXECUTION, // MSTORE8
|
||||
PRE_EXECUTION, // PUSH1
|
||||
POST_EXECUTION, // PUSH1
|
||||
PRE_EXECUTION, // PUSH1
|
||||
POST_EXECUTION, // PUSH1
|
||||
PRE_EXECUTION, // SSTORE
|
||||
POST_EXECUTION, // SSTORE
|
||||
PRE_EXECUTION, // PUSH1
|
||||
POST_EXECUTION, // PUSH1
|
||||
PRE_EXECUTION, // PUSH1
|
||||
POST_EXECUTION, // PUSH1
|
||||
PRE_EXECUTION, // PUSH1
|
||||
POST_EXECUTION, // PUSH1
|
||||
PRE_EXECUTION, // PUSH1
|
||||
POST_EXECUTION, // PUSH1
|
||||
PRE_EXECUTION, // PUSH1
|
||||
POST_EXECUTION, // PUSH1
|
||||
PRE_EXECUTION, // GAS
|
||||
POST_EXECUTION, // GAS
|
||||
PRE_EXECUTION, // STATICCALL
|
||||
POST_EXECUTION, // STATICCALL
|
||||
CONTEXT_ENTER, // STATICCALL
|
||||
CONTEXT_EXIT, // STATICCALL
|
||||
PRE_EXECUTION, // PUSH1
|
||||
POST_EXECUTION, // PUSH1
|
||||
PRE_EXECUTION, // RETURN
|
||||
POST_EXECUTION // RETURN
|
||||
); |
||||
|
||||
assertThat(contextTracer.traceHistory()).isEqualTo(expectedTraces); |
||||
} |
||||
|
||||
static class ContextTracer implements OperationTracer { |
||||
enum TRACE_TYPE { |
||||
PRE_EXECUTION, |
||||
POST_EXECUTION, |
||||
CONTEXT_ENTER, |
||||
CONTEXT_EXIT |
||||
} |
||||
|
||||
private final List<TRACE_TYPE> traceHistory = new ArrayList<>(); |
||||
|
||||
@Override |
||||
public void tracePreExecution(final MessageFrame frame) { |
||||
traceHistory.add(PRE_EXECUTION); |
||||
} |
||||
|
||||
@Override |
||||
public void tracePostExecution( |
||||
final MessageFrame frame, final Operation.OperationResult operationResult) { |
||||
traceHistory.add(POST_EXECUTION); |
||||
} |
||||
|
||||
@Override |
||||
public void traceContextEnter(final MessageFrame frame) { |
||||
traceHistory.add(TRACE_TYPE.CONTEXT_ENTER); |
||||
} |
||||
|
||||
@Override |
||||
public void traceContextExit(final MessageFrame frame) { |
||||
traceHistory.add(TRACE_TYPE.CONTEXT_EXIT); |
||||
} |
||||
|
||||
public List<TRACE_TYPE> traceHistory() { |
||||
return traceHistory; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
/* |
||||
* Copyright Hyperledger Besu Contributors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.evm.processor; |
||||
|
||||
import org.hyperledger.besu.evm.EVM; |
||||
import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry; |
||||
|
||||
import org.junit.jupiter.api.Nested; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.jupiter.MockitoExtension; |
||||
|
||||
@Nested |
||||
@ExtendWith(MockitoExtension.class) |
||||
class MessageCallProcessorTest extends AbstractMessageProcessorTest<MessageCallProcessor> { |
||||
|
||||
@Mock EVM evm; |
||||
@Mock PrecompileContractRegistry precompileContractRegistry; |
||||
|
||||
@Override |
||||
protected MessageCallProcessor getAbstractMessageProcessor() { |
||||
return new MessageCallProcessor(evm, precompileContractRegistry); |
||||
} |
||||
} |
Loading…
Reference in new issue