code: package blockchain.data;
  
  import blockchain.Blockchain;
  import blockchain.Utils;
  import proto.types.block.*;
  import utils.statistics.BCStat;
  
  import java.io.IOException;
  import java.nio.file.Paths;
  import java.util.List;
  import static blockchain.Utils.createBlockchain;
  import static com.google.common.primitives.Ints.min;
  import static java.lang.Integer.max;
  
  public class BCS {
      static private Blockchain[] bcs;
      private final static Object newBlockNotifier = new Object();
      private static int n;
      private static int f;
      private static int workers;
  
      public BCS(int id, int n, int f, int workers) {
          BCS.n = n;
          BCS.f = f;
          BCS.workers = workers;
          bcs = new Blockchain[workers];
          for (int i = 0 ; i < workers ; i++) {
              bcs[i] = createBlockchain(Utils.BCT.SGC, id, max(f + 2, 10), // TODO: check the cache size
                      Paths.get("blocks", String.valueOf(i)));
          }
      }
  
      public static boolean validateBlockHash(int w, Block b) {
          return bcs[w].validateBlockHash(b);
      }
  
      public static boolean validateCurrentLeader(int w, int currLeader, int currF) {
          return bcs[w].validateCurrentLeader(currLeader, currF);
      }
  
      public static void addBlock(int w, Block b) {
          bcs[w].addBlock(b);
      }
  
      public static void removeBlock(int w, int h) throws IOException {
          bcs[w].removeBlock(h);
      }
  
      public static void setBlocks(int w, List<Block> blocks, int start) {
          bcs[w].setBlocks(blocks, start);
      }
  
      public static void notifyOnNewDefiniteBlock() {
          synchronized (newBlockNotifier) {
              newBlockNotifier.notifyAll();
          }
      }
  
      public static Block nbGetBlock(int w, int h) {
          return bcs[w].getBlock(h);
      }
  
      public static void setBlock(int w, int h, Block b) {
          bcs[w].setBlock(h, b);
      }
  
      public static boolean contains(int w, int h) {
          return bcs[w].contains(h);
      }
  
      public static List<Block> getBlocks(int w, int low, int high) {
          return bcs[w].getBlocks(low, high);
      }
  
      public static BCStat bGetBCStat(int w, int h) throws InterruptedException {
          synchronized (newBlockNotifier) {
              while (height() < h) {
                  newBlockNotifier.wait();
              }
              return bcs[w].getBlockSts(h);
          }
      }
  
      public static Block bGetBlock(int w, int h) throws InterruptedException {
          synchronized (newBlockNotifier) {
              while (height() < h) {
                  newBlockNotifier.wait();
              }
              return bcs[w].getBlock(h);
          }
      }
  
      public static int lastIndex(int w) {
          return bcs[w].lastIndex();
      }
  
  
      public static int height(int w) {
          return max(0, lastIndex(w) - (f + 2));
      }
  
      public static int height() {
          int mh = height(0);
          for (int i = 0 ; i < workers ; i++) {
              mh = min(mh, height(i));
          }
          return mh;
      }
  
      public static void writeNextToDiskAsync(int w) {
          bcs[w].writeNextToDiskAsync();
      }
  
      public static boolean isValid(int w) {
          return bcs[w].isValid();
      }
  
      public static boolean isValid() {
          for (int i = 0 ; i < workers ; i++) {
              if (!isValid(i)) return false;
          }
          return true;
      }
  }
  