@ -18,7 +18,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionT
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity ;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity ;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.Trace ;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.Trace ;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TracingUtils ;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TracingUtils ;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace.Context ;
import org.hyperledger.besu.ethereum.core.Address ;
import org.hyperledger.besu.ethereum.core.Address ;
import org.hyperledger.besu.ethereum.core.Gas ;
import org.hyperledger.besu.ethereum.core.Gas ;
import org.hyperledger.besu.ethereum.core.Transaction ;
import org.hyperledger.besu.ethereum.core.Transaction ;
@ -30,6 +29,7 @@ import java.util.ArrayDeque;
import java.util.ArrayList ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Arrays ;
import java.util.Deque ;
import java.util.Deque ;
import java.util.Iterator ;
import java.util.List ;
import java.util.List ;
import java.util.Locale ;
import java.util.Locale ;
import java.util.Optional ;
import java.util.Optional ;
@ -98,9 +98,12 @@ public class FlatTraceGenerator {
// declare the first transactionTrace context as the previous transactionTrace context
// declare the first transactionTrace context as the previous transactionTrace context
long cumulativeGasCost = 0 ;
long cumulativeGasCost = 0 ;
int traceFrameIndex = 0 ;
final Iterator < TraceFrame > iter = transactionTrace . getTraceFrames ( ) . iterator ( ) ;
final List < TraceFrame > traceFrames = transactionTrace . getTraceFrames ( ) ;
Optional < TraceFrame > nextTraceFrame =
for ( final TraceFrame traceFrame : traceFrames ) {
iter . hasNext ( ) ? Optional . of ( iter . next ( ) ) : Optional . empty ( ) ;
while ( nextTraceFrame . isPresent ( ) ) {
final TraceFrame traceFrame = nextTraceFrame . get ( ) ;
nextTraceFrame = iter . hasNext ( ) ? Optional . of ( iter . next ( ) ) : Optional . empty ( ) ;
cumulativeGasCost + =
cumulativeGasCost + =
traceFrame . getGasCost ( ) . orElse ( Gas . ZERO ) . toLong ( )
traceFrame . getGasCost ( ) . orElse ( Gas . ZERO ) . toLong ( )
+ traceFrame . getPrecompiledGasCost ( ) . orElse ( Gas . ZERO ) . toLong ( ) ;
+ traceFrame . getPrecompiledGasCost ( ) . orElse ( Gas . ZERO ) . toLong ( ) ;
@ -113,12 +116,10 @@ public class FlatTraceGenerator {
handleCall (
handleCall (
transactionTrace ,
transactionTrace ,
traceFrame ,
traceFrame ,
smartContractAddress ,
nextTraceFrame ,
flatTraces ,
flatTraces ,
cumulativeGasCost ,
cumulativeGasCost ,
tracesContexts ,
tracesContexts ,
traceFrameIndex ,
traceFrames ,
opcodeString . toLowerCase ( Locale . US ) ) ;
opcodeString . toLowerCase ( Locale . US ) ) ;
} else if ( "RETURN" . equals ( opcodeString ) | | "STOP" . equals ( opcodeString ) ) {
} else if ( "RETURN" . equals ( opcodeString ) | | "STOP" . equals ( opcodeString ) ) {
if ( currentContext ! = null ) {
if ( currentContext ! = null ) {
@ -136,19 +137,12 @@ public class FlatTraceGenerator {
flatTraces ,
flatTraces ,
tracesContexts ,
tracesContexts ,
cumulativeGasCost ,
cumulativeGasCost ,
traceFrameIndex ,
nextTraceFrame ) ;
traceFrames ) ;
} else if ( "REVERT" . equals ( opcodeString ) ) {
} else if ( "REVERT" . equals ( opcodeString ) ) {
currentContext = handleRevert ( tracesContexts , currentContext ) ;
currentContext = handleRevert ( tracesContexts , currentContext ) ;
} else if ( ! traceFrame . getExceptionalHaltReasons ( ) . isEmpty ( ) ) {
} else if ( ! traceFrame . getExceptionalHaltReasons ( ) . isEmpty ( ) ) {
currentContext
currentContext = handleHalt ( tracesContexts , currentContext , traceFrame ) ;
. getBuilder ( )
. error (
traceFrame . getExceptionalHaltReasons ( ) . stream ( )
. map ( ExceptionalHaltReason : : getDescription )
. reduce ( ( a , b ) - > a + ", " + b ) ) ;
}
}
traceFrameIndex + + ;
}
}
return flatTraces . stream ( ) . map ( FlatTrace . Builder : : build ) ;
return flatTraces . stream ( ) . map ( FlatTrace . Builder : : build ) ;
@ -157,21 +151,18 @@ public class FlatTraceGenerator {
private static FlatTrace . Context handleCall (
private static FlatTrace . Context handleCall (
final TransactionTrace transactionTrace ,
final TransactionTrace transactionTrace ,
final TraceFrame traceFrame ,
final TraceFrame traceFrame ,
final Optional < String > smartContractAddress ,
final Optional < TraceFrame > nextTraceFrame ,
final List < FlatTrace . Builder > flatTraces ,
final List < FlatTrace . Builder > flatTraces ,
final long cumulativeGasCost ,
final long cumulativeGasCost ,
final Deque < FlatTrace . Context > tracesContexts ,
final Deque < FlatTrace . Context > tracesContexts ,
final int traceFrameIndex ,
final List < TraceFrame > traceFrames ,
final String opcodeString ) {
final String opcodeString ) {
final TraceFrame nextTraceFrame = traceFrames . get ( traceFrameIndex + 1 ) ;
final Bytes32 [ ] stack = traceFrame . getStack ( ) . orElseThrow ( ) ;
final Bytes32 [ ] stack = traceFrame . getStack ( ) . orElseThrow ( ) ;
final Address contractCallAddress = toAddress ( stack [ stack . length - 2 ] ) ;
final Address contractCallAddress = toAddress ( stack [ stack . length - 2 ] ) ;
final FlatTrace . Context lastContext = tracesContexts . peekLast ( ) ;
final FlatTrace . Context lastContext = tracesContexts . peekLast ( ) ;
final String callingAddress = calculateCallingAddress ( lastContext ) ;
final String callingAddress = calculateCallingAddress ( lastContext ) ;
if ( contractCallAddress . numberOfLeadingZeroBytes ( ) > = 19 ) {
if ( traceFrame . getDepth ( ) > = nextTraceFrame . map ( TraceFrame : : getDepth ) . orElse ( 0 ) ) {
// don't log calls to precompiles
// don't log calls to calls that don't execute, such as insufficient value and precompiles
return tracesContexts . peekLast ( ) ;
return tracesContexts . peekLast ( ) ;
}
}
@ -181,13 +172,11 @@ public class FlatTraceGenerator {
. resultBuilder ( Result . builder ( ) ) ;
. resultBuilder ( Result . builder ( ) ) ;
final Action . Builder subTraceActionBuilder =
final Action . Builder subTraceActionBuilder =
Action . builder ( )
Action . builder ( )
. from ( smartContractAddress . orElse ( callingAddress ) )
. from ( callingAddress )
. to ( contractCallAddress . toString ( ) )
. to ( contractCallAddress . toString ( ) )
. input (
. input (
Optional . ofNullable ( nextTraceFrame . getInputData ( ) )
nextTraceFrame . map ( TraceFrame : : getInputData ) . map ( Bytes : : toHexString ) . orElse ( null ) )
. map ( Bytes : : toHexString )
. gas ( nextTraceFrame . map ( TraceFrame : : getGasRemaining ) . orElse ( Gas . ZERO ) . toHexString ( ) )
. orElse ( null ) )
. gas ( nextTraceFrame . getGasRemaining ( ) . toHexString ( ) )
. callType ( opcodeString . toLowerCase ( Locale . US ) )
. callType ( opcodeString . toLowerCase ( Locale . US ) )
. value ( Quantity . create ( transactionTrace . getTransaction ( ) . getValue ( ) ) ) ;
. value ( Quantity . create ( transactionTrace . getTransaction ( ) . getValue ( ) ) ) ;
@ -218,7 +207,7 @@ public class FlatTraceGenerator {
// set value for contract creation TXes, CREATE, and CREATE2
// set value for contract creation TXes, CREATE, and CREATE2
if ( actionBuilder . getCallType ( ) = = null & & traceFrame . getMaybeCode ( ) . isPresent ( ) ) {
if ( actionBuilder . getCallType ( ) = = null & & traceFrame . getMaybeCode ( ) . isPresent ( ) ) {
actionBuilder . init ( traceFrame . getMaybeCode ( ) . get ( ) . getBytes ( ) . toHexString ( ) ) ;
actionBuilder . init ( traceFrame . getMaybeCode ( ) . get ( ) . getBytes ( ) . toHexString ( ) ) ;
resultBuilder . code ( outputData . toHexString ( ) ) . address ( traceFrame . getRecipient ( ) . toHexString ( ) ) ;
resultBuilder . code ( outputData . toHexString ( ) ) ;
if ( currentContext . isCreateOp ( ) ) {
if ( currentContext . isCreateOp ( ) ) {
// this is from a CREATE/CREATE2, so add code deposit cost.
// this is from a CREATE/CREATE2, so add code deposit cost.
currentContext . incGasUsed ( outputData . size ( ) * 200L ) ;
currentContext . incGasUsed ( outputData . size ( ) * 200L ) ;
@ -275,9 +264,7 @@ public class FlatTraceGenerator {
final List < FlatTrace . Builder > flatTraces ,
final List < FlatTrace . Builder > flatTraces ,
final Deque < FlatTrace . Context > tracesContexts ,
final Deque < FlatTrace . Context > tracesContexts ,
final long cumulativeGasCost ,
final long cumulativeGasCost ,
final int traceFrameIndex ,
final Optional < TraceFrame > nextTraceFrame ) {
final List < TraceFrame > traceFrames ) {
final TraceFrame nextTraceFrame = traceFrames . get ( traceFrameIndex + 1 ) ;
final FlatTrace . Context lastContext = tracesContexts . peekLast ( ) ;
final FlatTrace . Context lastContext = tracesContexts . peekLast ( ) ;
final String callingAddress = calculateCallingAddress ( lastContext ) ;
final String callingAddress = calculateCallingAddress ( lastContext ) ;
@ -289,11 +276,15 @@ public class FlatTraceGenerator {
final Action . Builder subTraceActionBuilder =
final Action . Builder subTraceActionBuilder =
Action . builder ( )
Action . builder ( )
. from ( smartContractAddress . orElse ( callingAddress ) )
. from ( smartContractAddress . orElse ( callingAddress ) )
. gas ( nextTraceFrame . getGasRemaining ( ) . toHexString ( ) )
. gas ( nextTraceFrame . map ( TraceFrame : : getGasRemaining ) . orElse ( Gas . ZERO ) . toHexString ( ) )
. value ( Quantity . create ( nextTraceFrame . getValue ( ) ) ) ;
. value ( Quantity . create ( nextTraceFrame . map ( TraceFrame : : getValue ) . orElse ( Wei . ZERO ) ) ) ;
final FlatTrace . Context currentContext =
final FlatTrace . Context currentContext =
new FlatTrace . Context ( subTraceBuilder . actionBuilder ( subTraceActionBuilder ) ) ;
new FlatTrace . Context ( subTraceBuilder . actionBuilder ( subTraceActionBuilder ) ) ;
currentContext
. getBuilder ( )
. getResultBuilder ( )
. address ( nextTraceFrame . map ( TraceFrame : : getRecipient ) . orElse ( Address . ZERO ) . toHexString ( ) ) ;
currentContext . setCreateOp ( true ) ;
currentContext . setCreateOp ( true ) ;
currentContext . decGasUsed ( cumulativeGasCost ) ;
currentContext . decGasUsed ( cumulativeGasCost ) ;
tracesContexts . addLast ( currentContext ) ;
tracesContexts . addLast ( currentContext ) ;
@ -301,9 +292,33 @@ public class FlatTraceGenerator {
return currentContext ;
return currentContext ;
}
}
private static Context handleRevert (
private static FlatTrace . Context handleHalt (
final Deque < Context > tracesContexts , final FlatTrace . Context currentContext ) {
final Deque < FlatTrace . Context > tracesContexts ,
currentContext . getBuilder ( ) . error ( Optional . of ( "Reverted" ) ) ;
final FlatTrace . Context currentContext ,
final TraceFrame traceFrame ) {
final FlatTrace . Builder traceFrameBuilder = currentContext . getBuilder ( ) ;
traceFrameBuilder . error (
traceFrame . getExceptionalHaltReasons ( ) . stream ( )
. map ( ExceptionalHaltReason : : getDescription )
. reduce ( ( a , b ) - > a + ", " + b ) ) ;
if ( tracesContexts . size ( ) > 1 ) {
traceFrameBuilder . getActionBuilder ( ) . value ( "0x0" ) ;
}
tracesContexts . removeLast ( ) ;
final FlatTrace . Context nextContext = tracesContexts . peekLast ( ) ;
if ( nextContext ! = null ) {
nextContext . getBuilder ( ) . incSubTraces ( ) ;
}
return nextContext ;
}
private static FlatTrace . Context handleRevert (
final Deque < FlatTrace . Context > tracesContexts , final FlatTrace . Context currentContext ) {
final FlatTrace . Builder traceFrameBuilder = currentContext . getBuilder ( ) ;
traceFrameBuilder . error ( Optional . of ( "Reverted" ) ) ;
if ( tracesContexts . size ( ) > 1 ) {
traceFrameBuilder . getActionBuilder ( ) . value ( "0x0" ) ;
}
tracesContexts . removeLast ( ) ;
tracesContexts . removeLast ( ) ;
final FlatTrace . Context nextContext = tracesContexts . peekLast ( ) ;
final FlatTrace . Context nextContext = tracesContexts . peekLast ( ) ;
if ( nextContext ! = null ) {
if ( nextContext ! = null ) {
@ -313,16 +328,25 @@ public class FlatTraceGenerator {
}
}
private static String calculateCallingAddress ( final FlatTrace . Context lastContext ) {
private static String calculateCallingAddress ( final FlatTrace . Context lastContext ) {
if ( lastContext . getBuilder ( ) . getActionBuilder ( ) . getCallType ( ) = = null ) {
final FlatTrace . Builder lastContextBuilder = lastContext . getBuilder ( ) ;
return ZERO_ADDRESS_STRING ;
final Action . Builder lastActionBuilder = lastContextBuilder . getActionBuilder ( ) ;
if ( lastActionBuilder . getCallType ( ) = = null ) {
if ( "create" . equals ( lastContextBuilder . getType ( ) ) ) {
return lastContextBuilder . getResultBuilder ( ) . getAddress ( ) ;
} else {
return ZERO_ADDRESS_STRING ;
}
}
}
switch ( lastContext . getBuilder ( ) . getActionBuilder ( ) . getCallType ( ) ) {
switch ( lastActionBuilder . getCallType ( ) ) {
case "call" :
case "call" :
case "staticcall" :
case "staticcall" :
return lastContext . getBuilder ( ) . getActionBuilder ( ) . getTo ( ) ;
return lastActionBuilder . getTo ( ) ;
case "delegatecall" :
case "delegatecall" :
case "callcode" :
case "callcode" :
return lastContext . getBuilder ( ) . getActionBuilder ( ) . getFrom ( ) ;
return lastActionBuilder . getFrom ( ) ;
case "create" :
case "create2" :
return lastContextBuilder . getResultBuilder ( ) . getAddress ( ) ;
default :
default :
return ZERO_ADDRESS_STRING ;
return ZERO_ADDRESS_STRING ;
}
}