/** * 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; import java.util.Collection; import java.util.HashMap; import java.util.Arrays; import java.util.Set; import bftsmart.consensus.messages.ConsensusMessage; import bftsmart.reconfiguration.ServerViewController; import bftsmart.reconfiguration.views.View; import bftsmart.statemanagement.ApplicationState; import bftsmart.statemanagement.SMMessage; import bftsmart.statemanagement.StateManager; import bftsmart.tom.core.DeliveryThread; import bftsmart.tom.core.TOMLayer; import bftsmart.tom.leaderchange.LCManager; import bftsmart.tom.leaderchange.CertifiedDecision; import bftsmart.tom.util.Logger; import bftsmart.tom.util.TOMUtil; /** * * @author Marcel Santos * */ public abstract class BaseStateManager implements StateManager { protected TOMLayer tomLayer; protected ServerViewController SVController; protected DeliveryThread dt; protected HashMap<Integer, ApplicationState> senderStates = null; protected HashMap<Integer, View> senderViews = null; protected HashMap<Integer, Integer> senderRegencies = null; protected HashMap<Integer, Integer> senderLeaders = null; protected HashMap<Integer, CertifiedDecision> senderProofs = null; protected boolean appStateOnly; protected int waitingCID = -1; protected int lastCID; protected ApplicationState state; protected boolean isInitializing = true; private HashMap<Integer, Integer> senderCIDs = null; public BaseStateManager() { senderStates = new HashMap<>(); senderViews = new HashMap<>(); senderRegencies = new HashMap<>(); senderLeaders = new HashMap<>(); senderProofs = new HashMap<>(); } protected int getReplies() { return senderStates.size(); } protected boolean enoughReplies() { return senderStates.size() > SVController.getCurrentViewF(); } protected boolean enoughRegencies(int regency) { return senderRegencies.size() > SVController.getQuorum(); } protected boolean enoughLeaders(int leader) { return senderLeaders.size() > SVController.getQuorum(); } protected boolean enoughViews(View view) { Collection<View> views = senderViews.values(); int counter = 0; for (View v : views) { if (view.equals(v)) { counter++; } } boolean result = counter > SVController.getQuorum(); return result; } // check if the consensus messages are consistent without checking the mac/signatures // if it is consistent, it returns the respective consensus ID; otherwise, returns -1 private int proofIsConsistent(Set<ConsensusMessage> proof) { int id = -1; byte[] value = null; for (ConsensusMessage cm : proof) { if (id == -1) id = cm.getNumber(); if (value == null) value = cm.getValue(); if (id != cm.getNumber() || !Arrays.equals(value, cm.getValue())) { return -1; // they are not consistent, so the proof is invalid } } // if the values are still these, this means the proof is empty, thus is invalid if (id == -1 || value == null) return -1; return id; } protected boolean enoughProofs(int cid, LCManager lc) { int counter = 0; for (CertifiedDecision cDec : senderProofs.values()) { if (cDec != null && cid == proofIsConsistent(cDec.getConsMessages()) && lc.hasValidProof(cDec)) { counter++; } } boolean result = counter > SVController.getQuorum(); return result; } /** * Clear the collections and state hold by this object. Calls clear() in the * States, Leaders, regencies and Views collections. Sets the state to * null; */ protected void reset() { senderStates.clear(); senderLeaders.clear(); senderRegencies.clear(); senderViews.clear(); senderProofs.clear(); state = null; } public Collection<ApplicationState> receivedStates() { return senderStates.values(); } @Override public void setLastCID(int cid) { lastCID = cid; } @Override public int getLastCID() { return lastCID; } @Override public void requestAppState(int cid) { lastCID = cid + 1; waitingCID = cid; System.out.println("waitingcid is now " + cid); appStateOnly = true; requestState(); } @Override public void analyzeState(int cid) { Logger.println("(TOMLayer.analyzeState) The state transfer protocol is enabled"); if (waitingCID == -1) { Logger.println("(TOMLayer.analyzeState) I'm not waiting for any state, so I will keep record of this message"); if (tomLayer.execManager.isDecidable(cid)) { System.out.println("BaseStateManager.analyzeState: I have now more than " + SVController.getCurrentViewF() + " messages for CID " + cid + " which are beyond CID " + lastCID); lastCID = cid; waitingCID = cid - 1; System.out.println("analyzeState " + waitingCID); requestState(); } } } @Override public abstract void init(TOMLayer tomLayer, DeliveryThread dt); @Override public boolean isRetrievingState() { if (isInitializing) { return true; } return waitingCID > -1; } @Override public void askCurrentConsensusId() { int me = SVController.getStaticConf().getProcessId(); int[] target = SVController.getCurrentViewAcceptors(); SMMessage currentCID = new StandardSMMessage(me, -1, TOMUtil.SM_ASK_INITIAL, 0, null, null, 0, 0); tomLayer.getCommunication().send(target, currentCID); target = SVController.getCurrentViewOtherAcceptors(); while (isInitializing) { tomLayer.getCommunication().send(target, currentCID); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void currentConsensusIdAsked(int sender) { int me = SVController.getStaticConf().getProcessId(); int lastConsensusId = tomLayer.getLastExec(); SMMessage currentCIDReply = new StandardSMMessage(me, lastConsensusId, TOMUtil.SM_REPLY_INITIAL, 0, null, null, 0, 0); tomLayer.getCommunication().send(new int[]{sender}, currentCIDReply); } @Override public synchronized void currentConsensusIdReceived(SMMessage smsg) { if (!isInitializing || waitingCID > -1) { return; } if (senderCIDs == null) { senderCIDs = new HashMap<>(); } senderCIDs.put(smsg.getSender(), smsg.getCID()); if (senderCIDs.size() >= SVController.getQuorum()) { HashMap<Integer, Integer> cids = new HashMap<>(); for (int id : senderCIDs.keySet()) { int value = senderCIDs.get(id); Integer count = cids.get(value); if (count == null) { cids.put(value, 0); } else { cids.put(value, count + 1); } } for (int key : cids.keySet()) { if (cids.get(key) >= SVController.getQuorum()) { if (key == lastCID) { System.out.println("-- Replica state is up to date"); dt.deliverLock(); isInitializing = false; tomLayer.setLastExec(key); dt.canDeliver(); dt.deliverUnlock(); break; } else { //ask for state System.out.println("-- Requesting state from other replicas"); lastCID = key + 1; if (waitingCID == -1) { waitingCID = key; requestState(); } } } } } } protected abstract void requestState(); @Override public abstract void stateTimeout(); @Override public abstract void SMRequestDeliver(SMMessage msg, boolean isBFT); @Override public abstract void SMReplyDeliver(SMMessage msg, boolean isBFT); }