code: def booster_fit(
      self, ctx: Context, bin_train_data: DataFrame, binning_dict: dict
  ):
      if self._tree_mode == GUEST_FEAT_ONLY:
          logger.info(
              "this tree is a guest feat only tree, skip computation"
          )
          masked_tree, _ = self._sync_nodes(ctx)
          self._nodes = masked_tree
          return
  
      train_df = bin_train_data
      feat_max_bin, max_bin = self._get_column_max_bin(binning_dict)
      sample_pos = self._init_sample_pos(train_df)
  
      # Get Encrypted Grad And Hess
      ret = self._get_gh(ctx)
      en_grad_and_hess: DataFrame = ret[0]
      self._gh_pack = ret[1]
      self._pk, self._evaluator = ctx.guest.get("en_kit")
      self._pack_info = ctx.guest.get("pack_info")
      split_info_pack = self._pack_info.get("split_info_pack", False)
      root_node = self._initialize_root_node(ctx, train_df)
  
      # init histogram builder
      self.hist_builder = SBTHistogramBuilder(
          bin_train_data,
          binning_dict,
          random_seed=self._random_seed,
          global_random_seed=self._global_random_seed,
          hist_sub=self._hist_sub,
      )
      # splitter
      self.splitter = SBTSplitter(bin_train_data, binning_dict)
  
      node_map = {}
      cur_layer_node = [root_node]
      en_grad_and_hess["cnt"] = 1
      for cur_depth, sub_ctx in ctx.on_iterations.ctxs_range(
          self.max_depth
      ):
          if len(cur_layer_node) == 0:
              logger.info("no nodes to split, stop training")
              break
  
          node_map = {
              n.nid: idx for idx, n in enumerate(cur_layer_node)
          }
          # compute histogram with encrypted grad and hess
          (
              hist_inst,
              statistic_histogram,
          ) = self.hist_builder.compute_hist(
              sub_ctx,
              cur_layer_node,
              train_df,
              en_grad_and_hess,
              sample_pos,
              node_map,
              pk=self._pk,
              evaluator=self._evaluator,
              gh_pack=self._gh_pack,
          )
  
          if split_info_pack:
              logger.debug("packing split info")
              statistic_histogram.i_squeeze(
                  {
                      "gh": (
                          self._pack_info["total_pack_num"],
                          self._pack_info["split_point_shift_bit"],
                      )
                  }
              )
  
          self.splitter.split(
              sub_ctx, statistic_histogram, cur_layer_node, node_map
          )
          cur_layer_node, next_layer_nodes = self._sync_nodes(sub_ctx)
          self._convert_split_id(
              sub_ctx,
              cur_layer_node,
              node_map,
              self.hist_builder,
              statistic_histogram,
              self.splitter,
              train_df,
          )
          self._update_host_feature_importance(
              sub_ctx, cur_layer_node, train_df
          )
          logger.info(
              "cur layer node num: {}, next layer node num: {}".format(
                  len(cur_layer_node), len(next_layer_nodes)
              )
          )
          sample_pos = self._update_sample_pos(
              sub_ctx, cur_layer_node, sample_pos, train_df, node_map
          )
          (
              train_df,
              sample_pos,
              en_grad_and_hess,
          ) = self._drop_samples_on_leaves(
              sample_pos, train_df, en_grad_and_hess
          )
          self._nodes += cur_layer_node
          cur_layer_node = next_layer_nodes
          logger.info(
              "layer {} done: next layer will split {} nodes, active samples num {}".format(
                  cur_depth, len(cur_layer_node), len(sample_pos)
              )
          )
  
      # sync complete tree
      if len(cur_layer_node) != 0:
          for node in cur_layer_node:
              node.is_leaf = True
              node.sitename = (
                  ctx.guest.party[0] + "_" + ctx.guest.party[1]
              )
              self._nodes.append(node)
  
      # convert bid to split value
      self._nodes = self._convert_bin_idx_to_split_val(
          ctx, self._nodes, binning_dict, bin_train_data.schema
      )
  
imports: from fate.ml.ensemble.learner.decision_tree.tree_core.decision_tree import DecisionTree, Node, _update_sample_pos_on_local_nodes, FeatureImportance
  from fate.ml.ensemble.learner.decision_tree.tree_core.hist import SBTHistogramBuilder, DistributedHistogram
  from fate.ml.ensemble.learner.decision_tree.tree_core.splitter import SBTSplitter
  from fate.ml.ensemble.learner.decision_tree.tree_core.decision_tree import ALL_FEAT, GUEST_FEAT_ONLY
  from fate.arch import Context
  from fate.arch.dataframe import DataFrame
  from typing import List
  import functools
  import logging
label_desc: Use image node_id 7 for calling the HeteroDecisionTreeHost obj's underlying member method code with example usage: obj.booster_fit(ctx, bin_train_data, binning_dict) without return types