public static Stream<Trace> generateFromTransactionTrace(
      final ProtocolSchedule protocolSchedule,
      final TransactionTrace transactionTrace,
      final Block block,
      final AtomicInteger traceCounter,
      final Consumer<FlatTrace.Builder> consumer) {

    final FlatTrace.Builder firstFlatTraceBuilder = FlatTrace.freshBuilder(transactionTrace);

    final Transaction tx = transactionTrace.getTransaction();

    final Optional<String> smartContractCode =
        tx.getInit().map(__ -> transactionTrace.getResult().getOutput().toString());
    final Optional<String> smartContractAddress =
        smartContractCode.map(
            __ -> Address.contractAddress(tx.getSender(), tx.getNonce()).toHexString());
    final Optional<Bytes> revertReason = transactionTrace.getResult().getRevertReason();

    // set code field in result node
    smartContractCode.ifPresent(firstFlatTraceBuilder.getResultBuilder()::code);
    revertReason.ifPresent(r -> firstFlatTraceBuilder.revertReason(r.toHexString()));

    // set init field if transaction is a smart contract deployment
    tx.getInit().map(Bytes::toHexString).ifPresent(firstFlatTraceBuilder.getActionBuilder()::init);

    // set to, input and callType fields if not a smart contract
    if (tx.getTo().isPresent()) {
      final Bytes payload = tx.getPayload();
      firstFlatTraceBuilder
          .getActionBuilder()
          .to(tx.getTo().map(Bytes::toHexString).orElse(null))
          .callType("call")
          .input(payload == null ? "0x" : payload.toHexString());

      if (!transactionTrace.getTraceFrames().isEmpty()
          && hasRevertInSubCall(transactionTrace, transactionTrace.getTraceFrames().get(0))) {
        firstFlatTraceBuilder.error(Optional.of("Reverted"));
      }

    } else {
      firstFlatTraceBuilder
          .type("create")
          .getResultBuilder()
          .address(smartContractAddress.orElse(null));
    }

    if (!transactionTrace.getTraceFrames().isEmpty()) {
      final OptionalLong precompiledGasCost =
          transactionTrace.getTraceFrames().get(0).getPrecompiledGasCost();
      if (precompiledGasCost.isPresent()) {
        firstFlatTraceBuilder
            .getResultBuilder()
            .gasUsed("0x" + Long.toHexString(precompiledGasCost.getAsLong()));
      }
    }

    final List<FlatTrace.Builder> flatTraces = new ArrayList<>();

    // stack of previous contexts
    final Deque<FlatTrace.Context> tracesContexts = new ArrayDeque<>();

    // add the first transactionTrace context to the queue of transactionTrace contexts
    FlatTrace.Context currentContext = new FlatTrace.Context(firstFlatTraceBuilder);
    tracesContexts.addLast(currentContext);
    flatTraces.add(currentContext.getBuilder());
    // declare the first transactionTrace context as the previous transactionTrace context
    long cumulativeGasCost = 0;

    final Iterator<TraceFrame> iter = transactionTrace.getTraceFrames().iterator();
    Optional<TraceFrame> nextTraceFrame =
        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 +=
          traceFrame.getGasCost().orElse(0L) + traceFrame.getPrecompiledGasCost().orElse(0L);

      final String opcodeString = traceFrame.getOpcode();
      if ("CALL".equals(opcodeString)
          || "CALLCODE".equals(opcodeString)
          || "DELEGATECALL".equals(opcodeString)
          || "STATICCALL".equals(opcodeString)) {

        currentContext =
            handleCall(
                transactionTrace,
                traceFrame,
                nextTraceFrame,
                flatTraces,
                cumulativeGasCost,
                tracesContexts,
                opcodeString.toLowerCase(Locale.US));

      } else if ("CALLDATALOAD".equals(opcodeString)) {
        currentContext = handleCallDataLoad(currentContext, traceFrame);
      } else if ("RETURN".equals(opcodeString) || "STOP".equals(opcodeString)) {
        currentContext =
            handleReturn(
                protocolSchedule,
                transactionTrace,
                block,
                traceFrame,
                tracesContexts,
                currentContext);
      } else if ("SELFDESTRUCT".equals(opcodeString)) {
        if (traceFrame.getExceptionalHaltReason().isPresent()) {
          currentContext =
              handleCall(
                  transactionTrace,
                  traceFrame,
                  nextTraceFrame,
                  flatTraces,
                  cumulativeGasCost,
                  tracesContexts,
                  opcodeString.toLowerCase(Locale.US));
        } else {
          currentContext =
              handleSelfDestruct(traceFrame, tracesContexts, currentContext, flatTraces);
        }
      } else if (("CREATE".equals(opcodeString) || "CREATE2".equals(opcodeString))
          && (traceFrame.getExceptionalHaltReason().isEmpty() || traceFrame.getDepth() == 0)) {
        currentContext =
            handleCreateOperation(
                traceFrame,
                nextTraceFrame,
                flatTraces,
                cumulativeGasCost,
                tracesContexts,
                smartContractAddress);
      } else if ("REVERT".equals(opcodeString)) {
        currentContext = handleRevert(tracesContexts, currentContext);
      }

      if (traceFrame.getExceptionalHaltReason().isPresent()) {
        currentContext = handleHalt(flatTraces, tracesContexts, currentContext, traceFrame);
      }

      if (currentContext == null) {
        break;
      }
    }

    return flatTraces.stream().peek(consumer).map(FlatTrace.Builder::build);
  }
Generates a stream of {@link Trace} from the passed {@link TransactionTrace} data.

@param protocolSchedule the current {@link ProtocolSchedule} to use
@param transactionTrace the {@link TransactionTrace} to use
@param block the {@link Block} to use
@param traceCounter the current trace counter value
@param consumer to use to add additional contextual information to the trace
@return a stream of generated traces {@link Trace}
ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java