mirror of
				https://gitee.com/BDWare/cm
				synced 2025-11-04 02:22:14 +00:00 
			
		
		
		
	initial commit
This commit is contained in:
		
							parent
							
								
									35eed0d99a
								
							
						
					
					
						commit
						75cc8d39bc
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					/build/
 | 
				
			||||||
 | 
					*/build/*
 | 
				
			||||||
# Compiled class file
 | 
					# Compiled class file
 | 
				
			||||||
*.class
 | 
					*.class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										34
									
								
								build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								build.gradle
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					plugins {
 | 
				
			||||||
 | 
					    id 'java'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sourceSets {
 | 
				
			||||||
 | 
					    main {
 | 
				
			||||||
 | 
					        java {
 | 
				
			||||||
 | 
					            srcDirs 'src/main/java'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        resources {
 | 
				
			||||||
 | 
					            srcDir 'src/main/resources'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    test {
 | 
				
			||||||
 | 
					        java {
 | 
				
			||||||
 | 
					            srcDir 'src/test/java'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        resources {
 | 
				
			||||||
 | 
					            srcDir 'src/test/resources'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sourceCompatibility = 1.8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					repositories {
 | 
				
			||||||
 | 
					    mavenCentral()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dependencies {
 | 
				
			||||||
 | 
					    implementation project(":common")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    testImplementation 'junit:junit:4.13.2'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/main/java/org/bdware/sc/ChainOpener.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/main/java/org/bdware/sc/ChainOpener.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.OnHashCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface ChainOpener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void writeContractResultToLocalAndLedger(
 | 
				
			||||||
 | 
					            String result,
 | 
				
			||||||
 | 
					            ContractClient client,
 | 
				
			||||||
 | 
					            ContractRequest contractRequest,
 | 
				
			||||||
 | 
					            OnHashCallback cb, long start, long l);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void writeToChain(
 | 
				
			||||||
 | 
					            OnHashCallback cb,
 | 
				
			||||||
 | 
					            String from,
 | 
				
			||||||
 | 
					            String to,
 | 
				
			||||||
 | 
					            String data,
 | 
				
			||||||
 | 
					            String requestID,
 | 
				
			||||||
 | 
					            String namedLedger);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void writeToChainWithContract(
 | 
				
			||||||
 | 
					            OnHashCallback cb,
 | 
				
			||||||
 | 
					            String from,
 | 
				
			||||||
 | 
					            String to,
 | 
				
			||||||
 | 
					            String data,
 | 
				
			||||||
 | 
					            String requestID,
 | 
				
			||||||
 | 
					            String contractID,
 | 
				
			||||||
 | 
					            String namedLedger);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										501
									
								
								src/main/java/org/bdware/sc/ContractClient.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										501
									
								
								src/main/java/org/bdware/sc/ContractClient.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,501 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gson.JsonObject;
 | 
				
			||||||
 | 
					import com.google.gson.JsonPrimitive;
 | 
				
			||||||
 | 
					import com.google.gson.reflect.TypeToken;
 | 
				
			||||||
 | 
					import org.apache.commons.lang3.StringUtils;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractResult.Status;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.Contract;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractExecType;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.FunctionDesp;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.SocketGet;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.CMTables;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.KeyValueDBUtil;
 | 
				
			||||||
 | 
					import org.bdware.sc.encrypt.HardwareInfo;
 | 
				
			||||||
 | 
					import org.bdware.sc.encrypt.HardwareInfo.OSType;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.REvent.REventSemantics;
 | 
				
			||||||
 | 
					import org.bdware.sc.node.AnnotationNode;
 | 
				
			||||||
 | 
					import org.bdware.sc.node.YjsType;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.JsonUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayOutputStream;
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.PrintStream;
 | 
				
			||||||
 | 
					import java.lang.reflect.Field;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ContractClient {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = LogManager.getLogger(ContractClient.class);
 | 
				
			||||||
 | 
					    public static String cmi = "";
 | 
				
			||||||
 | 
					    public ContractMeta contractMeta;
 | 
				
			||||||
 | 
					    public boolean isDebug;
 | 
				
			||||||
 | 
					    transient SocketGet get;
 | 
				
			||||||
 | 
					    int port;
 | 
				
			||||||
 | 
					    String pid;
 | 
				
			||||||
 | 
					    transient ContractPrinter outputTracer;
 | 
				
			||||||
 | 
					    transient ContractPrinter errorTracer;
 | 
				
			||||||
 | 
					    Date timeTravel; // 当前所处时间
 | 
				
			||||||
 | 
					    transient Process process;
 | 
				
			||||||
 | 
					    long times = 0L;
 | 
				
			||||||
 | 
					    long traffic = 0L;
 | 
				
			||||||
 | 
					    long memory;
 | 
				
			||||||
 | 
					    //    boolean withInsnLimit; // 是否需要计算gas
 | 
				
			||||||
 | 
					    ContractStatus contractStatus;
 | 
				
			||||||
 | 
					    boolean isRunning;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     public boolean changeDumpPeriod(String period) {
 | 
				
			||||||
 | 
					    //        System.out.println("[ContractClient] changeDumpPeriod " + period);
 | 
				
			||||||
 | 
					    //        String res = get.syncGet("", "changeDumpPeriod",period);
 | 
				
			||||||
 | 
					    //        if(res.equals("success"))
 | 
				
			||||||
 | 
					    //            return true;
 | 
				
			||||||
 | 
					    //        return false;
 | 
				
			||||||
 | 
					    //    }
 | 
				
			||||||
 | 
					    long lastUpdate = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ContractClient(Contract c) {
 | 
				
			||||||
 | 
					        contractMeta = new ContractMeta();
 | 
				
			||||||
 | 
					        contractMeta.contract = c;
 | 
				
			||||||
 | 
					        contractMeta.id = contractMeta.contract.getID();
 | 
				
			||||||
 | 
					        // contractMeta will be rewrite later
 | 
				
			||||||
 | 
					        outputTracer = new ContractPrinter();
 | 
				
			||||||
 | 
					        errorTracer = new ContractPrinter();
 | 
				
			||||||
 | 
					        contractStatus = ContractStatus.Ready;
 | 
				
			||||||
 | 
					        memory = 0;
 | 
				
			||||||
 | 
					        timeTravel = new Date();
 | 
				
			||||||
 | 
					        isRunning = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Once the reconnect is called, all cp in the same node will reconnect to
 | 
				
			||||||
 | 
					    // current ContractManager
 | 
				
			||||||
 | 
					    // We only consider the.
 | 
				
			||||||
 | 
					    public ContractClient(int startPort) {
 | 
				
			||||||
 | 
					        // LOGGER.info("ContractClient  构造器 : ");
 | 
				
			||||||
 | 
					        contractMeta = new ContractMeta();
 | 
				
			||||||
 | 
					        outputTracer = new ContractPrinter();
 | 
				
			||||||
 | 
					        errorTracer = new ContractPrinter();
 | 
				
			||||||
 | 
					        contractStatus = ContractStatus.Ready;
 | 
				
			||||||
 | 
					        memory = 0;
 | 
				
			||||||
 | 
					        timeTravel = new Date();
 | 
				
			||||||
 | 
					        isRunning = false;
 | 
				
			||||||
 | 
					        port = startPort;
 | 
				
			||||||
 | 
					        // LOGGER.info("ContractClient----构造器----- 端口 " + startPort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        get = new SocketGet("127.0.0.1", startPort);
 | 
				
			||||||
 | 
					        // LOGGER.info("ContractClient----构造器----- position---2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String cpCMI = get.syncGet("", "isContractProcess", ""); // CMI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!cmi.equals(cpCMI)) {
 | 
				
			||||||
 | 
					            LOGGER.warn("contract listened to port " + startPort + " cpCMI = " + cpCMI);
 | 
				
			||||||
 | 
					            // LOGGER.info("ContractClient----构造器----- position---4");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        pid = get.syncGet("", "getPID", "");
 | 
				
			||||||
 | 
					        // LOGGER.info("ContractClient----构造器----- position---5");
 | 
				
			||||||
 | 
					        initProps();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // LOGGER.info("ContractClient----构造器----- position---6");
 | 
				
			||||||
 | 
					        // LOGGER.info("ContractClient----构造器----- position---7");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static boolean isBundlePath(String scriptStr) {
 | 
				
			||||||
 | 
					        return scriptStr.endsWith(".zip") || scriptStr.endsWith(".ypk");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static String getPid(Process process) {
 | 
				
			||||||
 | 
					        long pid = -1;
 | 
				
			||||||
 | 
					        Field field;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            Class<?> clazz = Class.forName("java.lang.UNIXProcess");
 | 
				
			||||||
 | 
					            field = clazz.getDeclaredField("pid");
 | 
				
			||||||
 | 
					            field.setAccessible(true);
 | 
				
			||||||
 | 
					            pid = (Integer) field.get(process);
 | 
				
			||||||
 | 
					        } catch (Throwable e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return String.valueOf(pid);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isProcessAlive() {
 | 
				
			||||||
 | 
					        return get != null && get.isAlive();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean killProcess() {
 | 
				
			||||||
 | 
					        process.destroy();
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void initProps() {
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----1");
 | 
				
			||||||
 | 
					        contractMeta.name = get.syncGet("", "getContractName", "");
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----2");
 | 
				
			||||||
 | 
					        String strEvent = get.syncGet("", "getDeclaredEvents", "");
 | 
				
			||||||
 | 
					        LOGGER.debug("event: " + strEvent);
 | 
				
			||||||
 | 
					        contractMeta.declaredEvents =
 | 
				
			||||||
 | 
					                JsonUtil.fromJson(
 | 
				
			||||||
 | 
					                        strEvent, new TypeToken<Map<String, REventSemantics>>() {
 | 
				
			||||||
 | 
					                        }.getType());
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----3");
 | 
				
			||||||
 | 
					        contractMeta.exportedFunctions =
 | 
				
			||||||
 | 
					                JsonUtil.fromJson(
 | 
				
			||||||
 | 
					                        get.syncGet("", "getExportedFunctions", ""),
 | 
				
			||||||
 | 
					                        new TypeToken<List<FunctionDesp>>() {
 | 
				
			||||||
 | 
					                        }.getType());
 | 
				
			||||||
 | 
					        contractMeta.dependentContracts = JsonUtil.fromJson(
 | 
				
			||||||
 | 
					                get.syncGet("", "getDependentContracts", ""),
 | 
				
			||||||
 | 
					                new TypeToken<Set<String>>() {
 | 
				
			||||||
 | 
					                }.getType());
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----4");
 | 
				
			||||||
 | 
					        contractMeta.logDetail = new HashMap<>();
 | 
				
			||||||
 | 
					        for (FunctionDesp desp : contractMeta.exportedFunctions) {
 | 
				
			||||||
 | 
					            StringBuilder str = new StringBuilder();
 | 
				
			||||||
 | 
					            for (AnnotationNode anno : desp.annotations) {
 | 
				
			||||||
 | 
					                if (anno.getType().equals("LogType")) {
 | 
				
			||||||
 | 
					                    for (String logType : anno.getArgs()) {
 | 
				
			||||||
 | 
					                        str.append(logType.replaceAll("\"", "")).append(";");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (anno.getType().equals("LogLocation")) {
 | 
				
			||||||
 | 
					                    for (String logLoc : anno.getArgs()) {
 | 
				
			||||||
 | 
					                        if (logLoc.equals("\"dataware\"")
 | 
				
			||||||
 | 
					                                || logLoc.equals("\"bdledger\"")
 | 
				
			||||||
 | 
					                                || logLoc.equals("\"bdledger:\"")) {
 | 
				
			||||||
 | 
					                            str.append("bdcontract;");
 | 
				
			||||||
 | 
					                        } else if (logLoc.startsWith("\"bdledger:") && logLoc.length() > 11) {
 | 
				
			||||||
 | 
					                            String tmp = logLoc.substring(1);
 | 
				
			||||||
 | 
					                            String tmp2 = tmp.substring(0, tmp.length() - 1);
 | 
				
			||||||
 | 
					                            str.append(tmp2).append(";");
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            contractMeta.logDetail.put(desp.functionName, str.toString());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----5");
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            String anno = get.syncGet("", "getAnnotations", "");
 | 
				
			||||||
 | 
					            contractMeta.annotations =
 | 
				
			||||||
 | 
					                    JsonUtil.fromJson(anno, new TypeToken<List<AnnotationNode>>() {
 | 
				
			||||||
 | 
					                    }.getType());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            // supoort contract process before version 0.70
 | 
				
			||||||
 | 
					            contractMeta.annotations = new ArrayList<>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----6");
 | 
				
			||||||
 | 
					        contractMeta.sigRequired = Boolean.parseBoolean(get.syncGet("", "isSigRequired", ""));
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----7");
 | 
				
			||||||
 | 
					        contractMeta.thisPermission = get.syncGet("", "showPermission", "");
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----8");
 | 
				
			||||||
 | 
					        isRunning = true;
 | 
				
			||||||
 | 
					        contractMeta.isDebug = Boolean.parseBoolean(get.syncGet("", "getDebug", ""));
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----9");
 | 
				
			||||||
 | 
					        get.syncGet("", "registerMangerPort", ContractManager.cPort.getCMPort() + "");
 | 
				
			||||||
 | 
					        contractMeta.contract =
 | 
				
			||||||
 | 
					                JsonUtil.fromJson(get.syncGet("", "getContract", ""), Contract.class);
 | 
				
			||||||
 | 
					        contractMeta.id = contractMeta.contract.getID();
 | 
				
			||||||
 | 
					        get.setOfflineExceptionHandler(
 | 
				
			||||||
 | 
					                new SocketGet.OfflineHandler() {
 | 
				
			||||||
 | 
					                    @Override
 | 
				
			||||||
 | 
					                    public void onException(SocketGet socketGet, Exception e) {
 | 
				
			||||||
 | 
					                        if (e.getMessage().contains("Connection refused")) {
 | 
				
			||||||
 | 
					                            contractMeta.status = ContractStatusEnum.HANGED;
 | 
				
			||||||
 | 
					                            ContractManager.instance.statusRecorder.resumeContractProcess(
 | 
				
			||||||
 | 
					                                    contractMeta.id);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					        loadTimesAndTraffic();
 | 
				
			||||||
 | 
					        // LOGGER.info("initProps  ----  position-----10");
 | 
				
			||||||
 | 
					        // LOGGER.debug("======= registerPort:" + ret + "-->" + ContractManager.startPort);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String startProcess(PrintStream ps) {
 | 
				
			||||||
 | 
					        isRunning = false;
 | 
				
			||||||
 | 
					        port = -1;
 | 
				
			||||||
 | 
					        LOGGER.info("port=" + port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String darg = "-Djava.library.path=";
 | 
				
			||||||
 | 
					        String classpath;
 | 
				
			||||||
 | 
					        File jniPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (ContractManager.yjsPath == null) {
 | 
				
			||||||
 | 
					            jniPath = new File("./jni/");
 | 
				
			||||||
 | 
					            classpath = System.getProperty("java.class.path");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            classpath = ContractManager.yjsPath;
 | 
				
			||||||
 | 
					            jniPath = new File(classpath).getParentFile();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String osJni = ((HardwareInfo.type == OSType.linux) ? "/jni/linux" : "/jni/mac");
 | 
				
			||||||
 | 
					        darg += jniPath.getAbsolutePath() + osJni;
 | 
				
			||||||
 | 
					        if (!new File(classpath).exists()) {
 | 
				
			||||||
 | 
					            ContractResult r =
 | 
				
			||||||
 | 
					                    new ContractResult(
 | 
				
			||||||
 | 
					                            Status.Exception, new JsonPrimitive("incorrect path: yjs.jar"));
 | 
				
			||||||
 | 
					            return JsonUtil.toJson(r);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!new File(jniPath, "libs").exists()) {
 | 
				
			||||||
 | 
					            ContractResult r =
 | 
				
			||||||
 | 
					                    new ContractResult(
 | 
				
			||||||
 | 
					                            Status.Exception,
 | 
				
			||||||
 | 
					                            new JsonPrimitive("incorrect path: yjs.jar, missing libs"));
 | 
				
			||||||
 | 
					            return JsonUtil.toJson(r);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        //        ProcessBuilder builder =
 | 
				
			||||||
 | 
					        //                new ProcessBuilder(
 | 
				
			||||||
 | 
					        //                        "java",
 | 
				
			||||||
 | 
					        //                        "-Dfile.encoding=UTF-8",
 | 
				
			||||||
 | 
					        //                        darg,
 | 
				
			||||||
 | 
					        //                        "-cp",
 | 
				
			||||||
 | 
					        //                        jniPath.getAbsolutePath() + "/libs/*:" + classpath,
 | 
				
			||||||
 | 
					        //                        "org.bdware.sc.ContractProcess",
 | 
				
			||||||
 | 
					        //                        "-port=" + cPort.getPort());
 | 
				
			||||||
 | 
					        int startPort = ContractManager.cPort.getPortAndInc();
 | 
				
			||||||
 | 
					        ProcessBuilder builder =
 | 
				
			||||||
 | 
					                new ProcessBuilder(
 | 
				
			||||||
 | 
					                        "java",
 | 
				
			||||||
 | 
					                        "-Dfile.encoding=UTF-8",
 | 
				
			||||||
 | 
					                        darg,
 | 
				
			||||||
 | 
					                        "-jar",
 | 
				
			||||||
 | 
					                        classpath,
 | 
				
			||||||
 | 
					                        "-port=" + startPort,
 | 
				
			||||||
 | 
					                        "-cmi=" + cmi, // cmi 区分不同CM的cp
 | 
				
			||||||
 | 
					                        (isDebug ? "-debug" : ""));
 | 
				
			||||||
 | 
					        File directory = new File("");
 | 
				
			||||||
 | 
					        LOGGER.debug("[CMD] path: " + directory.getAbsolutePath());
 | 
				
			||||||
 | 
					        LOGGER.debug(StringUtils.join(builder.command(), " "));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Map<String, String> map = builder.environment();
 | 
				
			||||||
 | 
					        map.put("java.library.path", jniPath.getAbsolutePath() + osJni);
 | 
				
			||||||
 | 
					        builder.directory(new File("./"));
 | 
				
			||||||
 | 
					        LOGGER.debug("start process:");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            process = builder.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.pid = getPid(process);
 | 
				
			||||||
 | 
					            LOGGER.info("[CP PPID  ] " + pid);
 | 
				
			||||||
 | 
					            PrintStream printStream = new PrintStream(process.getOutputStream());
 | 
				
			||||||
 | 
					            printStream.println("CP PID:" + pid);
 | 
				
			||||||
 | 
					            printStream.close();
 | 
				
			||||||
 | 
					            Scanner sc = new Scanner(process.getInputStream());
 | 
				
			||||||
 | 
					            String status = null;
 | 
				
			||||||
 | 
					            while (sc.hasNext()) {
 | 
				
			||||||
 | 
					                status = sc.nextLine();
 | 
				
			||||||
 | 
					                LOGGER.info("[CP] " + status);
 | 
				
			||||||
 | 
					                if (status.contains("mainPort")) {
 | 
				
			||||||
 | 
					                    try {
 | 
				
			||||||
 | 
					                        // Set contractPort to max(mainPort, contractPort)
 | 
				
			||||||
 | 
					                        int portIndex = status.indexOf("mainPort");
 | 
				
			||||||
 | 
					                        int port =
 | 
				
			||||||
 | 
					                                Integer.parseInt(
 | 
				
			||||||
 | 
					                                        status.substring(portIndex + 9, portIndex + 14)
 | 
				
			||||||
 | 
					                                                .replaceAll("\\s+", ""));
 | 
				
			||||||
 | 
					                        if (port != startPort) {
 | 
				
			||||||
 | 
					                            ContractManager.cPort.reSetPort(port + 1);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } catch (Exception ignored) {
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (status != null) {
 | 
				
			||||||
 | 
					                status = status.replaceAll(".*mainPort ", "");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            assert status != null;
 | 
				
			||||||
 | 
					            port = Integer.parseInt(status.split(" ")[0]);
 | 
				
			||||||
 | 
					            ContractManager.cPort.updateDb(port, true);
 | 
				
			||||||
 | 
					            get = new SocketGet("127.0.0.1", port);
 | 
				
			||||||
 | 
					            get.syncGet("", "setDBInfo", ContractManager.dbPath);
 | 
				
			||||||
 | 
					            String tagA = (ps == System.out ? "[Contract_" + port + "_out] " : "");
 | 
				
			||||||
 | 
					            String tagB = (ps == System.out ? "[Contract_" + port + "_err] " : "");
 | 
				
			||||||
 | 
					            outputTracer.track(process, sc, tagA, ps);
 | 
				
			||||||
 | 
					            errorTracer.track(process, new Scanner(process.getErrorStream()), tagB, ps);
 | 
				
			||||||
 | 
					            get.syncGet("", "registerMangerPort", String.valueOf(ContractManager.cPort.getCMPort()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isBundlePath(contractMeta.contract.getScriptStr())) {
 | 
				
			||||||
 | 
					                status =
 | 
				
			||||||
 | 
					                        get.syncGet(
 | 
				
			||||||
 | 
					                                "", "setContractBundle", JsonUtil.toJson(contractMeta.contract));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                status = get.syncGet("", "setContract", JsonUtil.toJson(contractMeta.contract));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            LOGGER.debug("port:" + port + " status:" + status);
 | 
				
			||||||
 | 
					            ContractResult r = JsonUtil.fromJson(status, ContractResult.class);
 | 
				
			||||||
 | 
					            if (r.status == Status.Success) {
 | 
				
			||||||
 | 
					                initProps();
 | 
				
			||||||
 | 
					                get.syncGet("", "setPID", pid);
 | 
				
			||||||
 | 
					            } else if (r.status == null) {
 | 
				
			||||||
 | 
					                r.status = Status.Error;
 | 
				
			||||||
 | 
					                r.result = new JsonPrimitive(status);
 | 
				
			||||||
 | 
					                status = JsonUtil.toJson(r);
 | 
				
			||||||
 | 
					                contractMeta.name = get.syncGet("", "getContractName", "");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return status;
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					            ByteArrayOutputStream bo = new ByteArrayOutputStream();
 | 
				
			||||||
 | 
					            e.printStackTrace(new PrintStream(bo));
 | 
				
			||||||
 | 
					            ContractResult r =
 | 
				
			||||||
 | 
					                    new ContractResult(Status.Exception, new JsonPrimitive(bo.toString()));
 | 
				
			||||||
 | 
					            return JsonUtil.toJson(r);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void updateMemory() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (System.currentTimeMillis() - lastUpdate < 2000) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            lastUpdate = System.currentTimeMillis();
 | 
				
			||||||
 | 
					            get.asyncGet(
 | 
				
			||||||
 | 
					                    "",
 | 
				
			||||||
 | 
					                    ".UsedMemory",
 | 
				
			||||||
 | 
					                    "",
 | 
				
			||||||
 | 
					                    new ResultCallback() {
 | 
				
			||||||
 | 
					                        @Override
 | 
				
			||||||
 | 
					                        public void onResult(String str) {
 | 
				
			||||||
 | 
					                            memory = Long.parseLong(str);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            // e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Map<String, REventSemantics> getEvents() {
 | 
				
			||||||
 | 
					        return contractMeta.declaredEvents;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public List<FunctionDesp> getExportedFunctions() {
 | 
				
			||||||
 | 
					        return contractMeta.exportedFunctions;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getIdentifier() {
 | 
				
			||||||
 | 
					        return get.syncGet("", "getIdentifier", " ");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setIdentifier(String alias) {
 | 
				
			||||||
 | 
					        get.asyncGet("", "setIdentifier", alias, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isUnit() {
 | 
				
			||||||
 | 
					        return contractMeta.contract.getType() != ContractExecType.Sole;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String signResult(String result) {
 | 
				
			||||||
 | 
					        return contractMeta.contract.signResult(result);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getPubkey() {
 | 
				
			||||||
 | 
					        return contractMeta.contract.getPublicKey();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getContractID() {
 | 
				
			||||||
 | 
					        return contractMeta.contract.getID();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getContractName() {
 | 
				
			||||||
 | 
					        return contractMeta.name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getContractDOI() {
 | 
				
			||||||
 | 
					        return contractMeta.contract.getDOI();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getContractKey() {
 | 
				
			||||||
 | 
					        return contractMeta.contract.getKey();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setContractKey(String key) {
 | 
				
			||||||
 | 
					        contractMeta.contract.setKey(key);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractExecType getContractType() {
 | 
				
			||||||
 | 
					        return contractMeta.contract.getType();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public List<AnnotationNode> getAnnotations() {
 | 
				
			||||||
 | 
					        return contractMeta.annotations;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getContractCopies() {
 | 
				
			||||||
 | 
					        return contractMeta.contract.getNumOfCopies();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getLogType(String action) {
 | 
				
			||||||
 | 
					        return contractMeta.logDetail.get(action);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isDebug() {
 | 
				
			||||||
 | 
					        return contractMeta.isDebug;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long getTimes() {
 | 
				
			||||||
 | 
					        return times;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long getTraffic() {
 | 
				
			||||||
 | 
					        return traffic;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public YjsType getYjsType() {
 | 
				
			||||||
 | 
					        return contractMeta.contract.getYjsType();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getPID() {
 | 
				
			||||||
 | 
					        return pid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getPort() {
 | 
				
			||||||
 | 
					        return "" + port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMemory() {
 | 
				
			||||||
 | 
					        return "" + memory;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void loadTimesAndTraffic() {
 | 
				
			||||||
 | 
					        String tempTime2 =
 | 
				
			||||||
 | 
					                KeyValueDBUtil.instance.getValue(
 | 
				
			||||||
 | 
					                        CMTables.ContractInfo.toString(), contractMeta.name + "-Times");
 | 
				
			||||||
 | 
					        String tempTraffic2 =
 | 
				
			||||||
 | 
					                KeyValueDBUtil.instance.getValue(
 | 
				
			||||||
 | 
					                        CMTables.ContractInfo.toString(), contractMeta.name + "-Traffic");
 | 
				
			||||||
 | 
					        if (tempTime2 != null && !tempTime2.equals("")) {
 | 
				
			||||||
 | 
					            times = Long.parseLong(tempTime2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (tempTraffic2 != null && !tempTraffic2.equals("")) {
 | 
				
			||||||
 | 
					            traffic = Long.parseLong(tempTraffic2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void saveTimesAndTraffic() {
 | 
				
			||||||
 | 
					        KeyValueDBUtil.instance.setValue(
 | 
				
			||||||
 | 
					                CMTables.ContractInfo.toString(), contractMeta.name + "-Times", times + "");
 | 
				
			||||||
 | 
					        KeyValueDBUtil.instance.setValue(
 | 
				
			||||||
 | 
					                CMTables.ContractInfo.toString(), contractMeta.name + "-Traffic", traffic + "");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setMask(JsonObject args) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //get.asyncGet("",,,,);
 | 
				
			||||||
 | 
					        get.asyncGet("", "setMask", args.toString(), null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setProjectConfig(String args) {
 | 
				
			||||||
 | 
					        get.asyncGet("", "setProjectConfig", args, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    //public String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static class ReqScript {
 | 
				
			||||||
 | 
					        String mode;
 | 
				
			||||||
 | 
					        String code;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2328
									
								
								src/main/java/org/bdware/sc/ContractManager.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2328
									
								
								src/main/java/org/bdware/sc/ContractManager.java
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										119
									
								
								src/main/java/org/bdware/sc/ContractMeta.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/main/java/org/bdware/sc/ContractMeta.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.Contract;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.FunctionDesp;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.IDSerializable;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.REvent;
 | 
				
			||||||
 | 
					import org.bdware.sc.node.AnnotationNode;
 | 
				
			||||||
 | 
					import org.bdware.sc.node.YjsType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ContractMeta implements IDSerializable {
 | 
				
			||||||
 | 
					    public Contract contract;
 | 
				
			||||||
 | 
					    ContractStatusEnum status;
 | 
				
			||||||
 | 
					    String name;
 | 
				
			||||||
 | 
					    String id;
 | 
				
			||||||
 | 
					    boolean isDebug;
 | 
				
			||||||
 | 
					    Map<String, REvent.REventSemantics> declaredEvents;
 | 
				
			||||||
 | 
					    List<FunctionDesp> exportedFunctions;
 | 
				
			||||||
 | 
					    Map<String, String> logDetail;
 | 
				
			||||||
 | 
					    List<AnnotationNode> annotations;
 | 
				
			||||||
 | 
					    Set<String> dependentContracts;
 | 
				
			||||||
 | 
					    transient Map<String, FunctionDesp> funCache;
 | 
				
			||||||
 | 
					    boolean sigRequired;
 | 
				
			||||||
 | 
					    String thisPermission; // 合约当前权限
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					       "name": "dx_substr",
 | 
				
			||||||
 | 
					       "parameter":
 | 
				
			||||||
 | 
					       {
 | 
				
			||||||
 | 
					           "columnIndex":5,
 | 
				
			||||||
 | 
					           "paras":["1","3"]
 | 
				
			||||||
 | 
					       }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    // Map<Object,Object>MaskInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractMeta() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractStatusEnum getStatus() {
 | 
				
			||||||
 | 
					        return status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getID() {
 | 
				
			||||||
 | 
					        return id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public List<AnnotationNode> getAnnotations() {
 | 
				
			||||||
 | 
					        return annotations;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public List<FunctionDesp> getExportedFunctions() {
 | 
				
			||||||
 | 
					        return exportedFunctions;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Set<String> getDependentContracts() { return dependentContracts; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Map<String, REvent.REventSemantics> getEvents() {
 | 
				
			||||||
 | 
					        return declaredEvents;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public YjsType getYjsType() {
 | 
				
			||||||
 | 
					        return contract.getYjsType();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getPubkey() {
 | 
				
			||||||
 | 
					        return contract.getPublicKey();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getThisPermission() {
 | 
				
			||||||
 | 
					        return thisPermission;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getName() {
 | 
				
			||||||
 | 
					        return name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean getIsDebug() {
 | 
				
			||||||
 | 
					        return isDebug;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public FunctionDesp getExportedFunction(String action) {
 | 
				
			||||||
 | 
					        if (funCache == null) funCache = new HashMap<>();
 | 
				
			||||||
 | 
					        FunctionDesp desp = funCache.get(action);
 | 
				
			||||||
 | 
					        if (desp != null) return desp;
 | 
				
			||||||
 | 
					        desp = seekFunction(action);
 | 
				
			||||||
 | 
					        if (desp != null) funCache.put(action, desp);
 | 
				
			||||||
 | 
					        return desp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // public setMask(){
 | 
				
			||||||
 | 
					    ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private FunctionDesp seekFunction(String action) {
 | 
				
			||||||
 | 
					        for (FunctionDesp desp : exportedFunctions) {
 | 
				
			||||||
 | 
					            if (desp != null && desp.functionName.equals(action)) return desp;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setContract(Contract c) {
 | 
				
			||||||
 | 
					        contract = c;
 | 
				
			||||||
 | 
					        id = c.getID();
 | 
				
			||||||
 | 
					        status = ContractStatusEnum.HANGED;
 | 
				
			||||||
 | 
					        isDebug = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setName(String name) {
 | 
				
			||||||
 | 
					        this.name = name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setStatus(ContractStatusEnum status) {
 | 
				
			||||||
 | 
					        this.status = status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								src/main/java/org/bdware/sc/ContractStatusEnum.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/main/java/org/bdware/sc/ContractStatusEnum.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum ContractStatusEnum {
 | 
				
			||||||
 | 
					    INIT(0, "初始化"),
 | 
				
			||||||
 | 
					    RUNNING(1, "内存运行中"),
 | 
				
			||||||
 | 
					    HANGED(2, "硬盘运行中"),
 | 
				
			||||||
 | 
					    KILLED(3, "已停止");
 | 
				
			||||||
 | 
					    private Integer code;
 | 
				
			||||||
 | 
					    private String desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ContractStatusEnum(Integer code, String desc) {
 | 
				
			||||||
 | 
					        this.code = code;
 | 
				
			||||||
 | 
					        this.desc = desc;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static ContractStatusEnum getContractStatusEnumByCode(Integer code) {
 | 
				
			||||||
 | 
					        if (code == null) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (ContractStatusEnum contractStatusEnum : ContractStatusEnum.values()) {
 | 
				
			||||||
 | 
					            if (contractStatusEnum.getCode().intValue() == code) {
 | 
				
			||||||
 | 
					                return contractStatusEnum;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Integer getCode() {
 | 
				
			||||||
 | 
					        return code;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setCode(Integer code) {
 | 
				
			||||||
 | 
					        this.code = code;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getDesc() {
 | 
				
			||||||
 | 
					        return desc;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setDesc(String desc) {
 | 
				
			||||||
 | 
					        this.desc = desc;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										423
									
								
								src/main/java/org/bdware/sc/ContractStatusRecorder.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										423
									
								
								src/main/java/org/bdware/sc/ContractStatusRecorder.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,423 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.apache.commons.lang3.StringUtils;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractPort.PortVisitor;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.Contract;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.CMTables;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.StatusRecorder;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.JsonUtil;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.LRUList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.text.SimpleDateFormat;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicInteger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author xiaoche (2021/4/25 18:05)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ContractStatusRecorder extends StatusRecorder<ContractMeta> {
 | 
				
			||||||
 | 
					    private static final String PREFIX = "CONTRACT_INFO_META_";
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = LogManager.getLogger(ContractStatusRecorder.class);
 | 
				
			||||||
 | 
					    public static AtomicInteger contractVersion = new AtomicInteger(0);
 | 
				
			||||||
 | 
					    // 对外会有一个getContractClient的过程,如果只是想用元信息的,改成getContractClientMeta
 | 
				
			||||||
 | 
					    // 如果是想调用的,再使用getContractClient。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     合约执行的状态 key:合同id value:合同状态
 | 
				
			||||||
 | 
					    //    private static final String DB_NAME = CMTables.ContractInfo.toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static {
 | 
				
			||||||
 | 
					        final Object flag = new Object();
 | 
				
			||||||
 | 
					        // 调度一个task,在delay(ms)后开始调度,每次调度完后,最少等待period(ms)后才开始调
 | 
				
			||||||
 | 
					        ContractManager.scheduledThreadPool.scheduleWithFixedDelay(
 | 
				
			||||||
 | 
					                () -> {
 | 
				
			||||||
 | 
					                    boolean cleared = dealTimerContractProcess();
 | 
				
			||||||
 | 
					                    if (cleared) {
 | 
				
			||||||
 | 
					                        synchronized (flag) {
 | 
				
			||||||
 | 
					                            try {
 | 
				
			||||||
 | 
					                                flag.wait(14000L);
 | 
				
			||||||
 | 
					                            } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                                e.printStackTrace();
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                0,
 | 
				
			||||||
 | 
					                1,
 | 
				
			||||||
 | 
					                TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // contract id to client, changes when some contract is started/stopped/resumed
 | 
				
			||||||
 | 
					    private final Map<String, ContractClient> id2ContractClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LRUList<ContractMeta> runningProcess;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractStatusRecorder(String dir) {
 | 
				
			||||||
 | 
					        super(dir, CMTables.ContractInfo.toString(), PREFIX);
 | 
				
			||||||
 | 
					        id2ContractClient = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					        runningProcess = new LRUList<>();
 | 
				
			||||||
 | 
					        LOGGER.info("Load Done!");
 | 
				
			||||||
 | 
					        LOGGER.debug("Load Done: " + JsonUtil.toJson(getStatus()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static boolean dealTimerContractProcess() {
 | 
				
			||||||
 | 
					        //        if (id2ContractStatus == null) {
 | 
				
			||||||
 | 
					        //            String value = readDataFromDB(id2ContractStatusKey);
 | 
				
			||||||
 | 
					        //            id2ContractStatus = GSON.fromJson(value, Map.class);
 | 
				
			||||||
 | 
					        //            if (id2ContractStatus == null) {
 | 
				
			||||||
 | 
					        //                return true;
 | 
				
			||||||
 | 
					        //            }
 | 
				
			||||||
 | 
					        //            for (Map.Entry<String, ContractStatusEnum> contractStatusEnumEntry :
 | 
				
			||||||
 | 
					        // id2ContractStatus.entrySet()) {
 | 
				
			||||||
 | 
					        //                //获取合同id
 | 
				
			||||||
 | 
					        //                String contractID = contractStatusEnumEntry.getKey();
 | 
				
			||||||
 | 
					        //                ContractStatusEnum contractStatusEnum =
 | 
				
			||||||
 | 
					        // contractStatusEnumEntry.getValue();
 | 
				
			||||||
 | 
					        //                //没有状态信息就停止
 | 
				
			||||||
 | 
					        //                if (contractStatusEnum == null) {
 | 
				
			||||||
 | 
					        //                    continue;
 | 
				
			||||||
 | 
					        //                }
 | 
				
			||||||
 | 
					        //                //拥有合同状态信息的,如果是已经执行结束,我们将合同挂起
 | 
				
			||||||
 | 
					        //                if
 | 
				
			||||||
 | 
					        // (contractStatusEnum.getCode().equals(ContractStatusEnum.STORE.getCode())) {
 | 
				
			||||||
 | 
					        //                    resumeContractProcess(contractID);
 | 
				
			||||||
 | 
					        //                } else if
 | 
				
			||||||
 | 
					        // (contractStatusEnum.getCode().equals(ContractStatusEnum.DONE.getCode())) {
 | 
				
			||||||
 | 
					        //                    hangUpContractProcess(contractID);
 | 
				
			||||||
 | 
					        //                }
 | 
				
			||||||
 | 
					        //            }
 | 
				
			||||||
 | 
					        //        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static String resumeStartContract(Contract contract) {
 | 
				
			||||||
 | 
					        return ContractManager.instance.startContractAndRedirect(contract, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean hasPID(int pid) {
 | 
				
			||||||
 | 
					        // TODO 是不是还有别的?这只是在内存里的哦。
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            for (ContractClient c : id2ContractClient.values()) {
 | 
				
			||||||
 | 
					                if (Integer.parseInt(c.getPID()) == pid) return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            //  e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void reSyncStatusAtStart() {
 | 
				
			||||||
 | 
					        List<String> toPrint = new ArrayList<>();
 | 
				
			||||||
 | 
					        for (ContractMeta meta : getStatus().values()) {
 | 
				
			||||||
 | 
					            toPrint.add(meta.status + " " + meta.id + " " + meta.name + " ");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        LOGGER.debug(JsonUtil.toPrettyJson(toPrint));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (String id : id2ContractClient.keySet()) {
 | 
				
			||||||
 | 
					            ContractMeta meta = getStatus().get(id);
 | 
				
			||||||
 | 
					            if (null == meta) {
 | 
				
			||||||
 | 
					                LOGGER.error("unknown id in meta db");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (meta != null && meta.status != ContractStatusEnum.RUNNING) {
 | 
				
			||||||
 | 
					                meta.status = ContractStatusEnum.RUNNING;
 | 
				
			||||||
 | 
					                updateValue(meta);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (ContractMeta meta : getStatus().values()) {
 | 
				
			||||||
 | 
					            if (meta.status == ContractStatusEnum.RUNNING) {
 | 
				
			||||||
 | 
					                if (id2ContractClient.get(meta.getID()) == null) {
 | 
				
			||||||
 | 
					                    meta.status = ContractStatusEnum.HANGED;
 | 
				
			||||||
 | 
					                    updateValue(meta);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void createContract(ContractClient client) {
 | 
				
			||||||
 | 
					        ContractMeta meta = client.contractMeta;
 | 
				
			||||||
 | 
					        contractVersion.getAndIncrement();
 | 
				
			||||||
 | 
					        meta.status = ContractStatusEnum.RUNNING;
 | 
				
			||||||
 | 
					        updateValue(meta);
 | 
				
			||||||
 | 
					        id2ContractClient.put(meta.id, client);
 | 
				
			||||||
 | 
					        runningProcess.add(meta);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void hangLeastUsedContractProcess() {
 | 
				
			||||||
 | 
					        ContractMeta meta = runningProcess.popOldest();
 | 
				
			||||||
 | 
					        while (null != meta && ContractStatusEnum.RUNNING != meta.status) {
 | 
				
			||||||
 | 
					            meta = runningProcess.popOldest();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (null != meta) {
 | 
				
			||||||
 | 
					            hangUpContractProcess(meta.id);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO index name to contract
 | 
				
			||||||
 | 
					    public ContractMeta getContractMeta(String idOrNameOrDOI) {
 | 
				
			||||||
 | 
					        if (idOrNameOrDOI == null) return null;
 | 
				
			||||||
 | 
					        ContractMeta meta = getStatus().get(idOrNameOrDOI);
 | 
				
			||||||
 | 
					        if (meta != null) return meta;
 | 
				
			||||||
 | 
					        for (ContractMeta cc : getStatus().values()) {
 | 
				
			||||||
 | 
					            if (cc.status == ContractStatusEnum.RUNNING || cc.status == ContractStatusEnum.HANGED)
 | 
				
			||||||
 | 
					                if (idOrNameOrDOI.equals(cc.name) || idOrNameOrDOI.equals(cc.contract.getDOI())) {
 | 
				
			||||||
 | 
					                    return cc;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (ContractMeta cc : getStatus().values()) {
 | 
				
			||||||
 | 
					            if (idOrNameOrDOI.equals(cc.name) || idOrNameOrDOI.equals(cc.contract.getDOI())) {
 | 
				
			||||||
 | 
					                return cc;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int stopAllContracts() {
 | 
				
			||||||
 | 
					        List<String> ids = new ArrayList<>(getStatus().keySet());
 | 
				
			||||||
 | 
					        for (String key : ids) {
 | 
				
			||||||
 | 
					            killContract(key);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        id2ContractClient.clear();
 | 
				
			||||||
 | 
					        return ids.size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String killContract(String idOrName) {
 | 
				
			||||||
 | 
					        ContractMeta meta = getContractMeta(idOrName);
 | 
				
			||||||
 | 
					        if (meta != null) return killContract(meta);
 | 
				
			||||||
 | 
					        return "no such contract " + idOrName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String killContract(ContractMeta meta) {
 | 
				
			||||||
 | 
					        contractVersion.getAndIncrement();
 | 
				
			||||||
 | 
					        meta.status = ContractStatusEnum.KILLED;
 | 
				
			||||||
 | 
					        updateValue(meta);
 | 
				
			||||||
 | 
					        ContractClient client = id2ContractClient.get(meta.id);
 | 
				
			||||||
 | 
					        if (client != null) {
 | 
				
			||||||
 | 
					            client.saveTimesAndTraffic();
 | 
				
			||||||
 | 
					            String ff = client.get.syncGet("", "getMemorySet", "");
 | 
				
			||||||
 | 
					            LOGGER.info("[killContract] memorySet : " + ff);
 | 
				
			||||||
 | 
					            if (null != ff && ff.contains("kill")) {
 | 
				
			||||||
 | 
					                // String[] strings = ff.split(";");
 | 
				
			||||||
 | 
					                LOGGER.info("need dump when stop");
 | 
				
			||||||
 | 
					                String contractName = client.getContractName();
 | 
				
			||||||
 | 
					                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd.HH:mm:ss");
 | 
				
			||||||
 | 
					                File f =
 | 
				
			||||||
 | 
					                        new File(
 | 
				
			||||||
 | 
					                                ContractManager.dir + "/memory/" + contractName,
 | 
				
			||||||
 | 
					                                df.format(new Date()));
 | 
				
			||||||
 | 
					                client.get.syncGet("", "getMemoryDump", f.getAbsolutePath());
 | 
				
			||||||
 | 
					                ContractManager.instance.addLocalContractLog("dumpContract", meta);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ContractManager.instance.invokeContractSuicide(client);
 | 
				
			||||||
 | 
					            ContractManager.cPort.updateDb(client.port, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        id2ContractClient.remove(meta.id);
 | 
				
			||||||
 | 
					        runningProcess.remove(meta);
 | 
				
			||||||
 | 
					        return "success";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int stopAllContractsWithOwner(String owner) {
 | 
				
			||||||
 | 
					        List<ContractMeta> toKill = new ArrayList<>();
 | 
				
			||||||
 | 
					        for (ContractMeta meta : getStatus().values()) {
 | 
				
			||||||
 | 
					            if (meta.contract.getOwner().equals(owner)) {
 | 
				
			||||||
 | 
					                toKill.add(meta);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (ContractMeta meta : toKill) {
 | 
				
			||||||
 | 
					            killContract(meta);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return toKill.size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractClient getContractClient(String idOrName) {
 | 
				
			||||||
 | 
					        if (StringUtils.isBlank(idOrName)) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // TODO ensure load contract client
 | 
				
			||||||
 | 
					        // TODO ensure load contract client
 | 
				
			||||||
 | 
					        // TODO ensure load contract client
 | 
				
			||||||
 | 
					        // TODO ensure load contract client
 | 
				
			||||||
 | 
					        ContractMeta meta = getContractMeta(idOrName);
 | 
				
			||||||
 | 
					        if (null == meta) {
 | 
				
			||||||
 | 
					            LOGGER.error("meta of contract " + idOrName + " not found!");
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return id2ContractClient.get(meta.id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void ensureRunning(ContractRequest contractRequest) {
 | 
				
			||||||
 | 
					        // 获取合同id
 | 
				
			||||||
 | 
					        String contractID = contractRequest.getContractID();
 | 
				
			||||||
 | 
					        ContractMeta meta = getContractMeta(contractID);
 | 
				
			||||||
 | 
					        contractID = meta.id;
 | 
				
			||||||
 | 
					        // 判断目前合同的状态信息
 | 
				
			||||||
 | 
					        if (meta.status == ContractStatusEnum.HANGED) {
 | 
				
			||||||
 | 
					            resumeContractProcess(contractID);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // 似乎这样就可以了?
 | 
				
			||||||
 | 
					        //        } else if (meta.status == ContractStatusEnum.RUNNING) {
 | 
				
			||||||
 | 
					        //            hangUpContractProcess(contractID);
 | 
				
			||||||
 | 
					        //        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 解决思路: 1、CMActions#在executeContract 执行ContractStatusRecorder.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void hangUpContractProcess(String contractID) {
 | 
				
			||||||
 | 
					        // dump stop 这里是正常流程代码
 | 
				
			||||||
 | 
					        //        ContractClient contractClient = getContractClientById(contractID);
 | 
				
			||||||
 | 
					        //        if (contractClient == null) {
 | 
				
			||||||
 | 
					        //            return;
 | 
				
			||||||
 | 
					        //        }
 | 
				
			||||||
 | 
					        //        //挂起
 | 
				
			||||||
 | 
					        //        Contract contract = contractClient.contract;
 | 
				
			||||||
 | 
					        //        ContractManager.instance.dumpContract(contract.getID(), null);
 | 
				
			||||||
 | 
					        //        setContractClient(contractID, contractClient);
 | 
				
			||||||
 | 
					        //        ContractManager.instance.hangUpStopContract(contract.getID());
 | 
				
			||||||
 | 
					        // todo 临时想强制测试下效果
 | 
				
			||||||
 | 
					        ContractMeta contractMeta = getContractMeta(contractID);
 | 
				
			||||||
 | 
					        // 挂起
 | 
				
			||||||
 | 
					        Contract contract = contractMeta.contract;
 | 
				
			||||||
 | 
					        // System.out.println("hangUp"+contract.Mask);
 | 
				
			||||||
 | 
					        if (contractMeta.status == ContractStatusEnum.RUNNING) {
 | 
				
			||||||
 | 
					            contractMeta.status = ContractStatusEnum.HANGED;
 | 
				
			||||||
 | 
					            updateValue(contractMeta);
 | 
				
			||||||
 | 
					            ContractClient client = id2ContractClient.get(contractMeta.id);
 | 
				
			||||||
 | 
					            if (null != client) {
 | 
				
			||||||
 | 
					                client.saveTimesAndTraffic();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd.HH_mm_ss"); // 设置日期格式
 | 
				
			||||||
 | 
					                File f =
 | 
				
			||||||
 | 
					                        new File(
 | 
				
			||||||
 | 
					                                ContractManager.dir + "/memory/" + contractMeta.name,
 | 
				
			||||||
 | 
					                                df.format(new Date()));
 | 
				
			||||||
 | 
					                File parent = f.getParentFile();
 | 
				
			||||||
 | 
					                if (!parent.exists()) {
 | 
				
			||||||
 | 
					                    LOGGER.trace("make directory " + parent.getAbsolutePath() + ":" + parent.mkdirs());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ContractManager.instance.dumpContract(contract.getID(), f.getAbsolutePath());
 | 
				
			||||||
 | 
					                ContractManager.instance.invokeContractSuicide(client);
 | 
				
			||||||
 | 
					                id2ContractClient.remove(contractMeta.getID());
 | 
				
			||||||
 | 
					                runningProcess.remove(contractMeta);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized void resumeContractProcess(String contractID) {
 | 
				
			||||||
 | 
					        // 从参数获取合同名称
 | 
				
			||||||
 | 
					        ContractMeta meta = getContractMeta(contractID);
 | 
				
			||||||
 | 
					        // 启动合约
 | 
				
			||||||
 | 
					        // 加载所需要的内存
 | 
				
			||||||
 | 
					        // 该方法调用方法已经考虑了内存问题,但是内存不足的时候,重试或者等待,或者将合约压到失败中稍后重试,可以尝试设计消息重试机制
 | 
				
			||||||
 | 
					        // 如果是内存中则唤起合同信息 todo 内存不足  关闭合约追赶,补偿数据
 | 
				
			||||||
 | 
					        if (meta.status == ContractStatusEnum.HANGED || meta.status == ContractStatusEnum.KILLED) {
 | 
				
			||||||
 | 
					            ContractStatusEnum preStatus = meta.status;
 | 
				
			||||||
 | 
					            // TODO Resume里复用的是start逻辑,启动完了就成Running了哦。可能不能这么来。
 | 
				
			||||||
 | 
					            String startContractResult = resumeStartContract(meta.contract);
 | 
				
			||||||
 | 
					            ContractResult contractResult =
 | 
				
			||||||
 | 
					                    JsonUtil.fromJson(startContractResult, ContractResult.class);
 | 
				
			||||||
 | 
					            if (contractResult.status == ContractResult.Status.Error) {
 | 
				
			||||||
 | 
					                // 记录错误日志
 | 
				
			||||||
 | 
					                ContractManager.instance.addLocalContractLog(
 | 
				
			||||||
 | 
					                        "resumeContract error",
 | 
				
			||||||
 | 
					                        meta.contract.getID(),
 | 
				
			||||||
 | 
					                        meta.name,
 | 
				
			||||||
 | 
					                        meta.contract.getOwner());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                ContractManager.instance.addLocalContractLog(
 | 
				
			||||||
 | 
					                        "resumeContract success",
 | 
				
			||||||
 | 
					                        meta.contract.getID(),
 | 
				
			||||||
 | 
					                        meta.name,
 | 
				
			||||||
 | 
					                        meta.contract.getOwner());
 | 
				
			||||||
 | 
					                // TODO 可能会重复load相同的镜像?
 | 
				
			||||||
 | 
					                // 如果是killed 只根据manifest去判断是否加载memory
 | 
				
			||||||
 | 
					                // 可增加一个判断,如果hanged朋manifest里是加载memory,这里就不再加载了。
 | 
				
			||||||
 | 
					                ContractClient client = id2ContractClient.get(meta.getID());
 | 
				
			||||||
 | 
					                if (preStatus == ContractStatusEnum.HANGED) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    ContractManager.instance.loadMemory(
 | 
				
			||||||
 | 
					                            client, ContractManager.instance.findNewestMemory(meta.name));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                client.contractMeta.setStatus(ContractStatusEnum.RUNNING);
 | 
				
			||||||
 | 
					                updateValue(client.contractMeta);
 | 
				
			||||||
 | 
					                // 还要判断一发,是不是已有permission的值。如果有,要setPermission一把。
 | 
				
			||||||
 | 
					                //                meta.status = ContractStatusEnum.RUNNING;
 | 
				
			||||||
 | 
					                //                updateValue(meta);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PortVisitor getVisitor() {
 | 
				
			||||||
 | 
					        return new ReconnectVisitor();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ReconnectVisitor implements PortVisitor {
 | 
				
			||||||
 | 
					        private final Set<Integer> existed = new HashSet<>(); // 已经连上的合约端口
 | 
				
			||||||
 | 
					        private final Set<String> contractIDs = new HashSet<>(); // 已经连上的合约id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReconnectVisitor() {
 | 
				
			||||||
 | 
					            for (ContractClient c : id2ContractClient.values()) {
 | 
				
			||||||
 | 
					                existed.add(c.port);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public boolean visit(int i) {
 | 
				
			||||||
 | 
					            if (i == ContractManager.cPort.getCMPort()) {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (existed.contains(i)) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                ContractClient client = new ContractClient(i);
 | 
				
			||||||
 | 
					                // LOGGER.info("[CM重连] position-----1");
 | 
				
			||||||
 | 
					                if (client.isRunning) {
 | 
				
			||||||
 | 
					                    LOGGER.debug("CP listened to port " + i + " is running");
 | 
				
			||||||
 | 
					                    if (contractIDs.contains(client.getContractID())) {
 | 
				
			||||||
 | 
					                        LOGGER.info("find contract process with duplicate contractID, kill!");
 | 
				
			||||||
 | 
					                        ContractManager.instance.invokeContractSuicide(client);
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    contractIDs.add(client.getContractID());
 | 
				
			||||||
 | 
					                    if (client.contractMeta.name.equals("analysis_client")) {
 | 
				
			||||||
 | 
					                        LOGGER.info("kill analysis_client");
 | 
				
			||||||
 | 
					                        ContractManager.instance.invokeContractSuicide(client);
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    LOGGER.debug(
 | 
				
			||||||
 | 
					                            String.format(
 | 
				
			||||||
 | 
					                                    "CP listened to port %d:\n\tID=%s\n\tName=%s\n\tType=%s\n"
 | 
				
			||||||
 | 
					                                            + "\tKey=%s\n\tPubKey=%s\n\tCopies=%d\n\t%s",
 | 
				
			||||||
 | 
					                                    i,
 | 
				
			||||||
 | 
					                                    client.getContractID(),
 | 
				
			||||||
 | 
					                                    client.contractMeta.name,
 | 
				
			||||||
 | 
					                                    client.getContractType(),
 | 
				
			||||||
 | 
					                                    client.getContractKey(),
 | 
				
			||||||
 | 
					                                    client.getPubkey(),
 | 
				
			||||||
 | 
					                                    client.getContractCopies(),
 | 
				
			||||||
 | 
					                                    client.contractMeta.contract.startInfo));
 | 
				
			||||||
 | 
					                    client.get.syncGet("", "setDir", ContractManager.dir);
 | 
				
			||||||
 | 
					                    //                    String str = client.getIdentifier();
 | 
				
			||||||
 | 
					                    client.get.syncGet("", "getDumpPeriod", "a");
 | 
				
			||||||
 | 
					                    client.get.syncGet("", "startAutoDump", "a");
 | 
				
			||||||
 | 
					                    createContract(client);
 | 
				
			||||||
 | 
					                    LOGGER.info(
 | 
				
			||||||
 | 
					                            String.format("reconnect to port %d: contract %s",
 | 
				
			||||||
 | 
					                                    i, client.contractMeta.name));
 | 
				
			||||||
 | 
					                    return true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                //just ignore
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/main/java/org/bdware/sc/MasterElectTimeRecorder.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/main/java/org/bdware/sc/MasterElectTimeRecorder.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//Node
 | 
				
			||||||
 | 
					public class MasterElectTimeRecorder {
 | 
				
			||||||
 | 
					    public static Long findMasterCrash;   //发现master崩溃的时间
 | 
				
			||||||
 | 
					    public static Long newMasterStart;    //作为新的master newMasterStart被调用时间
 | 
				
			||||||
 | 
					    public static Long setLocalMaster;    //在本地标记自己为master
 | 
				
			||||||
 | 
					    public static Long slaveConnectFinish;   //被slave连接完成
 | 
				
			||||||
 | 
					    public static Long masterStartRecover;   //开始master恢复
 | 
				
			||||||
 | 
					    public static Long masterRecoverFinish;   //master恢复结束
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/main/java/org/bdware/sc/MasterStub.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/main/java/org/bdware/sc/MasterStub.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface MasterStub {
 | 
				
			||||||
 | 
					    // String executeGlobally(ContractRequest c, OnHashCallback cb);
 | 
				
			||||||
 | 
					    void executeByMaster(ContractClient client, ResultCallback rcb, ContractRequest c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void transferToOtherNode(String pubKey, String contractID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void executeByOtherNodeAsync(String pubKey, ContractRequest c, ResultCallback cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean hasConnection(String pubKey);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										47
									
								
								src/main/java/org/bdware/sc/MultiContractRecorder.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/main/java/org/bdware/sc/MultiContractRecorder.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.CMTables;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.KeyValueDBUtil;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.StatusRecorder;
 | 
				
			||||||
 | 
					import org.bdware.sc.units.MultiContractMeta;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MultiContractRecorder extends StatusRecorder<MultiContractMeta> {
 | 
				
			||||||
 | 
					    static final String dbName = CMTables.UnitContracts.toString();
 | 
				
			||||||
 | 
					    static final String prefix = "Multi_C_Meta_";
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = LogManager.getLogger(MultiContractRecorder.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MultiContractRecorder(String dir) {
 | 
				
			||||||
 | 
					        super(dir, dbName, prefix);
 | 
				
			||||||
 | 
					        for (MultiContractMeta meta : getStatus().values()) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                meta.initQueue();
 | 
				
			||||||
 | 
					                int lastExeSeq =
 | 
				
			||||||
 | 
					                        Integer.parseInt(
 | 
				
			||||||
 | 
					                                KeyValueDBUtil.instance.getValue(
 | 
				
			||||||
 | 
					                                        CMTables.LastExeSeq.toString(), meta.getContractID()));
 | 
				
			||||||
 | 
					                meta.setLastExeSeq(lastExeSeq);
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                e.printStackTrace();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MultiContractMeta getMultiContractMeta(String idOrNameOrDOI) {
 | 
				
			||||||
 | 
					        if (idOrNameOrDOI == null) return null;
 | 
				
			||||||
 | 
					        ContractMeta meta = ContractManager.instance.statusRecorder.getContractMeta(idOrNameOrDOI);
 | 
				
			||||||
 | 
					        if (meta == null) return null;
 | 
				
			||||||
 | 
					        return getStatus().get(meta.id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MultiContractMeta createIfNotExist(String contractID) {
 | 
				
			||||||
 | 
					        MultiContractMeta ret = getMultiContractMeta(contractID);
 | 
				
			||||||
 | 
					        if (null == ret) {
 | 
				
			||||||
 | 
					            LOGGER.info("requests don't contain contract " + contractID);
 | 
				
			||||||
 | 
					            ret = new MultiContractMeta(contractID);
 | 
				
			||||||
 | 
					            updateValue(ret);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/main/java/org/bdware/sc/ProjectRecorder.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/main/java/org/bdware/sc/ProjectRecorder.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ProjectConfig;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.CMTables;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.StatusRecorder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ProjectRecorder extends StatusRecorder<ProjectConfig> {
 | 
				
			||||||
 | 
					    static final String dbName = CMTables.ProjectConfig.toString();
 | 
				
			||||||
 | 
					    static final String prefix = "Project_Config_";
 | 
				
			||||||
 | 
					//    private static final Logger LOGGER = LogManager.getLogger(ProjectRecorder.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ProjectRecorder(String dir) {
 | 
				
			||||||
 | 
					        super(dir, dbName, prefix);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ProjectConfig getProjectConfig(String idOrNameOrDOI) {
 | 
				
			||||||
 | 
					        if (null == idOrNameOrDOI) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ContractMeta meta = ContractManager.instance.statusRecorder.getContractMeta(idOrNameOrDOI);
 | 
				
			||||||
 | 
					        if (meta == null) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (null == meta.contract.sourcePath) {
 | 
				
			||||||
 | 
					            if (meta.contract.getScriptStr().contains("public")) {
 | 
				
			||||||
 | 
					                meta.contract.sourcePath = "public/" + meta.name;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                meta.contract.sourcePath = meta.contract.getPublicKey() + "/" + meta.name;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ProjectConfig config = getStatus().get(meta.contract.sourcePath);
 | 
				
			||||||
 | 
					        if (config == null) {
 | 
				
			||||||
 | 
					            return createDefaultConfig(meta.contract.sourcePath);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return config;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ProjectConfig createDefaultConfig(String sourcePath) {
 | 
				
			||||||
 | 
					        ProjectConfig config = new ProjectConfig(sourcePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        updateValue(config);
 | 
				
			||||||
 | 
					        return config;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										44
									
								
								src/main/java/org/bdware/sc/RecoverMechTimeRecorder.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/main/java/org/bdware/sc/RecoverMechTimeRecorder.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class RecoverMechTimeRecorder {
 | 
				
			||||||
 | 
					    //slave记录
 | 
				
			||||||
 | 
					    public static Long startCMHttpServer;   //启动
 | 
				
			||||||
 | 
					    public static Long startFinish;   //启动完成,开始连接NC
 | 
				
			||||||
 | 
					    public static Long connectNCFinish;    //连接NC完成
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Long startReconnectCP;    //开始重连CP
 | 
				
			||||||
 | 
					    public static Long reconnectCPFinish;   //重连CP完成
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Long startQueryMaster;   //开始查看master是谁
 | 
				
			||||||
 | 
					    public static Long queryMasterFinish;   //查到master是谁,开始连接master
 | 
				
			||||||
 | 
					    public static Long connectMasterFinish;    //连接master完成
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Long startRecover;    //开始恢复,slave的askForRecover被调用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Long startCommonRecover;   //slave开始从common恢复
 | 
				
			||||||
 | 
					    public static Long startStableRecover;   //slave开始从stable恢复
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Long stableLoadFinish;   //stable恢复模式load完成
 | 
				
			||||||
 | 
					    public static Long stableRedoFinish;   //stable恢复模式redo完成
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Long startRedoTransFromMaster;   //redo从master传来的trans
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //master记录,key是slave的pubKey
 | 
				
			||||||
 | 
					    public static Map<String,Long> masterStartRecoverNode = new HashMap<String, Long>();    //master的askForRecover被调用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Map<String,Long> startJudgeRecoverMethod = new HashMap<String,Long>();   //开始判断某个节点的恢复方式
 | 
				
			||||||
 | 
					    public static Map<String,Long> judgeRecoverMethodFinish = new HashMap<String,Long>();   //判断某节点恢复方式完成
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Map<String,Long> startRecoverFromCommon =  new HashMap<String,Long>();    //slave从common恢复master的nodeRestartFromCommonMode方法开始时间
 | 
				
			||||||
 | 
					    public static Map<String,Long> writeCEIStart = new HashMap<String, Long>();  //开始dumpContract等cei信息
 | 
				
			||||||
 | 
					    public static Map<String,Long> finishWriteCEI = new HashMap<String, Long>();  //将cei写入文件完成
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Map<String,Long> startRecoverFromStable =  new HashMap<String,Long>();    //slave从common恢复master的restartFromStableMode方法开始时间
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Map<String,Long> recoverFinish = new HashMap<String,Long>();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										479
									
								
								src/main/java/org/bdware/sc/event/EventBroker.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										479
									
								
								src/main/java/org/bdware/sc/event/EventBroker.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,479 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gson.JsonObject;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractClient;
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractManager;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.CMTables;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.clients.ContractConsumer;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.clients.IEventConsumer;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.clients.NodeConsumer;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.HashUtil;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.JsonUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.bdware.sc.event.REvent.REventSemantics.NEED_RETRY;
 | 
				
			||||||
 | 
					import static org.bdware.sc.event.REvent.REventSemantics.ONLY_ONCE;
 | 
				
			||||||
 | 
					import static org.bdware.sc.event.REvent.REventType.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Kaidong Wu
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class EventBroker {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = LogManager.getLogger(EventBroker.class);
 | 
				
			||||||
 | 
					    private static final long EXPIRED_TIME = 90 * 1000L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final EventCenter center;
 | 
				
			||||||
 | 
					    private final EventRecorder recorder;
 | 
				
			||||||
 | 
					    private final Map<String, Set<String>> topic2cIds;
 | 
				
			||||||
 | 
					    private final Map<String, IEventConsumer> id2Consumers;
 | 
				
			||||||
 | 
					    private final Map<String, ThreadFlag> threadFlags;
 | 
				
			||||||
 | 
					    private final Map<String, Long> tempTopics;
 | 
				
			||||||
 | 
					    //    private final Map<String, Stack<REvent>> client2Events;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public EventBroker() {
 | 
				
			||||||
 | 
					        center = new EventCenter();
 | 
				
			||||||
 | 
					        recorder = new EventRecorder(CMTables.EventRegistry.toString(), this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        threadFlags = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // recover registries from database
 | 
				
			||||||
 | 
					        EventRecorder.CheckPoint cp = recorder.recoverRegistryFromDb();
 | 
				
			||||||
 | 
					        topic2cIds = cp.topic2cIds;
 | 
				
			||||||
 | 
					        id2Consumers = cp.id2Consumers;
 | 
				
			||||||
 | 
					        tempTopics = recorder.recoverTempTopicsFromDb();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // regularly check temporary topics and clean them
 | 
				
			||||||
 | 
					        ContractManager.scheduledThreadPool.scheduleWithFixedDelay(
 | 
				
			||||||
 | 
					                () -> {
 | 
				
			||||||
 | 
					                    long current = System.currentTimeMillis();
 | 
				
			||||||
 | 
					                    int oldSize = tempTopics.size();
 | 
				
			||||||
 | 
					                    tempTopics.keySet().forEach(topic -> {
 | 
				
			||||||
 | 
					                        if (tempTopics.get(topic) + EXPIRED_TIME > current) {
 | 
				
			||||||
 | 
					                            String reqID =
 | 
				
			||||||
 | 
					                                    ContractManager.instance.nodeCenterConn.getNodeKeyPair().getPublicKeyStr() +
 | 
				
			||||||
 | 
					                                            "_" + System.currentTimeMillis();
 | 
				
			||||||
 | 
					                            REvent cleanEvent =
 | 
				
			||||||
 | 
					                                    new REvent(
 | 
				
			||||||
 | 
					                                            topic,
 | 
				
			||||||
 | 
					                                            UNSUBSCRIBE,
 | 
				
			||||||
 | 
					                                            null,
 | 
				
			||||||
 | 
					                                            reqID);
 | 
				
			||||||
 | 
					                            cleanEvent.doSignature(ContractManager.instance.nodeCenterConn.getNodeKeyPair());
 | 
				
			||||||
 | 
					                            handle(cleanEvent);
 | 
				
			||||||
 | 
					                            tempTopics.remove(topic);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    if (oldSize != tempTopics.size()) {
 | 
				
			||||||
 | 
					                        recorder.saveTempTopics(tempTopics);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                0L,
 | 
				
			||||||
 | 
					                EXPIRED_TIME,
 | 
				
			||||||
 | 
					                TimeUnit.MILLISECONDS);
 | 
				
			||||||
 | 
					        // regularly create check point in database
 | 
				
			||||||
 | 
					        ContractManager.scheduledThreadPool.scheduleAtFixedRate(
 | 
				
			||||||
 | 
					                () -> recorder.createCheckPoint(topic2cIds, id2Consumers),
 | 
				
			||||||
 | 
					                EXPIRED_TIME,
 | 
				
			||||||
 | 
					                EXPIRED_TIME,
 | 
				
			||||||
 | 
					                TimeUnit.MILLISECONDS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        NodeConsumer.setCenter(center);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//        client2Events = new HashMap<>();
 | 
				
			||||||
 | 
					        LOGGER.info("Event Broker starts!");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * handle the event after checking the signature
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param event event request
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void handle(REvent event) {
 | 
				
			||||||
 | 
					        if (!event.verifySignature()) {
 | 
				
			||||||
 | 
					            LOGGER.debug(JsonUtil.toJson(event));
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String topic = event.getTopic();
 | 
				
			||||||
 | 
					        switch (event.getType()) {
 | 
				
			||||||
 | 
					            case SUBSCRIBE:
 | 
				
			||||||
 | 
					                if (null != topic && !topic.isEmpty()) {
 | 
				
			||||||
 | 
					                    doSubscribe(event);
 | 
				
			||||||
 | 
					                    // save & try to sub in center
 | 
				
			||||||
 | 
					                    recorder.appendEvent(event);
 | 
				
			||||||
 | 
					                    center.subInCenter(event.getTopic(), event.getSemantics());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case UNSUBSCRIBE:
 | 
				
			||||||
 | 
					                doUnsubscribe(event);
 | 
				
			||||||
 | 
					                recorder.appendEvent(event);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case PUBLISH:
 | 
				
			||||||
 | 
					            case PREPUB:
 | 
				
			||||||
 | 
					            case PRESUB:
 | 
				
			||||||
 | 
					                LOGGER.info(String.format("Receive %s event from topic %s", event.getSemantics(), topic));
 | 
				
			||||||
 | 
					                LOGGER.debug(String.format("Receive %s event %s: %s",
 | 
				
			||||||
 | 
					                        event.getSemantics(), topic, event.getContent()));
 | 
				
			||||||
 | 
					                if (event.isForward()) {
 | 
				
			||||||
 | 
					                    // send event to the event center
 | 
				
			||||||
 | 
					                    event.setForward(center.deliverEvent(topic, event));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // if the event is from or in event center, save and do publishing
 | 
				
			||||||
 | 
					                if (!event.isForward()) {
 | 
				
			||||||
 | 
					                    recorder.appendEvent(event);
 | 
				
			||||||
 | 
					                    doPublish(event);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void doSubscribe(REvent e) {
 | 
				
			||||||
 | 
					        subInReg(e.getTopic(), parseConsumer(e.getContent()), this.topic2cIds, this.id2Consumers);
 | 
				
			||||||
 | 
					        // for events with semantics ONLY_ONCE, mark the topic is a temporary topic
 | 
				
			||||||
 | 
					        if (ONLY_ONCE.equals(e.getSemantics()) && !tempTopics.containsKey(e.getTopic())) {
 | 
				
			||||||
 | 
					            tempTopics.put(e.getTopic(), System.currentTimeMillis());
 | 
				
			||||||
 | 
					            recorder.saveTempTopics(tempTopics);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * do subscribing in registry
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param topic        event topic
 | 
				
			||||||
 | 
					     * @param consumer     the consumer
 | 
				
			||||||
 | 
					     * @param topic2cIds   topic registry of broker or a check point in event recorder
 | 
				
			||||||
 | 
					     * @param id2Consumers consumer registry of broker or a check point in event recorder
 | 
				
			||||||
 | 
					     * @return if the subscribing succeeds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean subInReg(
 | 
				
			||||||
 | 
					            String topic,
 | 
				
			||||||
 | 
					            IEventConsumer consumer,
 | 
				
			||||||
 | 
					            Map<String, Set<String>> topic2cIds,
 | 
				
			||||||
 | 
					            Map<String, IEventConsumer> id2Consumers) {
 | 
				
			||||||
 | 
					        if (null == consumer) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String cId = consumer.getId();
 | 
				
			||||||
 | 
					        if (!id2Consumers.containsKey(cId)) {
 | 
				
			||||||
 | 
					            id2Consumers.put(cId, consumer);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!topic2cIds.containsKey(topic)) {
 | 
				
			||||||
 | 
					            topic2cIds.put(topic, new HashSet<>());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        topic2cIds.get(topic).add(cId);
 | 
				
			||||||
 | 
					        if (consumer instanceof ContractConsumer) {
 | 
				
			||||||
 | 
					            LOGGER.info("contract " + ((ContractConsumer) consumer).getContract() + " subscribes topic " + topic);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            LOGGER.info("node " + consumer.getId() + " subscribes topic " + topic);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void doUnsubscribe(REvent e) {
 | 
				
			||||||
 | 
					        unsubInReg(e.getTopic(), parseConsumer(e.getContent()), this.topic2cIds, this.id2Consumers);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * do subscribing in registry<br/>
 | 
				
			||||||
 | 
					     * topic and consumer must not be null at the same time
 | 
				
			||||||
 | 
					     * <ul>
 | 
				
			||||||
 | 
					     *     <li>if consumer is null and topic is not, it means the topic is a temporary topic, remove it</li>
 | 
				
			||||||
 | 
					     *     <li>if topic is null and consumer is not,
 | 
				
			||||||
 | 
					     *     it means a consumer or a contract wants to unsubscribe all topics,
 | 
				
			||||||
 | 
					     *     remove all related consumers in topic registry and consumer registry</li>
 | 
				
			||||||
 | 
					     *     <li>if two of them is not null, do unsubscribing in two registries</li>
 | 
				
			||||||
 | 
					     * </ul>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param topic        event topic
 | 
				
			||||||
 | 
					     * @param consumer     the consumer, just id is required
 | 
				
			||||||
 | 
					     * @param topic2cIds   topic registry of broker or a check point in event recorder
 | 
				
			||||||
 | 
					     * @param id2Consumers consumer registry of broker or a check point in event recorder
 | 
				
			||||||
 | 
					     * @return if the subscribing succeeds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean unsubInReg(
 | 
				
			||||||
 | 
					            String topic,
 | 
				
			||||||
 | 
					            IEventConsumer consumer,
 | 
				
			||||||
 | 
					            Map<String, Set<String>> topic2cIds,
 | 
				
			||||||
 | 
					            Map<String, IEventConsumer> id2Consumers) {
 | 
				
			||||||
 | 
					        if (null == topic && null == consumer) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (null == consumer) {
 | 
				
			||||||
 | 
					            topic2cIds.remove(topic);
 | 
				
			||||||
 | 
					            LOGGER.info("clean temporary topic " + topic);
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String cId = consumer.getId();
 | 
				
			||||||
 | 
					        List<String> toRmIds;
 | 
				
			||||||
 | 
					        String contract; // just used for log
 | 
				
			||||||
 | 
					        if (id2Consumers.containsKey(cId)) {
 | 
				
			||||||
 | 
					            // if cId belongs to a contract consumer, use the cId as a singleton list
 | 
				
			||||||
 | 
					            toRmIds = Collections.singletonList(cId);
 | 
				
			||||||
 | 
					            contract = ((ContractConsumer) id2Consumers.get(cId)).getContract();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // if cId belongs to a contract, find all related consumers
 | 
				
			||||||
 | 
					            toRmIds = new ArrayList<>();
 | 
				
			||||||
 | 
					            id2Consumers.forEach((k, c) -> {
 | 
				
			||||||
 | 
					                if (c instanceof ContractConsumer && ((ContractConsumer) c).getContract().equals(cId)) {
 | 
				
			||||||
 | 
					                    toRmIds.add(k);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            contract = cId;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (toRmIds.isEmpty()) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (null == topic || topic.isEmpty()) {
 | 
				
			||||||
 | 
					            topic2cIds.values().forEach(cIds -> toRmIds.forEach(cIds::remove));
 | 
				
			||||||
 | 
					            toRmIds.forEach(id2Consumers::remove);
 | 
				
			||||||
 | 
					            LOGGER.info("contract " + contract + " unsubscribes all topics");
 | 
				
			||||||
 | 
					        } else if (topic2cIds.containsKey(topic)) {
 | 
				
			||||||
 | 
					            Set<String> topic2Id = topic2cIds.get(topic);
 | 
				
			||||||
 | 
					            toRmIds.forEach(topic2Id::remove);
 | 
				
			||||||
 | 
					            LOGGER.info("contract " + contract + " unsubscribes topic " + topic);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * parse consumer information from content str<br/>
 | 
				
			||||||
 | 
					     * if caller wants to select all consumers of a contract, the content str is also parsed into a node consumer
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param content json string, {"subscriber": "[subscriber]", "handler?": "[handler]"}
 | 
				
			||||||
 | 
					     * @return a node consumer or contract consumer, or null if exception is thrown
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public IEventConsumer parseConsumer(String content) {
 | 
				
			||||||
 | 
					        if (null == content || content.isEmpty()) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            JsonObject json = JsonUtil.parseString(content);
 | 
				
			||||||
 | 
					            String subscriber = json.get("subscriber").getAsString();
 | 
				
			||||||
 | 
					            String handler = json.has("handler") ? json.get("handler").getAsString() : null;
 | 
				
			||||||
 | 
					            if (null == subscriber || subscriber.isEmpty()) {
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            IEventConsumer consumer;
 | 
				
			||||||
 | 
					            if (null != handler && !handler.isEmpty()) {
 | 
				
			||||||
 | 
					                consumer = new ContractConsumer(subscriber, handler);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                consumer = new NodeConsumer(subscriber);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return consumer;
 | 
				
			||||||
 | 
					        } catch (Exception ignored) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // handle publishing; the process varies with the semantic of the event
 | 
				
			||||||
 | 
					    private void doPublish(REvent event) {
 | 
				
			||||||
 | 
					        String topic = event.getTopic();
 | 
				
			||||||
 | 
					        // publish simple event message to contract consumer
 | 
				
			||||||
 | 
					        String cEventStr = JsonUtil.toJson(new Event(topic, event.getContent(), event.getSemantics()));
 | 
				
			||||||
 | 
					        // publish full event message to node consumer
 | 
				
			||||||
 | 
					        String nEventStr = JsonUtil.toJson(event);
 | 
				
			||||||
 | 
					        if (!topic2cIds.containsKey(topic)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Set<String> topicConsumers = topic2cIds.get(topic);
 | 
				
			||||||
 | 
					        switch (event.getSemantics()) {
 | 
				
			||||||
 | 
					            case AT_LEAST_ONCE:
 | 
				
			||||||
 | 
					            case NEED_RETRY:
 | 
				
			||||||
 | 
					                // send events to all
 | 
				
			||||||
 | 
					                topicConsumers.forEach(cId ->
 | 
				
			||||||
 | 
					                        deliverEvent(event, cEventStr, nEventStr, cId, topic, true));
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case AT_MOST_ONCE:
 | 
				
			||||||
 | 
					                // send event to a random consumer
 | 
				
			||||||
 | 
					                // AT_MOST_ONCE, so broker don't need to do anything when delivering fails
 | 
				
			||||||
 | 
					                deliverEvent(
 | 
				
			||||||
 | 
					                        event,
 | 
				
			||||||
 | 
					                        cEventStr,
 | 
				
			||||||
 | 
					                        nEventStr,
 | 
				
			||||||
 | 
					                        topicConsumers.toArray()[(int) (Math.random() * topicConsumers.size())].toString(),
 | 
				
			||||||
 | 
					                        topic,
 | 
				
			||||||
 | 
					                        true);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case ONLY_ONCE:
 | 
				
			||||||
 | 
					                switch (event.getType()) {
 | 
				
			||||||
 | 
					                    case PRESUB:
 | 
				
			||||||
 | 
					                        // receive PRESUB events and deliver the first one to the thread
 | 
				
			||||||
 | 
					                        String[] contentArr = event.getContent().split("\\|");
 | 
				
			||||||
 | 
					                        if (contentArr.length == 3) {
 | 
				
			||||||
 | 
					                            ThreadFlag topicFlag;
 | 
				
			||||||
 | 
					                            synchronized (topicFlag = threadFlags.get(contentArr[1])) {
 | 
				
			||||||
 | 
					                                if (topicFlag.get().isEmpty()) {
 | 
				
			||||||
 | 
					                                    topicFlag.set(event.getContent());
 | 
				
			||||||
 | 
					                                    topicFlag.notify();
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    case PUBLISH:
 | 
				
			||||||
 | 
					                        // the ONLY_ONCE event won't be delivered to the non-center
 | 
				
			||||||
 | 
					                        String contentHash = HashUtil.sha3(event.getContent());
 | 
				
			||||||
 | 
					                        if (!threadFlags.containsKey(contentHash)) {
 | 
				
			||||||
 | 
					                            final ThreadFlag flag = new ThreadFlag();
 | 
				
			||||||
 | 
					                            threadFlags.put(contentHash, flag);
 | 
				
			||||||
 | 
					                            // send PREPUB events to all consumers
 | 
				
			||||||
 | 
					                            // TODO if there are no consumers to receive the ONLY_ONCE events?
 | 
				
			||||||
 | 
					                            ContractManager.threadPool.execute(() -> {
 | 
				
			||||||
 | 
					                                REvent prePubMsg = new REvent(event.getTopic(),
 | 
				
			||||||
 | 
					                                        PREPUB,
 | 
				
			||||||
 | 
					                                        contentHash,
 | 
				
			||||||
 | 
					                                        event.getRequestID());
 | 
				
			||||||
 | 
					                                prePubMsg.doSignature(ContractManager.instance.nodeCenterConn.getNodeKeyPair());
 | 
				
			||||||
 | 
					                                topicConsumers.forEach(cId ->
 | 
				
			||||||
 | 
					                                        deliverEvent(prePubMsg,
 | 
				
			||||||
 | 
					                                                JsonUtil.toJson(
 | 
				
			||||||
 | 
					                                                        new Event(event.getTopic(),
 | 
				
			||||||
 | 
					                                                                contentHash,
 | 
				
			||||||
 | 
					                                                                prePubMsg.getSemantics())),
 | 
				
			||||||
 | 
					                                                JsonUtil.toJson(prePubMsg),
 | 
				
			||||||
 | 
					                                                cId,
 | 
				
			||||||
 | 
					                                                topic,
 | 
				
			||||||
 | 
					                                                false));
 | 
				
			||||||
 | 
					                                // wait for responses from contracts (PRESUB events)
 | 
				
			||||||
 | 
					                                while (true) {
 | 
				
			||||||
 | 
					                                    try {
 | 
				
			||||||
 | 
					                                        synchronized (flag) {
 | 
				
			||||||
 | 
					                                            flag.wait(30 * 1000L);
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        if (!flag.get().isEmpty()) {
 | 
				
			||||||
 | 
					                                            REvent finalMsg = new REvent(flag.get(),
 | 
				
			||||||
 | 
					                                                    PUBLISH,
 | 
				
			||||||
 | 
					                                                    event.getContent(),
 | 
				
			||||||
 | 
					                                                    HashUtil.sha3(
 | 
				
			||||||
 | 
					                                                            contentHash + System.currentTimeMillis()));
 | 
				
			||||||
 | 
					                                            // if the delivering fails, retry publishing
 | 
				
			||||||
 | 
					                                            finalMsg.setSemantics(NEED_RETRY);
 | 
				
			||||||
 | 
					                                            finalMsg.doSignature(
 | 
				
			||||||
 | 
					                                                    ContractManager.instance.nodeCenterConn.getNodeKeyPair());
 | 
				
			||||||
 | 
					                                            handle(finalMsg);
 | 
				
			||||||
 | 
					                                            break;
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                                        LOGGER.warn("ONLY_ONE event delivering is interrupted: " + e.getMessage());
 | 
				
			||||||
 | 
					//                                        e.printStackTrace();
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * publish the event to the consumer
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param event     event message
 | 
				
			||||||
 | 
					     * @param cEventStr simple event message to the contract consumer, only the topic and the content
 | 
				
			||||||
 | 
					     * @param nEventStr event message to the node
 | 
				
			||||||
 | 
					     * @param cId       consumer id
 | 
				
			||||||
 | 
					     * @param topic     topic of the event
 | 
				
			||||||
 | 
					     * @param isPub     if the event is published or pre-published
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void deliverEvent(
 | 
				
			||||||
 | 
					            REvent event,
 | 
				
			||||||
 | 
					            String cEventStr,
 | 
				
			||||||
 | 
					            String nEventStr,
 | 
				
			||||||
 | 
					            String cId,
 | 
				
			||||||
 | 
					            String topic,
 | 
				
			||||||
 | 
					            boolean isPub) {
 | 
				
			||||||
 | 
					        if (id2Consumers.containsKey(cId)) {
 | 
				
			||||||
 | 
					            IEventConsumer consumer = id2Consumers.get(cId);
 | 
				
			||||||
 | 
					            if (consumer instanceof ContractConsumer) {
 | 
				
			||||||
 | 
					                // contract consumer
 | 
				
			||||||
 | 
					                ContractManager.threadPool.execute(() -> {
 | 
				
			||||||
 | 
					                    ResultCallback cb = new ResultCallback() {
 | 
				
			||||||
 | 
					                        @Override
 | 
				
			||||||
 | 
					                        public void onResult(String str) {
 | 
				
			||||||
 | 
					                            // if the delivering fails, unsubscribe the consumer
 | 
				
			||||||
 | 
					                            if (null != str) {
 | 
				
			||||||
 | 
					                                ContractConsumer c = (ContractConsumer) consumer;
 | 
				
			||||||
 | 
					                                ContractClient client = ContractManager.instance.getClient(c.getContract());
 | 
				
			||||||
 | 
					                                String reqID =
 | 
				
			||||||
 | 
					                                        ContractManager.instance.nodeCenterConn.getNodeKeyPair().getPublicKeyStr() +
 | 
				
			||||||
 | 
					                                                "_" + System.currentTimeMillis();
 | 
				
			||||||
 | 
					                                REvent unsubEvent =
 | 
				
			||||||
 | 
					                                        new REvent(
 | 
				
			||||||
 | 
					                                                topic,
 | 
				
			||||||
 | 
					                                                UNSUBSCRIBE,
 | 
				
			||||||
 | 
					                                                "{\"subscriber\":\"" + cId + "\"}",
 | 
				
			||||||
 | 
					                                                reqID);
 | 
				
			||||||
 | 
					                                unsubEvent.doSignature(client.getPubkey(), client.getContractKey());
 | 
				
			||||||
 | 
					                                handle(unsubEvent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                // if the event is an ONLY_ONCE event, retry publishing
 | 
				
			||||||
 | 
					                                if (NEED_RETRY.equals(event.getSemantics())) {
 | 
				
			||||||
 | 
					                                    REvent newMsg =
 | 
				
			||||||
 | 
					                                            new REvent(
 | 
				
			||||||
 | 
					                                                    topic.split("\\|")[0],
 | 
				
			||||||
 | 
					                                                    PUBLISH,
 | 
				
			||||||
 | 
					                                                    event.getContent(),
 | 
				
			||||||
 | 
					                                                    event.getRequestID());
 | 
				
			||||||
 | 
					                                    newMsg.setSemantics(ONLY_ONCE);
 | 
				
			||||||
 | 
					                                    newMsg.setHash(event.getHash());
 | 
				
			||||||
 | 
					                                    newMsg.setTxHash(event.getTxHash());
 | 
				
			||||||
 | 
					                                    newMsg.doSignature(ContractManager.instance.nodeCenterConn.getNodeKeyPair());
 | 
				
			||||||
 | 
					                                    handle(newMsg);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    if (isPub) {
 | 
				
			||||||
 | 
					                        consumer.publishEvent(cEventStr, cb, event.getRequestID(), event.getHash());
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        consumer.competeSub(cEventStr, cb, event.getRequestID(), event.getHash());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // node consumer
 | 
				
			||||||
 | 
					                ContractManager.threadPool.execute(() -> {
 | 
				
			||||||
 | 
					                    if (isPub) {
 | 
				
			||||||
 | 
					                        consumer.publishEvent(nEventStr, null);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        consumer.competeSub(nEventStr, null);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            topic2cIds.get(topic).remove(cId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * return the number of topics, for node center statistics
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of topics
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int countEvents() {
 | 
				
			||||||
 | 
					        return topic2cIds.size() - tempTopics.size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * thread flag, used in ONLY_ONCE event publishing
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static class ThreadFlag {
 | 
				
			||||||
 | 
					        private String flag = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public String get() {
 | 
				
			||||||
 | 
					            return flag;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void set(String flag) {
 | 
				
			||||||
 | 
					            this.flag = flag;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										161
									
								
								src/main/java/org/bdware/sc/event/EventCenter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								src/main/java/org/bdware/sc/event/EventCenter.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,161 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.NodeCenterConn;
 | 
				
			||||||
 | 
					import org.bdware.sc.NodeCenterConn.NodeKey;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.HashUtil;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.JsonUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.math.BigInteger;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.bdware.sc.ContractManager.instance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Kaidong Wu
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class EventCenter {
 | 
				
			||||||
 | 
					//    private static final Logger LOGGER = LogManager.getLogger(EventCenter.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * get the nearest node to the topic in the hash function range
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param topic the topic
 | 
				
			||||||
 | 
					     * @return id of the node
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getCenterByTopic(String topic) {
 | 
				
			||||||
 | 
					        String[] centers = getCentersByTopic(topic, 1);
 | 
				
			||||||
 | 
					        if (null == centers) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return centers[0];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * get k nearest nodes to the topic in the hash function range
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param topic the topic
 | 
				
			||||||
 | 
					     * @param k     the number of required node ids
 | 
				
			||||||
 | 
					     * @return ids of k nearest nodes
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String[] getCentersByTopic(String topic, int k) {
 | 
				
			||||||
 | 
					        NodeCenterConn nodeCenterConn = instance.nodeCenterConn;
 | 
				
			||||||
 | 
					        if (null == nodeCenterConn) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        NodeKey[] nodes = nodeCenterConn.listNodes();
 | 
				
			||||||
 | 
					        if (nodes.length == 0) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (nodes.length == 1) {
 | 
				
			||||||
 | 
					            return new String[]{nodeCenterConn.getNodeId(nodes[0].id)};
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // get hash with enough length
 | 
				
			||||||
 | 
					        String hash = HashUtil.sha3ToFixedLen(topic, nodes[0].id.length());
 | 
				
			||||||
 | 
					        BigInteger biH = new BigInteger(hash, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // binary search, to find the nearest node with hash
 | 
				
			||||||
 | 
					        int l = 0, r = nodes.length - 1, m = 0,
 | 
				
			||||||
 | 
					                comL = biH.compareTo(nodes[l].biId), comR = nodes[r].biId.compareTo(biH),
 | 
				
			||||||
 | 
					                comM;
 | 
				
			||||||
 | 
					        String selected;
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            if (comL < 1) {
 | 
				
			||||||
 | 
					                selected = nodes[l].id;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (comR < 1) {
 | 
				
			||||||
 | 
					                selected = nodes[r].id;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (l + 1 == r) {
 | 
				
			||||||
 | 
					                if (biH.subtract(nodes[l].biId).compareTo(nodes[r].biId.subtract(biH)) < 1) {
 | 
				
			||||||
 | 
					                    selected = nodes[l].id;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    selected = nodes[r].id;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            m = (l + r) >> 1;
 | 
				
			||||||
 | 
					            comM = biH.compareTo(nodes[m].biId);
 | 
				
			||||||
 | 
					            if (comM < 1) {
 | 
				
			||||||
 | 
					                r = m;
 | 
				
			||||||
 | 
					                comR = -comM;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                l = m;
 | 
				
			||||||
 | 
					                comL = comM;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } while (true);
 | 
				
			||||||
 | 
					        List<String> ret = new ArrayList<>();
 | 
				
			||||||
 | 
					        ret.add(nodeCenterConn.getNodeId(selected));
 | 
				
			||||||
 | 
					        if (k > 1) {
 | 
				
			||||||
 | 
					            l = m - 1;
 | 
				
			||||||
 | 
					            r = m + 1;
 | 
				
			||||||
 | 
					            while (ret.size() < k && (l >= 0 || r < nodes.length)) {
 | 
				
			||||||
 | 
					                if (l < 0) {
 | 
				
			||||||
 | 
					                    ret.add(nodeCenterConn.getNodeId(nodes[r++].id));
 | 
				
			||||||
 | 
					                } else if (r >= nodes.length) {
 | 
				
			||||||
 | 
					                    ret.add(nodeCenterConn.getNodeId(nodes[l--].id));
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    if (biH.subtract(nodes[l].biId).compareTo(nodes[r].biId.subtract(biH)) < 1) {
 | 
				
			||||||
 | 
					                        ret.add(nodeCenterConn.getNodeId(nodes[l--].id));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        ret.add(nodeCenterConn.getNodeId(nodes[r++].id));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return ret.toArray(new String[0]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * subscribe a topic in center
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param topic     event topic
 | 
				
			||||||
 | 
					     * @param semantics event semantics, used to mark PRESUB events
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void subInCenter(String topic, REvent.REventSemantics semantics) {
 | 
				
			||||||
 | 
					        if (null == instance.nodeCenterConn) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        REvent msg = new REvent(
 | 
				
			||||||
 | 
					                topic,
 | 
				
			||||||
 | 
					                REvent.REventType.SUBSCRIBE,
 | 
				
			||||||
 | 
					                String.format("{\"subscriber\":\"%s\"}",
 | 
				
			||||||
 | 
					                        instance.nodeCenterConn.getNodeId(null)),
 | 
				
			||||||
 | 
					                "");
 | 
				
			||||||
 | 
					        msg.setSemantics(semantics);
 | 
				
			||||||
 | 
					        msg.doSignature(instance.nodeCenterConn.getNodeKeyPair());
 | 
				
			||||||
 | 
					        String nodeId = getCenterByTopic(topic);
 | 
				
			||||||
 | 
					        instance.nodeCenterConn.deliverEvent(JsonUtil.toJson(msg), nodeId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * deliver an event to center
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param topic event topic
 | 
				
			||||||
 | 
					     * @param event the event
 | 
				
			||||||
 | 
					     * @return if this node is the center, return false; otherwise, return true
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean deliverEvent(String topic, REvent event) {
 | 
				
			||||||
 | 
					        if (null == instance.nodeCenterConn) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String nodeId = getCenterByTopic(topic);
 | 
				
			||||||
 | 
					        return instance.nodeCenterConn.deliverEvent(JsonUtil.toJson(event), nodeId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * publish an event to another node; used by NodeConsumer
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param eStr   event string
 | 
				
			||||||
 | 
					     * @param target id of the target node
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void publishEvent(String eStr, String target) {
 | 
				
			||||||
 | 
					        if (null != instance.nodeCenterConn) {
 | 
				
			||||||
 | 
					            instance.nodeCenterConn.deliverEvent(eStr, target);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										237
									
								
								src/main/java/org/bdware/sc/event/EventRecorder.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								src/main/java/org/bdware/sc/event/EventRecorder.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,237 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gson.JsonObject;
 | 
				
			||||||
 | 
					import com.google.gson.reflect.TypeToken;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.KeyValueRocksDBUtil;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.REvent.REventSemantics;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.REvent.REventType;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.clients.ContractConsumer;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.clients.IEventConsumer;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.clients.NodeConsumer;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.HashUtil;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.JsonUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.lang.reflect.Type;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					import java.util.Stack;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.bdware.sc.event.REvent.REventType.SUBSCRIBE;
 | 
				
			||||||
 | 
					import static org.bdware.sc.event.REvent.REventType.UNSUBSCRIBE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Kaidong Wu
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class EventRecorder {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = LogManager.getLogger(EventRecorder.class);
 | 
				
			||||||
 | 
					    private static final String LATEST_EVENT_KEY = "LATEST_EVENT", TEMP_TOPIC_KEYS = "TEMP_TOPICS";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final String dbName;
 | 
				
			||||||
 | 
					    private final EventBroker broker;
 | 
				
			||||||
 | 
					    private final RecordPointer latestEvent = new RecordPointer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public EventRecorder(String dbName, EventBroker broker) {
 | 
				
			||||||
 | 
					        this.dbName = dbName;
 | 
				
			||||||
 | 
					        this.broker = broker;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void appendEvent(REvent e) {
 | 
				
			||||||
 | 
					        ETransaction transaction = new ETransaction(e);
 | 
				
			||||||
 | 
					        synchronized (latestEvent) {
 | 
				
			||||||
 | 
					            transaction.prev = latestEvent.get();
 | 
				
			||||||
 | 
					            String tranJson = "et" + JsonUtil.toJson(transaction);
 | 
				
			||||||
 | 
					            String key = HashUtil.sha3(tranJson);
 | 
				
			||||||
 | 
					            latestEvent.set(key);
 | 
				
			||||||
 | 
					            latestEvent.setCp(false);
 | 
				
			||||||
 | 
					            KeyValueRocksDBUtil.instance.setValue(dbName, LATEST_EVENT_KEY, latestEvent.get());
 | 
				
			||||||
 | 
					            KeyValueRocksDBUtil.instance.setValue(dbName, key, tranJson);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void createCheckPoint(Map<String, Set<String>> topic2cIds,
 | 
				
			||||||
 | 
					                                 Map<String, IEventConsumer> id2Consumers) {
 | 
				
			||||||
 | 
					        CheckPoint cp = new CheckPoint(topic2cIds, id2Consumers);
 | 
				
			||||||
 | 
					        synchronized (latestEvent) {
 | 
				
			||||||
 | 
					            if (!latestEvent.isCp()) {
 | 
				
			||||||
 | 
					                cp.prev = latestEvent.get();
 | 
				
			||||||
 | 
					                String cpJson = "cp" + JsonUtil.toJson(cp);
 | 
				
			||||||
 | 
					                String key = HashUtil.sha3(cpJson);
 | 
				
			||||||
 | 
					                latestEvent.set(key);
 | 
				
			||||||
 | 
					                latestEvent.setCp(true);
 | 
				
			||||||
 | 
					                KeyValueRocksDBUtil.instance.setValue(dbName, LATEST_EVENT_KEY, latestEvent.get());
 | 
				
			||||||
 | 
					                KeyValueRocksDBUtil.instance.setValue(dbName, key, cpJson);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * recover registry from database
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a check point, including topic registry and client registry
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public CheckPoint recoverRegistryFromDb() {
 | 
				
			||||||
 | 
					        Stack<Object> stack = new Stack<>();
 | 
				
			||||||
 | 
					        // recover the latest event pointer from database
 | 
				
			||||||
 | 
					        String key = KeyValueRocksDBUtil.instance.getValue(dbName, LATEST_EVENT_KEY);
 | 
				
			||||||
 | 
					        latestEvent.set(key);
 | 
				
			||||||
 | 
					        CheckPoint cp = new CheckPoint();
 | 
				
			||||||
 | 
					        Type topic2cIdsType = TypeToken.getParameterized(ConcurrentHashMap.class, String.class,
 | 
				
			||||||
 | 
					                TypeToken.getParameterized(Set.class, String.class,
 | 
				
			||||||
 | 
					                        String.class).getType()).getType();
 | 
				
			||||||
 | 
					        // retrieving transactions from database
 | 
				
			||||||
 | 
					        while (null != key && !key.isEmpty()) {
 | 
				
			||||||
 | 
					            String json = KeyValueRocksDBUtil.instance.getValue(dbName, key);
 | 
				
			||||||
 | 
					            if (null == json || json.isEmpty()) {
 | 
				
			||||||
 | 
					                LOGGER.warn("record damaged! " + key);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                if (json.startsWith("cp")) {
 | 
				
			||||||
 | 
					                    // create check point by the transaction and stop retrieving
 | 
				
			||||||
 | 
					                    JsonObject data = JsonUtil.parseString(json.substring(2));
 | 
				
			||||||
 | 
					                    cp.topic2cIds = JsonUtil.fromJson(data.get("topic2cIds").toString(), topic2cIdsType);
 | 
				
			||||||
 | 
					                    JsonObject id2Consumers = data.getAsJsonObject("id2Consumers");
 | 
				
			||||||
 | 
					                    for (String k : id2Consumers.keySet()) {
 | 
				
			||||||
 | 
					                        JsonObject consumer = id2Consumers.getAsJsonObject(k);
 | 
				
			||||||
 | 
					                        if (consumer.has("handler")) {
 | 
				
			||||||
 | 
					                            cp.id2Consumers.put(k,
 | 
				
			||||||
 | 
					                                    new ContractConsumer(consumer.get("contract").getAsString(),
 | 
				
			||||||
 | 
					                                            consumer.get("handler").getAsString()));
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            cp.id2Consumers.put(k, new NodeConsumer(k));
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    cp.prev = data.get("prev").getAsString();
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                } else if (json.startsWith("et")) {
 | 
				
			||||||
 | 
					                    // push transactions into the stack
 | 
				
			||||||
 | 
					                    ETransaction tran = JsonUtil.fromJson(json.substring(2), ETransaction.class);
 | 
				
			||||||
 | 
					                    key = tran.prev;
 | 
				
			||||||
 | 
					                    // just sub or unsub event need to be processed
 | 
				
			||||||
 | 
					                    if (tran.type == SUBSCRIBE || tran.type == UNSUBSCRIBE) {
 | 
				
			||||||
 | 
					                        stack.push(tran);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    LOGGER.warn("record damaged! " + key);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (Exception ignored) {
 | 
				
			||||||
 | 
					                LOGGER.warn("record damaged! " + key);
 | 
				
			||||||
 | 
					                LOGGER.debug("record damaged! " + key + ": " + json);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (stack.isEmpty()) {
 | 
				
			||||||
 | 
					            // if empty, return the check point
 | 
				
			||||||
 | 
					            latestEvent.setCp(true);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // on the base of old check point, process following sub or unsub events to recover registry
 | 
				
			||||||
 | 
					            while (!stack.empty()) {
 | 
				
			||||||
 | 
					                Object record = stack.pop();
 | 
				
			||||||
 | 
					                if (record instanceof CheckPoint) {
 | 
				
			||||||
 | 
					                    cp = (CheckPoint) record;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    ETransaction tran = (ETransaction) record;
 | 
				
			||||||
 | 
					                    IEventConsumer consumer = broker.parseConsumer(tran.content);
 | 
				
			||||||
 | 
					                    switch (tran.type) {
 | 
				
			||||||
 | 
					                        case SUBSCRIBE:
 | 
				
			||||||
 | 
					                            if (!broker.subInReg(tran.topic, consumer, cp.topic2cIds, cp.id2Consumers)) {
 | 
				
			||||||
 | 
					                                LOGGER.warn("record damaged! " + key);
 | 
				
			||||||
 | 
					                                LOGGER.debug("record damaged! " + key + ": " + JsonUtil.toJson(tran));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        case UNSUBSCRIBE:
 | 
				
			||||||
 | 
					                            if (!broker.unsubInReg(tran.topic, consumer, cp.topic2cIds, cp.id2Consumers)) {
 | 
				
			||||||
 | 
					                                LOGGER.warn("record damaged! " + key);
 | 
				
			||||||
 | 
					                                LOGGER.debug("record damaged! " + key + ": " + JsonUtil.toJson(tran));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        LOGGER.info("recover registry from database done!");
 | 
				
			||||||
 | 
					        return cp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void saveTempTopics(Map<String, Long> tempTopics) {
 | 
				
			||||||
 | 
					        KeyValueRocksDBUtil.instance.setValue(dbName, TEMP_TOPIC_KEYS, JsonUtil.toJson(tempTopics));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Map<String, Long> recoverTempTopicsFromDb() {
 | 
				
			||||||
 | 
					        String json = KeyValueRocksDBUtil.instance.getValue(dbName, TEMP_TOPIC_KEYS);
 | 
				
			||||||
 | 
					        Map<String, Long> ret = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            JsonObject jo = JsonUtil.parseString(json);
 | 
				
			||||||
 | 
					            for (String key : jo.keySet()) {
 | 
				
			||||||
 | 
					                ret.put(key, jo.get(key).getAsLong());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (Exception ignored) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static class ETransaction {
 | 
				
			||||||
 | 
					        REventType type;
 | 
				
			||||||
 | 
					        REventSemantics semantics;
 | 
				
			||||||
 | 
					        String topic;
 | 
				
			||||||
 | 
					        String content;
 | 
				
			||||||
 | 
					        String requestID;
 | 
				
			||||||
 | 
					        // public key of sender
 | 
				
			||||||
 | 
					        String sender;
 | 
				
			||||||
 | 
					        String signature;
 | 
				
			||||||
 | 
					        String prev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ETransaction(REvent e) {
 | 
				
			||||||
 | 
					            type = e.getType();
 | 
				
			||||||
 | 
					            semantics = e.getSemantics();
 | 
				
			||||||
 | 
					            topic = e.getTopic();
 | 
				
			||||||
 | 
					            content = e.getContent();
 | 
				
			||||||
 | 
					            requestID = e.getRequestID();
 | 
				
			||||||
 | 
					            sender = e.getPublicKey();
 | 
				
			||||||
 | 
					            signature = e.getSignature();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static class CheckPoint {
 | 
				
			||||||
 | 
					        Map<String, Set<String>> topic2cIds;
 | 
				
			||||||
 | 
					        Map<String, IEventConsumer> id2Consumers;
 | 
				
			||||||
 | 
					        String prev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public CheckPoint() {
 | 
				
			||||||
 | 
					            topic2cIds = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					            id2Consumers = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public CheckPoint(Map<String, Set<String>> topic2cIds,
 | 
				
			||||||
 | 
					                          Map<String, IEventConsumer> id2Consumers) {
 | 
				
			||||||
 | 
					            this.topic2cIds = topic2cIds;
 | 
				
			||||||
 | 
					            this.id2Consumers = id2Consumers;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static class RecordPointer {
 | 
				
			||||||
 | 
					        private String str;
 | 
				
			||||||
 | 
					        private boolean isCp = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public String get() {
 | 
				
			||||||
 | 
					            return str;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void set(String str) {
 | 
				
			||||||
 | 
					            this.str = str;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean isCp() {
 | 
				
			||||||
 | 
					            return isCp;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void setCp(boolean cp) {
 | 
				
			||||||
 | 
					            isCp = cp;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.event.clients;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Kaidong Wu
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ClientConsumer implements IEventConsumer {
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getId() {
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void publishEvent(String msg, ResultCallback rc, String... options) {
 | 
				
			||||||
 | 
					// TODO
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void competeSub(String msg, ResultCallback rc, String... options) {
 | 
				
			||||||
 | 
					// TODO
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										119
									
								
								src/main/java/org/bdware/sc/event/clients/ContractConsumer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/main/java/org/bdware/sc/event/clients/ContractConsumer.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.event.clients;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gson.annotations.Expose;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractClient;
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractManager;
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractResult;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.HashUtil;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.JsonUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
 | 
					import java.util.concurrent.ScheduledFuture;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicInteger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Kaidong Wu
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ContractConsumer implements IEventConsumer {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = LogManager.getLogger(ContractConsumer.class);
 | 
				
			||||||
 | 
					    private static final long PERIOD = 2500L;
 | 
				
			||||||
 | 
					    private static final int TIMEOUT_COUNT = 5;
 | 
				
			||||||
 | 
					    private static final Map<String, ScheduledFuture<?>> scheduledFutures =
 | 
				
			||||||
 | 
					            new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final String contract;
 | 
				
			||||||
 | 
					    private final String handler;
 | 
				
			||||||
 | 
					    @Expose(serialize = false, deserialize = false)
 | 
				
			||||||
 | 
					    private final Object flag = new Object();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractConsumer(String contract, String handler) {
 | 
				
			||||||
 | 
					        this.contract = contract;
 | 
				
			||||||
 | 
					        this.handler = handler;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getContract() {
 | 
				
			||||||
 | 
					        return contract;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getId() {
 | 
				
			||||||
 | 
					        return HashUtil.sha3(contract + handler);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void publishEvent(String msg, ResultCallback rc, String... options) {
 | 
				
			||||||
 | 
					        executeContract(msg, this.handler, rc, options);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void competeSub(String msg, ResultCallback rc, String... options) {
 | 
				
			||||||
 | 
					        executeContract(msg, "_preSub", rc, options);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void executeContract(String msg, String handler, ResultCallback rc, String... options) {
 | 
				
			||||||
 | 
					        ContractRequest cr = new ContractRequest();
 | 
				
			||||||
 | 
					        if (options.length > 1 && null != options[1]) {
 | 
				
			||||||
 | 
					            cr.setRequester(options[1]);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            cr.setRequester("event");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        cr.setContractID(contract);
 | 
				
			||||||
 | 
					        cr.setAction(handler);
 | 
				
			||||||
 | 
					        cr.setArg(msg);
 | 
				
			||||||
 | 
					        cr.setRequestID(options[0]);
 | 
				
			||||||
 | 
					        ContractClient cc = ContractManager.instance.getClient(contract);
 | 
				
			||||||
 | 
					        if (null == cc) {
 | 
				
			||||||
 | 
					            rc.onResult("");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        AtomicInteger callCount = new AtomicInteger(0);
 | 
				
			||||||
 | 
					        ScheduledFuture<?> future = ContractManager.scheduledThreadPool.scheduleAtFixedRate(
 | 
				
			||||||
 | 
					                () -> ContractManager.instance.executeContractInternal(cr, new ResultCallback() {
 | 
				
			||||||
 | 
					                    @Override
 | 
				
			||||||
 | 
					                    public void onResult(String str) {
 | 
				
			||||||
 | 
					                        boolean ret = true;
 | 
				
			||||||
 | 
					                        try {
 | 
				
			||||||
 | 
					                            ContractResult result = JsonUtil.fromJson(str, ContractResult.class);
 | 
				
			||||||
 | 
					                            if (result.status == ContractResult.Status.Success) {
 | 
				
			||||||
 | 
					                                rc.onResult(null);
 | 
				
			||||||
 | 
					                            } else if (callCount.get() == TIMEOUT_COUNT ||
 | 
				
			||||||
 | 
					                                    (result.status == ContractResult.Status.Exception &&
 | 
				
			||||||
 | 
					                                            result.result.toString().contains("not exported"))) {
 | 
				
			||||||
 | 
					                                rc.onResult("");
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                ret = false;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } catch (Exception e) {
 | 
				
			||||||
 | 
					                            LOGGER.warn("receiving event error! " + contract + "." + handler + ": " + e.getMessage());
 | 
				
			||||||
 | 
					                            rc.onResult("");
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        callCount.incrementAndGet();
 | 
				
			||||||
 | 
					                        if (ret) {
 | 
				
			||||||
 | 
					                            synchronized (flag) {
 | 
				
			||||||
 | 
					                                try {
 | 
				
			||||||
 | 
					                                    // wait for setting scheduledFutures
 | 
				
			||||||
 | 
					                                    flag.wait(500L);
 | 
				
			||||||
 | 
					                                } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                                    e.printStackTrace();
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            scheduledFutures.get(getId()).cancel(true);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }, (reqID, hashStr) -> {
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					                500L,
 | 
				
			||||||
 | 
					                PERIOD,
 | 
				
			||||||
 | 
					                TimeUnit.MILLISECONDS);
 | 
				
			||||||
 | 
					        scheduledFutures.put(getId(), future);
 | 
				
			||||||
 | 
					        synchronized (flag) {
 | 
				
			||||||
 | 
					            flag.notify();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.event.clients;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface IEventConsumer {
 | 
				
			||||||
 | 
					    String getId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void publishEvent(String msg, ResultCallback rc, String... options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void competeSub(String msg, ResultCallback rc, String... options);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								src/main/java/org/bdware/sc/event/clients/NodeConsumer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/main/java/org/bdware/sc/event/clients/NodeConsumer.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.event.clients;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.EventCenter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Kaidong Wu
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class NodeConsumer implements IEventConsumer {
 | 
				
			||||||
 | 
					    private static EventCenter center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final String nodeId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public NodeConsumer(String nodeId) {
 | 
				
			||||||
 | 
					        this.nodeId = nodeId;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void setCenter(EventCenter center) {
 | 
				
			||||||
 | 
					        NodeConsumer.center = center;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getId() {
 | 
				
			||||||
 | 
					        return this.nodeId;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void publishEvent(String msg, ResultCallback rc, String... options) {
 | 
				
			||||||
 | 
					        center.publishEvent(msg, nodeId);
 | 
				
			||||||
 | 
					        if (null != rc) {
 | 
				
			||||||
 | 
					            rc.onResult("");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void competeSub(String msg, ResultCallback rc, String... options) {
 | 
				
			||||||
 | 
					        publishEvent(msg, rc, options);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										101
									
								
								src/main/java/org/bdware/sc/handler/ManagerHandler.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/main/java/org/bdware/sc/handler/ManagerHandler.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.handler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractManager;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.Contract;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.Description;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.MsgHandler;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					import org.bdware.sc.event.REvent;
 | 
				
			||||||
 | 
					import org.bdware.sc.get.GetMessage;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.JsonUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ManagerHandler extends MsgHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final ContractManager cm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ManagerHandler(ContractManager contractManager) {
 | 
				
			||||||
 | 
					        cm = contractManager;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description(value = "start Contract, {\"type\":\"Data\",\"id\":\"123bbaa\"}", isAsync = true)
 | 
				
			||||||
 | 
					    public void startContract(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        Contract c = JsonUtil.fromJson(msg.arg, Contract.class);
 | 
				
			||||||
 | 
					        cb.onResult(cm.startContract(c));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description("request Contract, TODO")
 | 
				
			||||||
 | 
					    public void requestContract(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        ContractRequest c = JsonUtil.fromJson(msg.arg, ContractRequest.class);
 | 
				
			||||||
 | 
					        cb.onResult(cm.requestContract(c));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description("get how many times a contract has been executed")
 | 
				
			||||||
 | 
					    public void getTimesOfExecution(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        cb.onResult(cm.getTimesOfExecution(msg.arg));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description(
 | 
				
			||||||
 | 
					            value = "execute Contract, {\"contractID\":\"112233\",\"arg\":\"\"}",
 | 
				
			||||||
 | 
					            isAsync = true)
 | 
				
			||||||
 | 
					    public void executeContract(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        ContractRequest c;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            c = JsonUtil.fromJson(msg.arg, ContractRequest.class);
 | 
				
			||||||
 | 
					            cm.executeContractInternal(c, cb, null);
 | 
				
			||||||
 | 
					        } catch (Exception ignored) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description("stop Contract, {\"id\":\"112233\"}")
 | 
				
			||||||
 | 
					    public void stopContract(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        ContractRequest c = JsonUtil.fromJson(msg.arg, ContractRequest.class);
 | 
				
			||||||
 | 
					        cb.onResult(cm.stopContract(c.getContractID()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description("list Contracts")
 | 
				
			||||||
 | 
					    public void listContracts(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        cb.onResult(cm.listContracts(null));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description("stop all Contracts")
 | 
				
			||||||
 | 
					    public void stopAllContracts(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        int count = cm.stopAllContracts();
 | 
				
			||||||
 | 
					        cb.onResult(count + " contracts stoped!");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description("exit ContractManager!")
 | 
				
			||||||
 | 
					    public void exit(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        ContractManager.threadPool.execute(() -> {
 | 
				
			||||||
 | 
					            System.out.println("ManagerHandler: exit in 3 seconds!");
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                Thread.sleep(3000);
 | 
				
			||||||
 | 
					            } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                e.printStackTrace();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            System.exit(0);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        cb.onResult("success");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description("query distributed execution port by contractID/name")
 | 
				
			||||||
 | 
					    public void queryDEPort(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        cb.onResult(cm.queryDEPort(msg.arg));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Description("add distributed execution member, sample: {contractID:id/name, arg:ipAndPort}")
 | 
				
			||||||
 | 
					    public void addDEMember(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        ContractRequest cr = JsonUtil.fromJson(msg.arg, ContractRequest.class);
 | 
				
			||||||
 | 
					        cb.onResult(cm.addDEMember(cr.getContractID(), cr.getArg()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @author Kaidong Wu
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Description("Deliver event message")
 | 
				
			||||||
 | 
					    public void deliverEMessage(GetMessage msg, ResultCallback cb) {
 | 
				
			||||||
 | 
					        REvent eMsg = JsonUtil.fromJson(msg.arg, REvent.class);
 | 
				
			||||||
 | 
					        cb.onResult(cm.deliverEMessage(eMsg));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								src/main/java/org/bdware/sc/sequencing/Committer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/main/java/org/bdware/sc/sequencing/Committer.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.sequencing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface Committer {
 | 
				
			||||||
 | 
					    void onCommit(ContractRequest data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										382
									
								
								src/main/java/org/bdware/sc/sequencing/PBFTAlgorithm.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								src/main/java/org/bdware/sc/sequencing/PBFTAlgorithm.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,382 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.sequencing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractManager;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.Node;
 | 
				
			||||||
 | 
					import org.bdware.sc.units.TrustfulExecutorConnection;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.JsonUtil;
 | 
				
			||||||
 | 
					import org.zz.gmhelper.SM2KeyPair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicLong;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PBFTAlgorithm implements SequencingAlgorithm {
 | 
				
			||||||
 | 
					    static byte[] GETPUBKEY = "GETPUBKEY".getBytes();
 | 
				
			||||||
 | 
					    Committer committer;
 | 
				
			||||||
 | 
					    Map<Node, PBFTMember> members;
 | 
				
			||||||
 | 
					    AtomicLong allocatedID = new AtomicLong(0);
 | 
				
			||||||
 | 
					    Map<Long, PCInfo> info;
 | 
				
			||||||
 | 
					    List<PBFTMessage> commitedMsg;
 | 
				
			||||||
 | 
					    AtomicLong commitedOrder = new AtomicLong(0);
 | 
				
			||||||
 | 
					    Map<Integer, Pair<Node, PBFTMessage>> original;
 | 
				
			||||||
 | 
					    boolean isMaster;
 | 
				
			||||||
 | 
					    private TrustfulExecutorConnection connection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PBFTAlgorithm(boolean isMaster) {
 | 
				
			||||||
 | 
					        commitedMsg = new ArrayList<>();
 | 
				
			||||||
 | 
					        info = new HashMap<>();
 | 
				
			||||||
 | 
					        members = new HashMap<>();
 | 
				
			||||||
 | 
					        original = new HashMap<>();
 | 
				
			||||||
 | 
					        this.isMaster = isMaster;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setCommitter(Committer c) {
 | 
				
			||||||
 | 
					        committer = c;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setConnection(TrustfulExecutorConnection c) {
 | 
				
			||||||
 | 
					        connection = c;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void onPBFTMessage(final Node sender, final PBFTMessage pbftMessage) {
 | 
				
			||||||
 | 
					        // pbftMessage.type != Request
 | 
				
			||||||
 | 
					        // verify signed message
 | 
				
			||||||
 | 
					        // System.out.println(getPort() + " recv:" + pbftMessage.type + " len:" +
 | 
				
			||||||
 | 
					        // pbftMessage.content.length);
 | 
				
			||||||
 | 
					        PCInfo temp;
 | 
				
			||||||
 | 
					        PBFTMessage prepareMsg;
 | 
				
			||||||
 | 
					        System.out.println("[PBFTAlgorithm] recv: " + pbftMessage.getDisplayStr());
 | 
				
			||||||
 | 
					        switch (pbftMessage.type) {
 | 
				
			||||||
 | 
					            case AddMember:
 | 
				
			||||||
 | 
					                PBFTMember member = PBFTMember.parse(pbftMessage.content);
 | 
				
			||||||
 | 
					                if (member != null)
 | 
				
			||||||
 | 
					                    members.put(sender, member);
 | 
				
			||||||
 | 
					                // case DeleteMember:
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Request:
 | 
				
			||||||
 | 
					                // all nodes save full content, clientIP, clientPort !!!!
 | 
				
			||||||
 | 
					                int hash = java.util.Arrays.hashCode(pbftMessage.content);
 | 
				
			||||||
 | 
					                original.put(hash, new Pair<Node, PBFTMessage>(sender, pbftMessage));
 | 
				
			||||||
 | 
					                if (isMaster) {
 | 
				
			||||||
 | 
					                    prepareMsg = new PBFTMessage();
 | 
				
			||||||
 | 
					                    prepareMsg.order = allocatedID.incrementAndGet();
 | 
				
			||||||
 | 
					                    prepareMsg.type = PBFTType.PrePrepare;
 | 
				
			||||||
 | 
					                    prepareMsg.content = (java.util.Arrays.hashCode(pbftMessage.content) + "").getBytes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    temp = new PCInfo();
 | 
				
			||||||
 | 
					                    temp.request = pbftMessage;
 | 
				
			||||||
 | 
					                    if (info.get(prepareMsg.order) != null) {
 | 
				
			||||||
 | 
					                        System.out.println("==========================Error!!!");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    info.put(prepareMsg.order, temp);
 | 
				
			||||||
 | 
					                    temp.isPrePrepareReceived = true;
 | 
				
			||||||
 | 
					                    broadcast(prepareMsg);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // System.out.println(getPort() + " Request:" + pbftMessage.getDisplayStr());
 | 
				
			||||||
 | 
					                    matchPrePrepareFromOriginReqeust(hash, pbftMessage);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                reply(sender, "success");
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case PrePrepare:
 | 
				
			||||||
 | 
					                // assert !isMaster
 | 
				
			||||||
 | 
					                // Get Pubkey of Master
 | 
				
			||||||
 | 
					                // verify the content matches full content !!!! and client info
 | 
				
			||||||
 | 
					                if (info.get(pbftMessage.order) == null) {
 | 
				
			||||||
 | 
					                    info.put(pbftMessage.order, new PCInfo());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                matchOriginalReqeustFromPrePrepare(pbftMessage);
 | 
				
			||||||
 | 
					                updatePrepare(info.get(pbftMessage.order));
 | 
				
			||||||
 | 
					                // TODO 触发prepare
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Prepare:
 | 
				
			||||||
 | 
					                temp = info.get(pbftMessage.order);
 | 
				
			||||||
 | 
					                // System.out.println(getPort() + ": receive Prepare from:" + packet.getPort());
 | 
				
			||||||
 | 
					                if (temp == null) {
 | 
				
			||||||
 | 
					                    PCInfo pcInfo = new PCInfo();
 | 
				
			||||||
 | 
					                    pcInfo.buff.add(pbftMessage);
 | 
				
			||||||
 | 
					                    info.put(pbftMessage.order, pcInfo);
 | 
				
			||||||
 | 
					                    requetPrePrepareFromMaster(pbftMessage.order);
 | 
				
			||||||
 | 
					                } else if (temp.updatePrepare(pbftMessage, this)) {
 | 
				
			||||||
 | 
					                    // check the sender (is master) and order is in range !!!!
 | 
				
			||||||
 | 
					                    PBFTMessage commitMsg = new PBFTMessage();
 | 
				
			||||||
 | 
					                    commitMsg.type = PBFTType.Commit;
 | 
				
			||||||
 | 
					                    commitMsg.order = pbftMessage.order;
 | 
				
			||||||
 | 
					                    commitMsg.content = new byte[1];
 | 
				
			||||||
 | 
					                    updateCommit(commitMsg);
 | 
				
			||||||
 | 
					                    broadcast(commitMsg);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Commit:
 | 
				
			||||||
 | 
					                temp = info.get(pbftMessage.order);
 | 
				
			||||||
 | 
					                if (temp == null) {
 | 
				
			||||||
 | 
					                    PCInfo pcInfo = new PCInfo();
 | 
				
			||||||
 | 
					                    pcInfo.buff.add(pbftMessage);
 | 
				
			||||||
 | 
					                    info.put(pbftMessage.order, pcInfo);
 | 
				
			||||||
 | 
					                    requetPrePrepareFromMaster(pbftMessage.order);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (temp.updateCommit(pbftMessage, this)) {
 | 
				
			||||||
 | 
					                    // check all messages' order !!!!
 | 
				
			||||||
 | 
					                    // write order to content !!!!
 | 
				
			||||||
 | 
					                    PBFTMessage commitMsg = new PBFTMessage();
 | 
				
			||||||
 | 
					                    commitMsg.type = PBFTType.Reply;
 | 
				
			||||||
 | 
					                    // execute in order
 | 
				
			||||||
 | 
					                    onCommit(pbftMessage);// once complete automatically send to client !!!!
 | 
				
			||||||
 | 
					                    // broadCast(commitMsg);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Reply:// client receive this and check f+1 same result
 | 
				
			||||||
 | 
					                if (isMaster) {
 | 
				
			||||||
 | 
					                    System.out.println(pbftMessage.order + " " + pbftMessage.sendID);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case ReSend:
 | 
				
			||||||
 | 
					                // TODO
 | 
				
			||||||
 | 
					                if (isMaster) {
 | 
				
			||||||
 | 
					                    prepareMsg = new PBFTMessage();
 | 
				
			||||||
 | 
					                    prepareMsg.order = pbftMessage.order;
 | 
				
			||||||
 | 
					                    prepareMsg.type = PBFTType.PrePrepare;
 | 
				
			||||||
 | 
					                    temp = info.get(prepareMsg.order);
 | 
				
			||||||
 | 
					                    prepareMsg.content = (java.util.Arrays.hashCode(temp.request.content) + "").getBytes();
 | 
				
			||||||
 | 
					                    broadcast(pbftMessage);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // printDebugInfo();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void updateCommit(PBFTMessage commitMsg) {
 | 
				
			||||||
 | 
					        PCInfo temp = info.get(commitMsg.order);
 | 
				
			||||||
 | 
					        if (temp.updateCommit(commitMsg, this)) {
 | 
				
			||||||
 | 
					            // check all messages' order !!!!
 | 
				
			||||||
 | 
					            // write order to content !!!!
 | 
				
			||||||
 | 
					            PBFTMessage replyMsg = new PBFTMessage();
 | 
				
			||||||
 | 
					            commitMsg.type = PBFTType.Reply;
 | 
				
			||||||
 | 
					            // execute in order
 | 
				
			||||||
 | 
					            onCommit(commitMsg);// once complete automatically send to client !!!!
 | 
				
			||||||
 | 
					            // broadCast(commitMsg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void updatePrepare(PCInfo pcInfo) {
 | 
				
			||||||
 | 
					        for (PBFTMessage msg : pcInfo.buff) {
 | 
				
			||||||
 | 
					            if (msg.type == PBFTType.Prepare) {
 | 
				
			||||||
 | 
					                if (pcInfo.updatePrepare(msg, this)) {
 | 
				
			||||||
 | 
					                    PBFTMessage commitMsg = new PBFTMessage();
 | 
				
			||||||
 | 
					                    commitMsg.type = PBFTType.Commit;
 | 
				
			||||||
 | 
					                    commitMsg.order = msg.order;
 | 
				
			||||||
 | 
					                    commitMsg.content = new byte[1];
 | 
				
			||||||
 | 
					                    updateCommit(commitMsg);
 | 
				
			||||||
 | 
					                    broadcast(commitMsg);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void requetPrePrepareFromMaster(long order) {
 | 
				
			||||||
 | 
					        PBFTMessage message = new PBFTMessage();
 | 
				
			||||||
 | 
					        message.type = PBFTType.ReSend;
 | 
				
			||||||
 | 
					        message.order = order;
 | 
				
			||||||
 | 
					        message.content = new byte[1];
 | 
				
			||||||
 | 
					        sendToMaster(message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void matchPrePrepareFromOriginReqeust(int hash, PBFTMessage pbftMessage) {
 | 
				
			||||||
 | 
					        for (PCInfo pcInfo : info.values()) {
 | 
				
			||||||
 | 
					            for (PBFTMessage msg : pcInfo.buff) {
 | 
				
			||||||
 | 
					                if (msg.type == PBFTType.PrePrepare && Integer.parseInt(new String(msg.content)) == hash) {
 | 
				
			||||||
 | 
					                    handlePrePrepare(msg, original.get(hash).second);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void reply(Node receiver, String data) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void retryLater(int delay, final Node sender, final PBFTMessage pbftMessage) {
 | 
				
			||||||
 | 
					        ContractManager.scheduledThreadPool.schedule(
 | 
				
			||||||
 | 
					                () -> onPBFTMessage(sender, pbftMessage),
 | 
				
			||||||
 | 
					                delay,
 | 
				
			||||||
 | 
					                TimeUnit.MILLISECONDS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void handlePrePrepare(PBFTMessage pbftMessage, PBFTMessage req) {
 | 
				
			||||||
 | 
					        PBFTMessage prepareMsg = new PBFTMessage();
 | 
				
			||||||
 | 
					        prepareMsg.type = PBFTType.Prepare;
 | 
				
			||||||
 | 
					        prepareMsg.order = pbftMessage.order;
 | 
				
			||||||
 | 
					        prepareMsg.content = pbftMessage.content;
 | 
				
			||||||
 | 
					        PCInfo temp;
 | 
				
			||||||
 | 
					        if (info.get(prepareMsg.order) == null) {
 | 
				
			||||||
 | 
					            temp = new PCInfo();
 | 
				
			||||||
 | 
					            info.put(prepareMsg.order, temp);
 | 
				
			||||||
 | 
					        } else
 | 
				
			||||||
 | 
					            temp = info.get(prepareMsg.order);
 | 
				
			||||||
 | 
					        temp.request = req;
 | 
				
			||||||
 | 
					        temp.isPrePrepareReceived = true;
 | 
				
			||||||
 | 
					        temp.updatePrepare(prepareMsg, this);
 | 
				
			||||||
 | 
					        // System.out.println(getPort() + ":sendPrepare " + pbftMessage.order);
 | 
				
			||||||
 | 
					        broadcast(prepareMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void matchOriginalReqeustFromPrePrepare(PBFTMessage pbftMessage) {
 | 
				
			||||||
 | 
					        int receivedHash = Integer.parseInt(new String(pbftMessage.content));
 | 
				
			||||||
 | 
					        if (original.get(receivedHash) != null) {
 | 
				
			||||||
 | 
					            handlePrePrepare(pbftMessage, original.get(receivedHash).second);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            info.get(pbftMessage.order).buff.add(pbftMessage);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private synchronized void onCommit(PBFTMessage pbftMessage) {
 | 
				
			||||||
 | 
					        PBFTMessage original = getOriginalMessage(pbftMessage.order);
 | 
				
			||||||
 | 
					        // execute(pbftMessage);
 | 
				
			||||||
 | 
					        if (pbftMessage.order == commitedOrder.get() + 1) {
 | 
				
			||||||
 | 
					            execute(original);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            commitedMsg.add(original);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private synchronized void execute(PBFTMessage pbftMessage) {
 | 
				
			||||||
 | 
					        commitedOrder.incrementAndGet();
 | 
				
			||||||
 | 
					        PBFTMessage original = getOriginalMessage(pbftMessage.order);
 | 
				
			||||||
 | 
					        ContractRequest msg = ContractRequest.parse(original.content);
 | 
				
			||||||
 | 
					        committer.onCommit(msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        System.out.println(": execute, " + JsonUtil.toJson(msg));
 | 
				
			||||||
 | 
					        info.remove(pbftMessage.order);
 | 
				
			||||||
 | 
					        // ContractProcess.instance.onEvent(msg);
 | 
				
			||||||
 | 
					        commitedMsg.sort((o1, o2) -> (int) (o1.order - o2.order));
 | 
				
			||||||
 | 
					        Set<PBFTMessage> executedMsgs = new HashSet<>();
 | 
				
			||||||
 | 
					        for (PBFTMessage message : commitedMsg) {
 | 
				
			||||||
 | 
					            if (commitedOrder.get() == message.order) {
 | 
				
			||||||
 | 
					                commitedOrder.incrementAndGet();
 | 
				
			||||||
 | 
					                msg = ContractRequest.parse(getOriginalMessage(message.order).content);
 | 
				
			||||||
 | 
					                committer.onCommit(msg);
 | 
				
			||||||
 | 
					                executedMsgs.add(message);
 | 
				
			||||||
 | 
					                // info.remove(pbftMessage.order);
 | 
				
			||||||
 | 
					            } else
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        commitedMsg.removeAll(executedMsgs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private PBFTMessage getOriginalMessage(long order) {
 | 
				
			||||||
 | 
					        return info.get(order).request;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void broadcast(PBFTMessage pbftMessage) {
 | 
				
			||||||
 | 
					        for (Node node : members.keySet()) {
 | 
				
			||||||
 | 
					            System.out.println("[PBFTAlgorithm] send: " + pbftMessage.getDisplayStr());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            connection.sendMessage(node, pbftMessage.getBytes());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void sendToMaster(PBFTMessage pbftMessage) {
 | 
				
			||||||
 | 
					        for (Node node : members.keySet()) {
 | 
				
			||||||
 | 
					            PBFTMember member = members.get(node);
 | 
				
			||||||
 | 
					            if (member.isMaster) {
 | 
				
			||||||
 | 
					                connection.sendMessage(node, pbftMessage.getBytes());
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getMemberSize() {
 | 
				
			||||||
 | 
					        return members.size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onMessage(Node node, byte[] msg) {
 | 
				
			||||||
 | 
					        PBFTMessage pbftMessage = PBFTMessage.parse(msg);
 | 
				
			||||||
 | 
					        onPBFTMessage(node, pbftMessage);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static class NodeInfo {
 | 
				
			||||||
 | 
					        SM2KeyPair privKey;
 | 
				
			||||||
 | 
					        int hash;
 | 
				
			||||||
 | 
					        boolean isMaster;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public NodeInfo(SM2KeyPair key) {
 | 
				
			||||||
 | 
					            privKey = key;
 | 
				
			||||||
 | 
					            hash = Arrays.hashCode(privKey.getPublicKey().getQ().getEncoded(false));
 | 
				
			||||||
 | 
					            isMaster = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getNodeID() {
 | 
				
			||||||
 | 
					            return hash;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean isMaster() {
 | 
				
			||||||
 | 
					            return isMaster;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static class PCInfo {
 | 
				
			||||||
 | 
					        public PBFTMessage request;
 | 
				
			||||||
 | 
					        Set<Integer> prepare;
 | 
				
			||||||
 | 
					        Set<Integer> commit;
 | 
				
			||||||
 | 
					        boolean isPrePrepareReceived;
 | 
				
			||||||
 | 
					        boolean isSendCommit;
 | 
				
			||||||
 | 
					        boolean isSendReply;
 | 
				
			||||||
 | 
					        List<PBFTMessage> buff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        PCInfo() {
 | 
				
			||||||
 | 
					            prepare = new HashSet<>();
 | 
				
			||||||
 | 
					            commit = new HashSet<>();
 | 
				
			||||||
 | 
					            buff = new ArrayList<>();
 | 
				
			||||||
 | 
					            isSendCommit = false;
 | 
				
			||||||
 | 
					            isSendReply = false;
 | 
				
			||||||
 | 
					            isPrePrepareReceived = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public synchronized boolean updatePrepare(PBFTMessage message, PBFTAlgorithm center) {
 | 
				
			||||||
 | 
					            if (isSendCommit)
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            prepare.add(message.sendID);
 | 
				
			||||||
 | 
					            if (!isPrePrepareReceived) {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					//			System.out.println("---: updatePrepare, size:" + prepare.size() + " -->"
 | 
				
			||||||
 | 
					//					+ (center.members.size() / 3 * 2 + 1) + " --senderID:" + message.sendID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (prepare.size() > center.members.size() / 3 * 2) {
 | 
				
			||||||
 | 
					                isSendCommit = true;
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public synchronized boolean updateCommit(PBFTMessage message, PBFTAlgorithm center) {
 | 
				
			||||||
 | 
					            if (isSendReply)
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            commit.add(message.sendID);
 | 
				
			||||||
 | 
					            if (!isSendCommit)
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            if (commit.size() > center.members.size() / 3 * 2) {
 | 
				
			||||||
 | 
					                prepare = null;
 | 
				
			||||||
 | 
					                commit = null;
 | 
				
			||||||
 | 
					                isSendReply = true;
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public String getDisplayStr() {
 | 
				
			||||||
 | 
					            return "pSize:" + (prepare == null ? "null" : prepare.size()) + " cSize:"
 | 
				
			||||||
 | 
					                    + (commit == null ? "null" : commit.size()) + " isSendCommit:" + isSendCommit + " isSendReply:"
 | 
				
			||||||
 | 
					                    + isSendReply + " buffSize:" + buff.size();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								src/main/java/org/bdware/sc/sequencing/PBFTMember.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/main/java/org/bdware/sc/sequencing/PBFTMember.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.sequencing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
 | 
					import java.io.ByteArrayOutputStream;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.ObjectInputStream;
 | 
				
			||||||
 | 
					import java.io.ObjectOutputStream;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PBFTMember implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final long serialVersionUID = -3058672609865345062L;
 | 
				
			||||||
 | 
					 	public boolean isMaster;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public boolean isMaster() {
 | 
				
			||||||
 | 
							return isMaster;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static PBFTMember parse(byte[] content) {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								ByteArrayInputStream input = new ByteArrayInputStream(content);
 | 
				
			||||||
 | 
								ObjectInputStream objectInputStream = new ObjectInputStream(input);
 | 
				
			||||||
 | 
								PBFTMember ret = (PBFTMember) objectInputStream.readObject();
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
							} catch (Exception e) {
 | 
				
			||||||
 | 
								e.printStackTrace();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public byte[] toByteArray() {
 | 
				
			||||||
 | 
							ObjectOutputStream out;
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								ByteArrayOutputStream bo = new ByteArrayOutputStream();
 | 
				
			||||||
 | 
								out = new ObjectOutputStream(bo);
 | 
				
			||||||
 | 
								out.writeObject(this);
 | 
				
			||||||
 | 
								return bo.toByteArray();
 | 
				
			||||||
 | 
							} catch (IOException e) {
 | 
				
			||||||
 | 
								e.printStackTrace();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										99
									
								
								src/main/java/org/bdware/sc/sequencing/PBFTMessage.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/main/java/org/bdware/sc/sequencing/PBFTMessage.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.sequencing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
 | 
					import java.io.ByteArrayOutputStream;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ByteUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PBFTMessage {
 | 
				
			||||||
 | 
						PBFTType type;
 | 
				
			||||||
 | 
						int sendID;
 | 
				
			||||||
 | 
						long order;
 | 
				
			||||||
 | 
						byte[] content;
 | 
				
			||||||
 | 
						String stamp;// time-stamp or other string, given by client, to identify the content
 | 
				
			||||||
 | 
						String signature;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public int getBytesHash() {
 | 
				
			||||||
 | 
							ByteArrayOutputStream bo = new ByteArrayOutputStream();
 | 
				
			||||||
 | 
							bo.write(type.toInt());
 | 
				
			||||||
 | 
							ByteUtil.writeLong(bo, sendID);
 | 
				
			||||||
 | 
							ByteUtil.writeLong(bo, order);
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								bo.write(content);
 | 
				
			||||||
 | 
							} catch (IOException e) {
 | 
				
			||||||
 | 
								e.printStackTrace();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							byte[] ret = bo.toByteArray();
 | 
				
			||||||
 | 
							int hashCode = ret.hashCode();
 | 
				
			||||||
 | 
							return hashCode;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public byte[] getBytes() {
 | 
				
			||||||
 | 
							ByteArrayOutputStream bo = new ByteArrayOutputStream();
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								bo.write(type.toInt());
 | 
				
			||||||
 | 
								ByteUtil.writeInt(bo, sendID);
 | 
				
			||||||
 | 
								ByteUtil.writeLong(bo, order);
 | 
				
			||||||
 | 
								if (signature != null) {
 | 
				
			||||||
 | 
									byte[] signature = this.signature.getBytes();
 | 
				
			||||||
 | 
									ByteUtil.writeInt(bo, signature.length);
 | 
				
			||||||
 | 
									bo.write(signature);
 | 
				
			||||||
 | 
								} else
 | 
				
			||||||
 | 
									ByteUtil.writeInt(bo, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bo.write(content);
 | 
				
			||||||
 | 
							} catch (IOException e) {
 | 
				
			||||||
 | 
								e.printStackTrace();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return bo.toByteArray();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static PBFTMessage parse(byte[] b) {
 | 
				
			||||||
 | 
							PBFTMessage msg = new PBFTMessage();
 | 
				
			||||||
 | 
							ByteArrayInputStream bi = new ByteArrayInputStream(b);
 | 
				
			||||||
 | 
							msg.type = PBFTType.fromByte(bi.read());
 | 
				
			||||||
 | 
							msg.sendID = ByteUtil.readInt(bi);
 | 
				
			||||||
 | 
							msg.order = ByteUtil.readLong(bi);
 | 
				
			||||||
 | 
							int sigLen = ByteUtil.readInt(bi);
 | 
				
			||||||
 | 
							if (sigLen > 0)
 | 
				
			||||||
 | 
								msg.signature = new String(ByteUtil.readBytes(bi, sigLen));
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								msg.signature = null;
 | 
				
			||||||
 | 
							msg.content = ByteUtil.readBytes(bi, b.length - 1 - 4 - 8 - 4 - sigLen);
 | 
				
			||||||
 | 
							return msg;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 	private static String IDA = "PBFTMsg";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//	public void sign(SM2KeyPair pair) {
 | 
				
			||||||
 | 
					//		sendID = Arrays.hashCode(pair.getPublicKey().getEncoded(false));
 | 
				
			||||||
 | 
					//		signature = sm02.sign(content, IDA, pair).toString();
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	public boolean verify(PBFTMember member) {
 | 
				
			||||||
 | 
					//		return sm02.verify(content, Signature.loadFromString(signature), IDA,
 | 
				
			||||||
 | 
					//				SM2KeyPair.publicKeyStr2ECPoint(member.pubKey));
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setType(PBFTType type) {
 | 
				
			||||||
 | 
							this.type = type;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setContent(byte[] content) {
 | 
				
			||||||
 | 
							this.content = content;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public String getDisplayStr() {
 | 
				
			||||||
 | 
							StringBuilder sb = new StringBuilder();
 | 
				
			||||||
 | 
							sb.append(sendID);
 | 
				
			||||||
 | 
							sb.append("_");
 | 
				
			||||||
 | 
							sb.append(order);
 | 
				
			||||||
 | 
							sb.append("_TYPE_").append(type);
 | 
				
			||||||
 | 
							sb.append(" --> sig:");
 | 
				
			||||||
 | 
							sb.append(signature);
 | 
				
			||||||
 | 
							sb.append(" content:");
 | 
				
			||||||
 | 
							sb.append(new String(content));
 | 
				
			||||||
 | 
							return sb.toString();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/main/java/org/bdware/sc/sequencing/PBFTType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/main/java/org/bdware/sc/sequencing/PBFTType.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.sequencing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum PBFTType {
 | 
				
			||||||
 | 
						Request(0), PrePrepare(1), Prepare(2), Commit(3), Reply(4), Unknown(5), ReSend(6), AddMember(7);
 | 
				
			||||||
 | 
						private int type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PBFTType(int i) {
 | 
				
			||||||
 | 
							type = i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static PBFTType fromByte(int i) {
 | 
				
			||||||
 | 
							switch (i) {
 | 
				
			||||||
 | 
							case 0:
 | 
				
			||||||
 | 
								return Request;
 | 
				
			||||||
 | 
							case 1:
 | 
				
			||||||
 | 
								return PrePrepare;
 | 
				
			||||||
 | 
							case 2:
 | 
				
			||||||
 | 
								return Prepare;
 | 
				
			||||||
 | 
							case 3:
 | 
				
			||||||
 | 
								return Commit;
 | 
				
			||||||
 | 
							case 4:
 | 
				
			||||||
 | 
								return Reply;
 | 
				
			||||||
 | 
							case 6:
 | 
				
			||||||
 | 
								return ReSend;
 | 
				
			||||||
 | 
							case 7:
 | 
				
			||||||
 | 
								return AddMember;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return Unknown;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public int toInt() {
 | 
				
			||||||
 | 
							return type;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/main/java/org/bdware/sc/sequencing/Pair.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/main/java/org/bdware/sc/sequencing/Pair.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.sequencing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class Pair<T1, T2> {
 | 
				
			||||||
 | 
						public T1 first;
 | 
				
			||||||
 | 
						public T2 second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Pair(T1 t1, T2 t2) {
 | 
				
			||||||
 | 
							first = t1;
 | 
				
			||||||
 | 
							second = t2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.sequencing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.Node;
 | 
				
			||||||
 | 
					import org.bdware.sc.units.TrustfulExecutorConnection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface SequencingAlgorithm {
 | 
				
			||||||
 | 
					    void onMessage(Node node, byte[] msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setCommitter(Committer c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setConnection(TrustfulExecutorConnection c);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								src/main/java/org/bdware/sc/sequencing/ViewAlgorithm.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main/java/org/bdware/sc/sequencing/ViewAlgorithm.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.sequencing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.Node;
 | 
				
			||||||
 | 
					import org.bdware.sc.units.TrustfulExecutorConnection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ViewAlgorithm implements SequencingAlgorithm {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Committer committer;
 | 
				
			||||||
 | 
					    private TrustfulExecutorConnection connection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ViewAlgorithm(boolean isMaster) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setCommitter(Committer c) {
 | 
				
			||||||
 | 
					        committer = c;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setConnection(TrustfulExecutorConnection c) {
 | 
				
			||||||
 | 
					        connection = c;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onMessage(Node node, byte[] msg) {
 | 
				
			||||||
 | 
					        PBFTMessage pbftMessage = PBFTMessage.parse(msg);
 | 
				
			||||||
 | 
					        if (pbftMessage.type == PBFTType.Request) {
 | 
				
			||||||
 | 
					            ContractRequest cr = ContractRequest.parse(pbftMessage.content);
 | 
				
			||||||
 | 
					            committer.onCommit(cr);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										53
									
								
								src/main/java/org/bdware/sc/units/ByteUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/main/java/org/bdware/sc/units/ByteUtil.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
 | 
					import java.io.ByteArrayOutputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ByteUtil {
 | 
				
			||||||
 | 
					//	public static byte[] readRest(ByteArrayInputStream bi) {
 | 
				
			||||||
 | 
					//		byte[] ret = new byte[bi.available()];
 | 
				
			||||||
 | 
					//		bi.read(ret, 0, ret.length);
 | 
				
			||||||
 | 
					//		return ret;
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static int readInt(ByteArrayInputStream bi) {
 | 
				
			||||||
 | 
							int ret = 0;
 | 
				
			||||||
 | 
							ret |= (bi.read() & 0xff);
 | 
				
			||||||
 | 
							ret <<= 8;
 | 
				
			||||||
 | 
							ret |= (bi.read() & 0xff);
 | 
				
			||||||
 | 
							ret <<= 8;
 | 
				
			||||||
 | 
							ret |= (bi.read() & 0xff);
 | 
				
			||||||
 | 
							ret <<= 8;
 | 
				
			||||||
 | 
							ret |= (bi.read() & 0xff);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static void writeInt(ByteArrayOutputStream bo, int i) {
 | 
				
			||||||
 | 
							bo.write((i >> 24) & 0xff);
 | 
				
			||||||
 | 
							bo.write((i >> 16) & 0xff);
 | 
				
			||||||
 | 
							bo.write((i >> 8) & 0xff);
 | 
				
			||||||
 | 
							bo.write(i & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static void writeLong(ByteArrayOutputStream bo, long l) {
 | 
				
			||||||
 | 
							for (int i = 56; i >= 0; i -= 8) {
 | 
				
			||||||
 | 
								bo.write(((int) (l >> i)) & 0xff);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static long readLong(ByteArrayInputStream bi) {
 | 
				
			||||||
 | 
							long ret = 0L;
 | 
				
			||||||
 | 
							for (int i = 0; i < 8; i++) {
 | 
				
			||||||
 | 
								ret <<= 8;
 | 
				
			||||||
 | 
								ret |= (bi.read() & 0xff);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static byte[] readBytes(ByteArrayInputStream bi, int len) {
 | 
				
			||||||
 | 
							byte[] ret = new byte[len];
 | 
				
			||||||
 | 
							bi.read(ret, 0, len);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										84
									
								
								src/main/java/org/bdware/sc/units/ContractRecord.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/main/java/org/bdware/sc/units/ContractRecord.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 节点启动后记录自己的units信息和合约的一些信息
 | 
				
			||||||
 | 
					 * 节点重新上线后,NC发要求让其恢复,则从本地读取自己的这些信息进行恢复
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ContractRecord implements Serializable {
 | 
				
			||||||
 | 
					/*    private static final long serialVersionUID = 704775241674568688L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public transient RecoverFlag recoverFlag = RecoverFlag.Fine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String contractID;
 | 
				
			||||||
 | 
					    public String contractName;
 | 
				
			||||||
 | 
					    public String key;
 | 
				
			||||||
 | 
					    public String contractpubKey;
 | 
				
			||||||
 | 
					    public ContractType type;
 | 
				
			||||||
 | 
					    public int copies = 1;
 | 
				
			||||||
 | 
					    public int lastExeSeq = -1;
 | 
				
			||||||
 | 
					    public boolean isPrivate = false;
 | 
				
			||||||
 | 
					    public String pubKeyPath;
 | 
				
			||||||
 | 
					    public String ypkName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //public Map<String,String> members;   //k-udpid,v-udpaddress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String memory = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //JavaScriptEntry
 | 
				
			||||||
 | 
					    public String invokeID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractRecord(String id){
 | 
				
			||||||
 | 
					        this.contractID = id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractRecord(String id,String contractName,String key,String contractpubKey,ContractType type,int copies){
 | 
				
			||||||
 | 
					        this.contractID = id;
 | 
				
			||||||
 | 
					        this.contractName = contractName;
 | 
				
			||||||
 | 
					        this.key = key;
 | 
				
			||||||
 | 
					        this.contractpubKey = contractpubKey;
 | 
				
			||||||
 | 
					        this.type = type;
 | 
				
			||||||
 | 
					        this.copies = copies;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void printContent(){
 | 
				
			||||||
 | 
					        System.out.println("==========ContractRecord========");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        System.out.println("contractID=" + contractID == null ? "null" : contractID);
 | 
				
			||||||
 | 
					        System.out.println("contractName=" + contractName == null ? "null" : contractName);
 | 
				
			||||||
 | 
					        System.out.println("key=" + key == null ? "null" : key);
 | 
				
			||||||
 | 
					        System.out.println("contractPubKey=" + contractpubKey == null ? "null" : contractpubKey);
 | 
				
			||||||
 | 
					        System.out.println("type=" + type == null ? "null" : type);
 | 
				
			||||||
 | 
					        System.out.println("lastExeSeq=" + lastExeSeq == null ? "null" : lastExeSeq);
 | 
				
			||||||
 | 
					        System.out.println("invokeID=" + invokeID == null ? "null" : invokeID);
 | 
				
			||||||
 | 
					        System.out.println("copies=" + copies);
 | 
				
			||||||
 | 
					        System.out.println("isPrivate=" + isPrivate);
 | 
				
			||||||
 | 
					        System.out.println("pubKeyPath=" + pubKeyPath == null ? "null" : pubKeyPath);
 | 
				
			||||||
 | 
					        System.out.println("ypkName=" + ypkName == null ? "null" : ypkName);
 | 
				
			||||||
 | 
					        if(members != null){
 | 
				
			||||||
 | 
					            for(String k : members.keySet()){
 | 
				
			||||||
 | 
					                System.out.println("members " + k + "-" + members.get(k));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        System.out.println("memory=" + memory == null ? "null" : memory);
 | 
				
			||||||
 | 
					        System.out.println("==========ContractRecord print finish=======");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void getContentFromFile(String path){
 | 
				
			||||||
 | 
					        File file = new File(path);
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            FileInputStream os = new FileInputStream(file);
 | 
				
			||||||
 | 
					            ObjectInputStream oos = new ObjectInputStream(os);
 | 
				
			||||||
 | 
					            ContractRecord record = (ContractRecord) oos.readObject();
 | 
				
			||||||
 | 
					            record.printContent();
 | 
				
			||||||
 | 
					        } catch (FileNotFoundException e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        } catch (ClassNotFoundException e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										156
									
								
								src/main/java/org/bdware/sc/units/ContractUnitController.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								src/main/java/org/bdware/sc/units/ContractUnitController.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,156 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractManager;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.Node;
 | 
				
			||||||
 | 
					import org.bdware.sc.sequencing.SequencingAlgorithm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ContractUnitController {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = LogManager.getLogger(ContractUnitController.class);
 | 
				
			||||||
 | 
					    private final TrustfulExecutorConnection connection;
 | 
				
			||||||
 | 
					    private final Map<String, ContractUnit> units;   //这个节点上<合约id,合约集群>,其中每个ContractUnit中存储了对应合约id的其他节点udp信息
 | 
				
			||||||
 | 
					    ContractManager manager;
 | 
				
			||||||
 | 
					    SequencingAlgorithmFactory algorithmFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractUnitController(
 | 
				
			||||||
 | 
					            TrustfulExecutorConnection connection,
 | 
				
			||||||
 | 
					            ContractManager manager,
 | 
				
			||||||
 | 
					            SequencingAlgorithmFactory factory) {
 | 
				
			||||||
 | 
					        this.connection = connection;
 | 
				
			||||||
 | 
					        this.manager = manager;
 | 
				
			||||||
 | 
					        algorithmFactory = factory;
 | 
				
			||||||
 | 
					        units = new HashMap<>();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onContractUnitMessage(final ContractUnitMessage cumsg, Node node) {
 | 
				
			||||||
 | 
					        ContractUnit unit;
 | 
				
			||||||
 | 
					        switch (cumsg.type) {
 | 
				
			||||||
 | 
					            case Start:
 | 
				
			||||||
 | 
					                ContractUnitStartRequest req = ContractUnitStartRequest.parse(cumsg.content);
 | 
				
			||||||
 | 
					                unit = new ContractUnit();
 | 
				
			||||||
 | 
					                unit.connection = connection;
 | 
				
			||||||
 | 
					                String result = manager.startContract(req.contract);
 | 
				
			||||||
 | 
					                unit.contractID = req.contract.getID();
 | 
				
			||||||
 | 
					                unit.sequencingAlgorithm = algorithmFactory.create(req, unit);
 | 
				
			||||||
 | 
					                unit.node2member = new HashMap<>();
 | 
				
			||||||
 | 
					                units.put(req.contract.getID(), unit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                System.out.println(
 | 
				
			||||||
 | 
					                        "[ContractUnitController] startContract:"
 | 
				
			||||||
 | 
					                                + result
 | 
				
			||||||
 | 
					                                + " isMaster:"
 | 
				
			||||||
 | 
					                                + req.isMaster
 | 
				
			||||||
 | 
					                                + " cid:"
 | 
				
			||||||
 | 
					                                + req.contract.getID());
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case AddMember:
 | 
				
			||||||
 | 
					                LOGGER.debug("contractID:" + cumsg.getContractID());
 | 
				
			||||||
 | 
					                unit = units.get(cumsg.getContractID());
 | 
				
			||||||
 | 
					                unit.addMember(node, cumsg);   //启动通过在UDPTrustExecutor中遍历memebers,使得这个合约集群中每个节点都有其他节点的UDP信息,便于之后护发UDP消息
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Sequencing:
 | 
				
			||||||
 | 
					                unit = units.get(cumsg.getContractID());
 | 
				
			||||||
 | 
					                ContractUnitMember member = unit.node2member.get(node);
 | 
				
			||||||
 | 
					                unit.sequencingAlgorithm.onMessage(member, cumsg.content);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Unknown:
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                //recover start
 | 
				
			||||||
 | 
					                ContractUnitStartRequest req2 = ContractUnitStartRequest.parse(cumsg.content);
 | 
				
			||||||
 | 
					                unit = new ContractUnit();
 | 
				
			||||||
 | 
					                unit.connection = connection;
 | 
				
			||||||
 | 
					                unit.contractID = req2.contract.getID();
 | 
				
			||||||
 | 
					                unit.sequencingAlgorithm = algorithmFactory.create(req2, unit);
 | 
				
			||||||
 | 
					                unit.node2member = new HashMap<>();
 | 
				
			||||||
 | 
					                units.put(req2.contract.getID(), unit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                System.out.println(
 | 
				
			||||||
 | 
					                        "[ContractUnitController] startContract:"
 | 
				
			||||||
 | 
					                                + " isMaster:"
 | 
				
			||||||
 | 
					                                + req2.isMaster
 | 
				
			||||||
 | 
					                                + " cid:"
 | 
				
			||||||
 | 
					                                + req2.contract.getID());
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ContractUnit getContractUnit(String id) {
 | 
				
			||||||
 | 
					        return units.get(id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class ContractUnit implements TrustfulExecutorConnection, Serializable {
 | 
				
			||||||
 | 
					        public String contractID;
 | 
				
			||||||
 | 
					        public Map<Node, ContractUnitMember> node2member;  //存储和自己在一个合约集群的其他节点信息
 | 
				
			||||||
 | 
					        private transient SequencingAlgorithm sequencingAlgorithm;
 | 
				
			||||||
 | 
					        private transient TrustfulExecutorConnection connection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public synchronized void broadcast(ContractUnitMessage cuMessage) {
 | 
				
			||||||
 | 
					            for (Node node : node2member.keySet()) {
 | 
				
			||||||
 | 
					                connection.sendMessage(node, cuMessage.toByteArray());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public String addMember(Node node, ContractUnitMessage msg) {
 | 
				
			||||||
 | 
					            ContractUnitMember member;
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                // TODO
 | 
				
			||||||
 | 
					                member = new ContractUnitMember(node);
 | 
				
			||||||
 | 
					                sequencingAlgorithm.onMessage(member, msg.content);
 | 
				
			||||||
 | 
					                node2member.put(node, member);
 | 
				
			||||||
 | 
					                return "success";
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                e.printStackTrace();
 | 
				
			||||||
 | 
					                return "failed";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void sendMessage(Node node, byte[] msg) {
 | 
				
			||||||
 | 
					            ContractUnitMember member = (ContractUnitMember) node;
 | 
				
			||||||
 | 
					            ContractUnitMessage unitMsg = new ContractUnitMessage();
 | 
				
			||||||
 | 
					            unitMsg.setContractID(contractID);
 | 
				
			||||||
 | 
					            unitMsg.type = ContractUnitType.Sequencing;
 | 
				
			||||||
 | 
					            unitMsg.content = msg;
 | 
				
			||||||
 | 
					            System.out.println("[ContractUnitController] sendMsg:" + unitMsg.getDisplayStr());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // TODO sig here.
 | 
				
			||||||
 | 
					            connection.sendMessage(member.getNode(), unitMsg.toByteArray());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static class PCInfo {
 | 
				
			||||||
 | 
					        public ContractUnitMessage request;
 | 
				
			||||||
 | 
					        Set<Integer> prepare;
 | 
				
			||||||
 | 
					        Set<Integer> commit;
 | 
				
			||||||
 | 
					        boolean isPrePrepareReceived;
 | 
				
			||||||
 | 
					        boolean isSendCommit;
 | 
				
			||||||
 | 
					        boolean isSendReply;
 | 
				
			||||||
 | 
					        List<ContractUnitMessage> buff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        PCInfo() {
 | 
				
			||||||
 | 
					            prepare = new HashSet<>();
 | 
				
			||||||
 | 
					            commit = new HashSet<>();
 | 
				
			||||||
 | 
					            buff = new ArrayList<>();
 | 
				
			||||||
 | 
					            isSendCommit = false;
 | 
				
			||||||
 | 
					            isSendReply = false;
 | 
				
			||||||
 | 
					            isPrePrepareReceived = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public String getDisplayStr() {
 | 
				
			||||||
 | 
					            return "pSize:"
 | 
				
			||||||
 | 
					                    + (prepare == null ? "null" : prepare.size())
 | 
				
			||||||
 | 
					                    + " cSize:"
 | 
				
			||||||
 | 
					                    + (commit == null ? "null" : commit.size())
 | 
				
			||||||
 | 
					                    + " isSendCommit:"
 | 
				
			||||||
 | 
					                    + isSendCommit
 | 
				
			||||||
 | 
					                    + " isSendReply:"
 | 
				
			||||||
 | 
					                    + isSendReply
 | 
				
			||||||
 | 
					                    + " buffSize:"
 | 
				
			||||||
 | 
					                    + buff.size();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/main/java/org/bdware/sc/units/ContractUnitMember.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/main/java/org/bdware/sc/units/ContractUnitMember.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.Node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ContractUnitMember extends Node {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final long serialVersionUID = 8175664649689078937L;
 | 
				
			||||||
 | 
						public Node node;
 | 
				
			||||||
 | 
						public String pubKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public ContractUnitMember(Node node) {
 | 
				
			||||||
 | 
							this.node = node;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Node getNode() {
 | 
				
			||||||
 | 
							return node;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										109
									
								
								src/main/java/org/bdware/sc/units/ContractUnitMessage.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/main/java/org/bdware/sc/units/ContractUnitMessage.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
 | 
					import java.io.ByteArrayOutputStream;
 | 
				
			||||||
 | 
					import java.io.ObjectInputStream;
 | 
				
			||||||
 | 
					import java.io.ObjectOutputStream;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 | 
				
			||||||
 | 
					import org.zz.gmhelper.BCECUtil;
 | 
				
			||||||
 | 
					import org.zz.gmhelper.SM2Util;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ContractUnitMessage implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final long serialVersionUID = 584934384202845750L;
 | 
				
			||||||
 | 
						ContractUnitType type;
 | 
				
			||||||
 | 
						private String contractID;
 | 
				
			||||||
 | 
						String requestID;
 | 
				
			||||||
 | 
						String signature;
 | 
				
			||||||
 | 
						byte[] content;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public ContractUnitMessage(String requestID, String contractID, byte[] sender) {
 | 
				
			||||||
 | 
							this.requestID = requestID;
 | 
				
			||||||
 | 
							this.setContractID(contractID);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public ContractUnitMessage() {
 | 
				
			||||||
 | 
							// TODO Auto-generated constructor stub
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static ContractUnitMessage parse(byte[] b) {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								ByteArrayInputStream bi = new ByteArrayInputStream(b);
 | 
				
			||||||
 | 
								ObjectInputStream input = new ObjectInputStream(bi);
 | 
				
			||||||
 | 
								ContractUnitMessage ret = (ContractUnitMessage) input.readObject();
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
							} catch (Exception e) {
 | 
				
			||||||
 | 
								e.printStackTrace();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public byte[] toByteArray() {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								ByteArrayOutputStream bo = new ByteArrayOutputStream();
 | 
				
			||||||
 | 
								ObjectOutputStream output = new ObjectOutputStream(bo);
 | 
				
			||||||
 | 
								output.writeObject(this);
 | 
				
			||||||
 | 
								return bo.toByteArray();
 | 
				
			||||||
 | 
							} catch (Exception e) {
 | 
				
			||||||
 | 
								e.printStackTrace();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static String IDA = "PBFTMsg";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//	public void sign(NodeInfo info) {
 | 
				
			||||||
 | 
					//		sender = info.getNodeID();
 | 
				
			||||||
 | 
					//		signature = sm02.sign(content, IDA, info.privKey).toString();
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public boolean verify(ContractUnitMember member) {
 | 
				
			||||||
 | 
							ECPublicKeyParameters param = BCECUtil.createECPublicKeyFromStrParameters(member.pubKey,SM2Util.CURVE,SM2Util.DOMAIN_PARAMS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return SM2Util.verify(param,content, signature.getBytes());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setType(ContractUnitType type) {
 | 
				
			||||||
 | 
							this.type = type;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public ContractUnitType getType() {
 | 
				
			||||||
 | 
							return type;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public byte[] getContent() {
 | 
				
			||||||
 | 
							return content;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setContent(byte[] content) {
 | 
				
			||||||
 | 
							this.content = content;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public String getContractID() {
 | 
				
			||||||
 | 
							return contractID;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public String getRequestID() {
 | 
				
			||||||
 | 
							return requestID;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public String getDisplayStr() {
 | 
				
			||||||
 | 
							StringBuilder sb = new StringBuilder();
 | 
				
			||||||
 | 
							sb.append(requestID);
 | 
				
			||||||
 | 
							sb.append("_TYPE_").append(type);
 | 
				
			||||||
 | 
							sb.append("_");
 | 
				
			||||||
 | 
							sb.append(getContractID());
 | 
				
			||||||
 | 
							sb.append("_");
 | 
				
			||||||
 | 
							sb.append(" content:");
 | 
				
			||||||
 | 
							sb.append(new String(content));
 | 
				
			||||||
 | 
							return sb.toString();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setContractID(String contractID) {
 | 
				
			||||||
 | 
							this.contractID = contractID;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
 | 
					import java.io.ByteArrayOutputStream;
 | 
				
			||||||
 | 
					import java.io.ObjectInputStream;
 | 
				
			||||||
 | 
					import java.io.ObjectOutputStream;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.Contract;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.Node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ContractUnitStartRequest implements Serializable {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private static final long serialVersionUID = -1540780483790689627L;
 | 
				
			||||||
 | 
						public Contract contract;
 | 
				
			||||||
 | 
						public boolean isMaster;
 | 
				
			||||||
 | 
						public Node[] members;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public static ContractUnitStartRequest parse(byte[] content) {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								ByteArrayInputStream input = new ByteArrayInputStream(content);
 | 
				
			||||||
 | 
								ObjectInputStream objInput;
 | 
				
			||||||
 | 
								objInput = new ObjectInputStream(input);
 | 
				
			||||||
 | 
								return (ContractUnitStartRequest) objInput.readObject();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} catch (Exception e) {
 | 
				
			||||||
 | 
								e.printStackTrace();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public byte[] toByteArray() {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								ByteArrayOutputStream bo = new ByteArrayOutputStream();
 | 
				
			||||||
 | 
								ObjectOutputStream objOutput = new ObjectOutputStream(bo);
 | 
				
			||||||
 | 
								objOutput.writeObject(this);
 | 
				
			||||||
 | 
								return bo.toByteArray();
 | 
				
			||||||
 | 
							} catch (Exception e) {
 | 
				
			||||||
 | 
								e.printStackTrace();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/main/java/org/bdware/sc/units/ContractUnitType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/main/java/org/bdware/sc/units/ContractUnitType.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum ContractUnitType implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Start(0), AddMember(1), Sequencing(2), Unknown(3);
 | 
				
			||||||
 | 
						private int type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private ContractUnitType(int i) {
 | 
				
			||||||
 | 
							type = i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static ContractUnitType fromByte(int i) {
 | 
				
			||||||
 | 
							switch (i) {
 | 
				
			||||||
 | 
							case 0:
 | 
				
			||||||
 | 
								return Start;
 | 
				
			||||||
 | 
							case 1:
 | 
				
			||||||
 | 
								return AddMember;
 | 
				
			||||||
 | 
							case 2:
 | 
				
			||||||
 | 
								return Sequencing;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return Unknown;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public int toInt() {
 | 
				
			||||||
 | 
							return type;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										219
									
								
								src/main/java/org/bdware/sc/units/MultiContractMeta.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								src/main/java/org/bdware/sc/units/MultiContractMeta.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,219 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gson.JsonArray;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractExecType;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.IDSerializable;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.CMTables;
 | 
				
			||||||
 | 
					import org.bdware.sc.db.KeyValueDBUtil;
 | 
				
			||||||
 | 
					import org.bdware.sc.redo.TransRecord;
 | 
				
			||||||
 | 
					import org.bdware.sc.util.JsonUtil;
 | 
				
			||||||
 | 
					import org.bdware.server.trustedmodel.ContractExecutor;
 | 
				
			||||||
 | 
					import org.bdware.server.trustedmodel.ContractUnitStatus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.PriorityQueue;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicInteger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 在每个节点上均有
 | 
				
			||||||
 | 
					public class MultiContractMeta implements IDSerializable {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = LogManager.getLogger(MultiContractMeta.class);
 | 
				
			||||||
 | 
					    private final AtomicInteger lastExeSeq; // last executed request seq
 | 
				
			||||||
 | 
					    public volatile int curExeSeq = -1; // 当前正在执行请求序号   for multipoint contract requests
 | 
				
			||||||
 | 
					    public String memory;
 | 
				
			||||||
 | 
					    public String key; // privateKey
 | 
				
			||||||
 | 
					    public String publicKey;
 | 
				
			||||||
 | 
					    public String invokeID; // TODO
 | 
				
			||||||
 | 
					    public ContractExecType type;
 | 
				
			||||||
 | 
					    public ContractUnitStatus unitStatus = ContractUnitStatus.CommonMode;
 | 
				
			||||||
 | 
					    public transient ContractExecutor contractExecutor;
 | 
				
			||||||
 | 
					    public transient PriorityQueue<ContractRequest> queue; // contract request
 | 
				
			||||||
 | 
					    public transient Map<Integer, String> uniReqIDMap; // 用于请求
 | 
				
			||||||
 | 
					    public transient Map<Integer, ResultCallback> resultMap; // 用于请求
 | 
				
			||||||
 | 
					    public transient PriorityQueue<TransRecord> trans_queue; // transRecord
 | 
				
			||||||
 | 
					    AtomicInteger masterOrder = new AtomicInteger(-1);
 | 
				
			||||||
 | 
					    private String contractID;
 | 
				
			||||||
 | 
					    private boolean isPrivate = false;
 | 
				
			||||||
 | 
					    private String pubKeyPath;
 | 
				
			||||||
 | 
					    private String ypkName;
 | 
				
			||||||
 | 
					    private String[] members; // 执行这个合约的所有节点的pubKey
 | 
				
			||||||
 | 
					    private boolean isMaster;
 | 
				
			||||||
 | 
					    private String masterNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MultiContractMeta(String s) {
 | 
				
			||||||
 | 
					        contractID = s;
 | 
				
			||||||
 | 
					        isMaster = false;
 | 
				
			||||||
 | 
					        masterNode = null;
 | 
				
			||||||
 | 
					        lastExeSeq = new AtomicInteger(-1);
 | 
				
			||||||
 | 
					        initQueue();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void initQueue() {
 | 
				
			||||||
 | 
					        queue = new PriorityQueue<>();
 | 
				
			||||||
 | 
					        trans_queue = new PriorityQueue<>();
 | 
				
			||||||
 | 
					        uniReqIDMap = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					        resultMap = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getCurSeqAtMaster() {
 | 
				
			||||||
 | 
					        return masterOrder.get();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void nextSeqAtMaster() {
 | 
				
			||||||
 | 
					        masterOrder.getAndIncrement();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setSeqAtMaster(int seq) {
 | 
				
			||||||
 | 
					        masterOrder = new AtomicInteger(seq);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void init(MultiContractMeta old) {
 | 
				
			||||||
 | 
					        if (old != null && old.hasQueues()) {
 | 
				
			||||||
 | 
					            queue = old.queue;
 | 
				
			||||||
 | 
					            trans_queue = old.trans_queue;
 | 
				
			||||||
 | 
					            uniReqIDMap = old.uniReqIDMap;
 | 
				
			||||||
 | 
					            resultMap = old.resultMap;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if (null == queue) {
 | 
				
			||||||
 | 
					                queue = new PriorityQueue<>();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (null == trans_queue) {
 | 
				
			||||||
 | 
					                trans_queue = new PriorityQueue<>();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (null == uniReqIDMap) {
 | 
				
			||||||
 | 
					                uniReqIDMap = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (null == resultMap) {
 | 
				
			||||||
 | 
					                resultMap = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean hasQueues() {
 | 
				
			||||||
 | 
					        return null != queue && null != trans_queue && null != uniReqIDMap && null != resultMap;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getLastExeSeq() {
 | 
				
			||||||
 | 
					        return this.lastExeSeq.get();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setLastExeSeq(int lastExeSeq) {
 | 
				
			||||||
 | 
					        this.lastExeSeq.set(lastExeSeq);
 | 
				
			||||||
 | 
					        if (KeyValueDBUtil.instance.containsKey(
 | 
				
			||||||
 | 
					                CMTables.LastExeSeq.toString(), contractID)) { // 如果现在是Stable模式就同步刷到磁盘
 | 
				
			||||||
 | 
					            KeyValueDBUtil.instance.setValue(
 | 
				
			||||||
 | 
					                    CMTables.LastExeSeq.toString(), contractID, String.valueOf(lastExeSeq));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isSequent(int a) {
 | 
				
			||||||
 | 
					        return a - lastExeSeq.get() == 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getContractID() {
 | 
				
			||||||
 | 
					        return contractID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setContractID(String id) {
 | 
				
			||||||
 | 
					        contractID = id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getID() {
 | 
				
			||||||
 | 
					        return contractID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setIsPrivate(boolean t) {
 | 
				
			||||||
 | 
					        isPrivate = t;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isPrivate() {
 | 
				
			||||||
 | 
					        return isPrivate;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getPubKeyPath() {
 | 
				
			||||||
 | 
					        if (!isPrivate) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return pubKeyPath;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setPubKeyPath(String p) {
 | 
				
			||||||
 | 
					        pubKeyPath = p;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getYpkName() {
 | 
				
			||||||
 | 
					        return ypkName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setYpkName(String s) {
 | 
				
			||||||
 | 
					        ypkName = s;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String[] getMembers() {
 | 
				
			||||||
 | 
					        return members;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setMembers(String[] m) {
 | 
				
			||||||
 | 
					        members = m;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setMembers(JsonArray members) {
 | 
				
			||||||
 | 
					        String[] copied = new String[members.size()];
 | 
				
			||||||
 | 
					        for (int i = 0; i < members.size(); i++) {
 | 
				
			||||||
 | 
					            copied[i] = members.get(i).getAsString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.members = copied;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void printSet() {
 | 
				
			||||||
 | 
					        for (ContractRequest cr : queue) {
 | 
				
			||||||
 | 
					            LOGGER.debug("请求-" + cr.seq);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void addRequestQueue(ContractRequest request, String uniReqID, ResultCallback result) {
 | 
				
			||||||
 | 
					        LOGGER.info("put to queue, seq:" + request.seq + " queue.size:" + queue.size());
 | 
				
			||||||
 | 
					        queue.add(request);
 | 
				
			||||||
 | 
					        int seq = request.seq;
 | 
				
			||||||
 | 
					        uniReqIDMap.put(seq, uniReqID);
 | 
				
			||||||
 | 
					        resultMap.put(seq, result);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void printContent() {
 | 
				
			||||||
 | 
					        System.out.println("contractID=" + (contractID == null ? "null" : contractID));
 | 
				
			||||||
 | 
					        System.out.println("isPrivate=" + isPrivate);
 | 
				
			||||||
 | 
					        System.out.println("pubKeyPath=" + (pubKeyPath == null ? "null" : pubKeyPath));
 | 
				
			||||||
 | 
					        System.out.println("ypkName=" + (ypkName == null ? "null" : ypkName));
 | 
				
			||||||
 | 
					        System.out.println("key=" + (key == null ? "null" : key));
 | 
				
			||||||
 | 
					        System.out.println("publicKey=" + (publicKey == null ? "null" : publicKey));
 | 
				
			||||||
 | 
					        System.out.println("invokeID=" + (invokeID == null ? "null" : invokeID));
 | 
				
			||||||
 | 
					        System.out.println("type=" + (type == null ? "null" : type));
 | 
				
			||||||
 | 
					        System.out.println("lastExeSeq=" + lastExeSeq);
 | 
				
			||||||
 | 
					        System.out.println("members=" + (members == null ? "null" : JsonUtil.toJson(members)));
 | 
				
			||||||
 | 
					        System.out.println("memory=" + (memory == null ? "null" : memory));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setIsMaster(boolean isMaster) {
 | 
				
			||||||
 | 
					        this.isMaster = isMaster;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isMaster() {
 | 
				
			||||||
 | 
					        return isMaster;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setMaster(String master) {
 | 
				
			||||||
 | 
					        masterNode = master;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMasterNode() {
 | 
				
			||||||
 | 
					        return masterNode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setContractExecutor(ContractExecutor contractExecutor) {
 | 
				
			||||||
 | 
					        this.contractExecutor = contractExecutor;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								src/main/java/org/bdware/sc/units/RecoverFlag.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/main/java/org/bdware/sc/units/RecoverFlag.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum RecoverFlag {
 | 
				
			||||||
 | 
					   Fine,ToRecover,Recovering;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										53
									
								
								src/main/java/org/bdware/sc/units/RespCache.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/main/java/org/bdware/sc/units/RespCache.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicInteger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class RespCache {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = LogManager.getLogger(RespCache.class);
 | 
				
			||||||
 | 
					    public String val = null;
 | 
				
			||||||
 | 
					    public int count; // copies
 | 
				
			||||||
 | 
					    public long time;
 | 
				
			||||||
 | 
					    public boolean isExecuting = false;
 | 
				
			||||||
 | 
					    AtomicInteger waiter = new AtomicInteger(0);
 | 
				
			||||||
 | 
					    boolean timeout = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean waitForHalf() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            synchronized (waiter) {
 | 
				
			||||||
 | 
					                LOGGER.info("waitForHalf, " + waiter.get() + " count:" + count);
 | 
				
			||||||
 | 
					                time = System.currentTimeMillis();
 | 
				
			||||||
 | 
					                if (waiter.incrementAndGet() * 2 > count) {
 | 
				
			||||||
 | 
					                    return timeout;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    waiter.wait(5000L);
 | 
				
			||||||
 | 
					                    timeout &= waiter.get() * 2 > count;
 | 
				
			||||||
 | 
					                    if (!timeout) waiter.notifyAll();
 | 
				
			||||||
 | 
					                    return timeout;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					            timeout &= waiter.get() * 2 > count;
 | 
				
			||||||
 | 
					            return timeout;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void notifyAllWaiter() {
 | 
				
			||||||
 | 
					        synchronized (waiter) {
 | 
				
			||||||
 | 
					            waiter.notifyAll();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void waitForResult() {
 | 
				
			||||||
 | 
					        synchronized (waiter) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                waiter.wait(5000L);
 | 
				
			||||||
 | 
					            } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                e.printStackTrace();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								src/main/java/org/bdware/sc/units/ResultCache.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/main/java/org/bdware/sc/units/ResultCache.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ResultCache {
 | 
				
			||||||
 | 
					    public String val = null;
 | 
				
			||||||
 | 
					    public long time;
 | 
				
			||||||
 | 
					    boolean timeout = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.sequencing.SequencingAlgorithm;
 | 
				
			||||||
 | 
					import org.bdware.sc.units.ContractUnitController.ContractUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface SequencingAlgorithmFactory {
 | 
				
			||||||
 | 
						public SequencingAlgorithm create(ContractUnitStartRequest req, ContractUnit unit);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.Node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface TrustfulExecutorConnection {
 | 
				
			||||||
 | 
						public void sendMessage(Node node, byte[] msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					package org.bdware.server.trustedmodel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.bean.ContractRequest;
 | 
				
			||||||
 | 
					import org.bdware.sc.conn.ResultCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface ContractExecutor {
 | 
				
			||||||
 | 
					    void execute(String requestID, ResultCallback rc, ContractRequest req);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					package org.bdware.server.trustedmodel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum ContractUnitStatus implements Serializable {
 | 
				
			||||||
 | 
					    CommonMode,
 | 
				
			||||||
 | 
					    StableMode;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/test/java/org/bdware/sc/test/ContractManagerTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/test/java/org/bdware/sc/test/ContractManagerTest.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bdware.sc.ContractManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ContractManagerTest {
 | 
				
			||||||
 | 
					    public static void main(String[] args) {
 | 
				
			||||||
 | 
					        ContractManager.yjsPath = "./generatedlib/yjs.jar";
 | 
				
			||||||
 | 
					        ContractManager.instance = new ContractManager();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										78
									
								
								src/test/java/org/bdware/sc/units/RespCacheTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/test/java/org/bdware/sc/units/RespCacheTest.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					package org.bdware.sc.units;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class RespCacheTest {
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void timeOutAllFalse() throws InterruptedException {
 | 
				
			||||||
 | 
					        RespCache cache = new RespCache();
 | 
				
			||||||
 | 
					        cache.count = 7;
 | 
				
			||||||
 | 
					        for (int i = 0; i < 7; i++) {
 | 
				
			||||||
 | 
					            final int j = i;
 | 
				
			||||||
 | 
					            new Thread(() -> {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    Thread.sleep(j * 1000);
 | 
				
			||||||
 | 
					                    if (j > 2) Thread.sleep(6 * 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                    e.printStackTrace();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                boolean waitResult = cache.waitForHalf();
 | 
				
			||||||
 | 
					                System.out.println(
 | 
				
			||||||
 | 
					                        "tid:"
 | 
				
			||||||
 | 
					                                + Thread.currentThread().getId()
 | 
				
			||||||
 | 
					                                + " reach target, waitResult:"
 | 
				
			||||||
 | 
					                                + waitResult);
 | 
				
			||||||
 | 
					            }).start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Thread.sleep(20000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void inTimeAllTrue() throws InterruptedException {
 | 
				
			||||||
 | 
					        RespCache cache = new RespCache();
 | 
				
			||||||
 | 
					        cache.count = 7;
 | 
				
			||||||
 | 
					        for (int i = 0; i < 7; i++) {
 | 
				
			||||||
 | 
					            final int j = i;
 | 
				
			||||||
 | 
					            new Thread(() -> {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    Thread.sleep(j * 1000);
 | 
				
			||||||
 | 
					                    if (j > 2) Thread.sleep(1900);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                    e.printStackTrace();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                boolean waitResult = cache.waitForHalf();
 | 
				
			||||||
 | 
					                System.out.println(
 | 
				
			||||||
 | 
					                        "tid:"
 | 
				
			||||||
 | 
					                                + Thread.currentThread().getId()
 | 
				
			||||||
 | 
					                                + " reach target, waitResult:"
 | 
				
			||||||
 | 
					                                + waitResult);
 | 
				
			||||||
 | 
					            }).start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Thread.sleep(20000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void fastAllTrue() throws InterruptedException {
 | 
				
			||||||
 | 
					        RespCache cache = new RespCache();
 | 
				
			||||||
 | 
					        cache.count = 7;
 | 
				
			||||||
 | 
					        for (int i = 0; i < 7; i++) {
 | 
				
			||||||
 | 
					            new Thread(() -> {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    Thread.sleep(1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                    e.printStackTrace();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                boolean waitResult = cache.waitForHalf();
 | 
				
			||||||
 | 
					                System.out.println(
 | 
				
			||||||
 | 
					                        "tid:"
 | 
				
			||||||
 | 
					                                + Thread.currentThread().getId()
 | 
				
			||||||
 | 
					                                + " reach target, waitResult:"
 | 
				
			||||||
 | 
					                                + waitResult);
 | 
				
			||||||
 | 
					            }).start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Thread.sleep(10000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user