package network.thunder.core.communication.processor.implementations.sync; import network.thunder.core.communication.objects.messages.impl.message.gossip.objects.P2PDataObject; import network.thunder.core.communication.objects.messages.impl.message.gossip.objects.PubkeyIPObject; import network.thunder.core.database.DBHandler; import network.thunder.core.etc.Tools; import java.util.*; /** * Structure for synchronizing new nodes. * <p> * While it is not important the new node gets a 100% consistent view of the network, it should still be fairly accurate. * If the topography after the complete sync is not good enough, we can always add further queries to it. */ public class SynchronizationHelper { private final static int NUMBER_OF_NODE_TO_SYNC_FROM = 10; private DBHandler dbHandler; private Map<Integer, List<P2PDataObject>> fragmentList = new HashMap<>(); private List<P2PDataObject> fullDataList = new ArrayList<>(); private Set<P2PDataObject> fullDataListSet = new HashSet<>(); private Map<Integer, Boolean> fragmentIsSyncedList = new HashMap<>(); private Map<Integer, Integer> fragmentJobList = new HashMap<>(); private List<PubkeyIPObject> ipObjectList = new ArrayList<>(); private boolean fullySynchronized = false; public SynchronizationHelper (DBHandler dbHandler) { this.dbHandler = dbHandler; resync(); } public synchronized int getNextFragmentIndexToSynchronize () { for (int i = 1; i < P2PDataObject.NUMBER_OF_FRAGMENTS + 1; i++) { if (!fragmentIsSyncedList.get(i)) { if ((Tools.currentTime() - fragmentJobList.get(i)) > 60) { //Give each fragment 60s to sync.. fragmentJobList.put(i, Tools.currentTime()); return i; } } } return 0; } public void resync () { for (int i = 1; i < P2PDataObject.NUMBER_OF_FRAGMENTS + 1; i++) { List<P2PDataObject> objList = new ArrayList<>(); fragmentList.put(i, objList); fragmentIsSyncedList.put(i, false); fragmentJobList.put(i, 0); } fullySynchronized = false; } public void newFragment (int index, List<P2PDataObject> newFragment) { for (P2PDataObject object : newFragment) { verifyDataObject(index, object); insertDataObjectInLists(object); } dbHandler.syncDatalist(newFragment); fragmentIsSyncedList.put(index, true); } public void newIPList (List<P2PDataObject> ipList) { for (P2PDataObject ip : ipList) { if (ip instanceof PubkeyIPObject) { ipObjectList.add((PubkeyIPObject) ip); } } dbHandler.insertIPObjects(ipList); } private boolean verifyDataObject (int index, P2PDataObject object) { try { //TODO: Probably a bottleneck here, maybe we can trust the data? if (index != object.getFragmentIndex()) { System.out.println("Object should not be in that index.. Is in: " + index + " Should be: " + object.getFragmentIndex()); } object.verify(); return true; } catch (Exception e) { e.printStackTrace(); } return false; } private void insertDataObjectInLists (P2PDataObject object) { List<P2PDataObject> objectArrayList = fragmentList.get(object.getFragmentIndex()); if (!objectArrayList.contains(object)) { objectArrayList.add(object); } if (!fullDataListSet.contains(object)) { fullDataList.add(object); } } public boolean fullySynchronized () { if (fullySynchronized) { return true; } for (int i = 1; i < P2PDataObject.NUMBER_OF_FRAGMENTS + 1; i++) { if (!fragmentIsSyncedList.get(i)) { return false; } } fullySynchronized = true; return true; } public void saveFullSyncToDatabase () { System.out.println("Received all sync data..."); //TODO: Validate all anchors we received. We need some kind of full blockchain to do that.. dbHandler.syncDatalist(fullDataList); } public List<P2PDataObject> getFragment (int index) { return dbHandler.getSyncDataByFragmentIndex(index); } public List<P2PDataObject> getIPAddresses () { return dbHandler.getSyncDataIPObjects(); } }