@Nullable
  String validateStack(
      final int codeSectionToValidate, final EOFLayout eofLayout, final WorkList workList) {
    if (!eofLayout.isValid()) {
      return "EOF Layout invalid - " + eofLayout.invalidReason();
    }
    try {
      CodeSection toValidate = eofLayout.getCodeSection(codeSectionToValidate);
      byte[] code =
          eofLayout.container().slice(toValidate.entryPoint, toValidate.length).toArrayUnsafe();
      int codeLength = code.length;
      int[] stack_min = new int[codeLength];
      int[] stack_max = new int[codeLength];
      Arrays.fill(stack_min, 1025);
      Arrays.fill(stack_max, -1);

      int initialStackHeight = toValidate.getInputs();
      int maxStackHeight = initialStackHeight;
      stack_min[0] = initialStackHeight;
      stack_max[0] = initialStackHeight;
      int unusedBytes = codeLength;

      int currentPC = 0;
      int currentMin = initialStackHeight;
      int currentMax = initialStackHeight;

      while (currentPC < codeLength) {
        int thisOp = code[currentPC] & 0xff;

        OpcodeInfo opcodeInfo = V1_OPCODES[thisOp];
        int stackInputs;
        int stackOutputs;
        int sectionStackUsed;
        int pcAdvance = opcodeInfo.pcAdvance();
        switch (thisOp) {
          case CallFOperation.OPCODE:
            int section = readBigEndianU16(currentPC + 1, code);
            workList.put(section);
            CodeSection codeSection = eofLayout.getCodeSection(section);
            stackInputs = codeSection.getInputs();
            stackOutputs = codeSection.getOutputs();
            sectionStackUsed = codeSection.getMaxStackHeight();
            break;
          case DupNOperation.OPCODE:
            int depth = code[currentPC + 1] & 0xff;
            stackInputs = depth + 1;
            stackOutputs = depth + 2;
            sectionStackUsed = 0;
            break;
          case SwapNOperation.OPCODE:
            int swapDepth = 2 + (code[currentPC + 1] & 0xff);
            stackInputs = swapDepth;
            stackOutputs = swapDepth;
            sectionStackUsed = 0;
            break;
          case ExchangeOperation.OPCODE:
            int imm = code[currentPC + 1] & 0xff;
            int exchangeDepth = (imm >> 4) + (imm & 0xf) + 3;
            stackInputs = exchangeDepth;
            stackOutputs = exchangeDepth;
            sectionStackUsed = 0;
            break;
          default:
            stackInputs = opcodeInfo.inputs();
            stackOutputs = opcodeInfo.outputs();
            sectionStackUsed = 0;
        }

        int nextPC;
        if (!opcodeInfo.valid()) {
          return format("undefined_instruction 0x%02x", thisOp);
        }
        nextPC = currentPC + pcAdvance;

        if (nextPC > codeLength) {
          return format(
              "Dangling immediate argument for opcode 0x%x at PC %d in code section %d.",
              thisOp, currentPC - pcAdvance, codeSectionToValidate);
        }
        if (stack_max[currentPC] < 0) {
          return format(
              "unreachable_instructions section 0x%x pc %d was not forward referenced",
              codeSectionToValidate, currentPC);
        }
        currentMin = min(stack_min[currentPC], currentMin);
        currentMax = max(stack_max[currentPC], currentMax);

        if (stackInputs > currentMin) {
          return format(
              "stack_underflow operation 0x%02X wants stack of %d but may only have %d",
              thisOp, stackInputs, currentMin);
        }

        int stackDelta = stackOutputs - stackInputs;
        currentMax = currentMax + stackDelta;
        currentMin = currentMin + stackDelta;
        if (currentMax + sectionStackUsed - stackOutputs > MAX_STACK_HEIGHT) {
          return "Stack height exceeds 1024";
        }

        unusedBytes -= pcAdvance;
        maxStackHeight = max(maxStackHeight, currentMax);

        switch (thisOp) {
          case RelativeJumpOperation.OPCODE:
            int jValue = readBigEndianI16(currentPC + 1, code);
            int targetPC = nextPC + jValue;
            if (targetPC > currentPC) {
              stack_min[targetPC] = min(stack_min[targetPC], currentMin);
              stack_max[targetPC] = max(stack_max[targetPC], currentMax);
            } else {
              if (stack_min[targetPC] != currentMin) {
                return format(
                    "stack_height_mismatch backwards RJUMP from %d to %d, min %d != %d",
                    currentPC, targetPC, stack_min[targetPC], currentMin);
              }
              if (stack_max[targetPC] != currentMax) {
                return format(
                    "stack_height_mismatch backwards RJUMP from %d to %d, max %d != %d",
                    currentPC, targetPC, stack_max[targetPC], currentMax);
              }
            }

            // terminal op, reset currentMin and currentMax to forward set values
            if (nextPC < codeLength) {
              currentMax = stack_max[nextPC];
              currentMin = stack_min[nextPC];
            }
            break;
          case RelativeJumpIfOperation.OPCODE:
            stack_max[nextPC] = max(stack_max[nextPC], currentMax);
            stack_min[nextPC] = min(stack_min[nextPC], currentMin);
            int jiValue = readBigEndianI16(currentPC + 1, code);
            int targetPCi = nextPC + jiValue;
            if (targetPCi > currentPC) {
              stack_min[targetPCi] = min(stack_min[targetPCi], currentMin);
              stack_max[targetPCi] = max(stack_max[targetPCi], currentMax);
            } else {
              if (stack_min[targetPCi] != currentMin) {
                return format(
                    "stack_height_mismatch backwards RJUMPI from %d to %d, min %d != %d",
                    currentPC, targetPCi, stack_min[targetPCi], currentMin);
              }
              if (stack_max[targetPCi] != currentMax) {
                return format(
                    "stack_height_mismatch backwards RJUMPI from %d to %d, max %d != %d",
                    currentPC, targetPCi, stack_max[targetPCi], currentMax);
              }
            }
            break;
          case RelativeJumpVectorOperation.OPCODE:
            int immediateDataSize = (code[currentPC + 1] & 0xff) * 2;
            unusedBytes -= immediateDataSize + 2;
            int tableEnd = immediateDataSize + currentPC + 4;
            nextPC = tableEnd;
            stack_max[nextPC] = max(stack_max[nextPC], currentMax);
            stack_min[nextPC] = min(stack_min[nextPC], currentMin);
            for (int i = currentPC + 2; i < tableEnd; i += 2) {
              int vValue = readBigEndianI16(i, code);
              int targetPCv = tableEnd + vValue;
              if (targetPCv > currentPC) {
                stack_min[targetPCv] = min(stack_min[targetPCv], currentMin);
                stack_max[targetPCv] = max(stack_max[targetPCv], currentMax);
              } else {
                if (stack_min[targetPCv] != currentMin) {
                  return format(
                      "stack_height_mismatch backwards RJUMPV from %d to %d, min %d != %d",
                      currentPC, targetPCv, stack_min[targetPCv], currentMin);
                }
                if (stack_max[targetPCv] != currentMax) {
                  return format(
                      "stack_height_mismatch backwards RJUMPV from %d to %d, max %d != %d",
                      currentPC, targetPCv, stack_max[targetPCv], currentMax);
                }
              }
            }
            break;
          case RetFOperation.OPCODE:
            int returnStackItems = toValidate.getOutputs();
            if (currentMin != currentMax) {
              return format(
                  "RETF in section %d has a stack range (%d/%d)and must have only one stack value",
                  codeSectionToValidate, currentMin, currentMax);
            }
            if (stack_min[currentPC] != returnStackItems
                || stack_min[currentPC] != stack_max[currentPC]) {
              return format(
                  "stack_higher_than_outputs RETF in section %d calculated height %d does not match configured return stack %d, min height %d, and max height %d",
                  codeSectionToValidate,
                  currentMin,
                  returnStackItems,
                  stack_min[currentPC],
                  stack_max[currentPC]);
            }
            // terminal op, reset currentMin and currentMax to forward set values
            if (nextPC < codeLength) {
              currentMax = stack_max[nextPC];
              currentMin = stack_min[nextPC];
            }
            break;
          case JumpFOperation.OPCODE:
            int jumpFTargetSectionNum = readBigEndianI16(currentPC + 1, code);
            workList.put(jumpFTargetSectionNum);
            CodeSection targetCs = eofLayout.getCodeSection(jumpFTargetSectionNum);
            if (currentMax + targetCs.getMaxStackHeight() - targetCs.getInputs()
                > MAX_STACK_HEIGHT) {
              return format(
                  "JUMPF at section %d pc %d would exceed maximum stack with %d items",
                  codeSectionToValidate,
                  currentPC,
                  currentMax + targetCs.getMaxStackHeight() - targetCs.getInputs());
            }
            if (targetCs.isReturning()) {
              if (currentMin != currentMax) {
                return format(
                    "JUMPF at section %d pc %d has a variable stack height %d/%d",
                    codeSectionToValidate, currentPC, currentMin, currentMax);
              }
              int expectedMax = toValidate.outputs + targetCs.inputs - targetCs.outputs;
              if (currentMax != expectedMax) {
                return format(
                    "%s JUMPF at section %d pc %d has incompatible stack height for returning section %d (%d != %d + %d - %d)",
                    currentMax < expectedMax ? "stack_underflow" : "stack_higher_than_outputs",
                    codeSectionToValidate,
                    currentPC,
                    jumpFTargetSectionNum,
                    currentMax,
                    toValidate.outputs,
                    targetCs.inputs,
                    targetCs.outputs);
              }
            } else {
              if (currentMin < targetCs.getInputs()) {
                return format(
                    "stack_underflow JUMPF at section %d pc %d has insufficient minimum stack height for non returning section %d (%d != %d)",
                    codeSectionToValidate,
                    currentPC,
                    jumpFTargetSectionNum,
                    currentMin,
                    targetCs.inputs);
              }
            }
          // fall through for terminal op handling
          case StopOperation.OPCODE,
          ReturnContractOperation.OPCODE,
          ReturnOperation.OPCODE,
          RevertOperation.OPCODE,
          InvalidOperation.OPCODE:
            // terminal op, reset currentMin and currentMax to forward set values
            if (nextPC < codeLength) {
              currentMax = stack_max[nextPC];
              currentMin = stack_min[nextPC];
            }
            break;
          default:
            // Ordinary operations, update stack for next operation
            if (nextPC < codeLength) {
              currentMax = max(stack_max[nextPC], currentMax);
              stack_max[nextPC] = currentMax;
              currentMin = min(stack_min[nextPC], currentMin);
              stack_min[nextPC] = min(stack_min[nextPC], currentMin);
            }
            break;
        }
        currentPC = nextPC;
      }

      if (maxStackHeight != toValidate.maxStackHeight) {
        return format(
            "invalid_max_stack_height Calculated (%d) != reported (%d)",
            maxStackHeight, toValidate.maxStackHeight);
      }
      if (unusedBytes != 0) {
        return format("Dead code detected in section %d", codeSectionToValidate);
      }

      return null;
    } catch (RuntimeException re) {
      re.printStackTrace();
      return "Internal Exception " + re.getMessage();
    }
  }
Validates the stack heights per <a href="https://eips.ethereum.org/EIPS/eip-5450">EIP-5450</a>.

<p>This presumes code validation has already been performed, so there are no RJUMPS into
immediates as well as no immediates falling off of the end of code sections.

@param codeSectionToValidate The index of code to validate in the code sections
@param eofLayout The EOF container to validate
@param workList The list of code sections needing validation
@return null if valid, otherwise an error string providing the validation error.
evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1Validation.java