class RewardTraceGenerator {

  public static Stream<Trace> generateFromBlock(
      final ProtocolSchedule protocolSchedule, final Block block) {

    final List<Trace> flatTraces = new ArrayList<>();

    final BlockHeader blockHeader = block.getHeader();
    final List<BlockHeader> ommers = block.getBody().getOmmers();
    final ProtocolSpec protocolSpec = protocolSchedule.getByBlockNumber(blockHeader.getNumber());
    final Wei blockReward = protocolSpec.getBlockReward();
    final MiningBeneficiaryCalculator miningBeneficiaryCalculator =
        protocolSpec.getMiningBeneficiaryCalculator();

    final Wei coinbaseReward =
        protocolSpec
            .getBlockProcessor()
            .getCoinbaseReward(blockReward, blockHeader.getNumber(), ommers.size());

    // add uncle reward traces
    ommers.forEach(
        ommerBlockHeader -> {
          final Wei ommerReward =
              protocolSpec
                  .getBlockProcessor()
                  .getOmmerReward(
                      blockReward, blockHeader.getNumber(), ommerBlockHeader.getNumber());
          final Action.Builder uncleActionBuilder =
              Action.builder()
                  .author(
                      miningBeneficiaryCalculator
                          .calculateBeneficiary(ommerBlockHeader)
                          .toHexString())
                  .rewardType(UNCLE_LABEL)
                  .value(ommerReward.toShortHexString());
          flatTraces.add(
              RewardTrace.builder()
                  .actionBuilder(uncleActionBuilder)
                  .blockHash(block.getHash().toHexString())
                  .blockNumber(blockHeader.getNumber())
                  .type(REWARD_LABEL)
                  .build());
        });

    // add block reward trace
    final Action.Builder blockActionBuilder =
        Action.builder()
            .author(miningBeneficiaryCalculator.calculateBeneficiary(blockHeader).toHexString())
            .rewardType(BLOCK_LABEL)
            .value(coinbaseReward.toShortHexString());
    flatTraces.add(
        0,
        RewardTrace.builder()
            .actionBuilder(blockActionBuilder)
            .blockHash(block.getHash().toHexString())
            .blockNumber(blockHeader.getNumber())
            .type(REWARD_LABEL)
            .build());

    return flatTraces.stream();
  }

}