code: /**
  Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags
  
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  
  http://www.apache.org/licenses/LICENSE-2.0
  
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  package bftsmart.statemanagement.strategy.durability;
  
  import bftsmart.consensus.messages.ConsensusMessage;
  import bftsmart.reconfiguration.ServerViewController;
  import bftsmart.statemanagement.ApplicationState;
  import bftsmart.tom.core.messages.TOMMessage;
  import bftsmart.tom.leaderchange.CertifiedDecision;
  import bftsmart.tom.server.defaultservices.CommandsInfo;
  import bftsmart.tom.util.BatchBuilder;
  
  import java.util.LinkedList;
  import java.util.Set;
  
  /**
   * Stores the data used to transfer the state to a recovering replica.
   * This class serves the three different seeders defined in the CST optimized
   * version for f=1.
   * In that version, the newest replica to take the checkpoint is expected to send
   * the hash of the checkpoint plus the upper portion of the log. The replica which
   * took the checkpoint before that, i.e., the middle replica is expected to send
   * the checkpoint it has plus hashes of the lower and upper portions of the log.
   * The oldest replica to take the checkpoint must send the lower portion of the
   * log.
   * This object must be passed to the state manager class which will combine the
   * replies from the seeders, validating the values and updating the state in the
   * leecher.
   * 
   * @author Marcel Santos
   *
   */
  public class CSTState implements ApplicationState {
  
      private static final long serialVersionUID = -7624656762922101703L;
  
      private final byte[] hashLogUpper;
      private final byte[] hashLogLower;
      private final byte[] hashCheckpoint;
  
      private final int checkpointCID;
      private final int lastCID;
  
      private final CommandsInfo[] logUpper;
      private final CommandsInfo[] logLower;
  
      private byte[] state;
  
      private final int pid;
      
      public CSTState(byte[] state, byte[] hashCheckpoint, CommandsInfo[] logLower, byte[] hashLogLower,
                      CommandsInfo[] logUpper, byte[] hashLogUpper, int checkpointCID, int lastCID, int pid) {
          setSerializedState(state);
          this.hashLogUpper = hashLogUpper;
          this.hashLogLower = hashLogLower;
          this.hashCheckpoint = hashCheckpoint;
          this.logUpper = logUpper;
          this.logLower = logLower;
          this.checkpointCID = checkpointCID;
          this.lastCID = lastCID;
          this.pid = pid;
      }
  
      @Override
      public boolean hasState() {
          return this.getSerializedState() != null;
      }
  
      @Override
      public byte[] getSerializedState() {
          return state;
      }
  
      @Override
      public byte[] getStateHash() {
          return hashCheckpoint;
      }
  
      @Override
      public void setSerializedState(byte[] state) {
          this.state = state;
      }
  
      @Override
      public int getLastCID() {
          return lastCID;
      }
  
      /**
       * Retrieves the certified decision for the last das present in this object
       * @param controller
       * @return The certified decision for the last das present in this object
       */
      @Override
      public CertifiedDecision getCertifiedDecision(ServerViewController controller) {
          CommandsInfo ci = getMessageBatch(getLastCID());
          if (ci != null && ci.msgCtx[0].getProof() != null) { // do I have a proof for the das?
              
              Set<ConsensusMessage> proof = ci.msgCtx[0].getProof();
              LinkedList<TOMMessage> requests = new LinkedList<>();
              
              //Recreate all TOMMessages ordered in the das
              for (int i = 0; i < ci.commands.length; i++) {
                  
                  requests.add(ci.msgCtx[i].recreateTOMMessage(ci.commands[i]));
                  
              }
              
              //Serialize the TOMMessages to re-create the proposed value
              BatchBuilder bb = new BatchBuilder(0);
              byte[] value = bb.makeBatch(requests, ci.msgCtx[0].getNumOfNonces(),
                      ci.msgCtx[0].getSeed(), ci.msgCtx[0].getTimestamp(), controller);
              
              //Assemble and return the certified decision
              return new CertifiedDecision(pid, getLastCID(), value, proof);
          }
          else return null; // there was no proof for the das
      }
  
      public int getCheckpointCID() {
          return checkpointCID;
      }
  
      /**
       * Retrieves the specified batch of messages
       * @param cid Consensus ID associated with the batch to be fetched
       * @return The batch of messages associated with the batch correspondent das ID
       */
      public CommandsInfo getMessageBatch(int cid) {
          if (cid >= checkpointCID && cid <= lastCID) {
              if(logLower != null) {
                  return logLower[cid - checkpointCID - 1];
              } else if(logUpper != null) {
                  return logUpper[cid - checkpointCID - 1];
              } else {
                  return null;
              }
          } else {
              return null;
          }
      }
  
      public byte[] getHashLogUpper() {
          return hashLogUpper;
      }
  
      public byte[] getHashLogLower() {
          return hashLogLower;
      }
  
      public CommandsInfo[] getLogUpper() {
          return logUpper;
      }
  
      public CommandsInfo[] getLogLower() {
          return logLower;
      }
  
      public byte[] getHashCheckpoint() {
          return hashCheckpoint;
      }
  }
  