# Conflicts: # source/sdk/sdk-base/pom.xml # source/storage/storage-redis/pom.xml # source/storage/storage-service/pom.xmltags/1.1.0
| @@ -0,0 +1,74 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> | |||||
| <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 --> | |||||
| <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 --> | |||||
| <configuration status="WARN" monitorInterval="60"> | |||||
| <!--先定义所有的appender --> | |||||
| <appenders> | |||||
| <!--这个输出控制台的配置 --> | |||||
| <console name="Console" target="SYSTEM_OUT"> | |||||
| <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> | |||||
| <ThresholdFilter level="error" onMatch="ACCEPT" | |||||
| onMismatch="DENY" /> | |||||
| <!--输出日志的格式 --> | |||||
| <PatternLayout | |||||
| pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" /> | |||||
| </console> | |||||
| <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用 --> | |||||
| <File name="log" fileName="../logs/test.log" append="false"> | |||||
| <PatternLayout | |||||
| pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" /> | |||||
| </File> | |||||
| <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --> | |||||
| <RollingFile name="PeerRollingInfo" | |||||
| fileName="../logs/peer.out.info.log" | |||||
| filePattern="../logs/$${date:yyyy-MM}/peer.out.info-%d{yyyy-MM-dd}-%i.log"> | |||||
| <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> | |||||
| <ThresholdFilter level="info" onMatch="ACCEPT" | |||||
| onMismatch="DENY" /> | |||||
| <PatternLayout | |||||
| pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" /> | |||||
| <Policies> | |||||
| <TimeBasedTriggeringPolicy /> | |||||
| <SizeBasedTriggeringPolicy size="100 MB" /> | |||||
| </Policies> | |||||
| </RollingFile> | |||||
| <RollingFile name="PeerRollingWarn" | |||||
| fileName="../logs/peer.out.warn.log" | |||||
| filePattern="../logs/$${date:yyyy-MM}/peer.out.warn-%d{yyyy-MM-dd}-%i.log"> | |||||
| <ThresholdFilter level="warn" onMatch="ACCEPT" | |||||
| onMismatch="DENY" /> | |||||
| <PatternLayout | |||||
| pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" /> | |||||
| <Policies> | |||||
| <TimeBasedTriggeringPolicy /> | |||||
| <SizeBasedTriggeringPolicy size="100 MB" /> | |||||
| </Policies> | |||||
| <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --> | |||||
| <DefaultRolloverStrategy max="20" /> | |||||
| </RollingFile> | |||||
| <RollingFile name="PeerRollingError" | |||||
| fileName="../logs/peer.out.error.log" | |||||
| filePattern="../logs/$${date:yyyy-MM}/peer.out.error-%d{yyyy-MM-dd}-%i.log"> | |||||
| <ThresholdFilter level="error" onMatch="ACCEPT" | |||||
| onMismatch="DENY" /> | |||||
| <PatternLayout | |||||
| pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" /> | |||||
| <Policies> | |||||
| <TimeBasedTriggeringPolicy /> | |||||
| <SizeBasedTriggeringPolicy size="100 MB" /> | |||||
| </Policies> | |||||
| </RollingFile> | |||||
| </appenders> | |||||
| <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 --> | |||||
| <loggers> | |||||
| <!--过滤掉spring的一些DEBUG信息 --> | |||||
| <logger name="org.springframework" level="INFO"></logger> | |||||
| <root level="all"> | |||||
| <appender-ref ref="Console" /> | |||||
| <appender-ref ref="PeerRollingInfo" /> | |||||
| <appender-ref ref="PeerRollingWarn" /> | |||||
| <appender-ref ref="PeerRollingError" /> | |||||
| </root> | |||||
| </loggers> | |||||
| </configuration> | |||||
| @@ -15,5 +15,9 @@ | |||||
| <artifactId>utils-common</artifactId> | <artifactId>utils-common</artifactId> | ||||
| <version>${project.version}</version> | <version>${project.version}</version> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>junit</groupId> | |||||
| <artifactId>junit</artifactId> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| </project> | </project> | ||||
| @@ -6,6 +6,7 @@ import java.util.concurrent.CopyOnWriteArrayList; | |||||
| import java.util.concurrent.ExecutorService; | import java.util.concurrent.ExecutorService; | ||||
| import java.util.concurrent.Executors; | import java.util.concurrent.Executors; | ||||
| import bftsmart.tom.*; | import bftsmart.tom.*; | ||||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| import com.jd.blockchain.consensus.ConsensusManageService; | import com.jd.blockchain.consensus.ConsensusManageService; | ||||
| @@ -49,12 +50,12 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer | |||||
| private BftsmartConsensusManageService manageService; | private BftsmartConsensusManageService manageService; | ||||
| private volatile BftsmartTopology topology; | private volatile BftsmartTopology topology; | ||||
| private volatile BftsmartConsensusSettings setting; | private volatile BftsmartConsensusSettings setting; | ||||
| private TOMConfiguration tomConfig; | private TOMConfiguration tomConfig; | ||||
| private TOMConfiguration outerTomConfig; | |||||
| private HostsConfig hostsConfig; | private HostsConfig hostsConfig; | ||||
| private Properties systemConfig; | private Properties systemConfig; | ||||
| @@ -123,14 +124,11 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer | |||||
| return; | return; | ||||
| } | } | ||||
| protected void initConfig(int id, String systemConfig, String hostsConfig) { | |||||
| this.tomConfig = new TOMConfiguration(id, systemConfig, hostsConfig); | |||||
| } | |||||
| protected void initConfig(int id, Properties systemsConfig, HostsConfig hostConfig) { | protected void initConfig(int id, Properties systemsConfig, HostsConfig hostConfig) { | ||||
| byte[] serialHostConf = BinarySerializeUtils.serialize(hostConfig); | |||||
| Properties sysConfClone = (Properties)systemsConfig.clone(); | |||||
| this.tomConfig = new TOMConfiguration(id, systemsConfig, hostConfig); | this.tomConfig = new TOMConfiguration(id, systemsConfig, hostConfig); | ||||
| this.outerTomConfig = new TOMConfiguration(id, sysConfClone, BinarySerializeUtils.deserialize(serialHostConf)); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -149,7 +147,7 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer | |||||
| } | } | ||||
| public TOMConfiguration getTomConfig() { | public TOMConfiguration getTomConfig() { | ||||
| return tomConfig; | |||||
| return outerTomConfig; | |||||
| } | } | ||||
| public int getId() { | public int getId() { | ||||
| @@ -161,7 +159,7 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer | |||||
| throw new IllegalArgumentException("ReplicaID is negative!"); | throw new IllegalArgumentException("ReplicaID is negative!"); | ||||
| } | } | ||||
| this.tomConfig.setProcessId(id); | this.tomConfig.setProcessId(id); | ||||
| this.outerTomConfig.setProcessId(id); | |||||
| } | } | ||||
| public BftsmartConsensusSettings getConsensusSetting() { | public BftsmartConsensusSettings getConsensusSetting() { | ||||
| @@ -243,6 +241,7 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer | |||||
| messageHandle.commitBatch(realmName, batchId); | messageHandle.commitBatch(realmName, batchId); | ||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| // todo 需要处理应答码 404 | // todo 需要处理应答码 404 | ||||
| LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||||
| messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE); | messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE); | ||||
| } | } | ||||
| @@ -87,6 +87,13 @@ | |||||
| <version>3.6.0</version> | <version>3.6.0</version> | ||||
| <scope>provided</scope> | <scope>provided</scope> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>org.apache.maven.shared</groupId> | |||||
| <artifactId>maven-invoker</artifactId> | |||||
| <version>3.0.1</version> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| <build> | <build> | ||||
| @@ -110,31 +117,6 @@ | |||||
| <showWarnings>false</showWarnings> | <showWarnings>false</showWarnings> | ||||
| </configuration> | </configuration> | ||||
| </plugin> | </plugin> | ||||
| <!--<plugin> --> | |||||
| <!--<artifactId>maven-assembly-plugin</artifactId> --> | |||||
| <!--<configuration> --> | |||||
| <!--<archive> --> | |||||
| <!--<manifest> --> | |||||
| <!--<!–这里要替换成jar包main方法所在类 –> --> | |||||
| <!--<mainClass>com.jd.blockchain.ContractDeployMojo</mainClass> --> | |||||
| <!--</manifest> --> | |||||
| <!--</archive> --> | |||||
| <!--<descriptorRefs> --> | |||||
| <!--<descriptojar-rRef>jar-with-dependencies</descriptojar-rRef> --> | |||||
| <!--</descriptorRefs> --> | |||||
| <!--</configuration> --> | |||||
| <!--<executions> --> | |||||
| <!--<execution> --> | |||||
| <!--<id>make-assembly</id> <!– this is used for inheritance merges | |||||
| –> --> | |||||
| <!--<phase>package</phase> <!– 指定在打包节点执行jar包合并操作 –> --> | |||||
| <!--<goals> --> | |||||
| <!--<goal>single</goal> --> | |||||
| <!--</goals> --> | |||||
| <!--</execution> --> | |||||
| <!--</executions> --> | |||||
| <!--</plugin> --> | |||||
| <plugin> | <plugin> | ||||
| <groupId>org.apache.maven.plugins</groupId> | <groupId>org.apache.maven.plugins</groupId> | ||||
| <artifactId>maven-surefire-plugin</artifactId> | <artifactId>maven-surefire-plugin</artifactId> | ||||
| @@ -142,29 +124,6 @@ | |||||
| <skipTests>true</skipTests> | <skipTests>true</skipTests> | ||||
| </configuration> | </configuration> | ||||
| </plugin> | </plugin> | ||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-surefire-plugin</artifactId> | |||||
| <configuration> | |||||
| <skipTests>true</skipTests> | |||||
| </configuration> | |||||
| </plugin> | |||||
| <!-- 配置Maven插件(mvn jetty:run可以运行项目) --> | |||||
| <!--<plugin> --> | |||||
| <!--<groupId>org.mortbay.jetty</groupId> --> | |||||
| <!--<artifactId>maven-jetty-plugin</artifactId> --> | |||||
| <!--<version>6.1.26</version> --> | |||||
| <!--<configuration> --> | |||||
| <!--<connectors> --> | |||||
| <!--<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector"> --> | |||||
| <!--<port>8000</port> --> | |||||
| <!--</connector> --> | |||||
| <!--</connectors> --> | |||||
| <!--</configuration> --> | |||||
| <!--</plugin> --> | |||||
| </plugins> | </plugins> | ||||
| </build> | </build> | ||||
| </project> | </project> | ||||
| @@ -0,0 +1,221 @@ | |||||
| package com.jd.blockchain; | |||||
| import com.jd.blockchain.utils.ConsoleUtils; | |||||
| import org.apache.maven.model.Model; | |||||
| import org.apache.maven.model.Plugin; | |||||
| import org.apache.maven.model.PluginExecution; | |||||
| import org.apache.maven.model.io.xpp3.MavenXpp3Reader; | |||||
| import org.apache.maven.model.io.xpp3.MavenXpp3Writer; | |||||
| import org.apache.maven.plugin.AbstractMojo; | |||||
| import org.apache.maven.plugins.annotations.Mojo; | |||||
| import org.apache.maven.plugins.annotations.Parameter; | |||||
| import org.apache.maven.project.MavenProject; | |||||
| import org.apache.maven.shared.invoker.*; | |||||
| import org.codehaus.plexus.util.xml.Xpp3Dom; | |||||
| import org.codehaus.plexus.util.xml.pull.XmlPullParserException; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import java.io.*; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Collections; | |||||
| import java.util.List; | |||||
| @Mojo(name = "contractCheck") | |||||
| public class ContractCheckMojo extends AbstractMojo { | |||||
| Logger logger = LoggerFactory.getLogger(ContractCheckMojo.class); | |||||
| @Parameter(defaultValue = "${project}", required = true, readonly = true) | |||||
| private MavenProject project; | |||||
| /** | |||||
| * jar's name; | |||||
| */ | |||||
| @Parameter | |||||
| private String finalName; | |||||
| /** | |||||
| * mainClass; | |||||
| */ | |||||
| @Parameter | |||||
| private String mainClass; | |||||
| /** | |||||
| * ledgerVersion; | |||||
| */ | |||||
| @Parameter | |||||
| private String ledgerVersion; | |||||
| /** | |||||
| * mvnHome; | |||||
| */ | |||||
| @Parameter | |||||
| private String mvnHome; | |||||
| /** | |||||
| * first compile the class, then parse it; | |||||
| */ | |||||
| @Override | |||||
| public void execute() { | |||||
| this.compileFiles(); | |||||
| } | |||||
| private void compileFiles(){ | |||||
| // 获取当前项目pom.xml文件所在路径 | |||||
| // URL targetClasses = this.getClass().getClassLoader().getResource(""); | |||||
| // File file = new File(targetClasses.getPath()); | |||||
| // String pomXmlPath = file.getParentFile().getParent() + File.separator + "pom.xml"; | |||||
| FileInputStream fis = null; | |||||
| try { | |||||
| // fis = new FileInputStream(new File(pomXmlPath)); | |||||
| fis = new FileInputStream(project.getFile()); | |||||
| MavenXpp3Reader reader = new MavenXpp3Reader(); | |||||
| Model model = reader.read(fis); | |||||
| //delete this plugin(contractCheck) from destination pom.xml;then add the proper plugins; | |||||
| Plugin plugin = model.getBuild().getPluginsAsMap().get("com.jd.blockchain:contract-maven-plugin"); | |||||
| if(plugin == null){ | |||||
| plugin = model.getBuild().getPluginsAsMap().get("org.apache.maven.plugins:contract-maven-plugin"); | |||||
| } | |||||
| if(plugin == null) { | |||||
| return; | |||||
| } | |||||
| model.getBuild().removePlugin(plugin); | |||||
| // model.getBuild().setPlugins(null); | |||||
| // ConsoleUtils.info("----- 不携带Plugin -----"); | |||||
| // print(model); | |||||
| List<Plugin> plugins = new ArrayList<>(); | |||||
| plugins.add(createAssembly()); | |||||
| plugins.add(createCheckImports()); | |||||
| model.getBuild().setPlugins(plugins); | |||||
| ConsoleUtils.info("----- add Plugin -----"); | |||||
| handle(model); | |||||
| } catch (FileNotFoundException e) { | |||||
| e.printStackTrace(); | |||||
| } catch (XmlPullParserException e) { | |||||
| e.printStackTrace(); | |||||
| } catch (IOException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| private void invokeCompile(File file) { | |||||
| InvocationRequest request = new DefaultInvocationRequest(); | |||||
| Invoker invoker = new DefaultInvoker(); | |||||
| try { | |||||
| request.setPomFile(file); | |||||
| request.setGoals( Collections.singletonList( "verify" ) ); | |||||
| // request.setMavenOpts("-DmainClass="+mainClass); | |||||
| invoker.setMavenHome(new File(mvnHome)); | |||||
| invoker.execute(request); | |||||
| } catch (MavenInvocationException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| private Plugin createCheckImports() { | |||||
| Plugin plugin = new Plugin(); | |||||
| plugin.setGroupId("com.jd.blockchain"); | |||||
| plugin.setArtifactId("contract-maven-plugin"); | |||||
| plugin.setVersion(ledgerVersion); | |||||
| Xpp3Dom finalNameNode = new Xpp3Dom("finalName"); | |||||
| finalNameNode.setValue(finalName); | |||||
| Xpp3Dom configuration = new Xpp3Dom("configuration"); | |||||
| configuration.addChild(finalNameNode); | |||||
| plugin.setConfiguration(configuration); | |||||
| PluginExecution pluginExecution = new PluginExecution(); | |||||
| pluginExecution.setId("make-assembly"); | |||||
| pluginExecution.setPhase("verify"); | |||||
| List <String> goals = new ArrayList<>(); | |||||
| goals.add("checkImports"); | |||||
| pluginExecution.setGoals(goals); | |||||
| List<PluginExecution> pluginExecutions = new ArrayList<>(); | |||||
| pluginExecutions.add(pluginExecution); | |||||
| plugin.setExecutions(pluginExecutions); | |||||
| return plugin; | |||||
| } | |||||
| private Plugin createAssembly() { | |||||
| Plugin plugin = new Plugin(); | |||||
| plugin.setArtifactId("maven-assembly-plugin"); | |||||
| Xpp3Dom configuration = new Xpp3Dom("configuration"); | |||||
| Xpp3Dom mainClassNode = new Xpp3Dom("mainClass"); | |||||
| mainClassNode.setValue(mainClass); | |||||
| Xpp3Dom manifest = new Xpp3Dom("manifest"); | |||||
| manifest.addChild(mainClassNode); | |||||
| Xpp3Dom archive = new Xpp3Dom("archive"); | |||||
| archive.addChild(manifest); | |||||
| Xpp3Dom finalNameNode = new Xpp3Dom("finalName"); | |||||
| finalNameNode.setValue(finalName); | |||||
| Xpp3Dom appendAssemblyId = new Xpp3Dom("appendAssemblyId"); | |||||
| appendAssemblyId.setValue("false"); | |||||
| Xpp3Dom descriptorRef = new Xpp3Dom("descriptorRef"); | |||||
| descriptorRef.setValue("jar-with-dependencies"); | |||||
| Xpp3Dom descriptorRefs = new Xpp3Dom("descriptorRefs"); | |||||
| descriptorRefs.addChild(descriptorRef); | |||||
| configuration.addChild(finalNameNode); | |||||
| configuration.addChild(appendAssemblyId); | |||||
| configuration.addChild(archive); | |||||
| configuration.addChild(descriptorRefs); | |||||
| plugin.setConfiguration(configuration); | |||||
| PluginExecution pluginExecution = new PluginExecution(); | |||||
| pluginExecution.setId("make-assembly"); | |||||
| pluginExecution.setPhase("package"); | |||||
| List <String> goals = new ArrayList<>(); | |||||
| goals.add("single"); | |||||
| pluginExecution.setGoals(goals); | |||||
| List<PluginExecution> pluginExecutions = new ArrayList<>(); | |||||
| pluginExecutions.add(pluginExecution); | |||||
| plugin.setExecutions(pluginExecutions); | |||||
| return plugin; | |||||
| } | |||||
| private void handle(Model model) throws IOException { | |||||
| MavenXpp3Writer mavenXpp3Writer = new MavenXpp3Writer(); | |||||
| ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | |||||
| mavenXpp3Writer.write(outputStream, model); | |||||
| byte[] buffer = outputStream.toByteArray(); | |||||
| //输出文件 | |||||
| // File fileOutput = new File("fileOut.xml"); | |||||
| // File fileOutput = File.createTempFile("fileOut",".xml"); | |||||
| File fileOutput = new File(project.getBasedir().getPath(),"fileOut.xml"); | |||||
| fileOutput.createNewFile(); | |||||
| ConsoleUtils.info("fileOutput's path="+fileOutput.getPath()); | |||||
| //创建文件输出流对象 | |||||
| FileOutputStream fos = new FileOutputStream(fileOutput); | |||||
| //将字节数组fileInput中的内容输出到文件fileOut.xml中; | |||||
| ConsoleUtils.info(new String(buffer)); | |||||
| fos.write(buffer); | |||||
| invokeCompile(fileOutput); | |||||
| fos.close(); | |||||
| } | |||||
| } | |||||
| @@ -16,7 +16,7 @@ | |||||
| <!--<outputDirectory>target/test-harness/project-to-test</outputDirectory>--> | <!--<outputDirectory>target/test-harness/project-to-test</outputDirectory>--> | ||||
| <!-- The defined stubs --> | <!-- The defined stubs --> | ||||
| <project implementation="com.jd.blockchain.ledger.MyProjectStub"/> | <project implementation="com.jd.blockchain.ledger.MyProjectStub"/> | ||||
| <finalName>contract1</finalName> | |||||
| <finalName>contract</finalName> | |||||
| </configuration> | </configuration> | ||||
| </plugin> | </plugin> | ||||
| </plugins> | </plugins> | ||||
| @@ -55,6 +55,16 @@ | |||||
| </execution> | </execution> | ||||
| </executions> | </executions> | ||||
| </plugin> | </plugin> | ||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-deploy-plugin</artifactId> | |||||
| <version>2.8.2</version> | |||||
| <configuration> | |||||
| <skip>true</skip> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | </plugins> | ||||
| </build> | </build> | ||||
| @@ -1,20 +0,0 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>crypto</artifactId> | |||||
| <version>0.9.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>crypto-impl</artifactId> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>crypto-framework</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| </project> | |||||
| @@ -1,220 +0,0 @@ | |||||
| //package com.jd.blockchain.crypto.impl; | |||||
| // | |||||
| //import com.jd.blockchain.crypto.Ciphertext; | |||||
| //import com.jd.blockchain.crypto.CryptoAlgorithm; | |||||
| //import com.jd.blockchain.crypto.PrivKey; | |||||
| //import com.jd.blockchain.crypto.PubKey; | |||||
| //import com.jd.blockchain.crypto.asymmetric.*; | |||||
| //import com.jd.blockchain.crypto.impl.jni.asymmetric.JNIED25519SignatureFunction; | |||||
| //import com.jd.blockchain.crypto.impl.sm.asymmetric.SM2CryptoFunction; | |||||
| //import com.jd.blockchain.crypto.service.classic.ED25519SignatureFunction; | |||||
| // | |||||
| //public class AsymmtricCryptographyImpl implements AsymmetricCryptography { | |||||
| // | |||||
| // private static final SignatureFunction ED25519_SIGF = new ED25519SignatureFunction(); | |||||
| // | |||||
| // private static final SignatureFunction SM2_SIGF = new SM2CryptoFunction(); | |||||
| // | |||||
| // private static final SignatureFunction JNIED25519_SIGF = new JNIED25519SignatureFunction(); | |||||
| // | |||||
| // private static final AsymmetricEncryptionFunction SM2_ENCF = new SM2CryptoFunction(); | |||||
| // | |||||
| // /** | |||||
| // * 封装了非对称密码算法对应的密钥生成算法 | |||||
| // */ | |||||
| // @Override | |||||
| // public CryptoKeyPair generateKeyPair(CryptoAlgorithm algorithm) { | |||||
| // | |||||
| // //判断算法是签名算法还是非对称加密算法,并根据算法生成密钥对,否则抛出异常 | |||||
| // if (algorithm.isSignable() && algorithm.hasAsymmetricKey()){ | |||||
| // return getSignatureFunction(algorithm).generateKeyPair(); | |||||
| // } | |||||
| // else if (algorithm.isEncryptable() && algorithm.hasAsymmetricKey()){ | |||||
| // return getAsymmetricEncryptionFunction(algorithm).generateKeyPair(); | |||||
| // } | |||||
| // else throw new IllegalArgumentException("The specified algorithm is not signature or asymmetric encryption algorithm!"); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public SignatureFunction getSignatureFunction(CryptoAlgorithm algorithm) { | |||||
| // //遍历签名算法,如果满足,则返回实例 | |||||
| // switch (algorithm) { | |||||
| // case ED25519: | |||||
| // return ED25519_SIGF; | |||||
| // case SM2: | |||||
| // return SM2_SIGF; | |||||
| // case JNIED25519: | |||||
| // return JNIED25519_SIGF; | |||||
| // default: | |||||
| // break; | |||||
| // } | |||||
| // throw new IllegalArgumentException("The specified algorithm is not signature algorithm!"); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public boolean verify(byte[] digestBytes, byte[] pubKeyBytes, byte[] data) { | |||||
| // | |||||
| // //得到SignatureDigest类型的签名摘要,并得到算法标识 | |||||
| // SignatureDigest signatureDigest = resolveSignatureDigest(digestBytes); | |||||
| // CryptoAlgorithm algorithm = signatureDigest.getAlgorithm(); | |||||
| // PubKey pubKey = resolvePubKey(pubKeyBytes); | |||||
| // | |||||
| // //验证两个输入中算法标识一致,否则抛出异常 | |||||
| // if (algorithm != signatureDigest.getAlgorithm()) | |||||
| // throw new IllegalArgumentException("Digest's algorithm and key's are not matching!"); | |||||
| // | |||||
| // //根据算法标识,调用对应算法实例来验证签名摘要 | |||||
| // return getSignatureFunction(algorithm).verify(signatureDigest,pubKey,data); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public AsymmetricEncryptionFunction getAsymmetricEncryptionFunction(CryptoAlgorithm algorithm) { | |||||
| // //遍历非对称加密算法,如果满足,则返回实例 | |||||
| // switch (algorithm) { | |||||
| // case SM2: | |||||
| // return SM2_ENCF; | |||||
| // default: | |||||
| // break; | |||||
| // } | |||||
| // throw new IllegalArgumentException("The specified algorithm is not asymmetric encryption algorithm!"); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public byte[] decrypt(byte[] privKeyBytes, byte[] ciphertextBytes) { | |||||
| // | |||||
| // //分别得到PrivKey和Ciphertext类型的密钥和密文,以及privKey对应的算法 | |||||
| // PrivKey privKey = resolvePrivKey(privKeyBytes); | |||||
| // Ciphertext ciphertext = resolveCiphertext(ciphertextBytes); | |||||
| // CryptoAlgorithm algorithm = privKey.getAlgorithm(); | |||||
| // | |||||
| // //验证两个输入中算法标识一致,否则抛出异常 | |||||
| // if (algorithm != ciphertext.getAlgorithm()) | |||||
| // throw new IllegalArgumentException("Ciphertext's algorithm and key's are not matching!"); | |||||
| // | |||||
| // //根据算法标识,调用对应算法实例来计算返回明文 | |||||
| // return getAsymmetricEncryptionFunction(algorithm).decrypt(privKey,ciphertext); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public Ciphertext resolveCiphertext(byte[] ciphertextBytes) { | |||||
| // Ciphertext ciphertext = tryResolveCiphertext(ciphertextBytes); | |||||
| // if (ciphertext == null) | |||||
| // throw new IllegalArgumentException("This ciphertextBytes cannot be resolved!"); | |||||
| // else return ciphertext; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public Ciphertext tryResolveCiphertext(byte[] ciphertextBytes) { | |||||
| // //遍历非对称加密算法,如果满足,则返回解析结果 | |||||
| // if (SM2_ENCF.supportCiphertext(ciphertextBytes)){ | |||||
| // return SM2_ENCF.resolveCiphertext(ciphertextBytes); | |||||
| // } | |||||
| // //否则返回null | |||||
| // return null; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public SignatureDigest resolveSignatureDigest(byte[] digestBytes) { | |||||
| // SignatureDigest signatureDigest = tryResolveSignatureDigest(digestBytes); | |||||
| // if (signatureDigest == null) | |||||
| // throw new IllegalArgumentException("This digestBytes cannot be resolved!"); | |||||
| // else return signatureDigest; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public SignatureDigest tryResolveSignatureDigest(byte[] digestBytes) { | |||||
| // //遍历签名算法,如果满足,则返回解析结果 | |||||
| // if (ED25519_SIGF.supportDigest(digestBytes)){ | |||||
| // return ED25519_SIGF.resolveDigest(digestBytes); | |||||
| // } | |||||
| // if (SM2_SIGF.supportDigest(digestBytes)){ | |||||
| // return SM2_SIGF.resolveDigest(digestBytes); | |||||
| // } | |||||
| // if (JNIED25519_SIGF.supportDigest(digestBytes)){ | |||||
| // return JNIED25519_SIGF.resolveDigest(digestBytes); | |||||
| // } | |||||
| // //否则返回null | |||||
| // return null; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public byte[] retrievePubKeyBytes(byte[] privKeyBytes) { | |||||
| // byte[] pubKeyBytes = tryRetrievePubKeyBytes(privKeyBytes); | |||||
| // if (pubKeyBytes == null) | |||||
| // throw new IllegalArgumentException("The specified algorithm in privKeyBytes is not signature or asymmetric encryption algorithm!"); | |||||
| // else return pubKeyBytes; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public byte[] tryRetrievePubKeyBytes(byte[] privKeyBytes) { | |||||
| // //解析私钥获得算法标识 | |||||
| // CryptoAlgorithm algorithm = resolvePrivKey(privKeyBytes).getAlgorithm(); | |||||
| // | |||||
| // //判断算法是签名算法还是非对称加密算法,并根据算法生成密钥对,否则抛出异常 | |||||
| // if (algorithm.isSignable() && algorithm.hasAsymmetricKey()){ | |||||
| // return getSignatureFunction(algorithm).retrievePubKeyBytes(privKeyBytes); | |||||
| // } | |||||
| // else if (algorithm.isEncryptable() && algorithm.hasAsymmetricKey()){ | |||||
| // return getAsymmetricEncryptionFunction(algorithm).retrievePubKeyBytes(privKeyBytes); | |||||
| // } | |||||
| // //否则返回null | |||||
| // return null; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public PubKey resolvePubKey(byte[] pubKeyBytes) { | |||||
| // PubKey pubKey = tryResolvePubKey(pubKeyBytes); | |||||
| // if (pubKey == null) | |||||
| // throw new IllegalArgumentException("This pubKeyBytes cannot be resolved!"); | |||||
| // else return pubKey; | |||||
| // | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public PubKey tryResolvePubKey(byte[] pubKeyBytes) { | |||||
| // //遍历签名算法,如果满足,则返回解析结果 | |||||
| // if (ED25519_SIGF.supportPubKey(pubKeyBytes)){ | |||||
| // return ED25519_SIGF.resolvePubKey(pubKeyBytes); | |||||
| // } | |||||
| // if (SM2_SIGF.supportPubKey(pubKeyBytes)){ | |||||
| // return SM2_SIGF.resolvePubKey(pubKeyBytes); | |||||
| // } | |||||
| // if (JNIED25519_SIGF.supportPubKey(pubKeyBytes)){ | |||||
| // return JNIED25519_SIGF.resolvePubKey(pubKeyBytes); | |||||
| // } | |||||
| // //遍历非对称加密算法,如果满足,则返回解析结果 | |||||
| // if (SM2_ENCF.supportPubKey(pubKeyBytes)){ | |||||
| // return SM2_ENCF.resolvePubKey(pubKeyBytes); | |||||
| // } | |||||
| // //否则返回null | |||||
| // return null; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public PrivKey resolvePrivKey(byte[] privKeyBytes) { | |||||
| // PrivKey privKey = tryResolvePrivKey(privKeyBytes); | |||||
| // if (privKey == null) | |||||
| // throw new IllegalArgumentException("This privKeyBytes cannot be resolved!"); | |||||
| // else return privKey; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public PrivKey tryResolvePrivKey(byte[] privKeyBytes) { | |||||
| // //遍历签名算法,如果满足,则返回解析结果 | |||||
| // if (ED25519_SIGF.supportPrivKey(privKeyBytes)){ | |||||
| // return ED25519_SIGF.resolvePrivKey(privKeyBytes); | |||||
| // } | |||||
| // if (SM2_SIGF.supportPrivKey(privKeyBytes)){ | |||||
| // return SM2_SIGF.resolvePrivKey(privKeyBytes); | |||||
| // } | |||||
| // if (JNIED25519_SIGF.supportPrivKey(privKeyBytes)){ | |||||
| // return JNIED25519_SIGF.resolvePrivKey(privKeyBytes); | |||||
| // } | |||||
| // //遍历非对称加密算法,如果满足,则返回解析结果 | |||||
| // if (SM2_ENCF.supportPrivKey(privKeyBytes)){ | |||||
| // return SM2_ENCF.resolvePrivKey(privKeyBytes); | |||||
| // } | |||||
| // //否则返回null | |||||
| // return null; | |||||
| // } | |||||
| //} | |||||
| @@ -1,30 +0,0 @@ | |||||
| //package com.jd.blockchain.crypto.impl; | |||||
| // | |||||
| //import com.jd.blockchain.crypto.CryptoFactory; | |||||
| //import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; | |||||
| //import com.jd.blockchain.crypto.hash.HashCryptography; | |||||
| //import com.jd.blockchain.crypto.symmetric.SymmetricCryptography; | |||||
| // | |||||
| //public class CryptoFactoryImpl implements CryptoFactory { | |||||
| // | |||||
| // //Field; | |||||
| // private static HashCryptography hashCryptography = new HashCryptographyImpl(); | |||||
| // private static AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); | |||||
| // private static SymmetricCryptography symmetricCryptography = new SymmetricCryptographyImpl(); | |||||
| // | |||||
| // @Override | |||||
| // public HashCryptography hashCryptography() { | |||||
| // return hashCryptography; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public AsymmetricCryptography asymmetricCryptography() { | |||||
| // return asymmetricCryptography; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public SymmetricCryptography symmetricCryptography() { | |||||
| // return symmetricCryptography; | |||||
| // } | |||||
| // | |||||
| //} | |||||
| @@ -1,84 +0,0 @@ | |||||
| //package com.jd.blockchain.crypto.impl; | |||||
| // | |||||
| //import com.jd.blockchain.crypto.CryptoAlgorithm; | |||||
| //import com.jd.blockchain.crypto.hash.HashCryptography; | |||||
| //import com.jd.blockchain.crypto.hash.HashDigest; | |||||
| //import com.jd.blockchain.crypto.hash.HashFunction; | |||||
| //import com.jd.blockchain.crypto.impl.jni.hash.JNIRIPEMD160HashFunction; | |||||
| //import com.jd.blockchain.crypto.impl.jni.hash.JNISHA256HashFunction; | |||||
| //import com.jd.blockchain.crypto.impl.sm.hash.SM3HashFunction; | |||||
| //import com.jd.blockchain.crypto.service.classic.RIPEMD160HashFunction; | |||||
| //import com.jd.blockchain.crypto.service.classic.SHA256HashFunction; | |||||
| // | |||||
| //public class HashCryptographyImpl implements HashCryptography { | |||||
| // | |||||
| // private static final HashFunction SHA256_FUNC = new SHA256HashFunction(); | |||||
| // private static final HashFunction RIPEMD160_FUNC = new RIPEMD160HashFunction(); | |||||
| // private static final HashFunction SM3_FUNC = new SM3HashFunction(); | |||||
| // | |||||
| // private static final HashFunction JNISHA256_FUNC = new JNISHA256HashFunction(); | |||||
| // private static final HashFunction JNIRIPEMD160_FUNC = new JNIRIPEMD160HashFunction(); | |||||
| // | |||||
| // @Override | |||||
| // public HashFunction getFunction(CryptoAlgorithm algorithm) { | |||||
| // | |||||
| // // 遍历哈希算法,如果满足,则返回实例 | |||||
| // switch (algorithm) { | |||||
| // case SHA256: | |||||
| // return SHA256_FUNC; | |||||
| // case RIPEMD160: | |||||
| // return RIPEMD160_FUNC; | |||||
| // case SM3: | |||||
| // return SM3_FUNC; | |||||
| // case JNISHA256: | |||||
| // return JNISHA256_FUNC; | |||||
| // case JNIRIPEMD160: | |||||
| // return JNIRIPEMD160_FUNC; | |||||
| // default: | |||||
| // break; | |||||
| // } | |||||
| // throw new IllegalArgumentException("The specified algorithm is not hash algorithm!"); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public boolean verify(byte[] digestBytes, byte[] data) { | |||||
| // HashDigest hashDigest = resolveHashDigest(digestBytes); | |||||
| // return verify(hashDigest,data); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public boolean verify(HashDigest digest, byte[] data) { | |||||
| // CryptoAlgorithm algorithm = digest.getAlgorithm(); | |||||
| // return getFunction(algorithm).verify(digest, data); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public HashDigest resolveHashDigest(byte[] digestBytes) { | |||||
| // HashDigest hashDigest = tryResolveHashDigest(digestBytes); | |||||
| // if (hashDigest == null) | |||||
| // throw new IllegalArgumentException("This digestBytes cannot be resolved!"); | |||||
| // else return hashDigest; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public HashDigest tryResolveHashDigest(byte[] digestBytes) { | |||||
| // //遍历哈希函数,如果满足,则返回解析结果 | |||||
| // if (SHA256_FUNC.supportHashDigest(digestBytes)) { | |||||
| // return SHA256_FUNC.resolveHashDigest(digestBytes); | |||||
| // } | |||||
| // if (RIPEMD160_FUNC.supportHashDigest(digestBytes)) { | |||||
| // return RIPEMD160_FUNC.resolveHashDigest(digestBytes); | |||||
| // } | |||||
| // if (SM3_FUNC.supportHashDigest(digestBytes)) { | |||||
| // return SM3_FUNC.resolveHashDigest(digestBytes); | |||||
| // } | |||||
| // if (JNISHA256_FUNC.supportHashDigest(digestBytes)) { | |||||
| // return JNISHA256_FUNC.resolveHashDigest(digestBytes); | |||||
| // } | |||||
| // if (JNIRIPEMD160_FUNC.supportHashDigest(digestBytes)) { | |||||
| // return JNIRIPEMD160_FUNC.resolveHashDigest(digestBytes); | |||||
| // } | |||||
| // //否则返回null | |||||
| // return null; | |||||
| // } | |||||
| //} | |||||
| @@ -1,101 +0,0 @@ | |||||
| //package com.jd.blockchain.crypto.impl; | |||||
| // | |||||
| //import com.jd.blockchain.crypto.Ciphertext; | |||||
| //import com.jd.blockchain.crypto.CryptoAlgorithm; | |||||
| //import com.jd.blockchain.crypto.SingleKey; | |||||
| //import com.jd.blockchain.crypto.impl.sm.symmetric.SM4SymmetricEncryptionFunction; | |||||
| //import com.jd.blockchain.crypto.service.classic.AESSymmetricEncryptionFunction; | |||||
| //import com.jd.blockchain.crypto.symmetric.SymmetricCryptography; | |||||
| //import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction; | |||||
| // | |||||
| //public class SymmetricCryptographyImpl implements SymmetricCryptography { | |||||
| // | |||||
| // private static final SymmetricEncryptionFunction AES_ENCF = new AESSymmetricEncryptionFunction(); | |||||
| // private static final SymmetricEncryptionFunction SM4_ENCF = new SM4SymmetricEncryptionFunction(); | |||||
| // | |||||
| // /** | |||||
| // * 封装了对称密码算法对应的密钥生成算法 | |||||
| // */ | |||||
| // @Override | |||||
| // public SingleKey generateKey(CryptoAlgorithm algorithm) { | |||||
| // | |||||
| // //验证算法标识是对称加密算法,并根据算法生成对称密钥,否则抛出异常 | |||||
| // if (algorithm.isEncryptable() && algorithm.isSymmetric() ){ | |||||
| // return (SingleKey) getSymmetricEncryptionFunction(algorithm).generateSymmetricKey(); | |||||
| // } | |||||
| // else throw new IllegalArgumentException("The specified algorithm is not symmetric encryption algorithm!"); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public SymmetricEncryptionFunction getSymmetricEncryptionFunction(CryptoAlgorithm algorithm) { | |||||
| // | |||||
| // // 遍历对称加密算法,如果满足,则返回实例 | |||||
| // switch (algorithm) { | |||||
| // case AES: | |||||
| // return AES_ENCF; | |||||
| // case SM4: | |||||
| // return SM4_ENCF; | |||||
| // default: | |||||
| // break; | |||||
| // } | |||||
| // throw new IllegalArgumentException("The specified algorithm is not symmetric encryption algorithm!"); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public byte[] decrypt(byte[] symmetricKeyBytes, byte[] ciphertextBytes) { | |||||
| // | |||||
| // //分别得到SymmetricKey和Ciphertext类型的密钥和密文,以及symmetricKey对应的算法 | |||||
| // SingleKey symmetricKey = resolveSymmetricKey(symmetricKeyBytes); | |||||
| // Ciphertext ciphertext = resolveCiphertext(ciphertextBytes); | |||||
| // CryptoAlgorithm algorithm = symmetricKey.getAlgorithm(); | |||||
| // | |||||
| // //验证两个输入中算法标识一致,否则抛出异常 | |||||
| // if (algorithm != ciphertext.getAlgorithm()) | |||||
| // throw new IllegalArgumentException("Ciphertext's algorithm and key's are not matching!"); | |||||
| // | |||||
| // //根据算法标识,调用对应算法实例来计算返回明文 | |||||
| // return getSymmetricEncryptionFunction(algorithm).decrypt(symmetricKey,ciphertext); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public Ciphertext resolveCiphertext(byte[] ciphertextBytes) { | |||||
| // Ciphertext ciphertext = tryResolveCiphertext(ciphertextBytes); | |||||
| // if (ciphertext == null) | |||||
| // throw new IllegalArgumentException("This ciphertextBytes cannot be resolved!"); | |||||
| // else return ciphertext; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public Ciphertext tryResolveCiphertext(byte[] ciphertextBytes) { | |||||
| // //遍历对称加密算法,如果满足,则返回解析结果 | |||||
| // if (AES_ENCF.supportCiphertext(ciphertextBytes)) { | |||||
| // return AES_ENCF.resolveCiphertext(ciphertextBytes); | |||||
| // } | |||||
| // if (SM4_ENCF.supportCiphertext(ciphertextBytes)) { | |||||
| // return SM4_ENCF.resolveCiphertext(ciphertextBytes); | |||||
| // } | |||||
| // //否则返回null | |||||
| // return null; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public SingleKey resolveSymmetricKey(byte[] symmetricKeyBytes) { | |||||
| // SingleKey symmetricKey = tryResolveSymmetricKey(symmetricKeyBytes); | |||||
| // if (symmetricKey == null) | |||||
| // throw new IllegalArgumentException("This symmetricKeyBytes cannot be resolved!"); | |||||
| // else return symmetricKey; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public SingleKey tryResolveSymmetricKey(byte[] symmetricKeyBytes) { | |||||
| // //遍历对称加密算法,如果满足,则返回解析结果 | |||||
| // if(AES_ENCF.supportSymmetricKey(symmetricKeyBytes)) { | |||||
| // return AES_ENCF.resolveSymmetricKey(symmetricKeyBytes); | |||||
| // } | |||||
| // if(SM4_ENCF.supportSymmetricKey(symmetricKeyBytes)) { | |||||
| // return SM4_ENCF.resolveSymmetricKey(symmetricKeyBytes); | |||||
| // } | |||||
| // //否则返回null | |||||
| // return null; | |||||
| // } | |||||
| //} | |||||
| @@ -1,55 +0,0 @@ | |||||
| //package test.com.jd.blockchain.crypto.performance; | |||||
| // | |||||
| //import com.jd.blockchain.crypto.Ciphertext; | |||||
| //import com.jd.blockchain.crypto.PrivKey; | |||||
| //import com.jd.blockchain.crypto.PubKey; | |||||
| //import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; | |||||
| //import com.jd.blockchain.crypto.impl.sm.asymmetric.SM2CryptoFunction; | |||||
| //import org.bouncycastle.util.encoders.Hex; | |||||
| // | |||||
| //public class MyAsymmetricEncryptionTest { | |||||
| // | |||||
| // public static void main(String[] args) { | |||||
| // | |||||
| // String string1K = "0123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210"; | |||||
| // String string1M = ""; | |||||
| // for (int i = 0; i < 1024 ; i++) | |||||
| // { | |||||
| // string1M = string1M + string1K; | |||||
| // } | |||||
| // | |||||
| // byte[] data1K = Hex.decode(string1K); | |||||
| // byte[] data1M = Hex.decode(string1M); | |||||
| // int count = 10000; | |||||
| // | |||||
| // SM2CryptoFunction sm2 = new SM2CryptoFunction(); | |||||
| // CryptoKeyPair keyPairSM2 = sm2.generateKeyPair(); | |||||
| // PrivKey privKeySM2 = keyPairSM2.getPrivKey(); | |||||
| // PubKey pubKeySM2 = keyPairSM2.getPubKey(); | |||||
| // | |||||
| // System.out.println("=================== do SM2 encrypt test ==================="); | |||||
| // Ciphertext ciphertextSM2 = null; | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // ciphertextSM2 = sm2.encrypt(pubKeySM2,data1K); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("SM2 Encrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // System.out.println("=================== do SM2 decrypt test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // sm2.decrypt(privKeySM2,ciphertextSM2); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("SM2 Decrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // } | |||||
| //} | |||||
| @@ -1,62 +0,0 @@ | |||||
| //package test.com.jd.blockchain.crypto.performance; | |||||
| // | |||||
| //import com.jd.blockchain.crypto.impl.sm.hash.SM3HashFunction; | |||||
| //import com.jd.blockchain.crypto.service.classic.RIPEMD160HashFunction; | |||||
| //import com.jd.blockchain.crypto.service.classic.SHA256HashFunction; | |||||
| // | |||||
| //import java.util.Random; | |||||
| // | |||||
| //public class MyHashTest { | |||||
| // | |||||
| // public static void main(String[] args) { | |||||
| // | |||||
| // Random rand = new Random(); | |||||
| // byte[] data1K = new byte[1024]; | |||||
| // rand.nextBytes(data1K); | |||||
| // int count = 1000000; | |||||
| // | |||||
| // SHA256HashFunction sha256hf = new SHA256HashFunction(); | |||||
| // | |||||
| // System.out.println("=================== do SHA256 hash test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // sha256hf.hash(data1K); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // RIPEMD160HashFunction ripemd160hf = new RIPEMD160HashFunction(); | |||||
| // | |||||
| // System.out.println("=================== do RIPEMD160 hash test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // ripemd160hf.hash(data1K); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("RIPEMD160 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // SM3HashFunction sm3hf = new SM3HashFunction(); | |||||
| // | |||||
| // System.out.println("=================== do SM3 hash test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // sm3hf.hash(data1K); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("SM3 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // } | |||||
| //} | |||||
| // | |||||
| @@ -1,83 +0,0 @@ | |||||
| //package test.com.jd.blockchain.crypto.performance; | |||||
| // | |||||
| //import com.jd.blockchain.crypto.PrivKey; | |||||
| //import com.jd.blockchain.crypto.PubKey; | |||||
| //import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; | |||||
| //import com.jd.blockchain.crypto.asymmetric.SignatureDigest; | |||||
| //import com.jd.blockchain.crypto.impl.sm.asymmetric.SM2CryptoFunction; | |||||
| //import com.jd.blockchain.crypto.service.classic.ED25519SignatureFunction; | |||||
| // | |||||
| //import java.util.Random; | |||||
| // | |||||
| //public class MySignatureTest { | |||||
| // | |||||
| // public static void main(String[] args) { | |||||
| // | |||||
| // Random rand = new Random(); | |||||
| // byte[] data = new byte[64]; | |||||
| // rand.nextBytes(data); | |||||
| // int count = 10000; | |||||
| // | |||||
| // ED25519SignatureFunction ed25519sf = new ED25519SignatureFunction(); | |||||
| // CryptoKeyPair keyPairED25519 = ed25519sf.generateKeyPair(); | |||||
| // PrivKey privKeyED25519 = keyPairED25519.getPrivKey(); | |||||
| // PubKey pubKeyED25519 = keyPairED25519.getPubKey(); | |||||
| // | |||||
| // System.out.println("=================== do ED25519 sign test ==================="); | |||||
| // SignatureDigest signatureDigestED25519 = null; | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // signatureDigestED25519 = ed25519sf.sign(privKeyED25519,data); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("ED25519 Signing Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // System.out.println("=================== do ED25519 verify test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // ed25519sf.verify(signatureDigestED25519,pubKeyED25519,data); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("ED25519 Verifying Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // SM2CryptoFunction sm2 = new SM2CryptoFunction(); | |||||
| // CryptoKeyPair keyPairSM2 = sm2.generateKeyPair(); | |||||
| // PrivKey privKeySM2 = keyPairSM2.getPrivKey(); | |||||
| // PubKey pubKeySM2 = keyPairSM2.getPubKey(); | |||||
| // | |||||
| // | |||||
| // System.out.println("=================== do SM2 sign test ==================="); | |||||
| // SignatureDigest signatureDigestSM2 = null; | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // signatureDigestSM2 = sm2.sign(privKeySM2,data); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("SM2 Signing Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // System.out.println("=================== do SM2 verify test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // sm2.verify(signatureDigestSM2,pubKeySM2,data); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("SM2 Verifying Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // } | |||||
| //} | |||||
| @@ -1,92 +0,0 @@ | |||||
| //package test.com.jd.blockchain.crypto.performance; | |||||
| // | |||||
| //import com.jd.blockchain.crypto.Ciphertext; | |||||
| //import com.jd.blockchain.crypto.SingleKey; | |||||
| //import com.jd.blockchain.crypto.impl.sm.symmetric.SM4SymmetricEncryptionFunction; | |||||
| //import com.jd.blockchain.crypto.service.classic.AESSymmetricEncryptionFunction; | |||||
| // | |||||
| //import org.bouncycastle.util.encoders.Hex; | |||||
| // | |||||
| //public class MySymmetricEncryptionTest { | |||||
| // | |||||
| // public static void main(String[] args) { | |||||
| // | |||||
| // String string1K = "0123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210"; | |||||
| // | |||||
| //// String string1M = ""; | |||||
| //// for (int i = 0; i < 1024 ; i++) | |||||
| //// { | |||||
| //// string1M = string1M + string1K; | |||||
| //// } | |||||
| // | |||||
| // byte[] data1K = Hex.decode(string1K); | |||||
| //// byte[] data1M = Hex.decode(string1M); | |||||
| // | |||||
| // int count = 100000; | |||||
| // | |||||
| // | |||||
| // AESSymmetricEncryptionFunction aes = new AESSymmetricEncryptionFunction(); | |||||
| // SingleKey keyAES = (SingleKey) aes.generateSymmetricKey(); | |||||
| // Ciphertext ciphertext1KAES = null; | |||||
| // Ciphertext ciphertext1MAES = null; | |||||
| // | |||||
| // System.out.println("=================== do AES encrypt test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // ciphertext1KAES = aes.encrypt(keyAES,data1K); | |||||
| //// ciphertext1MAES = aes.encrypt(keyAES,data1M); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("AES Encrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // | |||||
| // | |||||
| // System.out.println("=================== do AES decrypt test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // aes.decrypt(keyAES,ciphertext1KAES); | |||||
| //// aes.decrypt(keyAES,ciphertext1MAES); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("AES Decrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // SM4SymmetricEncryptionFunction sm4 = new SM4SymmetricEncryptionFunction(); | |||||
| // SingleKey keySM4 = (SingleKey) sm4.generateSymmetricKey(); | |||||
| // Ciphertext ciphertext1KSM4 = null; | |||||
| // Ciphertext ciphertext1MSM4 = null; | |||||
| // | |||||
| // System.out.println("=================== do SM4 encrypt test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // ciphertext1KSM4 = sm4.encrypt(keySM4,data1K); | |||||
| //// ciphertext1MSM4 =sm4.encrypt(keySM4,data1M); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("SM4 Encrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // | |||||
| // System.out.println("=================== do SM4 decrypt test ==================="); | |||||
| // for (int r = 0; r < 5; r++) { | |||||
| // System.out.println("------------- round[" + r + "] --------------"); | |||||
| // long startTS = System.currentTimeMillis(); | |||||
| // for (int i = 0; i < count; i++) { | |||||
| // sm4.decrypt(keySM4,ciphertext1KSM4); | |||||
| //// sm4.decrypt(keySM4,ciphertext1MSM4); | |||||
| // } | |||||
| // long elapsedTS = System.currentTimeMillis() - startTS; | |||||
| // System.out.println(String.format("SM4 Decrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, | |||||
| // (count * 1000.00D) / elapsedTS)); | |||||
| // } | |||||
| // } | |||||
| //} | |||||
| @@ -1,5 +1,6 @@ | |||||
| # JDChain安装部署指南 | # JDChain安装部署指南 | ||||
| 本部署指南基于JDChain1.0.0.RELEASE版本来构建。 | |||||
| ## 1. 部署环境 | ## 1. 部署环境 | ||||
| ### 1.1 系统部署结构 | ### 1.1 系统部署结构 | ||||
|  |  | ||||
| @@ -56,21 +57,21 @@ unzip jdchain-peer-$version.zip | |||||
| Peer打包程序解压完后的安装包结构如下: | Peer打包程序解压完后的安装包结构如下: | ||||
| + bin | + bin | ||||
| - keygen.sh | |||||
| - ledger-init.sh | |||||
| - startup.sh | |||||
| - shutdown.sh | |||||
| - keygen.sh | |||||
| - ledger-init.sh | |||||
| - startup.sh | |||||
| - shutdown.sh | |||||
| + config | + config | ||||
| - init | |||||
| + ledger-init.conf | |||||
| + local.conf | |||||
| + bftsmart.config(默认) | |||||
| - keys | |||||
| + %.priv | |||||
| + %.pub | |||||
| + %.pwd | |||||
| - ledger-binding.conf | |||||
| - application.properties | |||||
| - init | |||||
| + ledger.init | |||||
| + local.conf | |||||
| + bftsmart.config(默认) | |||||
| - keys | |||||
| + %.priv | |||||
| + %.pub | |||||
| + %.pwd | |||||
| - ledger-binding.conf | |||||
| - application.properties | |||||
| + docs | + docs | ||||
| + libs | + libs | ||||
| + system | + system | ||||
| @@ -93,10 +94,10 @@ chmod 777 * | |||||
| Gateway节点打包程序为:jdchain-gateway-$version.tar.gz或jdchain-gateway-$version.zip,其解压完后的安装包结构如下: | Gateway节点打包程序为:jdchain-gateway-$version.tar.gz或jdchain-gateway-$version.zip,其解压完后的安装包结构如下: | ||||
| + bin | + bin | ||||
| - startup.sh | |||||
| - shutdown.sh | |||||
| - startup.sh | |||||
| - shutdown.sh | |||||
| + config | + config | ||||
| - gateway.conf | |||||
| - gateway.conf | |||||
| + lib | + lib | ||||
| @@ -125,12 +126,12 @@ JDChain提供了创建用户的工具,可直接通过命令行生成一个新 | |||||
| **请将所有Peer节点都按照上述流程注册一个独立的用户,在实际生产环境中该过程可能由不同的参与方完成。** | **请将所有Peer节点都按照上述流程注册一个独立的用户,在实际生产环境中该过程可能由不同的参与方完成。** | ||||
| ### 2.3 账本初始化 | ### 2.3 账本初始化 | ||||
| 账本初始化是所有参与Peer节点进行共识,并生成初始化账本至数据库的过程。该过程需要所有节点共同参与,同时启动。 | |||||
| 账本初始化是所有参与Peer节点进行共识,并生成初始化账本至数据库的过程。该过程需要所有节点共同参与,**同时启动**。 | |||||
| #### 2.3.1 初始化配置 | #### 2.3.1 初始化配置 | ||||
| *config/init* 目录下有三个配置文件需要修改:**local.conf** 、 **ledger-init.conf** 和 **bftsmart.config** 。 | |||||
| *config/init* 目录下有三个配置文件需要修改:**local.conf** 、 **ledger.init** 和 **bftsmart.config** 。 | |||||
| + local.conf描述账本初始化的本地(即当前节点)配置; | + local.conf描述账本初始化的本地(即当前节点)配置; | ||||
| + ledger-init.conf描述账本初始化过程中涉及到的其他参与Peer节点配置信息; | |||||
| + ledger.init描述账本初始化过程中涉及到的其他参与Peer节点配置信息; | |||||
| + bftsmart.config为BFTSmart进行共识的相关配置。 | + bftsmart.config为BFTSmart进行共识的相关配置。 | ||||
| ##### 2.3.1.1 local.conf配置 | ##### 2.3.1.1 local.conf配置 | ||||
| @@ -157,18 +158,11 @@ ledger.db.uri=rocksdb:///export/app/peer/rocks.db/rocksdb0.db | |||||
| #账本数据库的连接口令 | #账本数据库的连接口令 | ||||
| ledger.db.pwd= | ledger.db.pwd= | ||||
| #共识配置文件路径 | |||||
| consensus.conf=../bftsmart.config | |||||
| #共识Providers配置 | |||||
| #BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||||
| consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||||
| ``` | ``` | ||||
| 其中local.parti.id从 **0** 开始编号,且不同Peer节点不能相同;local.parti.privkey为当前节点私钥,即 *config/keys/%.priv* 文件中的内容;其他参数根据实际环境进行配置。 | 其中local.parti.id从 **0** 开始编号,且不同Peer节点不能相同;local.parti.privkey为当前节点私钥,即 *config/keys/%.priv* 文件中的内容;其他参数根据实际环境进行配置。 | ||||
| ##### 2.3.1.2 ledger-init.conf配置 | |||||
| ##### 2.3.1.2 ledger.init配置 | |||||
| ```config | ```config | ||||
| #账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; | #账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; | ||||
| ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe | ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe | ||||
| @@ -176,37 +170,49 @@ ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323 | |||||
| #账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; | #账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; | ||||
| #ledger.name= | #ledger.name= | ||||
| #声明的账本创建时间;格式为 “yyyy-MM-dd HH:mm:ss.SSSZ”,表示”年-月-日 时:分:秒:毫秒时区“;例如:“2019-10-17 05:21:58.069+0800”,其中,+0800 表示时区是东8区 | |||||
| created-time=2019-10-17 05:21:58.069+0800 | |||||
| #共识服务提供者;必须; | |||||
| consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||||
| #共识服务的参数配置;必须; | |||||
| #consensus.conf=/export/app/peer/config/init/bftsmart.config | |||||
| consensus.conf=bftsmart.config | |||||
| #密码服务提供者列表,以英文逗点“,”分隔;必须; | |||||
| crypto.service-providers=com.jd.blockchain.crypto.service.classic.ClassicCryptoService, \ | |||||
| com.jd.blockchain.crypto.service.sm.SMCryptoService | |||||
| #参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; | #参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; | ||||
| cons_parti.count=4 | cons_parti.count=4 | ||||
| #第0个参与方的名称 | |||||
| cons_parti.0.name=parti-0.com | |||||
| #第0个参与方的公钥文件路径 | |||||
| #第0个参与方的名称; | |||||
| cons_parti.0.name= | |||||
| #第0个参与方的公钥文件路径; | |||||
| cons_parti.0.pubkey-path= | cons_parti.0.pubkey-path= | ||||
| #第0个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 | |||||
| cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna | |||||
| #第0个参与方的账本初始服务的主机 | |||||
| #第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
| cons_parti.0.pubkey= | |||||
| #第0个参与方的账本初始服务的主机; | |||||
| cons_parti.0.initializer.host=127.0.0.1 | cons_parti.0.initializer.host=127.0.0.1 | ||||
| #第0个参与方的账本初始服务的端口 | |||||
| cons_parti.0.initializer.port=17000 | |||||
| #第0个参与方的账本初始服务是否开启安全连接 | |||||
| #第0个参与方的账本初始服务的端口; | |||||
| cons_parti.0.initializer.port=8800 | |||||
| #第0个参与方的账本初始服务是否开启安全连接; | |||||
| cons_parti.0.initializer.secure=false | cons_parti.0.initializer.secure=false | ||||
| #第1个参与方的名称 | |||||
| cons_parti.1.name=parti-1.com | |||||
| #第1个参与方的公钥文件路径 | |||||
| #第1个参与方的名称; | |||||
| cons_parti.1.name= | |||||
| #第1个参与方的公钥文件路径; | |||||
| cons_parti.1.pubkey-path= | cons_parti.1.pubkey-path= | ||||
| #第1个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 | |||||
| cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ | |||||
| #第1个参与方的账本初始服务的主机 | |||||
| #第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
| cons_parti.1.pubkey= | |||||
| #第1个参与方的账本初始服务的主机; | |||||
| cons_parti.1.initializer.host=127.0.0.1 | cons_parti.1.initializer.host=127.0.0.1 | ||||
| #第1个参与方的账本初始服务的端口 | |||||
| cons_parti.1.initializer.port=17010 | |||||
| #第1个参与方的账本初始服务是否开启安全连接 | |||||
| #第1个参与方的账本初始服务的端口; | |||||
| cons_parti.1.initializer.port=8810 | |||||
| #第1个参与方的账本初始服务是否开启安全连接; | |||||
| cons_parti.1.initializer.secure=false | cons_parti.1.initializer.secure=false | ||||
| ``` | ``` | ||||
| 账本初始化过程中,需要其他Peer节点的公钥信息进行验证及保存,因此每个节点都需要获取其他Peer节点的公钥信息至本地。配置中cons_parti.N.*****中的N需按照实际每个参与方定义的序号配置(该配置为local.conf中的local.parti.id)。其他参数根据实际环境进行配置。 | 账本初始化过程中,需要其他Peer节点的公钥信息进行验证及保存,因此每个节点都需要获取其他Peer节点的公钥信息至本地。配置中cons_parti.N.*****中的N需按照实际每个参与方定义的序号配置(该配置为local.conf中的local.parti.id)。其他参数根据实际环境进行配置。 | ||||
| @@ -214,14 +220,12 @@ cons_parti.1.initializer.secure=false | |||||
| ##### 2.3.1.1 bftsmart.config配置 | ##### 2.3.1.1 bftsmart.config配置 | ||||
| ```config | ```config | ||||
| system.block.txsize=15 | |||||
| system.block.maxdelay=500 | |||||
| system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna | |||||
| system.server.0.network.host=127.0.0.1 | system.server.0.network.host=127.0.0.1 | ||||
| system.server.0.network.port=16000 | system.server.0.network.port=16000 | ||||
| system.server.1.network.host=127.0.0.1 | |||||
| system.server.1.network.port=16010 | |||||
| system.servers.num = 4 | system.servers.num = 4 | ||||
| system.servers.f = 1 | system.servers.f = 1 | ||||
| @@ -231,9 +235,6 @@ system.initial.view = 0,1,2,3 | |||||
| > 注意:上述只是将bftsmart.config文件中主要需要修改的参数显示,以方便进行相关说明,其他参数的修改可参考具体文件中的具体说明 | > 注意:上述只是将bftsmart.config文件中主要需要修改的参数显示,以方便进行相关说明,其他参数的修改可参考具体文件中的具体说明 | ||||
| 参数具体说明如下: | 参数具体说明如下: | ||||
| + system.block.txsize:每个区块结块期望含交易数量; | |||||
| + system.block.maxdelay:区块结块时最大时延(单位:毫秒),该参数与system.block.txsize符合优先发生原则; | |||||
| + system.server.$n.pubkey:第n节点的公钥信息; | |||||
| + system.server.$n.network.host:第n个节点的域名; | + system.server.$n.network.host:第n个节点的域名; | ||||
| + system.server.$n.network.port:第n个节点的共识端口; | + system.server.$n.network.port:第n个节点的共识端口; | ||||
| + system.servers.num:共识节点总数; | + system.servers.num:共识节点总数; | ||||
| @@ -249,7 +250,7 @@ cd bin | |||||
| 执行命令之后,会在 *config* 目录下生成ledger-binding.conf文件,该文件即账本初始化生成的文件,Peer节点启动时需要依赖该文件。 | 执行命令之后,会在 *config* 目录下生成ledger-binding.conf文件,该文件即账本初始化生成的文件,Peer节点启动时需要依赖该文件。 | ||||
| > 1)注意:因为JDChain支持多账本形式,若config/ledger-binding.conf文件在初始化之前就存在的话,初始化操作后不会覆盖其中的内容,会以追加的方式写入。若第一次创建账本,建议先将该文件删除再进行初始化! | > 1)注意:因为JDChain支持多账本形式,若config/ledger-binding.conf文件在初始化之前就存在的话,初始化操作后不会覆盖其中的内容,会以追加的方式写入。若第一次创建账本,建议先将该文件删除再进行初始化! | ||||
| > 2)注意:Peer节点会定时检测ledger-binding.conf,有新账本加入时会自动进行更新,不需要重启Peer节点! | |||||
| > 2)注意:Peer节点会定时检测ledger-binding.conf,有新账本加入时会自动进行更新,不需要重启Peer节点!目前默认时间为封5自动更新,即:每个小时的5/15/25/35/45/55分钟会执行; | |||||
| 账本初始化成功后,每个Peer节点对应的Rocksdb都会写入该账本相关的数据。 | 账本初始化成功后,每个Peer节点对应的Rocksdb都会写入该账本相关的数据。 | ||||
| @@ -262,9 +263,9 @@ Peer节点启动依赖于 *config* 目录下ledger-binding.conf的配置,该 | |||||
| cd bin | cd bin | ||||
| ./startup.sh | ./startup.sh | ||||
| ``` | ``` | ||||
| > 1)注意:startup.sh命令中可修改启动端口,默认为:-p 17080; | |||||
| > 1)注意:startup.sh命令中可修改启动端口,默认为:-p 7080; | |||||
| > 2)注意:Peer节点会与账本中涉及到的参与方进行通信,当通信不成功(例如有节点尚未启动)时,会自动进行重试,因此多个Peer节点启动可不必完全同时进行。 | |||||
| > 2)注意:Peer节点会与账本中涉及到的参与方进行通信,当通信不成功(例如有节点尚未启动)时,会自动进行重试,因此多个Peer节点启动可不必完全同时进行。目前默认设置为重试16次操作,每次间隔时间2秒。 | |||||
| ### 2.5 Gateway节点安装 | ### 2.5 Gateway节点安装 | ||||
| GateWay(网关)节点可以认为是一个过滤节点,交易的提交及账本的查询都需要通过网关节点与Peer节点进行通信。 | GateWay(网关)节点可以认为是一个过滤节点,交易的提交及账本的查询都需要通过网关节点与Peer节点进行通信。 | ||||
| @@ -272,7 +273,7 @@ Gateway程序可独立部署,不需要依赖Peer节点,它的操作环境是 | |||||
| ```config | ```config | ||||
| #网关的HTTP服务地址; | #网关的HTTP服务地址; | ||||
| http.host=127.0.0.1 | |||||
| http.host=0.0.0.0 | |||||
| #网关的HTTP服务端口; | #网关的HTTP服务端口; | ||||
| http.port=8081 | http.port=8081 | ||||
| #网关的HTTP服务上下文路径,可选; | #网关的HTTP服务上下文路径,可选; | ||||
| @@ -292,15 +293,15 @@ peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||||
| data.retrieval.url=http://192.168.151.39:10001 | data.retrieval.url=http://192.168.151.39:10001 | ||||
| #默认公钥的内容(Base58编码数据); | #默认公钥的内容(Base58编码数据); | ||||
| keys.default.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna | |||||
| keys.default.pubkey= | |||||
| #默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; | #默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; | ||||
| keys.default.privkey-path= | keys.default.privkey-path= | ||||
| #默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; | #默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; | ||||
| keys.default.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY | |||||
| keys.default.privkey= | |||||
| #默认私钥的解码密码; | #默认私钥的解码密码; | ||||
| keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY | |||||
| keys.default.privkey-password= | |||||
| ``` | ``` | ||||
| 其中keys.default.%配置是网关的角色配置,目前暂不支持网关自定义角色,因此,网关可选择4个参与方的任何一个作为其配置(主要是密钥对)。 | |||||
| 其中keys.default.% 配置是网关的角色配置,目前暂不支持网关自定义角色,因此,网关可选择4个参与方的任何一个作为其配置(主要是密钥对)。 | |||||
| >注意:keys.default.privkey-password填写内容为*2.2章节*中生成的%.pwd文件中的内容! | >注意:keys.default.privkey-password填写内容为*2.2章节*中生成的%.pwd文件中的内容! | ||||
| 启动网关节点只需要执行:startup.sh即可,参考命令: | 启动网关节点只需要执行:startup.sh即可,参考命令: | ||||
| @@ -8,7 +8,7 @@ | |||||
| <!--这个输出控制台的配置--> | <!--这个输出控制台的配置--> | ||||
| <console name="Console" target="SYSTEM_OUT"> | <console name="Console" target="SYSTEM_OUT"> | ||||
| <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | ||||
| <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
| <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
| <!--输出日志的格式--> | <!--输出日志的格式--> | ||||
| <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | ||||
| </console> | </console> | ||||
| @@ -62,8 +62,13 @@ | |||||
| <version>${project.version}</version> | <version>${project.version}</version> | ||||
| <scope>test</scope> | <scope>test</scope> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>org.mockito</groupId> | |||||
| <artifactId>mockito-core</artifactId> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| </dependencies> | |||||
| <build> | <build> | ||||
| <plugins> | <plugins> | ||||
| @@ -78,42 +83,32 @@ | |||||
| </plugins> | </plugins> | ||||
| </build> | </build> | ||||
| <!--<build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-surefire-plugin</artifactId> | |||||
| <version>2.5</version> | |||||
| <configuration> | |||||
| <excludes> | |||||
| <exclude>**/TransactionBatchProcessorTest.java</exclude> | |||||
| </excludes> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build>--> | |||||
| <!--<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-surefire-plugin</artifactId> <version>2.5</version> <configuration> | |||||
| <excludes> <exclude>**/TransactionBatchProcessorTest.java</exclude> </excludes> | |||||
| </configuration> </plugin> </plugins> </build> --> | |||||
| <!--<build>--> | |||||
| <!--<plugins>--> | |||||
| <!--<plugin>--> | |||||
| <!--<groupId>org.apache.maven.plugins</groupId>--> | |||||
| <!--<artifactId>maven-compiler-plugin</artifactId>--> | |||||
| <!--<version>3.1</version>--> | |||||
| <!--<configuration>--> | |||||
| <!--<source>1.8</source>--> | |||||
| <!--<target>1.8</target>--> | |||||
| <!--<encoding>UTF-8</encoding>--> | |||||
| <!--<compilerArgs>--> | |||||
| <!--<!–<arg>-verbose</arg>–>--> | |||||
| <!--<!–<arg>-Xlint:unchecked</arg>–>--> | |||||
| <!--<!–<arg>-Xlint:deprecation</arg>–>--> | |||||
| <!--<!–<arg>-bootclasspath</arg>–>--> | |||||
| <!--<!–<arg>${env.JAVA_HOME}/jre/lib/rt.jar</arg>–>--> | |||||
| <!--<arg>-extdirs</arg>--> | |||||
| <!--<arg>${project.basedir}/../contract/contract-libs;$JAVA_HOME/jre/lib/ext</arg>--> | |||||
| <!--</compilerArgs>--> | |||||
| <!--</configuration>--> | |||||
| <!--</plugin>--> | |||||
| <!--</plugins>--> | |||||
| <!--</build>--> | |||||
| <!--<build> --> | |||||
| <!--<plugins> --> | |||||
| <!--<plugin> --> | |||||
| <!--<groupId>org.apache.maven.plugins</groupId> --> | |||||
| <!--<artifactId>maven-compiler-plugin</artifactId> --> | |||||
| <!--<version>3.1</version> --> | |||||
| <!--<configuration> --> | |||||
| <!--<source>1.8</source> --> | |||||
| <!--<target>1.8</target> --> | |||||
| <!--<encoding>UTF-8</encoding> --> | |||||
| <!--<compilerArgs> --> | |||||
| <!--<!–<arg>-verbose</arg>–> --> | |||||
| <!--<!–<arg>-Xlint:unchecked</arg>–> --> | |||||
| <!--<!–<arg>-Xlint:deprecation</arg>–> --> | |||||
| <!--<!–<arg>-bootclasspath</arg>–> --> | |||||
| <!--<!–<arg>${env.JAVA_HOME}/jre/lib/rt.jar</arg>–> --> | |||||
| <!--<arg>-extdirs</arg> --> | |||||
| <!--<arg>${project.basedir}/../contract/contract-libs;$JAVA_HOME/jre/lib/ext</arg> --> | |||||
| <!--</compilerArgs> --> | |||||
| <!--</configuration> --> | |||||
| <!--</plugin> --> | |||||
| <!--</plugins> --> | |||||
| <!--</build> --> | |||||
| </project> | </project> | ||||
| @@ -333,9 +333,9 @@ public class AccountSet implements Transactional, MerkleProvable { | |||||
| if (!updated) { | if (!updated) { | ||||
| return; | return; | ||||
| } | } | ||||
| String[] addresses = new String[latestAccountsCache.size()]; | |||||
| Bytes[] addresses = new Bytes[latestAccountsCache.size()]; | |||||
| latestAccountsCache.keySet().toArray(addresses); | latestAccountsCache.keySet().toArray(addresses); | ||||
| for (String address : addresses) { | |||||
| for (Bytes address : addresses) { | |||||
| VersioningAccount acc = latestAccountsCache.remove(address); | VersioningAccount acc = latestAccountsCache.remove(address); | ||||
| // cancel; | // cancel; | ||||
| if (acc.isUpdated()) { | if (acc.isUpdated()) { | ||||
| @@ -14,9 +14,9 @@ public class DataAccountSet implements MerkleProvable, Transactional { | |||||
| private AccountSet accountSet; | private AccountSet accountSet; | ||||
| public DataAccountSet(CryptoSetting cryptoSetting, String prefix,ExPolicyKVStorage exStorage, | |||||
| public DataAccountSet(CryptoSetting cryptoSetting, String prefix, ExPolicyKVStorage exStorage, | |||||
| VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { | VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { | ||||
| accountSet = new AccountSet(cryptoSetting,prefix, exStorage, verStorage, accessPolicy); | |||||
| accountSet = new AccountSet(cryptoSetting, prefix, exStorage, verStorage, accessPolicy); | |||||
| } | } | ||||
| public DataAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String prefix, | public DataAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String prefix, | ||||
| @@ -26,7 +26,7 @@ public class DataAccountSet implements MerkleProvable, Transactional { | |||||
| } | } | ||||
| public AccountHeader[] getAccounts(int fromIndex, int count) { | public AccountHeader[] getAccounts(int fromIndex, int count) { | ||||
| return accountSet.getAccounts(fromIndex,count); | |||||
| return accountSet.getAccounts(fromIndex, count); | |||||
| } | } | ||||
| public boolean isReadonly() { | public boolean isReadonly() { | ||||
| @@ -56,8 +56,18 @@ public class DataAccountSet implements MerkleProvable, Transactional { | |||||
| return new DataAccount(accBase); | return new DataAccount(accBase); | ||||
| } | } | ||||
| /** | |||||
| * 返回数据账户; <br> | |||||
| * 如果不存在,则返回 null; | |||||
| * | |||||
| * @param address | |||||
| * @return | |||||
| */ | |||||
| public DataAccount getDataAccount(Bytes address) { | public DataAccount getDataAccount(Bytes address) { | ||||
| BaseAccount accBase = accountSet.getAccount(address); | BaseAccount accBase = accountSet.getAccount(address); | ||||
| if (accBase == null) { | |||||
| return null; | |||||
| } | |||||
| return new DataAccount(accBase); | return new DataAccount(accBase); | ||||
| } | } | ||||
| @@ -1,5 +1,6 @@ | |||||
| package com.jd.blockchain.ledger.core; | package com.jd.blockchain.ledger.core; | ||||
| import com.jd.blockchain.crypto.HashDigest; | |||||
| import com.jd.blockchain.ledger.LedgerBlock; | import com.jd.blockchain.ledger.LedgerBlock; | ||||
| import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
| import com.jd.blockchain.ledger.LedgerTransaction; | import com.jd.blockchain.ledger.LedgerTransaction; | ||||
| @@ -18,6 +19,20 @@ import com.jd.blockchain.ledger.TransactionRequest; | |||||
| */ | */ | ||||
| public interface LedgerEditor { | public interface LedgerEditor { | ||||
| /** | |||||
| * 账本Hash; | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| HashDigest getLedgerHash(); | |||||
| /** | |||||
| * 新区块的高度; | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| long getBlockHeight(); | |||||
| /** | /** | ||||
| * 开始新事务;<br> | * 开始新事务;<br> | ||||
| * | * | ||||
| @@ -32,11 +47,14 @@ public interface LedgerEditor { | |||||
| * 或者全部回滚(通过方法 {@link LedgerTransactionContext#rollback()}),以此实现原子性写入; | * 或者全部回滚(通过方法 {@link LedgerTransactionContext#rollback()}),以此实现原子性写入; | ||||
| * <p> | * <p> | ||||
| * | * | ||||
| * 每一次事务性的账本写入操作在提交后,都会记录该事务相关的系统全局快照,以交易对象 {@link LedgerTransaction} 进行保存;<p> | |||||
| * 每一次事务性的账本写入操作在提交后,都会记录该事务相关的系统全局快照,以交易对象 {@link LedgerTransaction} 进行保存; | |||||
| * <p> | |||||
| * | * | ||||
| * 注:方法不解析、不执行交易中的操作; | |||||
| * | * | ||||
| * @param txRequest | |||||
| * | |||||
| * 注:方法不解析、不执行交易中的操作;<p> | |||||
| * | |||||
| * @param txRequest 交易请求; | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| LedgerTransactionContext newTransaction(TransactionRequest txRequest); | LedgerTransactionContext newTransaction(TransactionRequest txRequest); | ||||
| @@ -24,7 +24,7 @@ public interface LedgerTransactionContext { | |||||
| * | * | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| TransactionRequest getRequestTX(); | |||||
| TransactionRequest getTransactionRequest(); | |||||
| /** | /** | ||||
| * 提交对账本数据的修改,以指定的交易状态提交交易; | * 提交对账本数据的修改,以指定的交易状态提交交易; | ||||
| @@ -0,0 +1,51 @@ | |||||
| package com.jd.blockchain.ledger.core; | |||||
| public class SettingContext { | |||||
| private static final TxSettingContext txSettings = new TxSettingContext(); | |||||
| private static final QueryingSettingContext queryingSettings = new QueryingSettingContext(); | |||||
| public static TxSettingContext txSettings() { | |||||
| return txSettings; | |||||
| } | |||||
| public static QueryingSettingContext queryingSettings() { | |||||
| return queryingSettings; | |||||
| } | |||||
| /** | |||||
| * 与交易处理相关的设置; | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| public static class TxSettingContext { | |||||
| public boolean verifyLedger() { | |||||
| return true; | |||||
| } | |||||
| public boolean verifySignature() { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 与账本查询相关的设置; | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| public static class QueryingSettingContext { | |||||
| /** | |||||
| * 查询区块等具有 hash 标识符的对象时是否重新校验哈希; | |||||
| * @return | |||||
| */ | |||||
| public boolean verifyHash() { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -33,7 +33,9 @@ public class LedgerBlockData implements LedgerBlock { | |||||
| // private HashDigest contractPrivilegeHash; | // private HashDigest contractPrivilegeHash; | ||||
| private HashDigest transactionSetHash; | private HashDigest transactionSetHash; | ||||
| private long timestamp; | |||||
| public LedgerBlockData() { | public LedgerBlockData() { | ||||
| } | } | ||||
| @@ -155,4 +157,14 @@ public class LedgerBlockData implements LedgerBlock { | |||||
| this.ledgerHash = ledgerHash; | this.ledgerHash = ledgerHash; | ||||
| } | } | ||||
| public long getTimestamp() { | |||||
| return timestamp; | |||||
| } | |||||
| public void setTimestamp(long timestamp) { | |||||
| this.timestamp = timestamp; | |||||
| } | |||||
| } | } | ||||
| @@ -1,398 +1,399 @@ | |||||
| package com.jd.blockchain.ledger.core.impl; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| import com.jd.blockchain.contract.ContractException; | |||||
| import com.jd.blockchain.crypto.HashDigest; | |||||
| import com.jd.blockchain.ledger.*; | |||||
| import com.jd.blockchain.ledger.core.ContractAccountSet; | |||||
| import com.jd.blockchain.ledger.core.DataAccount; | |||||
| import com.jd.blockchain.ledger.core.DataAccountSet; | |||||
| import com.jd.blockchain.ledger.core.LedgerAdministration; | |||||
| import com.jd.blockchain.ledger.core.LedgerRepository; | |||||
| import com.jd.blockchain.ledger.core.LedgerService; | |||||
| import com.jd.blockchain.ledger.core.TransactionSet; | |||||
| import com.jd.blockchain.ledger.core.UserAccountSet; | |||||
| import com.jd.blockchain.transaction.BlockchainQueryService; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| import com.jd.blockchain.utils.QueryUtil; | |||||
| public class LedgerQueryService implements BlockchainQueryService { | |||||
| private LedgerService ledgerService; | |||||
| public LedgerQueryService(LedgerService ledgerService) { | |||||
| this.ledgerService = ledgerService; | |||||
| } | |||||
| @Override | |||||
| public HashDigest[] getLedgerHashs() { | |||||
| return ledgerService.getLedgerHashs(); | |||||
| } | |||||
| @Override | |||||
| public LedgerInfo getLedger(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerInfo ledgerInfo = new LedgerInfo(); | |||||
| ledgerInfo.setHash(ledger.getHash()); | |||||
| ledgerInfo.setLatestBlockHash(ledger.getLatestBlockHash()); | |||||
| ledgerInfo.setLatestBlockHeight(ledger.getLatestBlockHeight()); | |||||
| return ledgerInfo; | |||||
| } | |||||
| @Override | |||||
| public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { | |||||
| return ledgerAdministration(ledgerHash).getParticipants(); | |||||
| } | |||||
| @Override | |||||
| public LedgerMetadata getLedgerMetadata(HashDigest ledgerHash) { | |||||
| return ledgerAdministration(ledgerHash).getMetadata(); | |||||
| } | |||||
| @Override | |||||
| public LedgerBlock getBlock(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| return ledger.getBlock(height); | |||||
| } | |||||
| @Override | |||||
| public LedgerBlock getBlock(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| return ledger.getBlock(blockHash); | |||||
| } | |||||
| @Override | |||||
| public long getTransactionCount(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(height); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getTransactionCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(blockHash); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getTransactionTotalCount(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getDataAccountCount(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(height); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| return dataAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getDataAccountCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(blockHash); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| return dataAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getDataAccountTotalCount(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| return dataAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getUserCount(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(height); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| return userAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getUserCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(blockHash); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| return userAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getUserTotalCount(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| return userAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getContractCount(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(height); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| return contractAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getContractCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(blockHash); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| return contractAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getContractTotalCount(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| return contractAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public LedgerTransaction[] getTransactions(HashDigest ledgerHash, long height, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock ledgerBlock = ledger.getBlock(height); | |||||
| TransactionSet transactionSet = ledger.getTransactionSet(ledgerBlock); | |||||
| int lastHeightTxTotalNums = 0; | |||||
| if (height > 0) { | |||||
| lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height - 1)).getTotalCount(); | |||||
| } | |||||
| int currentHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height)).getTotalCount(); | |||||
| // 取当前高度的增量交易数,在增量交易里进行查找 | |||||
| int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; | |||||
| // | |||||
| // if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { | |||||
| // fromIndex = 0; | |||||
| // } | |||||
| // if (count == -1) { | |||||
| // fromIndex = 0; | |||||
| // count = currentHeightTxNums; | |||||
| // } | |||||
| // if (count > currentHeightTxNums) { | |||||
| // count = currentHeightTxNums - fromIndex; | |||||
| // } | |||||
| int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex, count, currentHeightTxNums); | |||||
| return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0], indexAndCount[1]); | |||||
| } | |||||
| @Override | |||||
| public LedgerTransaction[] getTransactions(HashDigest ledgerHash, HashDigest blockHash, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock ledgerBlock = ledger.getBlock(blockHash); | |||||
| long height = ledgerBlock.getHeight(); | |||||
| TransactionSet transactionSet = ledger.getTransactionSet(ledgerBlock); | |||||
| int lastHeightTxTotalNums = 0; | |||||
| if (height > 0) { | |||||
| lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height - 1)).getTotalCount(); | |||||
| } | |||||
| int currentHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height)).getTotalCount(); | |||||
| // 取当前块hash的增量交易数,在增量交易里进行查找 | |||||
| int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; | |||||
| // if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { | |||||
| // fromIndex = 0; | |||||
| // } | |||||
| // if (count == -1) { | |||||
| // fromIndex = 0; | |||||
| // count = currentHeightTxNums; | |||||
| // } | |||||
| // if (count > currentHeightTxNums) { | |||||
| // count = currentHeightTxNums - fromIndex; | |||||
| // } | |||||
| int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex, count, currentHeightTxNums); | |||||
| return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0], indexAndCount[1]); | |||||
| } | |||||
| @Override | |||||
| public LedgerTransaction getTransactionByContentHash(HashDigest ledgerHash, HashDigest contentHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.get(contentHash); | |||||
| } | |||||
| @Override | |||||
| public TransactionState getTransactionStateByContentHash(HashDigest ledgerHash, HashDigest contentHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.getTxState(contentHash); | |||||
| } | |||||
| @Override | |||||
| public UserInfo getUser(HashDigest ledgerHash, String address) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| return userAccountSet.getUser(address); | |||||
| } | |||||
| @Override | |||||
| public AccountHeader getDataAccount(HashDigest ledgerHash, String address) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| return dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| } | |||||
| @Override | |||||
| public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||||
| if (keys == null || keys.length == 0) { | |||||
| return null; | |||||
| } | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| KVDataEntry[] entries = new KVDataEntry[keys.length]; | |||||
| long ver; | |||||
| for (int i = 0; i < entries.length; i++) { | |||||
| final String currKey = keys[i]; | |||||
| ver = dataAccount.getDataVersion(Bytes.fromString(currKey)); | |||||
| if (ver < 0) { | |||||
| entries[i] = new KVDataObject(currKey, -1, null); | |||||
| } else { | |||||
| BytesValue value = dataAccount.getBytes(Bytes.fromString(currKey), ver); | |||||
| entries[i] = new KVDataObject(currKey, ver, value); | |||||
| } | |||||
| } | |||||
| return entries; | |||||
| } | |||||
| public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||||
| // parse kvInfoVO; | |||||
| List<String> keyList = new ArrayList<>(); | |||||
| List<Long> versionList = new ArrayList<>(); | |||||
| if (kvInfoVO != null) { | |||||
| for (KVDataVO kvDataVO : kvInfoVO.getData()) { | |||||
| for (Long version : kvDataVO.getVersion()) { | |||||
| keyList.add(kvDataVO.getKey()); | |||||
| versionList.add(version); | |||||
| } | |||||
| } | |||||
| } | |||||
| String[] keys = keyList.toArray(new String[keyList.size()]); | |||||
| Long[] versions = versionList.toArray(new Long[versionList.size()]); | |||||
| if (keys == null || keys.length == 0) { | |||||
| return null; | |||||
| } | |||||
| if (versions == null || versions.length == 0) { | |||||
| return null; | |||||
| } | |||||
| if (keys.length != versions.length) { | |||||
| throw new ContractException("keys.length!=versions.length!"); | |||||
| } | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| KVDataEntry[] entries = new KVDataEntry[keys.length]; | |||||
| long ver = -1; | |||||
| for (int i = 0; i < entries.length; i++) { | |||||
| // ver = dataAccount.getDataVersion(Bytes.fromString(keys[i])); | |||||
| // dataAccount.getBytes(Bytes.fromString(keys[i]),1); | |||||
| ver = versions[i]; | |||||
| if (ver < 0) { | |||||
| entries[i] = new KVDataObject(keys[i], -1, null); | |||||
| } else { | |||||
| if (dataAccount.getDataEntriesTotalCount() == 0 | |||||
| || dataAccount.getBytes(Bytes.fromString(keys[i]), ver) == null) { | |||||
| // is the address is not exist; the result is null; | |||||
| entries[i] = new KVDataObject(keys[i], -1, null); | |||||
| } else { | |||||
| BytesValue value = dataAccount.getBytes(Bytes.fromString(keys[i]), ver); | |||||
| entries[i] = new KVDataObject(keys[i], ver, value); | |||||
| } | |||||
| } | |||||
| } | |||||
| return entries; | |||||
| } | |||||
| @Override | |||||
| public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| return dataAccount.getDataEntries(fromIndex, count); | |||||
| } | |||||
| @Override | |||||
| public long getDataEntriesTotalCount(HashDigest ledgerHash, String address) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| return dataAccount.getDataEntriesTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public ContractInfo getContract(HashDigest ledgerHash, String address) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| return contractAccountSet.getContract(Bytes.fromBase58(address)); | |||||
| } | |||||
| @Override | |||||
| public AccountHeader[] getUsers(HashDigest ledgerHash, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) userAccountSet.getTotalCount()); | |||||
| return userAccountSet.getAccounts(pages[0], pages[1]); | |||||
| } | |||||
| @Override | |||||
| public AccountHeader[] getDataAccounts(HashDigest ledgerHash, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) dataAccountSet.getTotalCount()); | |||||
| return dataAccountSet.getAccounts(pages[0], pages[1]); | |||||
| } | |||||
| @Override | |||||
| public AccountHeader[] getContractAccounts(HashDigest ledgerHash, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) contractAccountSet.getTotalCount()); | |||||
| return contractAccountSet.getAccounts(pages[0], pages[1]); | |||||
| } | |||||
| private LedgerAdministration ledgerAdministration(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| LedgerAdministration administration = ledger.getAdminAccount(block); | |||||
| return administration; | |||||
| } | |||||
| } | |||||
| package com.jd.blockchain.ledger.core.impl; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| import com.jd.blockchain.contract.ContractException; | |||||
| import com.jd.blockchain.crypto.HashDigest; | |||||
| import com.jd.blockchain.ledger.*; | |||||
| import com.jd.blockchain.ledger.core.ContractAccountSet; | |||||
| import com.jd.blockchain.ledger.core.DataAccount; | |||||
| import com.jd.blockchain.ledger.core.DataAccountSet; | |||||
| import com.jd.blockchain.ledger.core.LedgerAdministration; | |||||
| import com.jd.blockchain.ledger.core.LedgerRepository; | |||||
| import com.jd.blockchain.ledger.core.LedgerService; | |||||
| import com.jd.blockchain.ledger.core.TransactionSet; | |||||
| import com.jd.blockchain.ledger.core.UserAccountSet; | |||||
| import com.jd.blockchain.transaction.BlockchainQueryService; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| import com.jd.blockchain.utils.QueryUtil; | |||||
| public class LedgerQueryService implements BlockchainQueryService { | |||||
| private LedgerService ledgerService; | |||||
| public LedgerQueryService(LedgerService ledgerService) { | |||||
| this.ledgerService = ledgerService; | |||||
| } | |||||
| @Override | |||||
| public HashDigest[] getLedgerHashs() { | |||||
| return ledgerService.getLedgerHashs(); | |||||
| } | |||||
| @Override | |||||
| public LedgerInfo getLedger(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerInfo ledgerInfo = new LedgerInfo(); | |||||
| ledgerInfo.setHash(ledger.getHash()); | |||||
| ledgerInfo.setLatestBlockHash(ledger.getLatestBlockHash()); | |||||
| ledgerInfo.setLatestBlockHeight(ledger.getLatestBlockHeight()); | |||||
| return ledgerInfo; | |||||
| } | |||||
| @Override | |||||
| public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { | |||||
| return ledgerAdministration(ledgerHash).getParticipants(); | |||||
| } | |||||
| @Override | |||||
| public LedgerMetadata getLedgerMetadata(HashDigest ledgerHash) { | |||||
| return ledgerAdministration(ledgerHash).getMetadata(); | |||||
| } | |||||
| @Override | |||||
| public LedgerBlock getBlock(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| return ledger.getBlock(height); | |||||
| } | |||||
| @Override | |||||
| public LedgerBlock getBlock(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| return ledger.getBlock(blockHash); | |||||
| } | |||||
| @Override | |||||
| public long getTransactionCount(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(height); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getTransactionCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(blockHash); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getTransactionTotalCount(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getDataAccountCount(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(height); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| return dataAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getDataAccountCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(blockHash); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| return dataAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getDataAccountTotalCount(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| return dataAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getUserCount(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(height); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| return userAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getUserCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(blockHash); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| return userAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getUserTotalCount(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| return userAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getContractCount(HashDigest ledgerHash, long height) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(height); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| return contractAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getContractCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getBlock(blockHash); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| return contractAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public long getContractTotalCount(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| return contractAccountSet.getTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public LedgerTransaction[] getTransactions(HashDigest ledgerHash, long height, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock ledgerBlock = ledger.getBlock(height); | |||||
| TransactionSet transactionSet = ledger.getTransactionSet(ledgerBlock); | |||||
| int lastHeightTxTotalNums = 0; | |||||
| if (height > 0) { | |||||
| lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height - 1)).getTotalCount(); | |||||
| } | |||||
| int currentHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height)).getTotalCount(); | |||||
| // 取当前高度的增量交易数,在增量交易里进行查找 | |||||
| int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; | |||||
| // | |||||
| // if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { | |||||
| // fromIndex = 0; | |||||
| // } | |||||
| // if (count == -1) { | |||||
| // fromIndex = 0; | |||||
| // count = currentHeightTxNums; | |||||
| // } | |||||
| // if (count > currentHeightTxNums) { | |||||
| // count = currentHeightTxNums - fromIndex; | |||||
| // } | |||||
| int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex, count, currentHeightTxNums); | |||||
| return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0], indexAndCount[1]); | |||||
| } | |||||
| @Override | |||||
| public LedgerTransaction[] getTransactions(HashDigest ledgerHash, HashDigest blockHash, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock ledgerBlock = ledger.getBlock(blockHash); | |||||
| long height = ledgerBlock.getHeight(); | |||||
| TransactionSet transactionSet = ledger.getTransactionSet(ledgerBlock); | |||||
| int lastHeightTxTotalNums = 0; | |||||
| if (height > 0) { | |||||
| lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height - 1)).getTotalCount(); | |||||
| } | |||||
| int currentHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height)).getTotalCount(); | |||||
| // 取当前块hash的增量交易数,在增量交易里进行查找 | |||||
| int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; | |||||
| // if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { | |||||
| // fromIndex = 0; | |||||
| // } | |||||
| // if (count == -1) { | |||||
| // fromIndex = 0; | |||||
| // count = currentHeightTxNums; | |||||
| // } | |||||
| // if (count > currentHeightTxNums) { | |||||
| // count = currentHeightTxNums - fromIndex; | |||||
| // } | |||||
| int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex, count, currentHeightTxNums); | |||||
| return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0], indexAndCount[1]); | |||||
| } | |||||
| @Override | |||||
| public LedgerTransaction getTransactionByContentHash(HashDigest ledgerHash, HashDigest contentHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.get(contentHash); | |||||
| } | |||||
| @Override | |||||
| public TransactionState getTransactionStateByContentHash(HashDigest ledgerHash, HashDigest contentHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| TransactionSet txset = ledger.getTransactionSet(block); | |||||
| return txset.getTxState(contentHash); | |||||
| } | |||||
| @Override | |||||
| public UserInfo getUser(HashDigest ledgerHash, String address) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| return userAccountSet.getUser(address); | |||||
| } | |||||
| @Override | |||||
| public AccountHeader getDataAccount(HashDigest ledgerHash, String address) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| return dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| } | |||||
| @Override | |||||
| public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||||
| if (keys == null || keys.length == 0) { | |||||
| return null; | |||||
| } | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| KVDataEntry[] entries = new KVDataEntry[keys.length]; | |||||
| long ver; | |||||
| for (int i = 0; i < entries.length; i++) { | |||||
| final String currKey = keys[i]; | |||||
| ver = dataAccount.getDataVersion(Bytes.fromString(currKey)); | |||||
| if (ver < 0) { | |||||
| entries[i] = new KVDataObject(currKey, -1, null); | |||||
| } else { | |||||
| BytesValue value = dataAccount.getBytes(Bytes.fromString(currKey), ver); | |||||
| entries[i] = new KVDataObject(currKey, ver, value); | |||||
| } | |||||
| } | |||||
| return entries; | |||||
| } | |||||
| public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||||
| // parse kvInfoVO; | |||||
| List<String> keyList = new ArrayList<>(); | |||||
| List<Long> versionList = new ArrayList<>(); | |||||
| if (kvInfoVO != null) { | |||||
| for (KVDataVO kvDataVO : kvInfoVO.getData()) { | |||||
| for (Long version : kvDataVO.getVersion()) { | |||||
| keyList.add(kvDataVO.getKey()); | |||||
| versionList.add(version); | |||||
| } | |||||
| } | |||||
| } | |||||
| String[] keys = keyList.toArray(new String[keyList.size()]); | |||||
| Long[] versions = versionList.toArray(new Long[versionList.size()]); | |||||
| if (keys == null || keys.length == 0) { | |||||
| return null; | |||||
| } | |||||
| if (versions == null || versions.length == 0) { | |||||
| return null; | |||||
| } | |||||
| if (keys.length != versions.length) { | |||||
| throw new ContractException("keys.length!=versions.length!"); | |||||
| } | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| KVDataEntry[] entries = new KVDataEntry[keys.length]; | |||||
| long ver = -1; | |||||
| for (int i = 0; i < entries.length; i++) { | |||||
| // ver = dataAccount.getDataVersion(Bytes.fromString(keys[i])); | |||||
| // dataAccount.getBytes(Bytes.fromString(keys[i]),1); | |||||
| ver = versions[i]; | |||||
| if (ver < 0) { | |||||
| entries[i] = new KVDataObject(keys[i], -1, null); | |||||
| } else { | |||||
| if (dataAccount.getDataEntriesTotalCount() == 0 | |||||
| || dataAccount.getBytes(Bytes.fromString(keys[i]), ver) == null) { | |||||
| // is the address is not exist; the result is null; | |||||
| entries[i] = new KVDataObject(keys[i], -1, null); | |||||
| } else { | |||||
| BytesValue value = dataAccount.getBytes(Bytes.fromString(keys[i]), ver); | |||||
| entries[i] = new KVDataObject(keys[i], ver, value); | |||||
| } | |||||
| } | |||||
| } | |||||
| return entries; | |||||
| } | |||||
| @Override | |||||
| public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) dataAccount.getDataEntriesTotalCount()); | |||||
| return dataAccount.getDataEntries(pages[0], pages[1]); | |||||
| } | |||||
| @Override | |||||
| public long getDataEntriesTotalCount(HashDigest ledgerHash, String address) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); | |||||
| return dataAccount.getDataEntriesTotalCount(); | |||||
| } | |||||
| @Override | |||||
| public ContractInfo getContract(HashDigest ledgerHash, String address) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| return contractAccountSet.getContract(Bytes.fromBase58(address)); | |||||
| } | |||||
| @Override | |||||
| public AccountHeader[] getUsers(HashDigest ledgerHash, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| UserAccountSet userAccountSet = ledger.getUserAccountSet(block); | |||||
| int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) userAccountSet.getTotalCount()); | |||||
| return userAccountSet.getAccounts(pages[0], pages[1]); | |||||
| } | |||||
| @Override | |||||
| public AccountHeader[] getDataAccounts(HashDigest ledgerHash, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); | |||||
| int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) dataAccountSet.getTotalCount()); | |||||
| return dataAccountSet.getAccounts(pages[0], pages[1]); | |||||
| } | |||||
| @Override | |||||
| public AccountHeader[] getContractAccounts(HashDigest ledgerHash, int fromIndex, int count) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||||
| int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) contractAccountSet.getTotalCount()); | |||||
| return contractAccountSet.getAccounts(pages[0], pages[1]); | |||||
| } | |||||
| private LedgerAdministration ledgerAdministration(HashDigest ledgerHash) { | |||||
| LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||||
| LedgerBlock block = ledger.getLatestBlock(); | |||||
| LedgerAdministration administration = ledger.getAdminAccount(block); | |||||
| return administration; | |||||
| } | |||||
| } | |||||
| @@ -15,6 +15,7 @@ import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
| import com.jd.blockchain.ledger.core.LedgerEditor; | import com.jd.blockchain.ledger.core.LedgerEditor; | ||||
| import com.jd.blockchain.ledger.core.LedgerRepository; | import com.jd.blockchain.ledger.core.LedgerRepository; | ||||
| import com.jd.blockchain.ledger.core.LedgerTransactionContext; | import com.jd.blockchain.ledger.core.LedgerTransactionContext; | ||||
| import com.jd.blockchain.ledger.core.SettingContext; | |||||
| import com.jd.blockchain.ledger.core.TransactionSet; | import com.jd.blockchain.ledger.core.TransactionSet; | ||||
| import com.jd.blockchain.ledger.core.UserAccountSet; | import com.jd.blockchain.ledger.core.UserAccountSet; | ||||
| import com.jd.blockchain.storage.service.ExPolicyKVStorage; | import com.jd.blockchain.storage.service.ExPolicyKVStorage; | ||||
| @@ -77,7 +78,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
| this.ledgerIndexKey = encodeLedgerIndexKey(ledgerHash); | this.ledgerIndexKey = encodeLedgerIndexKey(ledgerHash); | ||||
| if (getLatestBlockHeight() < 0) { | if (getLatestBlockHeight() < 0) { | ||||
| throw new LedgerException("Ledger doesn't exist!"); | |||||
| throw new RuntimeException("Ledger doesn't exist!"); | |||||
| } | } | ||||
| } | } | ||||
| @@ -205,15 +206,14 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
| LedgerBlockData block = new LedgerBlockData(deserialize(blockBytes)); | LedgerBlockData block = new LedgerBlockData(deserialize(blockBytes)); | ||||
| if (!blockHash.equals(block.getHash())) { | if (!blockHash.equals(block.getHash())) { | ||||
| throw new LedgerException("Block hash not equals to it's storage key!"); | |||||
| throw new RuntimeException("Block hash not equals to it's storage key!"); | |||||
| } | } | ||||
| // verify hash; | // verify hash; | ||||
| // boolean requiredVerifyHash = | // boolean requiredVerifyHash = | ||||
| // adminAccount.getMetadata().getSetting().getCryptoSetting().getAutoVerifyHash(); | // adminAccount.getMetadata().getSetting().getCryptoSetting().getAutoVerifyHash(); | ||||
| // TODO: 未实现从配置中加载是否校验 Hash 的设置; | // TODO: 未实现从配置中加载是否校验 Hash 的设置; | ||||
| boolean requiredVerifyHash = false; | |||||
| if (requiredVerifyHash) { | |||||
| if (SettingContext.queryingSettings().verifyHash()) { | |||||
| byte[] blockBodyBytes = null; | byte[] blockBodyBytes = null; | ||||
| if (block.getHeight() == 0) { | if (block.getHeight() == 0) { | ||||
| // 计算创世区块的 hash 时,不包括 ledgerHash 字段; | // 计算创世区块的 hash 时,不包括 ledgerHash 字段; | ||||
| @@ -227,14 +227,14 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
| HashFunction hashFunc = Crypto.getHashFunction(blockHash.getAlgorithm()); | HashFunction hashFunc = Crypto.getHashFunction(blockHash.getAlgorithm()); | ||||
| boolean pass = hashFunc.verify(blockHash, blockBodyBytes); | boolean pass = hashFunc.verify(blockHash, blockBodyBytes); | ||||
| if (!pass) { | if (!pass) { | ||||
| throw new LedgerException("Block hash verification fail!"); | |||||
| throw new RuntimeException("Block hash verification fail!"); | |||||
| } | } | ||||
| } | } | ||||
| // verify height; | // verify height; | ||||
| HashDigest indexedHash = getBlockHash(block.getHeight()); | HashDigest indexedHash = getBlockHash(block.getHeight()); | ||||
| if (indexedHash == null || !indexedHash.equals(blockHash)) { | if (indexedHash == null || !indexedHash.equals(blockHash)) { | ||||
| throw new LedgerException( | |||||
| throw new RuntimeException( | |||||
| "Illegal ledger state in storage that ledger height index doesn't match it's block data in height[" | "Illegal ledger state in storage that ledger height index doesn't match it's block data in height[" | ||||
| + block.getHeight() + "] and block hash[" + Base58Utils.encode(blockHash.toBytes()) | + block.getHeight() + "] and block hash[" + Base58Utils.encode(blockHash.toBytes()) | ||||
| + "] !"); | + "] !"); | ||||
| @@ -394,15 +394,15 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
| @Override | @Override | ||||
| public synchronized LedgerEditor createNextBlock() { | public synchronized LedgerEditor createNextBlock() { | ||||
| if (closed) { | if (closed) { | ||||
| throw new LedgerException("Ledger repository has been closed!"); | |||||
| throw new RuntimeException("Ledger repository has been closed!"); | |||||
| } | } | ||||
| if (this.nextBlockEditor != null) { | if (this.nextBlockEditor != null) { | ||||
| throw new LedgerException( | |||||
| throw new RuntimeException( | |||||
| "A new block is in process, cann't create another one until it finish by committing or canceling."); | "A new block is in process, cann't create another one until it finish by committing or canceling."); | ||||
| } | } | ||||
| LedgerBlock previousBlock = getLatestBlock(); | LedgerBlock previousBlock = getLatestBlock(); | ||||
| LedgerTransactionalEditor editor = LedgerTransactionalEditor.createEditor( | |||||
| getAdminInfo().getMetadata().getSetting(), previousBlock, keyPrefix, exPolicyStorage, | |||||
| LedgerTransactionalEditor editor = LedgerTransactionalEditor.createEditor(previousBlock, | |||||
| getAdminInfo().getMetadata().getSetting(), keyPrefix, exPolicyStorage, | |||||
| versioningStorage); | versioningStorage); | ||||
| NewBlockCommittingMonitor committingMonitor = new NewBlockCommittingMonitor(editor, this); | NewBlockCommittingMonitor committingMonitor = new NewBlockCommittingMonitor(editor, this); | ||||
| this.nextBlockEditor = committingMonitor; | this.nextBlockEditor = committingMonitor; | ||||
| @@ -420,7 +420,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
| return; | return; | ||||
| } | } | ||||
| if (this.nextBlockEditor != null) { | if (this.nextBlockEditor != null) { | ||||
| throw new LedgerException("A new block is in process, cann't close the ledger repository!"); | |||||
| throw new RuntimeException("A new block is in process, cann't close the ledger repository!"); | |||||
| } | } | ||||
| closed = true; | closed = true; | ||||
| } | } | ||||
| @@ -479,7 +479,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
| } | } | ||||
| static TransactionSet newTransactionSet(LedgerSetting ledgerSetting, String keyPrefix, | static TransactionSet newTransactionSet(LedgerSetting ledgerSetting, String keyPrefix, | ||||
| ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { | |||||
| ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { | |||||
| // TransactionSet transactionSet = new | // TransactionSet transactionSet = new | ||||
| // TransactionSet(ledgerSetting.getCryptoSetting(), | // TransactionSet(ledgerSetting.getCryptoSetting(), | ||||
| // PrefixAppender.prefix(TRANSACTION_SET_PREFIX, ledgerExStorage), | // PrefixAppender.prefix(TRANSACTION_SET_PREFIX, ledgerExStorage), | ||||
| @@ -576,6 +576,16 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
| this.ledgerRepo = ledgerRepo; | this.ledgerRepo = ledgerRepo; | ||||
| } | } | ||||
| @Override | |||||
| public HashDigest getLedgerHash() { | |||||
| return editor.getLedgerHash(); | |||||
| } | |||||
| @Override | |||||
| public long getBlockHeight() { | |||||
| return editor.getBlockHeight(); | |||||
| } | |||||
| @Override | @Override | ||||
| public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { | public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { | ||||
| return editor.newTransaction(txRequest); | return editor.newTransaction(txRequest); | ||||
| @@ -590,7 +600,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
| public void commit() { | public void commit() { | ||||
| try { | try { | ||||
| editor.commit(); | editor.commit(); | ||||
| LedgerBlock latestBlock = editor.getNewlyBlock(); | |||||
| LedgerBlock latestBlock = editor.getCurrentBlock(); | |||||
| ledgerRepo.latestState = new LedgerState(latestBlock); | ledgerRepo.latestState = new LedgerState(latestBlock); | ||||
| } finally { | } finally { | ||||
| ledgerRepo.nextBlockEditor = null; | ledgerRepo.nextBlockEditor = null; | ||||
| @@ -1,8 +1,5 @@ | |||||
| package com.jd.blockchain.ledger.core.impl; | package com.jd.blockchain.ledger.core.impl; | ||||
| import java.util.Arrays; | |||||
| import java.util.Comparator; | |||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.ledger.DigitalSignature; | import com.jd.blockchain.ledger.DigitalSignature; | ||||
| import com.jd.blockchain.ledger.LedgerTransaction; | import com.jd.blockchain.ledger.LedgerTransaction; | ||||
| @@ -29,48 +26,27 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
| private OperationResult[] operationResults; | private OperationResult[] operationResults; | ||||
| // private HashDigest adminAccountHash; | |||||
| // | |||||
| // private HashDigest userAccountSetHash; | |||||
| // | |||||
| // private HashDigest dataAccountSetHash; | |||||
| // | |||||
| // private HashDigest contractAccountSetHash; | |||||
| /** | /** | ||||
| * Declare a private no-arguments constructor for deserializing purpose; | * Declare a private no-arguments constructor for deserializing purpose; | ||||
| */ | */ | ||||
| @SuppressWarnings("unused") | |||||
| private LedgerTransactionData() { | private LedgerTransactionData() { | ||||
| // this.txSnapshot = new TransactionStagedSnapshot(); | |||||
| } | } | ||||
| /** | /** | ||||
| * @param blockHeight | |||||
| * 区块链高度; | |||||
| * @param txReq | |||||
| * 交易请求; | |||||
| * @param execState | |||||
| * 执行状态; | |||||
| * @param txSnapshot | |||||
| * 交易级的系统快照; | |||||
| * @param blockHeight 区块链高度; | |||||
| * @param txReq 交易请求; | |||||
| * @param execState 执行状态; | |||||
| * @param txSnapshot 交易级的系统快照; | |||||
| */ | */ | ||||
| public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, | public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, | ||||
| TransactionStagedSnapshot txSnapshot, OperationResult... opResults) { | TransactionStagedSnapshot txSnapshot, OperationResult... opResults) { | ||||
| this.blockHeight = blockHeight; | this.blockHeight = blockHeight; | ||||
| // this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; | |||||
| this.txSnapshot = txSnapshot; | this.txSnapshot = txSnapshot; | ||||
| this.transactionContent = txReq.getTransactionContent(); | this.transactionContent = txReq.getTransactionContent(); | ||||
| this.endpointSignatures = txReq.getEndpointSignatures(); | this.endpointSignatures = txReq.getEndpointSignatures(); | ||||
| this.nodeSignatures = txReq.getNodeSignatures(); | this.nodeSignatures = txReq.getNodeSignatures(); | ||||
| this.executionState = execState; | this.executionState = execState; | ||||
| if (opResults != null) { | |||||
| Arrays.sort(opResults, new Comparator<OperationResult>() { | |||||
| @Override | |||||
| public int compare(OperationResult o1, OperationResult o2) { | |||||
| return o1.getIndex() - o2.getIndex(); | |||||
| } | |||||
| }); | |||||
| } | |||||
| this.operationResults = opResults; | this.operationResults = opResults; | ||||
| } | } | ||||
| @@ -116,17 +92,17 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
| @Override | @Override | ||||
| public HashDigest getUserAccountSetHash() { | public HashDigest getUserAccountSetHash() { | ||||
| return txSnapshot == null ? null :txSnapshot.getUserAccountSetHash(); | |||||
| return txSnapshot == null ? null : txSnapshot.getUserAccountSetHash(); | |||||
| } | } | ||||
| @Override | @Override | ||||
| public HashDigest getDataAccountSetHash() { | public HashDigest getDataAccountSetHash() { | ||||
| return txSnapshot == null ? null :txSnapshot.getDataAccountSetHash(); | |||||
| return txSnapshot == null ? null : txSnapshot.getDataAccountSetHash(); | |||||
| } | } | ||||
| @Override | @Override | ||||
| public HashDigest getContractAccountSetHash() { | public HashDigest getContractAccountSetHash() { | ||||
| return txSnapshot == null ? null :txSnapshot.getContractAccountSetHash(); | |||||
| return txSnapshot == null ? null : txSnapshot.getContractAccountSetHash(); | |||||
| } | } | ||||
| public void setTxSnapshot(TransactionStagedSnapshot txSnapshot) { | public void setTxSnapshot(TransactionStagedSnapshot txSnapshot) { | ||||
| @@ -140,20 +116,22 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
| this.transactionContent = content; | this.transactionContent = content; | ||||
| } | } | ||||
| public void setEndpointSignatures(Object[] participantSignatures) { | |||||
| int length = participantSignatures.length; | |||||
| this.endpointSignatures = new DigitalSignature[length]; | |||||
| for (int i = 0; i < length; i++) { | |||||
| this.endpointSignatures[i] = (DigitalSignature) participantSignatures[i]; | |||||
| } | |||||
| public void setEndpointSignatures(DigitalSignature[] participantSignatures) { | |||||
| this.endpointSignatures = participantSignatures; | |||||
| // int length = participantSignatures.length; | |||||
| // this.endpointSignatures = new DigitalSignature[length]; | |||||
| // for (int i = 0; i < length; i++) { | |||||
| // this.endpointSignatures[i] = (DigitalSignature) participantSignatures[i]; | |||||
| // } | |||||
| } | } | ||||
| public void setNodeSignatures(Object[] nodeSignatures) { | |||||
| int length = nodeSignatures.length; | |||||
| this.nodeSignatures = new DigitalSignature[length]; | |||||
| for (int i = 0; i < length; i++) { | |||||
| this.nodeSignatures[i] = (DigitalSignature) nodeSignatures[i]; | |||||
| } | |||||
| public void setNodeSignatures(DigitalSignature[] nodeSignatures) { | |||||
| this.nodeSignatures = nodeSignatures; | |||||
| // int length = nodeSignatures.length; | |||||
| // this.nodeSignatures = new DigitalSignature[length]; | |||||
| // for (int i = 0; i < length; i++) { | |||||
| // this.nodeSignatures[i] = (DigitalSignature) nodeSignatures[i]; | |||||
| // } | |||||
| } | } | ||||
| public void setExecutionState(TransactionState executionState) { | public void setExecutionState(TransactionState executionState) { | ||||
| @@ -1,19 +1,35 @@ | |||||
| package com.jd.blockchain.ledger.core.impl; | package com.jd.blockchain.ledger.core.impl; | ||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.Stack; | |||||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | import com.jd.blockchain.binaryproto.BinaryProtocol; | ||||
| import com.jd.blockchain.crypto.Crypto; | import com.jd.blockchain.crypto.Crypto; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.ledger.*; | |||||
| import com.jd.blockchain.ledger.BlockBody; | |||||
| import com.jd.blockchain.ledger.BlockRollbackException; | |||||
| import com.jd.blockchain.ledger.CryptoSetting; | |||||
| import com.jd.blockchain.ledger.DigitalSignature; | |||||
| import com.jd.blockchain.ledger.IllegalTransactionException; | |||||
| import com.jd.blockchain.ledger.LedgerBlock; | |||||
| import com.jd.blockchain.ledger.LedgerDataSnapshot; | |||||
| import com.jd.blockchain.ledger.LedgerInitSetting; | |||||
| import com.jd.blockchain.ledger.LedgerSetting; | |||||
| import com.jd.blockchain.ledger.LedgerTransaction; | |||||
| import com.jd.blockchain.ledger.OperationResult; | |||||
| import com.jd.blockchain.ledger.TransactionContent; | |||||
| import com.jd.blockchain.ledger.TransactionRequest; | |||||
| import com.jd.blockchain.ledger.TransactionRollbackException; | |||||
| import com.jd.blockchain.ledger.TransactionState; | |||||
| import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
| import com.jd.blockchain.ledger.core.LedgerEditor; | import com.jd.blockchain.ledger.core.LedgerEditor; | ||||
| import com.jd.blockchain.ledger.core.LedgerTransactionContext; | import com.jd.blockchain.ledger.core.LedgerTransactionContext; | ||||
| import com.jd.blockchain.ledger.core.SettingContext; | |||||
| import com.jd.blockchain.ledger.core.TransactionSet; | import com.jd.blockchain.ledger.core.TransactionSet; | ||||
| import com.jd.blockchain.storage.service.ExPolicyKVStorage; | import com.jd.blockchain.storage.service.ExPolicyKVStorage; | ||||
| import com.jd.blockchain.storage.service.VersioningKVStorage; | import com.jd.blockchain.storage.service.VersioningKVStorage; | ||||
| import com.jd.blockchain.storage.service.utils.BufferedKVStorage; | import com.jd.blockchain.storage.service.utils.BufferedKVStorage; | ||||
| import com.jd.blockchain.transaction.TxBuilder; | |||||
| import com.jd.blockchain.transaction.TxRequestBuilder; | |||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| import com.jd.blockchain.utils.codec.Base58Utils; | import com.jd.blockchain.utils.codec.Base58Utils; | ||||
| @@ -26,13 +42,18 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| System.out.println("------ [[ parallel-dbwrite=" + PARALLEL_DB_WRITE + " ]] ------"); | System.out.println("------ [[ parallel-dbwrite=" + PARALLEL_DB_WRITE + " ]] ------"); | ||||
| } | } | ||||
| /** | |||||
| * 账本Hash,创世区块的编辑器则返回 null; | |||||
| */ | |||||
| private HashDigest ledgerHash; | |||||
| private final String ledgerKeyPrefix; | private final String ledgerKeyPrefix; | ||||
| private CryptoSetting cryptoSetting; | private CryptoSetting cryptoSetting; | ||||
| private LedgerBlockData newlyBlock; | |||||
| private LedgerBlockData currentBlock; | |||||
| private Stack<StagedSnapshot> stagedSnapshots = new Stack<>(); | |||||
| // private Stack<StagedSnapshot> stagedSnapshots = new Stack<>(); | |||||
| private boolean prepared = false; | private boolean prepared = false; | ||||
| @@ -40,29 +61,70 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| private boolean committed = false; | private boolean committed = false; | ||||
| private BufferedKVStorage bufferedStorage; | |||||
| private StagedSnapshot startingPoint; | |||||
| /** | |||||
| * 当前区块的存储; | |||||
| */ | |||||
| private BufferedKVStorage baseStorage; | |||||
| /** | /** | ||||
| * 最近一个交易上下文; | |||||
| * 上一个交易的上下文; | |||||
| */ | */ | ||||
| private LedgerDataContext lastTxCtx; | |||||
| // private LedgerTransactionContextImpl previousTxCtx; | |||||
| private LedgerDataContext newTxCtx; | |||||
| private TxSnapshot previousTxSnapshot; | |||||
| private LedgerTransactionalEditor(CryptoSetting cryptoSetting, LedgerBlockData newlyBlock, | |||||
| /** | |||||
| * 当前交易的上下文; | |||||
| */ | |||||
| private volatile LedgerTransactionContextImpl currentTxCtx; | |||||
| /** | |||||
| * @param ledgerHash | |||||
| * @param cryptoSetting | |||||
| * @param currentBlock | |||||
| * @param startingPoint | |||||
| * @param ledgerKeyPrefix | |||||
| * @param bufferedStorage | |||||
| * @param verifyTx 是否校验交易请求;当外部调用者在调用前已经实施了验证时,将次参数设置为 false 能够提升性能; | |||||
| */ | |||||
| private LedgerTransactionalEditor(HashDigest ledgerHash, CryptoSetting cryptoSetting, LedgerBlockData currentBlock, | |||||
| StagedSnapshot startingPoint, String ledgerKeyPrefix, BufferedKVStorage bufferedStorage) { | StagedSnapshot startingPoint, String ledgerKeyPrefix, BufferedKVStorage bufferedStorage) { | ||||
| this.ledgerHash = ledgerHash; | |||||
| this.ledgerKeyPrefix = ledgerKeyPrefix; | this.ledgerKeyPrefix = ledgerKeyPrefix; | ||||
| this.cryptoSetting = cryptoSetting; | this.cryptoSetting = cryptoSetting; | ||||
| this.newlyBlock = newlyBlock; | |||||
| this.bufferedStorage = bufferedStorage; | |||||
| this.currentBlock = currentBlock; | |||||
| this.baseStorage = bufferedStorage; | |||||
| this.startingPoint = startingPoint; | |||||
| this.stagedSnapshots.push(startingPoint); | |||||
| // this.stagedSnapshots.push(startingPoint); | |||||
| } | } | ||||
| public static LedgerTransactionalEditor createEditor(LedgerSetting ledgerSetting, LedgerBlock previousBlock, | |||||
| /** | |||||
| * 创建账本新区块的编辑器; | |||||
| * | |||||
| * @param ledgerHash 账本哈希; | |||||
| * @param ledgerSetting 账本设置; | |||||
| * @param previousBlock 前置区块; | |||||
| * @param ledgerKeyPrefix 账本数据前缀; | |||||
| * @param ledgerExStorage 账本数据存储; | |||||
| * @param ledgerVerStorage 账本数据版本化存储; | |||||
| * @param verifyTx 是否校验交易请求;当外部调用者在调用前已经实施了验证时,将次参数设置为 false 能够提升性能; | |||||
| * @return | |||||
| */ | |||||
| public static LedgerTransactionalEditor createEditor(LedgerBlock previousBlock, LedgerSetting ledgerSetting, | |||||
| String ledgerKeyPrefix, ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { | String ledgerKeyPrefix, ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { | ||||
| // new block; | // new block; | ||||
| LedgerBlockData currBlock = new LedgerBlockData(previousBlock.getHeight() + 1, previousBlock.getLedgerHash(), | |||||
| HashDigest ledgerHash = previousBlock.getLedgerHash(); | |||||
| if (ledgerHash == null) { | |||||
| ledgerHash = previousBlock.getHash(); | |||||
| } | |||||
| if (ledgerHash == null) { | |||||
| throw new IllegalArgumentException("Illegal previous block was specified!"); | |||||
| } | |||||
| LedgerBlockData currBlock = new LedgerBlockData(previousBlock.getHeight() + 1, ledgerHash, | |||||
| previousBlock.getHash()); | previousBlock.getHash()); | ||||
| // init storage; | // init storage; | ||||
| @@ -71,163 +133,246 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| StagedSnapshot startingPoint = new TxSnapshot(previousBlock, previousBlock.getTransactionSetHash()); | StagedSnapshot startingPoint = new TxSnapshot(previousBlock, previousBlock.getTransactionSetHash()); | ||||
| // instantiate editor; | // instantiate editor; | ||||
| return new LedgerTransactionalEditor(ledgerSetting.getCryptoSetting(), currBlock, startingPoint, | |||||
| return new LedgerTransactionalEditor(ledgerHash, ledgerSetting.getCryptoSetting(), currBlock, startingPoint, | |||||
| ledgerKeyPrefix, txStagedStorage); | ledgerKeyPrefix, txStagedStorage); | ||||
| } | } | ||||
| /** | |||||
| * 创建创世区块的编辑器; | |||||
| * | |||||
| * @param initSetting | |||||
| * @param ledgerKeyPrefix | |||||
| * @param ledgerExStorage | |||||
| * @param ledgerVerStorage | |||||
| * @param verifyTx 是否校验交易请求;当外部调用者在调用前已经实施了验证时,将次参数设置为 false 能够提升性能; | |||||
| * @return | |||||
| */ | |||||
| public static LedgerTransactionalEditor createEditor(LedgerInitSetting initSetting, String ledgerKeyPrefix, | public static LedgerTransactionalEditor createEditor(LedgerInitSetting initSetting, String ledgerKeyPrefix, | ||||
| ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { | ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { | ||||
| LedgerBlockData genesisBlock = new LedgerBlockData(0, null, null); | LedgerBlockData genesisBlock = new LedgerBlockData(0, null, null); | ||||
| StagedSnapshot startingPoint = new GenesisSnapshot(initSetting); | StagedSnapshot startingPoint = new GenesisSnapshot(initSetting); | ||||
| // init storage; | // init storage; | ||||
| BufferedKVStorage txStagedStorage = new BufferedKVStorage(ledgerExStorage, ledgerVerStorage, false); | BufferedKVStorage txStagedStorage = new BufferedKVStorage(ledgerExStorage, ledgerVerStorage, false); | ||||
| return new LedgerTransactionalEditor(initSetting.getCryptoSetting(), genesisBlock, startingPoint, | |||||
| return new LedgerTransactionalEditor(null, initSetting.getCryptoSetting(), genesisBlock, startingPoint, | |||||
| ledgerKeyPrefix, txStagedStorage); | ledgerKeyPrefix, txStagedStorage); | ||||
| } | } | ||||
| private void commitTxSnapshot(TxSnapshot snapshot) { | private void commitTxSnapshot(TxSnapshot snapshot) { | ||||
| lastTxCtx = newTxCtx; | |||||
| newTxCtx = null; | |||||
| stagedSnapshots.push(snapshot); | |||||
| previousTxSnapshot = snapshot; | |||||
| currentTxCtx = null; | |||||
| } | } | ||||
| private void rollbackNewTx() { | |||||
| newTxCtx = null; | |||||
| private void rollbackCurrentTx() { | |||||
| currentTxCtx = null; | |||||
| } | } | ||||
| // public LedgerDataSet getLatestDataSet() { | |||||
| // if (lastTxCtx == null) { | |||||
| // return null; | |||||
| // } | |||||
| // return lastTxCtx.getDataSet(); | |||||
| // } | |||||
| LedgerBlock getCurrentBlock() { | |||||
| return currentBlock; | |||||
| } | |||||
| public LedgerBlock getNewlyBlock() { | |||||
| return newlyBlock; | |||||
| @Override | |||||
| public long getBlockHeight() { | |||||
| return currentBlock.getHeight(); | |||||
| } | } | ||||
| @Override | @Override | ||||
| public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { | |||||
| public HashDigest getLedgerHash() { | |||||
| return ledgerHash; | |||||
| } | |||||
| /** | |||||
| * 检查当前账本是否是指定交易请求的账本; | |||||
| * | |||||
| * @param txRequest | |||||
| * @return | |||||
| */ | |||||
| private boolean isRequestedLedger(TransactionRequest txRequest) { | |||||
| HashDigest reqLedgerHash = txRequest.getTransactionContent().getLedgerHash(); | |||||
| if (ledgerHash == reqLedgerHash) { | |||||
| return true; | |||||
| } | |||||
| if (ledgerHash == null || reqLedgerHash == null) { | |||||
| return false; | |||||
| } | |||||
| return ledgerHash.equals(reqLedgerHash); | |||||
| } | |||||
| private boolean verifyTxContent(TransactionRequest request) { | |||||
| TransactionContent txContent = request.getTransactionContent(); | |||||
| if (!TxBuilder.verifyTxContentHash(txContent, txContent.getHash())) { | |||||
| return false; | |||||
| } | |||||
| DigitalSignature[] endpointSignatures = request.getEndpointSignatures(); | |||||
| if (endpointSignatures != null) { | |||||
| for (DigitalSignature signature : endpointSignatures) { | |||||
| if (!TxRequestBuilder.verifyHashSignature(txContent.getHash(), signature.getDigest(), | |||||
| signature.getPubKey())) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| } | |||||
| DigitalSignature[] nodeSignatures = request.getNodeSignatures(); | |||||
| if (nodeSignatures != null) { | |||||
| for (DigitalSignature signature : nodeSignatures) { | |||||
| if (!TxRequestBuilder.verifyHashSignature(txContent.getHash(), signature.getDigest(), | |||||
| signature.getPubKey())) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| @Override | |||||
| public synchronized LedgerTransactionContext newTransaction(TransactionRequest txRequest) { | |||||
| if (SettingContext.txSettings().verifyLedger() && !isRequestedLedger(txRequest)) { | |||||
| throw new IllegalTransactionException( | |||||
| "Transaction request is dispatched to a wrong ledger! --[TxHash=" | |||||
| + txRequest.getTransactionContent().getHash() + "]!", | |||||
| TransactionState.IGNORED_BY_WRONG_LEDGER); | |||||
| } | |||||
| // TODO: 把验签和创建交易并行化; | |||||
| if (SettingContext.txSettings().verifySignature() && !verifyTxContent(txRequest)) { | |||||
| // 抛弃哈希和签名校验失败的交易请求; | |||||
| throw new IllegalTransactionException( | |||||
| "Wrong transaction signature! --[TxHash=" + txRequest.getTransactionContent().getHash() + "]!", | |||||
| TransactionState.IGNORED_BY_WRONG_CONTENT_SIGNATURE); | |||||
| } | |||||
| if (currentTxCtx != null) { | |||||
| throw new IllegalStateException( | |||||
| "Unable to open another new transaction before the current transaction is completed! --[TxHash=" | |||||
| + txRequest.getTransactionContent().getHash() + "]!"); | |||||
| } | |||||
| // 检查状态是否允许创建新的交易请求;; | |||||
| checkState(); | checkState(); | ||||
| // TODO:验证交易签名; | |||||
| BufferedKVStorage txBuffStorage = null; | |||||
| // init storage of new transaction; | |||||
| BufferedKVStorage txBufferedStorage = new BufferedKVStorage(baseStorage, baseStorage, false); | |||||
| LedgerDataSetImpl txDataset = null; | LedgerDataSetImpl txDataset = null; | ||||
| TransactionSet txset = null; | TransactionSet txset = null; | ||||
| if (lastTxCtx == null) { | |||||
| // init storage of new transaction; | |||||
| // txBuffStorage = new BufferedKVStorage(bufferedStorage, bufferedStorage, | |||||
| // false); | |||||
| txBuffStorage = bufferedStorage; | |||||
| if (previousTxSnapshot == null) { | |||||
| // load the starting point of the new transaction; | // load the starting point of the new transaction; | ||||
| StagedSnapshot previousSnapshot = stagedSnapshots.peek(); | |||||
| if (previousSnapshot instanceof GenesisSnapshot) { | |||||
| if (startingPoint instanceof GenesisSnapshot) { | |||||
| // 准备生成创世区块; | // 准备生成创世区块; | ||||
| GenesisSnapshot snpht = (GenesisSnapshot) previousSnapshot; | |||||
| txDataset = LedgerRepositoryImpl.newDataSet(snpht.initSetting, ledgerKeyPrefix, txBuffStorage, | |||||
| txBuffStorage); | |||||
| GenesisSnapshot snpht = (GenesisSnapshot) startingPoint; | |||||
| txDataset = LedgerRepositoryImpl.newDataSet(snpht.initSetting, ledgerKeyPrefix, txBufferedStorage, | |||||
| txBufferedStorage); | |||||
| txset = LedgerRepositoryImpl.newTransactionSet(txDataset.getAdminAccount().getSetting(), | txset = LedgerRepositoryImpl.newTransactionSet(txDataset.getAdminAccount().getSetting(), | ||||
| ledgerKeyPrefix, txBuffStorage, txBuffStorage); | |||||
| } else { | |||||
| ledgerKeyPrefix, txBufferedStorage, txBufferedStorage); | |||||
| } else if (startingPoint instanceof TxSnapshot) { | |||||
| // 新的区块; | // 新的区块; | ||||
| // TxSnapshot; reload dataset and txset; | // TxSnapshot; reload dataset and txset; | ||||
| TxSnapshot snpht = (TxSnapshot) previousSnapshot; | |||||
| TxSnapshot snpht = (TxSnapshot) startingPoint; | |||||
| // load dataset; | // load dataset; | ||||
| txDataset = LedgerRepositoryImpl.loadDataSet(snpht.dataSnapshot, ledgerKeyPrefix, txBuffStorage, | |||||
| txBuffStorage, false); | |||||
| txDataset = LedgerRepositoryImpl.loadDataSet(snpht.dataSnapshot, ledgerKeyPrefix, txBufferedStorage, | |||||
| txBufferedStorage, false); | |||||
| // load tx set; | |||||
| txset = LedgerRepositoryImpl.loadTransactionSet(snpht.transactionSetHash, this.cryptoSetting, | |||||
| ledgerKeyPrefix, txBuffStorage, txBuffStorage, false); | |||||
| // load txset; | |||||
| txset = LedgerRepositoryImpl.loadTransactionSet(snpht.txsetHash, this.cryptoSetting, ledgerKeyPrefix, | |||||
| txBufferedStorage, txBufferedStorage, false); | |||||
| } else { | |||||
| // Unreachable; | |||||
| throw new IllegalStateException("Unreachable code was accidentally executed!"); | |||||
| } | } | ||||
| lastTxCtx = new LedgerDataContext(txDataset, txset, txBuffStorage); | |||||
| } else { | } else { | ||||
| // Reuse previous object to optimize performance; | // Reuse previous object to optimize performance; | ||||
| txBuffStorage = lastTxCtx.storage; | |||||
| txDataset = lastTxCtx.dataset; | |||||
| txset = lastTxCtx.txset; | |||||
| // load dataset; | |||||
| txDataset = LedgerRepositoryImpl.loadDataSet(previousTxSnapshot.dataSnapshot, ledgerKeyPrefix, | |||||
| txBufferedStorage, txBufferedStorage, false); | |||||
| // load txset; | |||||
| txset = LedgerRepositoryImpl.loadTransactionSet(previousTxSnapshot.txsetHash, this.cryptoSetting, | |||||
| ledgerKeyPrefix, txBufferedStorage, txBufferedStorage, false); | |||||
| } | } | ||||
| // newTxCtx = new LedgerTransactionContextImpl(newlyBlock.getHeight(), | |||||
| // txRequest, txDataset, txset, txBuffStorage, | |||||
| // this); | |||||
| // return newTxCtx; | |||||
| currentTxCtx = new LedgerTransactionContextImpl(txRequest, txDataset, txset, txBufferedStorage, this); | |||||
| return new LedgerTransactionContextImpl(newlyBlock.getHeight(), txRequest, txDataset, txset, txBuffStorage, | |||||
| this); | |||||
| return currentTxCtx; | |||||
| } | } | ||||
| @Override | @Override | ||||
| public LedgerBlock prepare() { | public LedgerBlock prepare() { | ||||
| checkState(); | checkState(); | ||||
| if (newTxCtx != null) { | |||||
| throw new IllegalStateException("There is a opening transaction which isn't committed or rollbacked!"); | |||||
| if (currentTxCtx != null) { | |||||
| // 有进行中的交易尚未提交或回滚; | |||||
| throw new IllegalStateException( | |||||
| "There is an ongoing transaction that has been not committed or rolled back!"); | |||||
| } | } | ||||
| if (lastTxCtx == null) { | |||||
| // Genesis; | |||||
| throw new IllegalStateException("No transaction to prepare!"); | |||||
| if (previousTxSnapshot == null) { | |||||
| // 当前区块没有加入过交易,不允许产生空区块; | |||||
| throw new IllegalStateException( | |||||
| "There is no transaction in the current block, and no empty blocks is allowed!"); | |||||
| } | } | ||||
| // do commit when transaction isolation level is BLOCK; | // do commit when transaction isolation level is BLOCK; | ||||
| lastTxCtx.dataset.commit(); | |||||
| lastTxCtx.txset.commit(); | |||||
| currentBlock.setAdminAccountHash(previousTxSnapshot.getAdminAccountHash()); | |||||
| currentBlock.setUserAccountSetHash(previousTxSnapshot.getUserAccountSetHash()); | |||||
| currentBlock.setDataAccountSetHash(previousTxSnapshot.getDataAccountSetHash()); | |||||
| currentBlock.setContractAccountSetHash(previousTxSnapshot.getContractAccountSetHash()); | |||||
| currentBlock.setTransactionSetHash(previousTxSnapshot.getTransactionSetHash()); | |||||
| newlyBlock.setAdminAccountHash(lastTxCtx.dataset.getAdminAccount().getHash()); | |||||
| newlyBlock.setContractAccountSetHash(lastTxCtx.dataset.getContractAccountSet().getRootHash()); | |||||
| newlyBlock.setDataAccountSetHash(lastTxCtx.dataset.getDataAccountSet().getRootHash()); | |||||
| newlyBlock.setUserAccountSetHash(lastTxCtx.dataset.getUserAccountSet().getRootHash()); | |||||
| newlyBlock.setTransactionSetHash(lastTxCtx.txset.getRootHash()); | |||||
| // TODO: 根据所有交易的时间戳的平均值来生成区块的时间戳; | |||||
| // long timestamp = | |||||
| // currentBlock.setTimestamp(timestamp); | |||||
| // compute block hash; | // compute block hash; | ||||
| byte[] blockBodyBytes = BinaryProtocol.encode(newlyBlock, BlockBody.class); | |||||
| byte[] blockBodyBytes = BinaryProtocol.encode(currentBlock, BlockBody.class); | |||||
| HashDigest blockHash = Crypto.getHashFunction(cryptoSetting.getHashAlgorithm()).hash(blockBodyBytes); | HashDigest blockHash = Crypto.getHashFunction(cryptoSetting.getHashAlgorithm()).hash(blockBodyBytes); | ||||
| newlyBlock.setHash(blockHash); | |||||
| if (newlyBlock.getLedgerHash() == null) { | |||||
| // init GenesisBlock's ledger hash; | |||||
| newlyBlock.setLedgerHash(blockHash); | |||||
| } | |||||
| currentBlock.setHash(blockHash); | |||||
| // if (currentBlock.getLedgerHash() == null) { | |||||
| // // init GenesisBlock's ledger hash; | |||||
| // currentBlock.setLedgerHash(blockHash); | |||||
| // } | |||||
| // persist block bytes; | // persist block bytes; | ||||
| // only one version per block; | // only one version per block; | ||||
| byte[] blockBytes = BinaryProtocol.encode(newlyBlock, LedgerBlock.class); | |||||
| Bytes blockStorageKey = LedgerRepositoryImpl.encodeBlockStorageKey(newlyBlock.getHash()); | |||||
| long v = bufferedStorage.set(blockStorageKey, blockBytes, -1); | |||||
| byte[] blockBytes = BinaryProtocol.encode(currentBlock, LedgerBlock.class); | |||||
| Bytes blockStorageKey = LedgerRepositoryImpl.encodeBlockStorageKey(currentBlock.getHash()); | |||||
| long v = baseStorage.set(blockStorageKey, blockBytes, -1); | |||||
| if (v < 0) { | if (v < 0) { | ||||
| throw new IllegalStateException( | throw new IllegalStateException( | ||||
| "Block already exist! --[BlockHash=" + Base58Utils.encode(newlyBlock.getHash().toBytes()) + "]"); | |||||
| "Block already exist! --[BlockHash=" + Base58Utils.encode(currentBlock.getHash().toBytes()) + "]"); | |||||
| } | } | ||||
| // persist block hash to ledger index; | // persist block hash to ledger index; | ||||
| HashDigest ledgerHash = newlyBlock.getLedgerHash(); | |||||
| HashDigest ledgerHash = currentBlock.getLedgerHash(); | |||||
| if (ledgerHash == null) { | |||||
| ledgerHash = blockHash; | |||||
| } | |||||
| Bytes ledgerIndexKey = LedgerRepositoryImpl.encodeLedgerIndexKey(ledgerHash); | Bytes ledgerIndexKey = LedgerRepositoryImpl.encodeLedgerIndexKey(ledgerHash); | ||||
| long expectedVersion = newlyBlock.getHeight() - 1; | |||||
| v = bufferedStorage.set(ledgerIndexKey, newlyBlock.getHash().toBytes(), expectedVersion); | |||||
| long expectedVersion = currentBlock.getHeight() - 1; | |||||
| v = baseStorage.set(ledgerIndexKey, currentBlock.getHash().toBytes(), expectedVersion); | |||||
| if (v < 0) { | if (v < 0) { | ||||
| throw new IllegalStateException("Index of BlockHash already exist! --[BlockHash=" | |||||
| + Base58Utils.encode(newlyBlock.getHash().toBytes()) + "]"); | |||||
| throw new IllegalStateException( | |||||
| String.format("Index of BlockHash already exist! --[BlockHeight=%s][BlockHash=%s]", | |||||
| currentBlock.getHeight(), currentBlock.getHash())); | |||||
| } | } | ||||
| prepared = true; | prepared = true; | ||||
| return newlyBlock; | |||||
| return currentBlock; | |||||
| } | } | ||||
| @Override | @Override | ||||
| public void commit() { | public void commit() { | ||||
| if (committed) { | if (committed) { | ||||
| throw new IllegalStateException("LedgerEditor had been committed!"); | |||||
| throw new IllegalStateException("The current block has been committed!"); | |||||
| } | } | ||||
| if (canceled) { | if (canceled) { | ||||
| throw new IllegalStateException("LedgerEditor had been canceled!"); | |||||
| throw new IllegalStateException("The current block has been canceled!"); | |||||
| } | } | ||||
| if (!prepared) { | if (!prepared) { | ||||
| // 未就绪; | // 未就绪; | ||||
| throw new IllegalStateException("LedgerEditor has not prepared!"); | |||||
| throw new IllegalStateException("The current block is not ready yet!"); | |||||
| } | } | ||||
| bufferedStorage.flush(); | |||||
| baseStorage.flush(); | |||||
| committed = true; | committed = true; | ||||
| } | } | ||||
| @@ -235,38 +380,47 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| @Override | @Override | ||||
| public void cancel() { | public void cancel() { | ||||
| if (committed) { | if (committed) { | ||||
| throw new IllegalStateException("LedgerEditor had been committed!"); | |||||
| throw new IllegalStateException("The current block has been committed!"); | |||||
| } | } | ||||
| if (canceled) { | if (canceled) { | ||||
| return; | return; | ||||
| } | } | ||||
| canceled = true; | canceled = true; | ||||
| // if (newTxCtx != null) { | |||||
| // newTxCtx.rollback(); | |||||
| // newTxCtx = null; | |||||
| // } | |||||
| bufferedStorage.cancel(); | |||||
| baseStorage.cancel(); | |||||
| } | } | ||||
| private void checkState() { | private void checkState() { | ||||
| if (prepared) { | if (prepared) { | ||||
| throw new IllegalStateException("LedgerEditor has been prepared!"); | |||||
| throw new IllegalStateException("The current block is ready!"); | |||||
| } | } | ||||
| if (committed) { | if (committed) { | ||||
| throw new IllegalStateException("LedgerEditor has been committed!"); | |||||
| throw new IllegalStateException("The current block has been committed!"); | |||||
| } | } | ||||
| if (canceled) { | if (canceled) { | ||||
| throw new IllegalStateException("LedgerEditor has been canceled!"); | |||||
| throw new IllegalStateException("The current block has been canceled!"); | |||||
| } | } | ||||
| } | } | ||||
| // --------------------------- inner type -------------------------- | // --------------------------- inner type -------------------------- | ||||
| /** | |||||
| * 用于暂存交易上下文数据的快照对象; | |||||
| * | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| private static interface StagedSnapshot { | private static interface StagedSnapshot { | ||||
| } | } | ||||
| /** | |||||
| * 创世区块的快照对象; | |||||
| * | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| private static class GenesisSnapshot implements StagedSnapshot { | private static class GenesisSnapshot implements StagedSnapshot { | ||||
| private LedgerInitSetting initSetting; | private LedgerInitSetting initSetting; | ||||
| @@ -276,6 +430,12 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * 交易执行完毕后的快照对象; | |||||
| * | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| private static class TxSnapshot implements StagedSnapshot { | private static class TxSnapshot implements StagedSnapshot { | ||||
| /** | /** | ||||
| @@ -286,58 +446,90 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| /** | /** | ||||
| * 交易集合的快照(根哈希); | * 交易集合的快照(根哈希); | ||||
| */ | */ | ||||
| private HashDigest transactionSetHash; | |||||
| private HashDigest txsetHash; | |||||
| public TxSnapshot(LedgerDataSnapshot dataSnapshot, HashDigest txSetHash) { | |||||
| this.dataSnapshot = dataSnapshot; | |||||
| this.transactionSetHash = txSetHash; | |||||
| public HashDigest getAdminAccountHash() { | |||||
| return dataSnapshot.getAdminAccountHash(); | |||||
| } | } | ||||
| } | |||||
| private static class LedgerDataContext { | |||||
| public HashDigest getUserAccountSetHash() { | |||||
| return dataSnapshot.getUserAccountSetHash(); | |||||
| } | |||||
| protected LedgerDataSetImpl dataset; | |||||
| public HashDigest getDataAccountSetHash() { | |||||
| return dataSnapshot.getDataAccountSetHash(); | |||||
| } | |||||
| protected TransactionSet txset; | |||||
| public HashDigest getContractAccountSetHash() { | |||||
| return dataSnapshot.getContractAccountSetHash(); | |||||
| } | |||||
| protected BufferedKVStorage storage; | |||||
| public HashDigest getTransactionSetHash() { | |||||
| return txsetHash; | |||||
| } | |||||
| public LedgerDataContext(LedgerDataSetImpl dataset, TransactionSet txset, BufferedKVStorage storage) { | |||||
| this.dataset = dataset; | |||||
| this.txset = txset; | |||||
| this.storage = storage; | |||||
| public TxSnapshot(LedgerDataSnapshot dataSnapshot, HashDigest txsetHash) { | |||||
| this.dataSnapshot = dataSnapshot; | |||||
| this.txsetHash = txsetHash; | |||||
| } | } | ||||
| } | } | ||||
| private static class LedgerTransactionContextImpl extends LedgerDataContext implements LedgerTransactionContext { | |||||
| // /** | |||||
| // * 账本的数据上下文; | |||||
| // * | |||||
| // * @author huanghaiquan | |||||
| // * | |||||
| // */ | |||||
| // private static class LedgerDataContext { | |||||
| // | |||||
| // protected LedgerDataSetImpl dataset; | |||||
| // | |||||
| // protected TransactionSet txset; | |||||
| // | |||||
| // protected BufferedKVStorage storage; | |||||
| // | |||||
| // public LedgerDataContext(LedgerDataSetImpl dataset, TransactionSet txset, BufferedKVStorage storage) { | |||||
| // this.dataset = dataset; | |||||
| // this.txset = txset; | |||||
| // this.storage = storage; | |||||
| // } | |||||
| // | |||||
| // } | |||||
| private long blockHeight; | |||||
| /** | |||||
| * 交易的上下文; | |||||
| * | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| private static class LedgerTransactionContextImpl implements LedgerTransactionContext { | |||||
| private LedgerTransactionalEditor editor; | |||||
| private LedgerTransactionalEditor blockEditor; | |||||
| private TransactionRequest txRequest; | private TransactionRequest txRequest; | ||||
| // private LedgerDataSetImpl dataset; | |||||
| // | |||||
| // private TransactionSet txset; | |||||
| // | |||||
| // private BufferedKVStorage storage; | |||||
| private LedgerDataSetImpl dataset; | |||||
| private TransactionSet txset; | |||||
| private BufferedKVStorage storage; | |||||
| private boolean committed = false; | private boolean committed = false; | ||||
| private boolean rollbacked = false; | private boolean rollbacked = false; | ||||
| private LedgerTransactionContextImpl(long blockHeight, TransactionRequest txRequest, LedgerDataSetImpl dataset, | |||||
| private LedgerTransaction transaction; | |||||
| private HashDigest txRootHash; | |||||
| private LedgerTransactionContextImpl(TransactionRequest txRequest, LedgerDataSetImpl dataset, | |||||
| TransactionSet txset, BufferedKVStorage storage, LedgerTransactionalEditor editor) { | TransactionSet txset, BufferedKVStorage storage, LedgerTransactionalEditor editor) { | ||||
| super(dataset, txset, storage); | |||||
| this.txRequest = txRequest; | this.txRequest = txRequest; | ||||
| // this.dataset = dataset; | |||||
| // this.txset = txset; | |||||
| // this.storage = storage; | |||||
| this.editor = editor; | |||||
| this.blockHeight = blockHeight; | |||||
| this.dataset = dataset; | |||||
| this.txset = txset; | |||||
| this.storage = storage; | |||||
| this.blockEditor = editor; | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -346,7 +538,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public TransactionRequest getRequestTX() { | |||||
| public TransactionRequest getTransactionRequest() { | |||||
| return txRequest; | return txRequest; | ||||
| } | } | ||||
| @@ -360,24 +552,28 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| checkTxState(); | checkTxState(); | ||||
| // capture snapshot | // capture snapshot | ||||
| // this.dataset.commit(); | |||||
| // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | |||||
| // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | |||||
| // txResult, txDataSnapshot); | |||||
| LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, | |||||
| operationResultArray(operationResults)); | |||||
| this.txset.add(tx); | |||||
| // this.txset.commit(); | |||||
| // this.storage.flush(); | |||||
| this.dataset.commit(); | |||||
| TransactionStagedSnapshot txDataSnapshot = takeDataSnapshot(); | |||||
| LedgerTransactionData tx; | |||||
| try { | |||||
| tx = new LedgerTransactionData(blockEditor.getBlockHeight(), txRequest, txResult, txDataSnapshot, | |||||
| operationResultArray(operationResults)); | |||||
| this.txset.add(tx); | |||||
| this.txset.commit(); | |||||
| } catch (Exception e) { | |||||
| throw new TransactionRollbackException(e.getMessage(), e); | |||||
| } | |||||
| // TODO: 未处理出错时 dataset 和 txset 的内部状态恢复,有可能出现不一致的情况; | |||||
| try { | |||||
| this.storage.flush(); | |||||
| } catch (Exception e) { | |||||
| throw new BlockRollbackException(e.getMessage(), e); | |||||
| } | |||||
| // put snapshot into stack; | // put snapshot into stack; | ||||
| // TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); | |||||
| // editor.commitTxSnapshot(snapshot); | |||||
| TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); | |||||
| blockEditor.commitTxSnapshot(snapshot); | |||||
| committed = true; | committed = true; | ||||
| return tx; | return tx; | ||||
| @@ -393,29 +589,35 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| checkTxState(); | checkTxState(); | ||||
| // 未处理 | // 未处理 | ||||
| // dataset.cancel(); | |||||
| // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | |||||
| // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | |||||
| // txResult, txDataSnapshot); | |||||
| LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, | |||||
| operationResultArray(operationResults)); | |||||
| this.txset.add(tx); | |||||
| // this.txset.commit(); | |||||
| // this.storage.flush(); | |||||
| dataset.cancel(); | |||||
| TransactionStagedSnapshot txDataSnapshot = takeDataSnapshot(); | |||||
| LedgerTransactionData tx; | |||||
| try { | |||||
| tx = new LedgerTransactionData(blockEditor.getBlockHeight(), txRequest, txResult, txDataSnapshot, | |||||
| operationResultArray(operationResults)); | |||||
| this.txset.add(tx); | |||||
| this.txset.commit(); | |||||
| } catch (Exception e) { | |||||
| throw new TransactionRollbackException(e.getMessage(), e); | |||||
| } | |||||
| // TODO: 未处理出错时 dataset 和 txset 的内部状态恢复,有可能出现不一致的情况; | |||||
| try { | |||||
| this.storage.flush(); | |||||
| } catch (Exception e) { | |||||
| throw new BlockRollbackException(e.getMessage(), e); | |||||
| } | |||||
| // put snapshot into stack; | // put snapshot into stack; | ||||
| // TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); | |||||
| // editor.commitTxSnapshot(snapshot); | |||||
| TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); | |||||
| blockEditor.commitTxSnapshot(snapshot); | |||||
| committed = true; | committed = true; | ||||
| return tx; | return tx; | ||||
| } | } | ||||
| private TransactionStagedSnapshot takeSnapshot() { | |||||
| private TransactionStagedSnapshot takeDataSnapshot() { | |||||
| TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | ||||
| txDataSnapshot.setAdminAccountHash(dataset.getAdminAccount().getHash()); | txDataSnapshot.setAdminAccountHash(dataset.getAdminAccount().getHash()); | ||||
| txDataSnapshot.setContractAccountSetHash(dataset.getContractAccountSet().getRootHash()); | txDataSnapshot.setContractAccountSetHash(dataset.getContractAccountSet().getRootHash()); | ||||
| @@ -439,22 +641,22 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| return; | return; | ||||
| } | } | ||||
| if (this.committed) { | if (this.committed) { | ||||
| throw new IllegalStateException("Transaction had been committed!"); | |||||
| throw new IllegalStateException("This transaction had been committed!"); | |||||
| } | } | ||||
| // dataset.cancel(); | |||||
| // storage.cancel(); | |||||
| dataset.cancel(); | |||||
| storage.cancel(); | |||||
| // editor.rollbackNewTx(); | |||||
| blockEditor.rollbackCurrentTx(); | |||||
| rollbacked = true; | rollbacked = true; | ||||
| } | } | ||||
| private void checkTxState() { | private void checkTxState() { | ||||
| if (this.committed) { | if (this.committed) { | ||||
| throw new IllegalStateException("Transaction had been committed!"); | |||||
| throw new IllegalStateException("This transaction had been committed!"); | |||||
| } | } | ||||
| if (this.rollbacked) { | if (this.rollbacked) { | ||||
| throw new IllegalStateException("Transaction had been rollbacked!"); | |||||
| throw new IllegalStateException("This transaction had been rollbacked!"); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -8,7 +8,11 @@ import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.ledger.BlockRollbackException; | |||||
| import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
| import com.jd.blockchain.ledger.ContractDoesNotExistException; | |||||
| import com.jd.blockchain.ledger.DataAccountDoesNotExistException; | |||||
| import com.jd.blockchain.ledger.IllegalTransactionException; | |||||
| import com.jd.blockchain.ledger.LedgerBlock; | import com.jd.blockchain.ledger.LedgerBlock; | ||||
| import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| @@ -16,7 +20,9 @@ import com.jd.blockchain.ledger.OperationResult; | |||||
| import com.jd.blockchain.ledger.OperationResultData; | import com.jd.blockchain.ledger.OperationResultData; | ||||
| import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
| import com.jd.blockchain.ledger.TransactionResponse; | import com.jd.blockchain.ledger.TransactionResponse; | ||||
| import com.jd.blockchain.ledger.TransactionRollbackException; | |||||
| import com.jd.blockchain.ledger.TransactionState; | import com.jd.blockchain.ledger.TransactionState; | ||||
| import com.jd.blockchain.ledger.UserDoesNotExistException; | |||||
| import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
| import com.jd.blockchain.ledger.core.LedgerEditor; | import com.jd.blockchain.ledger.core.LedgerEditor; | ||||
| import com.jd.blockchain.ledger.core.LedgerService; | import com.jd.blockchain.ledger.core.LedgerService; | ||||
| @@ -26,6 +32,7 @@ import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
| import com.jd.blockchain.service.TransactionBatchProcess; | import com.jd.blockchain.service.TransactionBatchProcess; | ||||
| import com.jd.blockchain.service.TransactionBatchResult; | import com.jd.blockchain.service.TransactionBatchResult; | ||||
| import com.jd.blockchain.service.TransactionBatchResultHandle; | import com.jd.blockchain.service.TransactionBatchResultHandle; | ||||
| import com.jd.blockchain.transaction.TxResponseMessage; | |||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| public class TransactionBatchProcessor implements TransactionBatchProcess { | public class TransactionBatchProcessor implements TransactionBatchProcess { | ||||
| @@ -50,12 +57,9 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| private TransactionBatchResult batchResult; | private TransactionBatchResult batchResult; | ||||
| /** | /** | ||||
| * @param newBlockEditor | |||||
| * 新区块的数据编辑器; | |||||
| * @param previousBlockDataset | |||||
| * 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||||
| * @param opHandles | |||||
| * 操作处理对象注册表; | |||||
| * @param newBlockEditor 新区块的数据编辑器; | |||||
| * @param previousBlockDataset 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||||
| * @param opHandles 操作处理对象注册表; | |||||
| */ | */ | ||||
| public TransactionBatchProcessor(LedgerEditor newBlockEditor, LedgerDataSet previousBlockDataset, | public TransactionBatchProcessor(LedgerEditor newBlockEditor, LedgerDataSet previousBlockDataset, | ||||
| OperationHandleRegisteration opHandles, LedgerService ledgerService) { | OperationHandleRegisteration opHandles, LedgerService ledgerService) { | ||||
| @@ -74,14 +78,63 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| */ | */ | ||||
| @Override | @Override | ||||
| public TransactionResponse schedule(TransactionRequest request) { | public TransactionResponse schedule(TransactionRequest request) { | ||||
| // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | |||||
| LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | |||||
| TransactionState result; | |||||
| TransactionResponse resp; | |||||
| try { | |||||
| LOGGER.debug("Start handling transaction... --[BlockHeight={}][RequestHash={}][TxHash={}]", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); | |||||
| // 创建交易上下文; | |||||
| // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | |||||
| LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | |||||
| // 处理交易; | |||||
| resp = handleTx(request, txCtx); | |||||
| LOGGER.debug("Complete handling transaction. --[BlockHeight={}][RequestHash={}][TxHash={}]", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); | |||||
| } catch (IllegalTransactionException e) { | |||||
| // 抛弃发生处理异常的交易请求; | |||||
| resp = discard(request, e.getTxState()); | |||||
| LOGGER.error(String.format( | |||||
| "Ignore transaction caused by IllegalTransactionException! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
| e.getMessage()), e); | |||||
| } catch (BlockRollbackException e) { | |||||
| // 抛弃发生处理异常的交易请求; | |||||
| // resp = discard(request, TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK); | |||||
| LOGGER.error(String.format( | |||||
| "Ignore transaction caused by BlockRollbackException! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
| e.getMessage()), e); | |||||
| throw e; | |||||
| } catch (Exception e) { | |||||
| // 抛弃发生处理异常的交易请求; | |||||
| resp = discard(request, TransactionState.SYSTEM_ERROR); | |||||
| LOGGER.error(String.format( | |||||
| "Ignore transaction caused by the system exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
| e.getMessage()), e); | |||||
| List<OperationResult> operationResults = new ArrayList<>(); | |||||
| } | |||||
| try { | |||||
| responseList.add(resp); | |||||
| return resp; | |||||
| } | |||||
| /** | |||||
| * 处理交易;<br> | |||||
| * | |||||
| * 此方法会处理所有的异常,以不同结果的 {@link TransactionResponse} 返回; | |||||
| * | |||||
| * @param request | |||||
| * @param txCtx | |||||
| * @return | |||||
| */ | |||||
| private TransactionResponse handleTx(TransactionRequest request, LedgerTransactionContext txCtx) { | |||||
| TransactionState result; | |||||
| List<OperationResult> operationResults = new ArrayList<>(); | |||||
| try { | |||||
| LedgerDataSet dataset = txCtx.getDataSet(); | LedgerDataSet dataset = txCtx.getDataSet(); | ||||
| TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | ||||
| // TODO: 验证签名者的有效性; | // TODO: 验证签名者的有效性; | ||||
| @@ -101,7 +154,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| OperationHandleContext handleContext = new OperationHandleContext() { | OperationHandleContext handleContext = new OperationHandleContext() { | ||||
| @Override | @Override | ||||
| public void handle(Operation operation) { | public void handle(Operation operation) { | ||||
| //assert; Instance of operation are one of User related operations or DataAccount related operations; | |||||
| // assert; Instance of operation are one of User related operations or | |||||
| // DataAccount related operations; | |||||
| OperationHandle hdl = opHandles.getHandle(operation.getClass()); | OperationHandle hdl = opHandles.getHandle(operation.getClass()); | ||||
| hdl.process(operation, dataset, reqCtx, previousBlockDataset, this, ledgerService); | hdl.process(operation, dataset, reqCtx, previousBlockDataset, this, ledgerService); | ||||
| } | } | ||||
| @@ -110,7 +164,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| int opIndex = 0; | int opIndex = 0; | ||||
| for (Operation op : ops) { | for (Operation op : ops) { | ||||
| opHandle = opHandles.getHandle(op.getClass()); | opHandle = opHandles.getHandle(op.getClass()); | ||||
| BytesValue opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
| BytesValue opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, | |||||
| ledgerService); | |||||
| if (opResult != null) { | if (opResult != null) { | ||||
| operationResults.add(new OperationResultData(opIndex, opResult)); | operationResults.add(new OperationResultData(opIndex, opResult)); | ||||
| } | } | ||||
| @@ -119,19 +174,46 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| // 提交交易(事务); | // 提交交易(事务); | ||||
| result = TransactionState.SUCCESS; | result = TransactionState.SUCCESS; | ||||
| txCtx.commit(result, operationResults); | txCtx.commit(result, operationResults); | ||||
| } catch (TransactionRollbackException e) { | |||||
| result = TransactionState.IGNORED_BY_TX_FULL_ROLLBACK; | |||||
| txCtx.rollback(); | |||||
| LOGGER.error(String.format( | |||||
| "Transaction was full rolled back! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
| e.getMessage()), e); | |||||
| } catch (BlockRollbackException e) { | |||||
| result = TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK; | |||||
| txCtx.rollback(); | |||||
| LOGGER.error( | |||||
| String.format("Transaction was rolled back! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), | |||||
| request.getTransactionContent().getHash(), e.getMessage()), | |||||
| e); | |||||
| // 重新抛出由上层错误处理; | |||||
| throw e; | |||||
| } catch (LedgerException e) { | } catch (LedgerException e) { | ||||
| // TODO: 识别更详细的异常类型以及执行对应的处理; | // TODO: 识别更详细的异常类型以及执行对应的处理; | ||||
| result = TransactionState.LEDGER_ERROR; | result = TransactionState.LEDGER_ERROR; | ||||
| txCtx.discardAndCommit(TransactionState.LEDGER_ERROR, operationResults); | |||||
| LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", | |||||
| request.getHash().toBase58(), e.getMessage()), e); | |||||
| if (e instanceof DataAccountDoesNotExistException) { | |||||
| result = TransactionState.DATA_ACCOUNT_DOES_NOT_EXIST; | |||||
| } else if (e instanceof UserDoesNotExistException) { | |||||
| result = TransactionState.USER_DOES_NOT_EXIST; | |||||
| } else if (e instanceof ContractDoesNotExistException) { | |||||
| result = TransactionState.CONTRACT_DOES_NOT_EXIST; | |||||
| } | |||||
| txCtx.discardAndCommit(result, operationResults); | |||||
| LOGGER.error(String.format( | |||||
| "Due to ledger exception, the data changes resulting from the transaction will be rolled back and the results of the transaction will be committed! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
| e.getMessage()), e); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| result = TransactionState.SYSTEM_ERROR; | result = TransactionState.SYSTEM_ERROR; | ||||
| txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, operationResults); | txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, operationResults); | ||||
| LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", | |||||
| request.getHash().toBase58(), e.getMessage()), e); | |||||
| LOGGER.error(String.format( | |||||
| "Due to system exception, the data changes resulting from the transaction will be rolled back and the results of the transaction will be committed! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
| e.getMessage()), e); | |||||
| } | } | ||||
| TxResponseHandle resp = new TxResponseHandle(request, result); | TxResponseHandle resp = new TxResponseHandle(request, result); | ||||
| @@ -139,9 +221,24 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| OperationResult[] operationResultArray = new OperationResult[operationResults.size()]; | OperationResult[] operationResultArray = new OperationResult[operationResults.size()]; | ||||
| resp.setOperationResults(operationResults.toArray(operationResultArray)); | resp.setOperationResults(operationResults.toArray(operationResultArray)); | ||||
| } | } | ||||
| return resp; | |||||
| } | |||||
| responseList.add(resp); | |||||
| /** | |||||
| * 直接丢弃交易; | |||||
| * | |||||
| * @param request | |||||
| * @param txState | |||||
| * @return 丢弃交易的回复;只包含原始请求中的交易内容哈希和交易被丢弃的原因,而不包含区块信息; | |||||
| */ | |||||
| private TransactionResponse discard(TransactionRequest request, TransactionState txState) { | |||||
| // 丢弃交易的回复;只返回请求的交易内容哈希和交易被丢弃的原因, | |||||
| TxResponseMessage resp = new TxResponseMessage(request.getTransactionContent().getHash()); | |||||
| resp.setExecutionState(txState); | |||||
| LOGGER.error("Discard transaction request! --[BlockHeight={}][RequestHash={}][TxHash={}][ResponseState={}]", | |||||
| newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
| resp.getExecutionState()); | |||||
| return resp; | return resp; | ||||
| } | } | ||||
| @@ -4,6 +4,7 @@ import org.springframework.stereotype.Service; | |||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | import com.jd.blockchain.binaryproto.DataContractRegistry; | ||||
| import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
| import com.jd.blockchain.ledger.DataAccountDoesNotExistException; | |||||
| import com.jd.blockchain.ledger.DataAccountKVSetOperation; | import com.jd.blockchain.ledger.DataAccountKVSetOperation; | ||||
| import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry; | import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry; | ||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| @@ -26,9 +27,11 @@ public class DataAccountKVSetOperationHandle implements OperationHandle { | |||||
| LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
| DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | ||||
| DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); | DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); | ||||
| if (account == null) { | |||||
| throw new DataAccountDoesNotExistException("DataAccount doesn't exist!"); | |||||
| } | |||||
| KVWriteEntry[] writeSet = kvWriteOp.getWriteSet(); | KVWriteEntry[] writeSet = kvWriteOp.getWriteSet(); | ||||
| for (KVWriteEntry kvw : writeSet) { | for (KVWriteEntry kvw : writeSet) { | ||||
| // byte[] value = BinaryProtocol.encode(kvw.getValue(), BytesValue.class); | |||||
| account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion()); | account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion()); | ||||
| } | } | ||||
| return null; | return null; | ||||
| @@ -1,17 +1,11 @@ | |||||
| package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
| import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; | |||||
| import com.jd.blockchain.contract.engine.ContractCode; | import com.jd.blockchain.contract.engine.ContractCode; | ||||
| import com.jd.blockchain.contract.engine.ContractEngine; | import com.jd.blockchain.contract.engine.ContractEngine; | ||||
| import com.jd.blockchain.contract.engine.ContractServiceProviders; | import com.jd.blockchain.contract.engine.ContractServiceProviders; | ||||
| import com.jd.blockchain.ledger.Operation; | |||||
| import com.jd.blockchain.ledger.core.ContractAccount; | import com.jd.blockchain.ledger.core.ContractAccount; | ||||
| import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
| import com.jd.blockchain.ledger.core.LedgerService; | |||||
| import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
| import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | |||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; | |||||
| public class JVMContractEventSendOperationHandle extends AbtractContractEventHandle { | public class JVMContractEventSendOperationHandle extends AbtractContractEventHandle { | ||||
| @@ -1,45 +1,10 @@ | |||||
| package test.com.jd.blockchain.ledger; | package test.com.jd.blockchain.ledger; | ||||
| import static org.junit.Assert.assertArrayEquals; | |||||
| import static org.junit.Assert.assertEquals; | |||||
| import static org.junit.Assert.assertNotNull; | |||||
| import static org.junit.Assert.assertNull; | |||||
| import static org.mockito.Matchers.anyLong; | |||||
| import static org.mockito.Matchers.anyString; | |||||
| import static org.mockito.Mockito.times; | |||||
| import static org.mockito.Mockito.verify; | |||||
| import static org.mockito.Mockito.when; | |||||
| import java.util.Random; | |||||
| import org.junit.Test; | |||||
| import org.mockito.Mockito; | |||||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | import com.jd.blockchain.binaryproto.BinaryProtocol; | ||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | import com.jd.blockchain.binaryproto.DataContractRegistry; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
| import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.BytesData; | |||||
| import com.jd.blockchain.ledger.EndpointRequest; | |||||
| import com.jd.blockchain.ledger.LedgerBlock; | |||||
| import com.jd.blockchain.ledger.LedgerInitSetting; | |||||
| import com.jd.blockchain.ledger.LedgerTransaction; | |||||
| import com.jd.blockchain.ledger.NodeRequest; | |||||
| import com.jd.blockchain.ledger.OperationResult; | |||||
| import com.jd.blockchain.ledger.TransactionContent; | |||||
| import com.jd.blockchain.ledger.TransactionContentBody; | |||||
| import com.jd.blockchain.ledger.TransactionRequest; | |||||
| import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||||
| import com.jd.blockchain.ledger.TransactionResponse; | |||||
| import com.jd.blockchain.ledger.TransactionState; | |||||
| import com.jd.blockchain.ledger.UserRegisterOperation; | |||||
| import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
| import com.jd.blockchain.ledger.core.LedgerEditor; | |||||
| import com.jd.blockchain.ledger.core.LedgerRepository; | |||||
| import com.jd.blockchain.ledger.core.LedgerTransactionContext; | |||||
| import com.jd.blockchain.ledger.core.UserAccount; | |||||
| import com.jd.blockchain.ledger.*; | |||||
| import com.jd.blockchain.ledger.core.*; | |||||
| import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; | import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; | ||||
| import com.jd.blockchain.ledger.core.impl.LedgerManager; | import com.jd.blockchain.ledger.core.impl.LedgerManager; | ||||
| import com.jd.blockchain.ledger.core.impl.LedgerTransactionalEditor; | import com.jd.blockchain.ledger.core.impl.LedgerTransactionalEditor; | ||||
| @@ -48,6 +13,15 @@ import com.jd.blockchain.service.TransactionBatchResultHandle; | |||||
| import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | ||||
| import com.jd.blockchain.transaction.TxBuilder; | import com.jd.blockchain.transaction.TxBuilder; | ||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| import org.junit.Test; | |||||
| import org.mockito.Mockito; | |||||
| import java.util.Random; | |||||
| import static org.junit.Assert.*; | |||||
| import static org.mockito.Matchers.anyLong; | |||||
| import static org.mockito.Matchers.anyString; | |||||
| import static org.mockito.Mockito.*; | |||||
| public class ContractInvokingTest { | public class ContractInvokingTest { | ||||
| static { | static { | ||||
| @@ -184,7 +158,7 @@ public class ContractInvokingTest { | |||||
| // 创建账本; | // 创建账本; | ||||
| LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | ||||
| TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
| TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(partiKeys); | |||||
| LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
| LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | ||||
| @@ -204,10 +178,9 @@ public class ContractInvokingTest { | |||||
| assertEquals(0, block.getHeight()); | assertEquals(0, block.getHeight()); | ||||
| assertNotNull(block.getHash()); | assertNotNull(block.getHash()); | ||||
| assertNull(block.getLedgerHash()); | |||||
| assertNull(block.getPreviousHash()); | assertNull(block.getPreviousHash()); | ||||
| assertEquals(block.getHash(), block.getLedgerHash()); | |||||
| // 提交数据,写入存储; | // 提交数据,写入存储; | ||||
| ldgEdt.commit(); | ldgEdt.commit(); | ||||
| @@ -16,6 +16,7 @@ import com.jd.blockchain.crypto.SignatureFunction; | |||||
| import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | ||||
| import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | ||||
| import com.jd.blockchain.crypto.service.sm.SMCryptoService; | import com.jd.blockchain.crypto.service.sm.SMCryptoService; | ||||
| import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
| import com.jd.blockchain.ledger.BlockchainKeypair; | import com.jd.blockchain.ledger.BlockchainKeypair; | ||||
| import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
| import com.jd.blockchain.ledger.DataType; | import com.jd.blockchain.ledger.DataType; | ||||
| @@ -38,7 +39,7 @@ import com.jd.blockchain.utils.Bytes; | |||||
| import com.jd.blockchain.utils.io.BytesUtils; | import com.jd.blockchain.utils.io.BytesUtils; | ||||
| import com.jd.blockchain.utils.net.NetworkAddress; | import com.jd.blockchain.utils.net.NetworkAddress; | ||||
| public class LedgerEditerTest { | |||||
| public class LedgerEditorTest { | |||||
| private static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), | private static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), | ||||
| SMCryptoService.class.getName() }; | SMCryptoService.class.getName() }; | ||||
| @@ -52,6 +53,13 @@ public class LedgerEditerTest { | |||||
| private static final String LEDGER_KEY_PREFIX = "LDG://"; | private static final String LEDGER_KEY_PREFIX = "LDG://"; | ||||
| private SignatureFunction signatureFunction; | private SignatureFunction signatureFunction; | ||||
| private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; | |||||
| /** | /** | ||||
| * 初始化一个; | * 初始化一个; | ||||
| */ | */ | ||||
| @@ -74,8 +82,8 @@ public class LedgerEditerTest { | |||||
| return LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | return LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | ||||
| } | } | ||||
| private LedgerTransactionContext createGenisisTx(LedgerEditor ldgEdt) { | |||||
| TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
| private LedgerTransactionContext createGenisisTx(LedgerEditor ldgEdt, BlockchainKeypair[] partis) { | |||||
| TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(partis); | |||||
| LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
| @@ -86,7 +94,7 @@ public class LedgerEditerTest { | |||||
| @Test | @Test | ||||
| public void testWriteDataAccoutKvOp() { | public void testWriteDataAccoutKvOp() { | ||||
| LedgerEditor ldgEdt = createLedgerInitEditor(); | LedgerEditor ldgEdt = createLedgerInitEditor(); | ||||
| LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt); | |||||
| LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt, participants); | |||||
| LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | ||||
| AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | ||||
| @@ -119,7 +127,7 @@ public class LedgerEditerTest { | |||||
| @Test | @Test | ||||
| public void testGennesisBlockCreation() { | public void testGennesisBlockCreation() { | ||||
| LedgerEditor ldgEdt = createLedgerInitEditor(); | LedgerEditor ldgEdt = createLedgerInitEditor(); | ||||
| LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt); | |||||
| LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt, participants); | |||||
| LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | ||||
| AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | ||||
| @@ -130,7 +138,7 @@ public class LedgerEditerTest { | |||||
| LedgerTransaction tx = genisisTxCtx.commit(TransactionState.SUCCESS); | LedgerTransaction tx = genisisTxCtx.commit(TransactionState.SUCCESS); | ||||
| TransactionRequest genesisTxReq = genisisTxCtx.getRequestTX(); | |||||
| TransactionRequest genesisTxReq = genisisTxCtx.getTransactionRequest(); | |||||
| assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); | assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); | ||||
| assertEquals(0, tx.getBlockHeight()); | assertEquals(0, tx.getBlockHeight()); | ||||
| @@ -138,13 +146,12 @@ public class LedgerEditerTest { | |||||
| assertEquals(0, block.getHeight()); | assertEquals(0, block.getHeight()); | ||||
| assertNotNull(block.getHash()); | assertNotNull(block.getHash()); | ||||
| assertNull(block.getLedgerHash()); | |||||
| assertNull(block.getPreviousHash()); | assertNull(block.getPreviousHash()); | ||||
| assertEquals(block.getHash(), block.getLedgerHash()); | |||||
| // 提交数据,写入存储; | // 提交数据,写入存储; | ||||
| ldgEdt.commit(); | ldgEdt.commit(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -63,6 +63,13 @@ public class LedgerManagerTest { | |||||
| public static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), | public static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), | ||||
| SMCryptoService.class.getName() }; | SMCryptoService.class.getName() }; | ||||
| private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; | |||||
| private SignatureFunction signatureFunction; | private SignatureFunction signatureFunction; | ||||
| @Before | @Before | ||||
| @@ -83,13 +90,13 @@ public class LedgerManagerTest { | |||||
| LedgerEditor ldgEdt = ledgerManager.newLedger(initSetting, storage); | LedgerEditor ldgEdt = ledgerManager.newLedger(initSetting, storage); | ||||
| // 创建一个模拟的创世交易; | // 创建一个模拟的创世交易; | ||||
| TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
| TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(participants); | |||||
| // 记录交易,注册用户; | // 记录交易,注册用户; | ||||
| LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
| LedgerDataSet ldgDS = txCtx.getDataSet(); | LedgerDataSet ldgDS = txCtx.getDataSet(); | ||||
| BlockchainKeypair userKP = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair userKP = BlockchainKeyGenerator.getInstance().generate(); | ||||
| UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); | UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); | ||||
| userAccount.setProperty("Name", "孙悟空", -1); | userAccount.setProperty("Name", "孙悟空", -1); | ||||
| userAccount.setProperty("Age", "10000", -1); | userAccount.setProperty("Age", "10000", -1); | ||||
| @@ -109,7 +116,8 @@ public class LedgerManagerTest { | |||||
| assertEquals(0, genesisBlock.getHeight()); | assertEquals(0, genesisBlock.getHeight()); | ||||
| assertNotNull(genesisBlock.getHash()); | assertNotNull(genesisBlock.getHash()); | ||||
| assertNull(genesisBlock.getPreviousHash()); | assertNull(genesisBlock.getPreviousHash()); | ||||
| assertEquals(ledgerHash, genesisBlock.getLedgerHash()); | |||||
| // 创世区块的账本hash 为null;创世区块本身的哈希就代表了账本的哈希; | |||||
| assertNull(genesisBlock.getLedgerHash()); | |||||
| // 提交数据,写入存储; | // 提交数据,写入存储; | ||||
| ldgEdt.commit(); | ldgEdt.commit(); | ||||
| @@ -124,7 +132,8 @@ public class LedgerManagerTest { | |||||
| LedgerBlock latestBlock = reloadLedgerRepo.getLatestBlock(); | LedgerBlock latestBlock = reloadLedgerRepo.getLatestBlock(); | ||||
| assertEquals(0, latestBlock.getHeight()); | assertEquals(0, latestBlock.getHeight()); | ||||
| assertEquals(ledgerHash, latestBlock.getHash()); | assertEquals(ledgerHash, latestBlock.getHash()); | ||||
| assertEquals(ledgerHash, latestBlock.getLedgerHash()); | |||||
| // 创世区块的账本hash 为null;创世区块本身的哈希就代表了账本的哈希; | |||||
| assertNull(latestBlock.getLedgerHash()); | |||||
| LedgerEditor editor1 = reloadLedgerRepo.createNextBlock(); | LedgerEditor editor1 = reloadLedgerRepo.createNextBlock(); | ||||
| @@ -24,6 +24,7 @@ import com.jd.blockchain.transaction.ConsensusParticipantData; | |||||
| import com.jd.blockchain.transaction.LedgerInitSettingData; | import com.jd.blockchain.transaction.LedgerInitSettingData; | ||||
| import com.jd.blockchain.transaction.TransactionService; | import com.jd.blockchain.transaction.TransactionService; | ||||
| import com.jd.blockchain.transaction.TxBuilder; | import com.jd.blockchain.transaction.TxBuilder; | ||||
| import com.jd.blockchain.utils.Bytes; | |||||
| import com.jd.blockchain.utils.io.BytesUtils; | import com.jd.blockchain.utils.io.BytesUtils; | ||||
| import com.jd.blockchain.utils.net.NetworkAddress; | import com.jd.blockchain.utils.net.NetworkAddress; | ||||
| @@ -38,10 +39,10 @@ public class LedgerTestUtils { | |||||
| private static Random rand = new Random(); | private static Random rand = new Random(); | ||||
| public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash) { | |||||
| BlockchainKeypair key = BlockchainKeyGenerator.getInstance().generate(ED25519); | |||||
| return createTxRequest_UserReg(ledgerHash, key); | |||||
| } | |||||
| // public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash) { | |||||
| // BlockchainKeypair key = BlockchainKeyGenerator.getInstance().generate(ED25519); | |||||
| // return createTxRequest_UserReg(ledgerHash, key); | |||||
| // } | |||||
| public static LedgerInitSetting createLedgerInitSetting() { | public static LedgerInitSetting createLedgerInitSetting() { | ||||
| BlockchainKeypair[] partiKeys = new BlockchainKeypair[2]; | BlockchainKeypair[] partiKeys = new BlockchainKeypair[2]; | ||||
| @@ -81,25 +82,114 @@ public class LedgerTestUtils { | |||||
| return initSetting; | return initSetting; | ||||
| } | } | ||||
| public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, BlockchainKeypair userKeypair) { | |||||
| return createTxRequest_UserReg(ledgerHash, userKeypair, null); | |||||
| // public static TransactionRequest createTxRequest_UserReg(BlockchainKeypair userKeypair, HashDigest ledgerHash, BlockchainKeypair... partiKeys) { | |||||
| // return createTxRequest_UserReg(userKeypair, ledgerHash, null, null); | |||||
| // } | |||||
| public static TransactionRequest createLedgerInitTxRequest(BlockchainKeypair... participants) { | |||||
| TxBuilder txBuilder = new TxBuilder(null); | |||||
| for (BlockchainKeypair parti : participants) { | |||||
| txBuilder.users().register(parti.getIdentity()); | |||||
| } | |||||
| TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
| for (BlockchainKeypair parti : participants) { | |||||
| txReqBuilder.signAsNode(parti); | |||||
| } | |||||
| return txReqBuilder.buildRequest(); | |||||
| } | } | ||||
| public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, BlockchainKeypair userKeypair, | |||||
| BlockchainKeypair gatewayKeypair) { | |||||
| // public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, BlockchainKeypair nodeKeypair, | |||||
| // BlockchainKeypair... signers) { | |||||
| // return createTxRequest_UserReg(BlockchainKeyGenerator.getInstance().generate(), ledgerHash, nodeKeypair, | |||||
| // signers); | |||||
| // } | |||||
| public static TransactionRequest createTxRequest_UserReg(BlockchainKeypair userKeypair, HashDigest ledgerHash, | |||||
| BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { | |||||
| TxBuilder txBuilder = new TxBuilder(ledgerHash); | TxBuilder txBuilder = new TxBuilder(ledgerHash); | ||||
| txBuilder.users().register(userKeypair.getIdentity()); | txBuilder.users().register(userKeypair.getIdentity()); | ||||
| TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | ||||
| txReqBuilder.signAsEndpoint(userKeypair); | |||||
| if (gatewayKeypair != null) { | |||||
| txReqBuilder.signAsNode(gatewayKeypair); | |||||
| if (signers != null) { | |||||
| for (BlockchainKeypair signer : signers) { | |||||
| txReqBuilder.signAsEndpoint(signer); | |||||
| } | |||||
| } | |||||
| if (nodeKeypair != null) { | |||||
| txReqBuilder.signAsNode(nodeKeypair); | |||||
| } | |||||
| return txReqBuilder.buildRequest(); | |||||
| } | |||||
| public static TransactionRequest createTxRequest_DataAccountReg(BlockchainKeypair dataAccountID, HashDigest ledgerHash, | |||||
| BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { | |||||
| TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
| txBuilder.dataAccounts().register(dataAccountID.getIdentity()); | |||||
| TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
| if (signers != null) { | |||||
| for (BlockchainKeypair signer : signers) { | |||||
| txReqBuilder.signAsEndpoint(signer); | |||||
| } | |||||
| } | |||||
| if (nodeKeypair != null) { | |||||
| txReqBuilder.signAsNode(nodeKeypair); | |||||
| } | |||||
| return txReqBuilder.buildRequest(); | |||||
| } | |||||
| public static TransactionRequest createTxRequest_DataAccountWrite(Bytes dataAccountAddress, String key, String value, long version, HashDigest ledgerHash, | |||||
| BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { | |||||
| TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
| txBuilder.dataAccount(dataAccountAddress).setText(key, value, version); | |||||
| TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
| if (signers != null) { | |||||
| for (BlockchainKeypair signer : signers) { | |||||
| txReqBuilder.signAsEndpoint(signer); | |||||
| } | |||||
| } | |||||
| if (nodeKeypair != null) { | |||||
| txReqBuilder.signAsNode(nodeKeypair); | |||||
| } | } | ||||
| return txReqBuilder.buildRequest(); | return txReqBuilder.buildRequest(); | ||||
| } | } | ||||
| /** | |||||
| * @param userKeypair 要注册的用户key; | |||||
| * @param ledgerHash 账本哈希; | |||||
| * @param nodeKeypair 节点key; | |||||
| * @param signers 签名者列表; | |||||
| * @return | |||||
| */ | |||||
| public static TransactionRequest createTxRequest_MultiOPs_WithNotExistedDataAccount(BlockchainKeypair userKeypair, | |||||
| HashDigest ledgerHash, BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { | |||||
| TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
| txBuilder.users().register(userKeypair.getIdentity()); | |||||
| // 故意构建一个错误的 | |||||
| BlockchainKeypair testKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
| txBuilder.dataAccount(testKey.getAddress()).setBytes("AA", "Value".getBytes(), 1); | |||||
| TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
| txReqBuilder.signAsEndpoint(nodeKeypair); | |||||
| if (nodeKeypair != null) { | |||||
| txReqBuilder.signAsNode(nodeKeypair); | |||||
| } | |||||
| return txReqBuilder.buildRequest(); | |||||
| } | |||||
| public static TransactionStagedSnapshot generateRandomSnapshot() { | public static TransactionStagedSnapshot generateRandomSnapshot() { | ||||
| TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | ||||
| txDataSnapshot.setAdminAccountHash(generateRandomHash()); | txDataSnapshot.setAdminAccountHash(generateRandomHash()); | ||||
| @@ -58,6 +58,7 @@ public class MerkleDataSetTest { | |||||
| mds.setValue("C", "C".getBytes(), -1); | mds.setValue("C", "C".getBytes(), -1); | ||||
| mds.commit(); | mds.commit(); | ||||
| HashDigest root1 = mds.getRootHash(); | |||||
| // 1个KV项的存储KEY的数量= 1 + 1(保存SN) + Merkle节点数量; | // 1个KV项的存储KEY的数量= 1 + 1(保存SN) + Merkle节点数量; | ||||
| // 所以:3 项; | // 所以:3 项; | ||||
| @@ -68,6 +69,8 @@ public class MerkleDataSetTest { | |||||
| mds.setValue("B", "B".getBytes(), 0); | mds.setValue("B", "B".getBytes(), 0); | ||||
| mds.setValue("C", "C".getBytes(), 0); | mds.setValue("C", "C".getBytes(), 0); | ||||
| mds.commit(); | mds.commit(); | ||||
| HashDigest root2 = mds.getRootHash(); | |||||
| assertNotEquals(root1, root2); | |||||
| // Version changed only;仅仅增加 merkle 节点,此时 Merkle 树只有 1 层路径节点,因此只更新2个数据节点和 1 | // Version changed only;仅仅增加 merkle 节点,此时 Merkle 树只有 1 层路径节点,因此只更新2个数据节点和 1 | ||||
| // 个路径节点;(注:版本值是在同一个 key 下按序列保存的); | // 个路径节点;(注:版本值是在同一个 key 下按序列保存的); | ||||
| @@ -76,6 +79,9 @@ public class MerkleDataSetTest { | |||||
| mds.setValue("D", "DValue".getBytes(), -1); | mds.setValue("D", "DValue".getBytes(), -1); | ||||
| mds.commit(); | mds.commit(); | ||||
| HashDigest root3 = mds.getRootHash(); | |||||
| assertNotEquals(root2, root3); | |||||
| assertNotEquals(root1, root3); | |||||
| // New key added, include 1 versioning kv, 1 sn key, 2 merkle nodes; | // New key added, include 1 versioning kv, 1 sn key, 2 merkle nodes; | ||||
| // String[] keys = StringUtils.toStringArray(storage.keySet()); | // String[] keys = StringUtils.toStringArray(storage.keySet()); | ||||
| @@ -1,6 +1,7 @@ | |||||
| package test.com.jd.blockchain.ledger; | package test.com.jd.blockchain.ledger; | ||||
| import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertFalse; | |||||
| import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||
| import static org.junit.Assert.assertNull; | import static org.junit.Assert.assertNull; | ||||
| import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
| @@ -11,6 +12,8 @@ import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.ledger.BlockchainKeyGenerator; | import com.jd.blockchain.ledger.BlockchainKeyGenerator; | ||||
| import com.jd.blockchain.ledger.BlockchainKeypair; | import com.jd.blockchain.ledger.BlockchainKeypair; | ||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.DataAccountRegisterOperation; | |||||
| import com.jd.blockchain.ledger.EndpointRequest; | import com.jd.blockchain.ledger.EndpointRequest; | ||||
| import com.jd.blockchain.ledger.LedgerBlock; | import com.jd.blockchain.ledger.LedgerBlock; | ||||
| import com.jd.blockchain.ledger.LedgerInitSetting; | import com.jd.blockchain.ledger.LedgerInitSetting; | ||||
| @@ -22,6 +25,7 @@ import com.jd.blockchain.ledger.TransactionRequest; | |||||
| import com.jd.blockchain.ledger.TransactionResponse; | import com.jd.blockchain.ledger.TransactionResponse; | ||||
| import com.jd.blockchain.ledger.TransactionState; | import com.jd.blockchain.ledger.TransactionState; | ||||
| import com.jd.blockchain.ledger.UserRegisterOperation; | import com.jd.blockchain.ledger.UserRegisterOperation; | ||||
| import com.jd.blockchain.ledger.core.DataAccount; | |||||
| import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
| import com.jd.blockchain.ledger.core.LedgerEditor; | import com.jd.blockchain.ledger.core.LedgerEditor; | ||||
| import com.jd.blockchain.ledger.core.LedgerRepository; | import com.jd.blockchain.ledger.core.LedgerRepository; | ||||
| @@ -43,6 +47,7 @@ public class TransactionBatchProcessorTest { | |||||
| DataContractRegistry.register(EndpointRequest.class); | DataContractRegistry.register(EndpointRequest.class); | ||||
| DataContractRegistry.register(TransactionResponse.class); | DataContractRegistry.register(TransactionResponse.class); | ||||
| DataContractRegistry.register(UserRegisterOperation.class); | DataContractRegistry.register(UserRegisterOperation.class); | ||||
| DataContractRegistry.register(DataAccountRegisterOperation.class); | |||||
| } | } | ||||
| private static final String LEDGER_KEY_PREFIX = "LDG://"; | private static final String LEDGER_KEY_PREFIX = "LDG://"; | ||||
| @@ -54,19 +59,20 @@ public class TransactionBatchProcessorTest { | |||||
| private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | ||||
| private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | ||||
| private TransactionRequest transactionRequest; | |||||
| private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; | |||||
| // 采用基于内存的 Storage; | |||||
| private MemoryKVStorage storage = new MemoryKVStorage(); | |||||
| // TODO: 验证无效签名会被拒绝; | |||||
| @Test | @Test | ||||
| public void testTxReqProcess() { | |||||
| public void testSingleTxProcess() { | |||||
| final MemoryKVStorage STORAGE = new MemoryKVStorage(); | |||||
| // 初始化账本到指定的存储库; | // 初始化账本到指定的存储库; | ||||
| ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3); | |||||
| ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); | |||||
| // 加载账本; | // 加载账本; | ||||
| LedgerManager ledgerManager = new LedgerManager(); | LedgerManager ledgerManager = new LedgerManager(); | ||||
| LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
| LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
| // 验证参与方账户的存在; | // 验证参与方账户的存在; | ||||
| LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | ||||
| @@ -84,19 +90,283 @@ public class TransactionBatchProcessorTest { | |||||
| // 注册新用户; | // 注册新用户; | ||||
| BlockchainKeypair userKeypair = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair userKeypair = BlockchainKeyGenerator.getInstance().generate(); | ||||
| transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair, parti0); | |||||
| txbatchProcessor.schedule(transactionRequest); | |||||
| TransactionRequest transactionRequest = LedgerTestUtils.createTxRequest_UserReg(userKeypair, ledgerHash, parti0, | |||||
| parti0); | |||||
| TransactionResponse txResp = txbatchProcessor.schedule(transactionRequest); | |||||
| LedgerBlock newBlock = newBlockEditor.prepare(); | |||||
| newBlockEditor.commit(); | |||||
| // 验证正确性; | |||||
| ledgerManager = new LedgerManager(); | |||||
| ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
| LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | |||||
| assertEquals(newBlock.getHash(), latestBlock.getHash()); | |||||
| assertEquals(1, newBlock.getHeight()); | |||||
| assertEquals(TransactionState.SUCCESS, txResp.getExecutionState()); | |||||
| } | |||||
| @Test | |||||
| public void testMultiTxsProcess() { | |||||
| final MemoryKVStorage STORAGE = new MemoryKVStorage(); | |||||
| // 初始化账本到指定的存储库; | |||||
| ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); | |||||
| // 加载账本; | |||||
| LedgerManager ledgerManager = new LedgerManager(); | |||||
| LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
| // 验证参与方账户的存在; | |||||
| LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
| UserAccount user0 = previousBlockDataset.getUserAccountSet().getUser(parti0.getAddress()); | |||||
| assertNotNull(user0); | |||||
| boolean partiRegistered = previousBlockDataset.getUserAccountSet().contains(parti0.getAddress()); | |||||
| assertTrue(partiRegistered); | |||||
| // 生成新区块; | |||||
| LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
| OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
| TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
| opReg, ledgerManager); | |||||
| // 注册新用户; | |||||
| BlockchainKeypair userKeypair1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| TransactionRequest transactionRequest1 = LedgerTestUtils.createTxRequest_UserReg(userKeypair1, ledgerHash, | |||||
| parti0, parti0); | |||||
| TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest1); | |||||
| BlockchainKeypair userKeypair2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| TransactionRequest transactionRequest2 = LedgerTestUtils.createTxRequest_UserReg(userKeypair2, ledgerHash, | |||||
| parti0, parti0); | |||||
| TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest2); | |||||
| LedgerBlock newBlock = newBlockEditor.prepare(); | LedgerBlock newBlock = newBlockEditor.prepare(); | ||||
| newBlockEditor.commit(); | newBlockEditor.commit(); | ||||
| assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); | |||||
| assertEquals(TransactionState.SUCCESS, txResp2.getExecutionState()); | |||||
| // 验证正确性; | // 验证正确性; | ||||
| ledgerManager = new LedgerManager(); | ledgerManager = new LedgerManager(); | ||||
| ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
| ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
| LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | ||||
| assertEquals(newBlock.getHash(), latestBlock.getHash()); | assertEquals(newBlock.getHash(), latestBlock.getHash()); | ||||
| assertEquals(1, newBlock.getHeight()); | assertEquals(1, newBlock.getHeight()); | ||||
| LedgerDataSet ledgerDS = ledgerRepo.getDataSet(latestBlock); | |||||
| boolean existUser1 = ledgerDS.getUserAccountSet().contains(userKeypair1.getAddress()); | |||||
| boolean existUser2 = ledgerDS.getUserAccountSet().contains(userKeypair2.getAddress()); | |||||
| assertTrue(existUser1); | |||||
| assertTrue(existUser2); | |||||
| } | |||||
| @Test | |||||
| public void testTxRollback() { | |||||
| final MemoryKVStorage STORAGE = new MemoryKVStorage(); | |||||
| // 初始化账本到指定的存储库; | |||||
| ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); | |||||
| // 加载账本; | |||||
| LedgerManager ledgerManager = new LedgerManager(); | |||||
| LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
| // 验证参与方账户的存在; | |||||
| LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
| UserAccount user0 = previousBlockDataset.getUserAccountSet().getUser(parti0.getAddress()); | |||||
| assertNotNull(user0); | |||||
| boolean partiRegistered = previousBlockDataset.getUserAccountSet().contains(parti0.getAddress()); | |||||
| assertTrue(partiRegistered); | |||||
| // 生成新区块; | |||||
| LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
| OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
| TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
| opReg, ledgerManager); | |||||
| // 注册新用户; | |||||
| BlockchainKeypair userKeypair1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| TransactionRequest transactionRequest1 = LedgerTestUtils.createTxRequest_UserReg(userKeypair1, ledgerHash, | |||||
| parti0, parti0); | |||||
| TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest1); | |||||
| BlockchainKeypair userKeypair2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| TransactionRequest transactionRequest2 = LedgerTestUtils | |||||
| .createTxRequest_MultiOPs_WithNotExistedDataAccount(userKeypair2, ledgerHash, parti0, parti0); | |||||
| TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest2); | |||||
| BlockchainKeypair userKeypair3 = BlockchainKeyGenerator.getInstance().generate(); | |||||
| TransactionRequest transactionRequest3 = LedgerTestUtils.createTxRequest_UserReg(userKeypair3, ledgerHash, | |||||
| parti0, parti0); | |||||
| TransactionResponse txResp3 = txbatchProcessor.schedule(transactionRequest3); | |||||
| LedgerBlock newBlock = newBlockEditor.prepare(); | |||||
| newBlockEditor.commit(); | |||||
| assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); | |||||
| assertEquals(TransactionState.DATA_ACCOUNT_DOES_NOT_EXIST, txResp2.getExecutionState()); | |||||
| assertEquals(TransactionState.SUCCESS, txResp3.getExecutionState()); | |||||
| // 验证正确性; | |||||
| ledgerManager = new LedgerManager(); | |||||
| ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
| LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | |||||
| assertEquals(newBlock.getHash(), latestBlock.getHash()); | |||||
| assertEquals(1, newBlock.getHeight()); | |||||
| LedgerTransaction tx1 = ledgerRepo.getTransactionSet() | |||||
| .get(transactionRequest1.getTransactionContent().getHash()); | |||||
| LedgerTransaction tx2 = ledgerRepo.getTransactionSet() | |||||
| .get(transactionRequest2.getTransactionContent().getHash()); | |||||
| LedgerTransaction tx3 = ledgerRepo.getTransactionSet() | |||||
| .get(transactionRequest3.getTransactionContent().getHash()); | |||||
| assertNotNull(tx1); | |||||
| assertEquals(TransactionState.SUCCESS, tx1.getExecutionState()); | |||||
| assertNotNull(tx2); | |||||
| assertEquals(TransactionState.DATA_ACCOUNT_DOES_NOT_EXIST, tx2.getExecutionState()); | |||||
| assertNotNull(tx3); | |||||
| assertEquals(TransactionState.SUCCESS, tx3.getExecutionState()); | |||||
| LedgerDataSet ledgerDS = ledgerRepo.getDataSet(latestBlock); | |||||
| boolean existUser1 = ledgerDS.getUserAccountSet().contains(userKeypair1.getAddress()); | |||||
| boolean existUser2 = ledgerDS.getUserAccountSet().contains(userKeypair2.getAddress()); | |||||
| boolean existUser3 = ledgerDS.getUserAccountSet().contains(userKeypair3.getAddress()); | |||||
| assertTrue(existUser1); | |||||
| assertFalse(existUser2); | |||||
| assertTrue(existUser3); | |||||
| } | |||||
| @Test | |||||
| public void testTxRollbackByVersionsConfliction() { | |||||
| final MemoryKVStorage STORAGE = new MemoryKVStorage(); | |||||
| // 初始化账本到指定的存储库; | |||||
| ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); | |||||
| // 加载账本; | |||||
| LedgerManager ledgerManager = new LedgerManager(); | |||||
| LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
| // 验证参与方账户的存在; | |||||
| LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
| UserAccount user0 = previousBlockDataset.getUserAccountSet().getUser(parti0.getAddress()); | |||||
| assertNotNull(user0); | |||||
| boolean partiRegistered = previousBlockDataset.getUserAccountSet().contains(parti0.getAddress()); | |||||
| assertTrue(partiRegistered); | |||||
| // 注册数据账户; | |||||
| // 生成新区块; | |||||
| LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
| OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
| TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
| opReg, ledgerManager); | |||||
| BlockchainKeypair dataAccountKeypair = BlockchainKeyGenerator.getInstance().generate(); | |||||
| TransactionRequest transactionRequest1 = LedgerTestUtils.createTxRequest_DataAccountReg(dataAccountKeypair, | |||||
| ledgerHash, parti0, parti0); | |||||
| TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest1); | |||||
| LedgerBlock newBlock = newBlockEditor.prepare(); | |||||
| newBlockEditor.commit(); | |||||
| assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); | |||||
| DataAccount dataAccount = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()); | |||||
| assertNotNull(dataAccount); | |||||
| // 正确写入 KV 数据; | |||||
| TransactionRequest txreq1 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), | |||||
| "K1", "V-1-1", -1, ledgerHash, parti0, parti0); | |||||
| TransactionRequest txreq2 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), | |||||
| "K2", "V-2-1", -1, ledgerHash, parti0, parti0); | |||||
| TransactionRequest txreq3 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), | |||||
| "K3", "V-3-1", -1, ledgerHash, parti0, parti0); | |||||
| TransactionRequest txreq4 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), | |||||
| "K1", "V-1-2", 0, ledgerHash, parti0, parti0); | |||||
| newBlockEditor = ledgerRepo.createNextBlock(); | |||||
| previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
| txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, opReg, ledgerManager); | |||||
| txbatchProcessor.schedule(txreq1); | |||||
| txbatchProcessor.schedule(txreq2); | |||||
| txbatchProcessor.schedule(txreq3); | |||||
| txbatchProcessor.schedule(txreq4); | |||||
| newBlock = newBlockEditor.prepare(); | |||||
| newBlockEditor.commit(); | |||||
| BytesValue v1_0 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K1", | |||||
| 0); | |||||
| BytesValue v1_1 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K1", | |||||
| 1); | |||||
| BytesValue v2 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K2", | |||||
| 0); | |||||
| BytesValue v3 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K3", | |||||
| 0); | |||||
| assertNotNull(v1_0); | |||||
| assertNotNull(v1_1); | |||||
| assertNotNull(v2); | |||||
| assertNotNull(v3); | |||||
| assertEquals("V-1-1", v1_0.getValue().toUTF8String()); | |||||
| assertEquals("V-1-2", v1_1.getValue().toUTF8String()); | |||||
| assertEquals("V-2-1", v2.getValue().toUTF8String()); | |||||
| assertEquals("V-3-1", v3.getValue().toUTF8String()); | |||||
| // 提交多笔数据写入的交易,包含存在数据版本冲突的交易,验证交易是否正确回滚; | |||||
| TransactionRequest txreq5 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), | |||||
| "K3", "V-3-2", 0, ledgerHash, parti0, parti0); | |||||
| // 指定冲突的版本号,正确的应该是版本1; | |||||
| TransactionRequest txreq6 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), | |||||
| "K1", "V-1-3", 0, ledgerHash, parti0, parti0); | |||||
| newBlockEditor = ledgerRepo.createNextBlock(); | |||||
| previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
| txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, opReg, ledgerManager); | |||||
| txbatchProcessor.schedule(txreq5); | |||||
| txbatchProcessor.schedule(txreq6); | |||||
| newBlock = newBlockEditor.prepare(); | |||||
| newBlockEditor.commit(); | |||||
| BytesValue v1 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K1"); | |||||
| v3 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K3"); | |||||
| long k1_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getDataVersion("K1"); | |||||
| assertEquals(1, k1_version); | |||||
| long k3_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getDataVersion("K3"); | |||||
| assertEquals(1, k3_version); | |||||
| assertNotNull(v1); | |||||
| assertNotNull(v3); | |||||
| assertEquals("V-1-2", v1.getValue().toUTF8String()); | |||||
| assertEquals("V-3-2", v3.getValue().toUTF8String()); | |||||
| // // 验证正确性; | |||||
| // ledgerManager = new LedgerManager(); | |||||
| // ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
| // | |||||
| // LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | |||||
| // assertEquals(newBlock.getHash(), latestBlock.getHash()); | |||||
| // assertEquals(1, newBlock.getHeight()); | |||||
| // | |||||
| // LedgerTransaction tx1 = ledgerRepo.getTransactionSet() | |||||
| // .get(transactionRequest1.getTransactionContent().getHash()); | |||||
| // | |||||
| // assertNotNull(tx1); | |||||
| // assertEquals(TransactionState.SUCCESS, tx1.getExecutionState()); | |||||
| } | } | ||||
| private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | ||||
| @@ -106,7 +376,7 @@ public class TransactionBatchProcessorTest { | |||||
| // 创建账本; | // 创建账本; | ||||
| LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | ||||
| TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
| TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(partiKeys); | |||||
| LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
| LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | ||||
| @@ -128,7 +398,9 @@ public class TransactionBatchProcessorTest { | |||||
| assertNotNull(block.getHash()); | assertNotNull(block.getHash()); | ||||
| assertNull(block.getPreviousHash()); | assertNull(block.getPreviousHash()); | ||||
| assertEquals(block.getHash(), block.getLedgerHash()); | |||||
| // 创世区块的账本哈希为 null; | |||||
| assertNull(block.getLedgerHash()); | |||||
| assertNotNull(block.getHash()); | |||||
| // 提交数据,写入存储; | // 提交数据,写入存储; | ||||
| ldgEdt.commit(); | ldgEdt.commit(); | ||||
| @@ -102,8 +102,7 @@ public class TransactionSetTest { | |||||
| txSnapshot.setContractAccountSetHash(contractAccountSetHash); | txSnapshot.setContractAccountSetHash(contractAccountSetHash); | ||||
| long blockHeight = 8922L; | long blockHeight = 8922L; | ||||
| LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot, | |||||
| null); | |||||
| LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot); | |||||
| txset.add(tx); | txset.add(tx); | ||||
| assertTrue(txset.isUpdated()); | assertTrue(txset.isUpdated()); | ||||
| @@ -20,4 +20,7 @@ public interface BlockBody extends LedgerDataSnapshot{ | |||||
| @DataField(order=5, primitiveType = PrimitiveType.BYTES) | @DataField(order=5, primitiveType = PrimitiveType.BYTES) | ||||
| HashDigest getTransactionSetHash(); | HashDigest getTransactionSetHash(); | ||||
| @DataField(order=6, primitiveType = PrimitiveType.INT64) | |||||
| long getTimestamp(); | |||||
| } | } | ||||
| @@ -0,0 +1,33 @@ | |||||
| package com.jd.blockchain.ledger; | |||||
| public class BlockRollbackException extends LedgerException { | |||||
| private static final long serialVersionUID = 3583192000738807503L; | |||||
| private TransactionState state; | |||||
| public BlockRollbackException(String message) { | |||||
| this(TransactionState.SYSTEM_ERROR, message); | |||||
| } | |||||
| public BlockRollbackException(TransactionState state, String message) { | |||||
| super(message); | |||||
| assert TransactionState.SUCCESS != state; | |||||
| this.state = state; | |||||
| } | |||||
| public BlockRollbackException(String message, Throwable cause) { | |||||
| this(TransactionState.SYSTEM_ERROR, message, cause); | |||||
| } | |||||
| public BlockRollbackException(TransactionState state, String message, Throwable cause) { | |||||
| super(message, cause); | |||||
| assert TransactionState.SUCCESS != state; | |||||
| this.state = state; | |||||
| } | |||||
| public TransactionState getState() { | |||||
| return state; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,16 @@ | |||||
| package com.jd.blockchain.ledger; | |||||
| public class ContractDoesNotExistException extends LedgerException { | |||||
| private static final long serialVersionUID = 8685914012112243771L; | |||||
| public ContractDoesNotExistException(String message) { | |||||
| super(message); | |||||
| } | |||||
| public ContractDoesNotExistException(String message, Throwable cause) { | |||||
| super(message, cause); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,16 @@ | |||||
| package com.jd.blockchain.ledger; | |||||
| public class DataAccountDoesNotExistException extends LedgerException { | |||||
| private static final long serialVersionUID = -1889587937401974215L; | |||||
| public DataAccountDoesNotExistException(String message) { | |||||
| super(message); | |||||
| } | |||||
| public DataAccountDoesNotExistException(String message, Throwable cause) { | |||||
| super(message, cause); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,35 @@ | |||||
| package com.jd.blockchain.ledger; | |||||
| public class IllegalTransactionException extends RuntimeException { | |||||
| private static final long serialVersionUID = 6348921847690512944L; | |||||
| private TransactionState txState; | |||||
| public IllegalTransactionException(String message) { | |||||
| super(message); | |||||
| this.txState = TransactionState.SYSTEM_ERROR; | |||||
| } | |||||
| public IllegalTransactionException(String message, TransactionState txState) { | |||||
| super(message); | |||||
| assert TransactionState.SUCCESS != txState; | |||||
| this.txState = txState; | |||||
| } | |||||
| public IllegalTransactionException(String message, Throwable cause) { | |||||
| super(message, cause); | |||||
| this.txState = TransactionState.SYSTEM_ERROR; | |||||
| } | |||||
| public IllegalTransactionException(String message, Throwable cause, TransactionState txState) { | |||||
| super(message, cause); | |||||
| assert TransactionState.SUCCESS != txState; | |||||
| this.txState = txState; | |||||
| } | |||||
| public TransactionState getTxState() { | |||||
| return txState; | |||||
| } | |||||
| } | |||||
| @@ -3,6 +3,8 @@ package com.jd.blockchain.ledger; | |||||
| public class LedgerException extends RuntimeException { | public class LedgerException extends RuntimeException { | ||||
| private static final long serialVersionUID = -4090881296855827888L; | private static final long serialVersionUID = -4090881296855827888L; | ||||
| public LedgerException(String message) { | public LedgerException(String message) { | ||||
| super(message); | super(message); | ||||
| @@ -40,6 +40,6 @@ public interface TransactionContentBody { | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| @DataField(order = 3, primitiveType = PrimitiveType.INT64) | @DataField(order = 3, primitiveType = PrimitiveType.INT64) | ||||
| long getTime(); | |||||
| long getTimestamp(); | |||||
| } | } | ||||
| @@ -1,22 +0,0 @@ | |||||
| package com.jd.blockchain.ledger; | |||||
| public class TransactionException extends Exception { | |||||
| private static final long serialVersionUID = 3583192000738807503L; | |||||
| private TransactionState state; | |||||
| public TransactionException(TransactionState state) { | |||||
| this.state = state; | |||||
| } | |||||
| public TransactionException(TransactionState state, String message) { | |||||
| super(message); | |||||
| this.state = state; | |||||
| } | |||||
| public TransactionState getState() { | |||||
| return state; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,16 @@ | |||||
| package com.jd.blockchain.ledger; | |||||
| public class TransactionRollbackException extends RuntimeException { | |||||
| private static final long serialVersionUID = -1223140447229570029L; | |||||
| public TransactionRollbackException(String message) { | |||||
| super(message); | |||||
| } | |||||
| public TransactionRollbackException(String message, Throwable cause) { | |||||
| super(message, cause); | |||||
| } | |||||
| } | |||||
| @@ -11,7 +11,7 @@ import com.jd.blockchain.consts.DataCodes; | |||||
| * @author huanghaiquan | * @author huanghaiquan | ||||
| * | * | ||||
| */ | */ | ||||
| @EnumContract(code= DataCodes.ENUM_TYPE_TRANSACTION_STATE) | |||||
| @EnumContract(code = DataCodes.ENUM_TYPE_TRANSACTION_STATE) | |||||
| public enum TransactionState { | public enum TransactionState { | ||||
| /** | /** | ||||
| @@ -20,19 +20,58 @@ public enum TransactionState { | |||||
| SUCCESS((byte) 0), | SUCCESS((byte) 0), | ||||
| /** | /** | ||||
| * 共识错误; | |||||
| * 账本错误; | |||||
| */ | */ | ||||
| CONSENSUS_ERROR((byte) 1), | |||||
| LEDGER_ERROR((byte) 0x01), | |||||
| /** | /** | ||||
| * 账本错误; | |||||
| * 数据账户不存在; | |||||
| */ | |||||
| DATA_ACCOUNT_DOES_NOT_EXIST((byte) 0x02), | |||||
| /** | |||||
| * 用户不存在; | |||||
| */ | |||||
| USER_DOES_NOT_EXIST((byte) 0x03), | |||||
| /** | |||||
| * 合约不存在; | |||||
| */ | |||||
| CONTRACT_DOES_NOT_EXIST((byte) 0x04), | |||||
| /** | |||||
| * 由于在错误的账本上执行交易而被丢弃; | |||||
| */ | */ | ||||
| LEDGER_ERROR((byte) 2), | |||||
| IGNORED_BY_WRONG_LEDGER((byte) 0x40), | |||||
| /** | /** | ||||
| * 数据序列更新错误; | |||||
| * 由于交易内容的验签失败而丢弃; | |||||
| */ | */ | ||||
| DATA_SEQUENCE_UPDATE_ERROR((byte) 3), | |||||
| IGNORED_BY_WRONG_CONTENT_SIGNATURE((byte) 0x41), | |||||
| /** | |||||
| * 由于交易内容的验签失败而丢弃; | |||||
| */ | |||||
| IGNORED_BY_CONFLICTING_STATE((byte) 0x42), | |||||
| /** | |||||
| * 由于交易的整体回滚而丢弃; | |||||
| * <p> | |||||
| * | |||||
| * 注: “整体回滚”是指把交易引入的数据更改以及交易记录本身全部都回滚;<br> | |||||
| * “部分回滚”是指把交易引入的数据更改回滚了,但是交易记录本身以及相应的“交易结果({@link TransactionState})”都会提交;<br> | |||||
| */ | |||||
| IGNORED_BY_TX_FULL_ROLLBACK((byte) 0x43), | |||||
| /** | |||||
| * 由于区块的整体回滚而丢弃; | |||||
| * <p> | |||||
| * | |||||
| * 注: “整体回滚”是指把交易引入的数据更改以及交易记录本身全部都回滚;<br> | |||||
| * | |||||
| * “部分回滚”是指把交易引入的数据更改回滚了,但是交易记录本身以及相应的“交易结果({@link TransactionState})”都会提交;<br> | |||||
| */ | |||||
| IGNORED_BY_BLOCK_FULL_ROLLBACK((byte) 0x44), | |||||
| /** | /** | ||||
| * 系统错误; | * 系统错误; | ||||
| @@ -42,9 +81,14 @@ public enum TransactionState { | |||||
| /** | /** | ||||
| * 超时; | * 超时; | ||||
| */ | */ | ||||
| TIMEOUT((byte) 0x81); | |||||
| TIMEOUT((byte) 0x81), | |||||
| /** | |||||
| * 共识错误; | |||||
| */ | |||||
| CONSENSUS_ERROR((byte) 0x82); | |||||
| @EnumField(type= PrimitiveType.INT8) | |||||
| @EnumField(type = PrimitiveType.INT8) | |||||
| public final byte CODE; | public final byte CODE; | ||||
| private TransactionState(byte code) { | private TransactionState(byte code) { | ||||
| @@ -0,0 +1,15 @@ | |||||
| package com.jd.blockchain.ledger; | |||||
| public class UserDoesNotExistException extends LedgerException { | |||||
| private static final long serialVersionUID = 397450363050148898L; | |||||
| public UserDoesNotExistException(String message) { | |||||
| super(message); | |||||
| } | |||||
| public UserDoesNotExistException(String message, Throwable cause) { | |||||
| super(message, cause); | |||||
| } | |||||
| } | |||||
| @@ -55,12 +55,22 @@ public class TxBuilder implements TransactionBuilder { | |||||
| txContent.addOperations(opFactory.getOperations()); | txContent.addOperations(opFactory.getOperations()); | ||||
| txContent.setTime(time); | txContent.setTime(time); | ||||
| byte[] contentBodyBytes = BinaryProtocol.encode(txContent, TransactionContentBody.class); | |||||
| HashDigest contentHash = Crypto.getHashFunction(DEFAULT_HASH_ALGORITHM).hash(contentBodyBytes); | |||||
| HashDigest contentHash = computeTxContentHash(txContent); | |||||
| txContent.setHash(contentHash); | txContent.setHash(contentHash); | ||||
| return txContent; | return txContent; | ||||
| } | } | ||||
| public static HashDigest computeTxContentHash(TransactionContent txContent) { | |||||
| byte[] contentBodyBytes = BinaryProtocol.encode(txContent, TransactionContentBody.class); | |||||
| HashDigest contentHash = Crypto.getHashFunction(DEFAULT_HASH_ALGORITHM).hash(contentBodyBytes); | |||||
| return contentHash; | |||||
| } | |||||
| public static boolean verifyTxContentHash(TransactionContent txContent, HashDigest verifiedHash) { | |||||
| HashDigest hash = computeTxContentHash(txContent); | |||||
| return hash.equals(verifiedHash); | |||||
| } | |||||
| public Collection<OperationResultHandle> getReturnValuehandlers() { | public Collection<OperationResultHandle> getReturnValuehandlers() { | ||||
| return opFactory.getReturnValuetHandlers(); | return opFactory.getReturnValuetHandlers(); | ||||
| @@ -84,7 +84,7 @@ public class TxContentBlob implements TransactionContent { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public long getTime() { | |||||
| public long getTimestamp() { | |||||
| return time; | return time; | ||||
| } | } | ||||
| @@ -75,8 +75,14 @@ public class TxRequestBuilder implements TransactionRequestBuilder { | |||||
| } | } | ||||
| public static boolean verifySignature(TransactionContent txContent, SignatureDigest signDigest, PubKey pubKey) { | public static boolean verifySignature(TransactionContent txContent, SignatureDigest signDigest, PubKey pubKey) { | ||||
| return Crypto.getSignatureFunction(pubKey.getAlgorithm()).verify(signDigest, pubKey, | |||||
| txContent.getHash().toBytes()); | |||||
| if (!TxBuilder.verifyTxContentHash(txContent, txContent.getHash())) { | |||||
| return false; | |||||
| } | |||||
| return verifyHashSignature(txContent.getHash(), signDigest, pubKey); | |||||
| } | |||||
| public static boolean verifyHashSignature(HashDigest hash, SignatureDigest signDigest, PubKey pubKey) { | |||||
| return Crypto.getSignatureFunction(pubKey.getAlgorithm()).verify(signDigest, pubKey, hash.toBytes()); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -36,16 +36,16 @@ | |||||
| </dependency> | </dependency> | ||||
| </dependencies> | </dependencies> | ||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-deploy-plugin</artifactId> | |||||
| <version>2.8.2</version> | |||||
| <configuration> | |||||
| <skip>true</skip> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| <!--<build>--> | |||||
| <!--<plugins>--> | |||||
| <!--<plugin>--> | |||||
| <!--<groupId>org.apache.maven.plugins</groupId>--> | |||||
| <!--<artifactId>maven-deploy-plugin</artifactId>--> | |||||
| <!--<version>2.8.2</version>--> | |||||
| <!--<configuration>--> | |||||
| <!--<skip>true</skip>--> | |||||
| <!--</configuration>--> | |||||
| <!--</plugin>--> | |||||
| <!--</plugins>--> | |||||
| <!--</build>--> | |||||
| </project> | </project> | ||||
| @@ -8,7 +8,7 @@ | |||||
| <!--这个输出控制台的配置--> | <!--这个输出控制台的配置--> | ||||
| <console name="Console" target="SYSTEM_OUT"> | <console name="Console" target="SYSTEM_OUT"> | ||||
| <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | ||||
| <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
| <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
| <!--输出日志的格式--> | <!--输出日志的格式--> | ||||
| <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | ||||
| </console> | </console> | ||||
| @@ -8,11 +8,11 @@ | |||||
| <artifactId>spring-boot-starter-parent</artifactId> | <artifactId>spring-boot-starter-parent</artifactId> | ||||
| <version>2.0.6.RELEASE</version> | <version>2.0.6.RELEASE</version> | ||||
| </parent> | </parent> | ||||
| <!--<parent>--> | |||||
| <!--<groupId>org.sonatype.oss</groupId>--> | |||||
| <!--<artifactId>oss-parent</artifactId>--> | |||||
| <!--<version>7</version>--> | |||||
| <!--</parent>--> | |||||
| <!--<parent> --> | |||||
| <!--<groupId>org.sonatype.oss</groupId> --> | |||||
| <!--<artifactId>oss-parent</artifactId> --> | |||||
| <!--<version>7</version> --> | |||||
| <!--</parent> --> | |||||
| <groupId>com.jd.blockchain</groupId> | <groupId>com.jd.blockchain</groupId> | ||||
| <artifactId>jdchain-root</artifactId> | <artifactId>jdchain-root</artifactId> | ||||
| @@ -92,8 +92,12 @@ | |||||
| <artifactId>mockito-core</artifactId> | <artifactId>mockito-core</artifactId> | ||||
| <scope>test</scope> | <scope>test</scope> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-starter-log4j2</artifactId> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| <dependencyManagement> | <dependencyManagement> | ||||
| <dependencies> | <dependencies> | ||||
| <dependency> | <dependency> | ||||
| @@ -171,7 +175,7 @@ | |||||
| <dependency> | <dependency> | ||||
| <groupId>com.alibaba</groupId> | <groupId>com.alibaba</groupId> | ||||
| <artifactId>fastjson</artifactId> | <artifactId>fastjson</artifactId> | ||||
| <version>1.2.32</version> | |||||
| <version>1.2.58</version> | |||||
| </dependency> | </dependency> | ||||
| <dependency> | <dependency> | ||||
| @@ -446,22 +450,22 @@ | |||||
| </execution> | </execution> | ||||
| </executions> | </executions> | ||||
| </plugin> | </plugin> | ||||
| <!--<!– 生成sources源码包的插件 –>--> | |||||
| <!--<plugin>--> | |||||
| <!--<artifactId>maven-source-plugin</artifactId>--> | |||||
| <!--<version>2.4</version>--> | |||||
| <!--<configuration>--> | |||||
| <!--<attach>true</attach>--> | |||||
| <!--</configuration>--> | |||||
| <!--<executions>--> | |||||
| <!--<execution>--> | |||||
| <!--<phase>package</phase>--> | |||||
| <!--<goals>--> | |||||
| <!--<goal>jar-no-fork</goal>--> | |||||
| <!--</goals>--> | |||||
| <!--</execution>--> | |||||
| <!--</executions>--> | |||||
| <!--</plugin>--> | |||||
| <!--<!– 生成sources源码包的插件 –> --> | |||||
| <!--<plugin> --> | |||||
| <!--<artifactId>maven-source-plugin</artifactId> --> | |||||
| <!--<version>2.4</version> --> | |||||
| <!--<configuration> --> | |||||
| <!--<attach>true</attach> --> | |||||
| <!--</configuration> --> | |||||
| <!--<executions> --> | |||||
| <!--<execution> --> | |||||
| <!--<phase>package</phase> --> | |||||
| <!--<goals> --> | |||||
| <!--<goal>jar-no-fork</goal> --> | |||||
| <!--</goals> --> | |||||
| <!--</execution> --> | |||||
| <!--</executions> --> | |||||
| <!--</plugin> --> | |||||
| </plugins> | </plugins> | ||||
| </pluginManagement> | </pluginManagement> | ||||
| @@ -1,58 +1,58 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>sdk</artifactId> | |||||
| <version>1.1.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>sdk-base</artifactId> | |||||
| <dependencies> | |||||
| <!--<dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>ledger-core</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency>--> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>ledger-model</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>consensus-framework</artifactId> | |||||
| <version>${project.version}</version> | |||||
| <exclusions> | |||||
| <exclusion> | |||||
| <artifactId>binary-proto</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| </exclusion> | |||||
| <exclusion> | |||||
| <artifactId>crypto-framework</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| </exclusion> | |||||
| <exclusion> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| </exclusion> | |||||
| </exclusions> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-serialize</artifactId> | |||||
| <version>${project.version}</version> | |||||
| <exclusions> | |||||
| <exclusion> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| </exclusion> | |||||
| </exclusions> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <!-- <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> --> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>sdk</artifactId> | |||||
| <version>1.1.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>sdk-base</artifactId> | |||||
| <dependencies> | |||||
| <!--<dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>ledger-core</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency>--> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>ledger-model</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>consensus-framework</artifactId> | |||||
| <version>${project.version}</version> | |||||
| <exclusions> | |||||
| <exclusion> | |||||
| <artifactId>binary-proto</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| </exclusion> | |||||
| <exclusion> | |||||
| <artifactId>crypto-framework</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| </exclusion> | |||||
| <exclusion> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| </exclusion> | |||||
| </exclusions> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-serialize</artifactId> | |||||
| <version>${project.version}</version> | |||||
| <exclusions> | |||||
| <exclusion> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| </exclusion> | |||||
| </exclusions> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <!-- <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> --> | |||||
| </project> | </project> | ||||
| @@ -1,95 +1,91 @@ | |||||
| package test.com.jd.blockchain.sdk.proxy; | |||||
| import static org.junit.Assert.assertEquals; | |||||
| import static org.mockito.Matchers.any; | |||||
| import static org.mockito.Mockito.verify; | |||||
| public class BlockchainServiceProxyTest { | |||||
| // private static class ArgCaptorMatcher<T> extends CustomMatcher<T> { | |||||
| // | |||||
| // private T arg; | |||||
| // | |||||
| // public ArgCaptorMatcher() { | |||||
| // super("OK"); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public boolean matches(Object item) { | |||||
| // this.arg = (T) item; | |||||
| // return true; | |||||
| // } | |||||
| // | |||||
| // public T getArg() { | |||||
| // return arg; | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // @Test | |||||
| // public void testRegisterAccount() throws IOException { | |||||
| // | |||||
| // BlockchainKeyPair gatewayAccount = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); | |||||
| // BlockchainKeyPair sponsorAccount = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); | |||||
| // BlockchainKeyPair subjectAccount = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); | |||||
| // long sequenceNumber = 110; | |||||
| // | |||||
| // ArgCaptorMatcher<TransactionRequest> txReqCaptor = new ArgCaptorMatcher<>(); | |||||
| // | |||||
| // TransactionService consensusService = Mockito.mocker(TransactionService.class); | |||||
| // BlockchainQueryService queryService = Mockito.mocker(BlockchainQueryService.class); | |||||
| // | |||||
| // HashDigest txContentHash =CryptoUtils.hash(CryptoAlgorithm.SHA_256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); | |||||
| // TxResponseMessage expectedResponse = new TxResponseMessage(txContentHash); | |||||
| // expectedResponse.setExecutionState(ExecutionState.SUCCESS); | |||||
| // | |||||
| // when(consensusService.process(argThat(txReqCaptor))).thenReturn(expectedResponse); | |||||
| // | |||||
| // HashDigest ledgerHash = CryptoUtils.hash(CryptoAlgorithm.SHA_256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); | |||||
| // | |||||
| // BlockchainTransactionService serviceProxy = new BlockchainServiceProxy(consensusService, queryService); | |||||
| // | |||||
| // TransactionTemplate txTemplate = serviceProxy.newTransaction(ledgerHash); | |||||
| // txTemplate.setSubject(subjectAccount.getAddress(), sequenceNumber); | |||||
| // | |||||
| // BlockchainKeyPair regAccountKeyPair = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); | |||||
| // AccountStateType stateType = AccountStateType.MAP; | |||||
| // txTemplate.users().register(regAccountKeyPair.getIdentity(), stateType); | |||||
| // | |||||
| // PreparedTransaction prepTx = txTemplate.prepare(); | |||||
| // HashDigest txHash = prepTx.getHash(); | |||||
| // prepTx.sign(sponsorAccount); | |||||
| // | |||||
| // TransactionResponse result = prepTx.commit(); | |||||
| // | |||||
| // // 验证; | |||||
| // // 仅被提交一次; | |||||
| // verify(consensusService, times(1)).process(any()); | |||||
| // | |||||
| // assertEquals(ExecutionState.SUCCESS, result.getExecutionState()); | |||||
| // | |||||
| // // 验证内容; | |||||
| // TransactionRequest resolvedTxRequest = txReqCaptor.getArg(); | |||||
| // | |||||
| // TransactionContent resolvedTxContent = resolvedTxRequest.getTransactionContent(); | |||||
| // | |||||
| // assertEquals(txHash, resolvedTxContent.getHash()); | |||||
| // | |||||
| // assertEquals(subjectAccount.getAddress(), resolvedTxContent.getSubjectAccount()); | |||||
| // assertEquals(sequenceNumber, resolvedTxContent.getSequenceNumber()); | |||||
| // | |||||
| // | |||||
| // Operation[] resolvedOps = resolvedTxContent.getOperations(); | |||||
| // assertEquals(1, resolvedOps.length); | |||||
| // Operation resolvedOP = resolvedOps[0]; | |||||
| //// assertEquals(OperationType.REGISTER_USER.CODE, resolvedOP.getCode()); | |||||
| // | |||||
| // UserRegisterOpTemplate accRegOP = new UserRegisterOpTemplate(); | |||||
| // accRegOP.resolvFrom((OpBlob) resolvedOP); | |||||
| // | |||||
| // assertEquals(regAccountKeyPair.getAddress(), accRegOP.getId().getAddress()); | |||||
| // assertEquals(regAccountKeyPair.getPubKey().getType(), accRegOP.getId().getPubKey().getType()); | |||||
| // assertEquals(regAccountKeyPair.getPubKey().getValue(), accRegOP.getId().getPubKey().getValue()); | |||||
| // assertEquals(stateType, accRegOP.getStateType()); | |||||
| // } | |||||
| } | |||||
| package test.com.jd.blockchain.sdk.proxy; | |||||
| public class BlockchainServiceProxyTest { | |||||
| // private static class ArgCaptorMatcher<T> extends CustomMatcher<T> { | |||||
| // | |||||
| // private T arg; | |||||
| // | |||||
| // public ArgCaptorMatcher() { | |||||
| // super("OK"); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public boolean matches(Object item) { | |||||
| // this.arg = (T) item; | |||||
| // return true; | |||||
| // } | |||||
| // | |||||
| // public T getArg() { | |||||
| // return arg; | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // @Test | |||||
| // public void testRegisterAccount() throws IOException { | |||||
| // | |||||
| // BlockchainKeyPair gatewayAccount = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); | |||||
| // BlockchainKeyPair sponsorAccount = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); | |||||
| // BlockchainKeyPair subjectAccount = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); | |||||
| // long sequenceNumber = 110; | |||||
| // | |||||
| // ArgCaptorMatcher<TransactionRequest> txReqCaptor = new ArgCaptorMatcher<>(); | |||||
| // | |||||
| // TransactionService consensusService = Mockito.mocker(TransactionService.class); | |||||
| // BlockchainQueryService queryService = Mockito.mocker(BlockchainQueryService.class); | |||||
| // | |||||
| // HashDigest txContentHash =CryptoUtils.hash(CryptoAlgorithm.SHA_256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); | |||||
| // TxResponseMessage expectedResponse = new TxResponseMessage(txContentHash); | |||||
| // expectedResponse.setExecutionState(ExecutionState.SUCCESS); | |||||
| // | |||||
| // when(consensusService.process(argThat(txReqCaptor))).thenReturn(expectedResponse); | |||||
| // | |||||
| // HashDigest ledgerHash = CryptoUtils.hash(CryptoAlgorithm.SHA_256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); | |||||
| // | |||||
| // BlockchainTransactionService serviceProxy = new BlockchainServiceProxy(consensusService, queryService); | |||||
| // | |||||
| // TransactionTemplate txTemplate = serviceProxy.newTransaction(ledgerHash); | |||||
| // txTemplate.setSubject(subjectAccount.getAddress(), sequenceNumber); | |||||
| // | |||||
| // BlockchainKeyPair regAccountKeyPair = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); | |||||
| // AccountStateType stateType = AccountStateType.MAP; | |||||
| // txTemplate.users().register(regAccountKeyPair.getIdentity(), stateType); | |||||
| // | |||||
| // PreparedTransaction prepTx = txTemplate.prepare(); | |||||
| // HashDigest txHash = prepTx.getHash(); | |||||
| // prepTx.sign(sponsorAccount); | |||||
| // | |||||
| // TransactionResponse result = prepTx.commit(); | |||||
| // | |||||
| // // 验证; | |||||
| // // 仅被提交一次; | |||||
| // verify(consensusService, times(1)).process(any()); | |||||
| // | |||||
| // assertEquals(ExecutionState.SUCCESS, result.getExecutionState()); | |||||
| // | |||||
| // // 验证内容; | |||||
| // TransactionRequest resolvedTxRequest = txReqCaptor.getArg(); | |||||
| // | |||||
| // TransactionContent resolvedTxContent = resolvedTxRequest.getTransactionContent(); | |||||
| // | |||||
| // assertEquals(txHash, resolvedTxContent.getHash()); | |||||
| // | |||||
| // assertEquals(subjectAccount.getAddress(), resolvedTxContent.getSubjectAccount()); | |||||
| // assertEquals(sequenceNumber, resolvedTxContent.getSequenceNumber()); | |||||
| // | |||||
| // | |||||
| // Operation[] resolvedOps = resolvedTxContent.getOperations(); | |||||
| // assertEquals(1, resolvedOps.length); | |||||
| // Operation resolvedOP = resolvedOps[0]; | |||||
| //// assertEquals(OperationType.REGISTER_USER.CODE, resolvedOP.getCode()); | |||||
| // | |||||
| // UserRegisterOpTemplate accRegOP = new UserRegisterOpTemplate(); | |||||
| // accRegOP.resolvFrom((OpBlob) resolvedOP); | |||||
| // | |||||
| // assertEquals(regAccountKeyPair.getAddress(), accRegOP.getId().getAddress()); | |||||
| // assertEquals(regAccountKeyPair.getPubKey().getType(), accRegOP.getId().getPubKey().getType()); | |||||
| // assertEquals(regAccountKeyPair.getPubKey().getValue(), accRegOP.getId().getPubKey().getValue()); | |||||
| // assertEquals(stateType, accRegOP.getStateType()); | |||||
| // } | |||||
| } | |||||
| @@ -1,45 +0,0 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <parent> | |||||
| <artifactId>sdk</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <version>0.9.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <artifactId>sdk-mq</artifactId> | |||||
| <properties> | |||||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
| <maven.compiler.source>1.8</maven.compiler.source> | |||||
| <maven.compiler.target>1.8</maven.compiler.target> | |||||
| </properties> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>junit</groupId> | |||||
| <artifactId>junit</artifactId> | |||||
| <version>4.11</version> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>sdk-base</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.lmax</groupId> | |||||
| <artifactId>disruptor</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>io.nats</groupId> | |||||
| <artifactId>jnats</artifactId> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| </build> | |||||
| </project> | |||||
| @@ -1,34 +0,0 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>jdchain-root</artifactId> | |||||
| <version>0.9.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>state-transfer</artifactId> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>stp-communication</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-serialize</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| </project> | |||||
| @@ -1,75 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer; | |||||
| import java.net.InetSocketAddress; | |||||
| import java.util.LinkedList; | |||||
| /** | |||||
| * 测试过程建立的一个数据序列 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DataSequence { | |||||
| private InetSocketAddress address; | |||||
| private String id; | |||||
| // 每个数据序列维护了一系列的数据序列元素 | |||||
| private LinkedList<DataSequenceElement> dataSequenceElements = new LinkedList<>(); | |||||
| public DataSequence(InetSocketAddress address, String id) { | |||||
| this.address = address; | |||||
| this.id = id; | |||||
| } | |||||
| public String getId() { | |||||
| return id; | |||||
| } | |||||
| public InetSocketAddress getAddress() { | |||||
| return address; | |||||
| } | |||||
| public void addElements(DataSequenceElement[] elements) { | |||||
| for (DataSequenceElement element : elements) { | |||||
| addElement(element); | |||||
| } | |||||
| } | |||||
| public void addElement(DataSequenceElement element) { | |||||
| try { | |||||
| if (dataSequenceElements.size() == 0) { | |||||
| if (element.getHeight() != 0) { | |||||
| throw new IllegalArgumentException("Data sequence add element height error!"); | |||||
| } | |||||
| dataSequenceElements.addLast(element); | |||||
| } | |||||
| else { | |||||
| if (dataSequenceElements.getLast().getHeight() != element.getHeight() - 1) { | |||||
| throw new IllegalArgumentException("Data sequence add element height error!"); | |||||
| } | |||||
| dataSequenceElements.addLast(element); | |||||
| } | |||||
| } catch (Exception e) { | |||||
| System.out.println(e.getMessage()); | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| public LinkedList<DataSequenceElement> getDataSequenceElements() { | |||||
| return dataSequenceElements; | |||||
| } | |||||
| public DataSequenceInfo getDSInfo() { | |||||
| if (dataSequenceElements.size() == 0) { | |||||
| return new DataSequenceInfo(id, -1); | |||||
| } | |||||
| else { | |||||
| return new DataSequenceInfo(id, dataSequenceElements.getLast().getHeight()); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,53 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer; | |||||
| import java.io.Serializable; | |||||
| /** | |||||
| * 数据序列需要复制内容的元素或单位 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DataSequenceElement implements Serializable { | |||||
| private static final long serialVersionUID = -719578198150380571L; | |||||
| //数据序列的唯一标识符; | |||||
| private String id; | |||||
| //数据序列的某个高度; | |||||
| private long height; | |||||
| //对应某个高度的数据序列内容 | |||||
| private byte[][] data; | |||||
| public DataSequenceElement(String id, long height, byte[][] data) { | |||||
| this.id = id; | |||||
| this.height = height; | |||||
| this.data = data; | |||||
| } | |||||
| public long getHeight() { | |||||
| return height; | |||||
| } | |||||
| public void setHeight(long height) { | |||||
| this.height = height; | |||||
| } | |||||
| public String getId() { | |||||
| return id; | |||||
| } | |||||
| public void setId(String id) { | |||||
| id = id; | |||||
| } | |||||
| public byte[][] getData() { | |||||
| return data; | |||||
| } | |||||
| public void setData(byte[][] data) { | |||||
| this.data = data; | |||||
| } | |||||
| } | |||||
| @@ -1,37 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer; | |||||
| /** | |||||
| * 共识结点上的某个数据序列的当前状态信息,每个共识结点可以对应任意个数据序列; | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DataSequenceInfo { | |||||
| //数据序列的唯一标识 | |||||
| private String id; | |||||
| //数据序列的当前高度 | |||||
| private long height; | |||||
| public DataSequenceInfo(String id, long height) { | |||||
| this.id = id; | |||||
| this.height = height; | |||||
| } | |||||
| public String getId() { | |||||
| return id; | |||||
| } | |||||
| public void setId(String id) { | |||||
| this.id = id; | |||||
| } | |||||
| public long getHeight() { | |||||
| return height; | |||||
| } | |||||
| public void setHeight(long height) { | |||||
| this.height = height; | |||||
| } | |||||
| } | |||||
| @@ -1,39 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.callback; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceInfo; | |||||
| /** | |||||
| * 数据序列差异提供者需要使用的回调接口 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public interface DataSequenceReader { | |||||
| /** | |||||
| * 差异提供者根据数据序列标识符获取数据序列当前状态; | |||||
| * @param id 数据序列标识符 | |||||
| * @return 数据序列当前状态信息 | |||||
| */ | |||||
| DataSequenceInfo getDSInfo(String id); | |||||
| /** | |||||
| * 差异提供者根据数据序列标识符以及起始,结束高度提供数据序列该范围的差异内容; | |||||
| * @param id 数据序列标识符 | |||||
| * @param from 差异的起始高度 | |||||
| * @param to 差异的结束高度 | |||||
| * @return 差异元素组成的数组 | |||||
| */ | |||||
| DataSequenceElement[] getDSDiffContent(String id, long from, long to); | |||||
| /** | |||||
| * 差异提供者根据数据序列标识符以及高度提供数据序列的差异内容; | |||||
| * @param id 数据序列标识符 | |||||
| * @param height 要获得哪个高度的差异元素 | |||||
| * @return 差异元素 | |||||
| */ | |||||
| DataSequenceElement getDSDiffContent(String id, long height); | |||||
| } | |||||
| @@ -1,59 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.callback; | |||||
| import com.jd.blockchain.statetransfer.DataSequence; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceInfo; | |||||
| import java.util.LinkedList; | |||||
| /** | |||||
| * 数据序列差异的提供者需要使用的回调接口实现类 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/22 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DataSequenceReaderImpl implements DataSequenceReader { | |||||
| DataSequence currDataSequence; | |||||
| public DataSequenceReaderImpl(DataSequence currDataSequence) { | |||||
| this.currDataSequence = currDataSequence; | |||||
| } | |||||
| @Override | |||||
| public DataSequenceInfo getDSInfo(String id) { | |||||
| return currDataSequence.getDSInfo(); | |||||
| } | |||||
| @Override | |||||
| public DataSequenceElement[] getDSDiffContent(String id, long from, long to) { | |||||
| DataSequenceElement[] elements = new DataSequenceElement[(int)(to - from + 1)]; | |||||
| int i = 0; | |||||
| LinkedList<DataSequenceElement> dataSequenceElements = currDataSequence.getDataSequenceElements(); | |||||
| for (DataSequenceElement element : dataSequenceElements) { | |||||
| if (element.getHeight() < from || element.getHeight() > to) { | |||||
| continue; | |||||
| } | |||||
| else { | |||||
| elements[i++] = element; | |||||
| } | |||||
| } | |||||
| return elements; | |||||
| } | |||||
| @Override | |||||
| public DataSequenceElement getDSDiffContent(String id, long height) { | |||||
| for(DataSequenceElement dataSequenceElement : currDataSequence.getDataSequenceElements()) { | |||||
| if (dataSequenceElement.getHeight() == height) { | |||||
| return dataSequenceElement; | |||||
| } | |||||
| } | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @@ -1,30 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.callback; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceInfo; | |||||
| /** | |||||
| * 数据序列差异请求者获得差异内容后需要回调该接口 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public interface DataSequenceWriter { | |||||
| /** | |||||
| * 差异请求者更新本地数据序列的状态,一次可以更新多个差异元素 | |||||
| * @param dsInfo 数据序列当前状态信息 | |||||
| * @param diffContents 需要更新的差异元素数组 | |||||
| * @return 更新结果编码 | |||||
| */ | |||||
| int updateDSInfo(DataSequenceInfo dsInfo, DataSequenceElement[] diffContents); | |||||
| /** | |||||
| * 差异请求者更新本地数据序列的状态,一次只更新一个差异元素 | |||||
| * @param dsInfo 数据序列当前状态信息 | |||||
| * @param diffContent 需要更新的差异元素 | |||||
| * @return 更新结果编码 | |||||
| */ | |||||
| // int updateDSInfo(DataSequenceInfo dsInfo, DataSequenceElement diffContent); | |||||
| } | |||||
| @@ -1,142 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.callback; | |||||
| import com.jd.blockchain.statetransfer.DataSequence; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceInfo; | |||||
| import java.util.ArrayList; | |||||
| /** | |||||
| * 数据序列差异的请求者需要使用的回调接口实现类 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/22 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DataSequenceWriterImpl implements DataSequenceWriter { | |||||
| private long currHeight; | |||||
| private DataSequence currDataSequence; | |||||
| private ArrayList<DataSequenceElement> deceidedElements = new ArrayList<DataSequenceElement>(); | |||||
| public DataSequenceWriterImpl(DataSequence currDataSequence) { | |||||
| this.currDataSequence = currDataSequence; | |||||
| } | |||||
| /** | |||||
| * 检查数据序列差异元素中的高度是否合理; | |||||
| * @param currHeight 当前结点的账本高度 | |||||
| * @param dsUpdateElements 需要更新到本地结点的数据序列元素List | |||||
| * @return | |||||
| */ | |||||
| private int checkElementsHeight(long currHeight, ArrayList<DataSequenceElement> dsUpdateElements) { | |||||
| boolean lossMiddleElements = false; | |||||
| // lose first element | |||||
| if (currHeight + 1 < dsUpdateElements.get(0).getHeight()){ | |||||
| System.out.println("Diff response loss first element error!"); | |||||
| return DataSequenceErrorType.DATA_SEQUENCE_LOSS_FIRST_ELEMENT.CODE; | |||||
| } | |||||
| else { | |||||
| for (int i = 0; i < dsUpdateElements.size(); i++) { | |||||
| if (dsUpdateElements.get(i).getHeight() == currHeight + 1 + i) { | |||||
| deceidedElements.add(dsUpdateElements.get(i)); | |||||
| } | |||||
| // lose middle elements | |||||
| else { | |||||
| lossMiddleElements = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (lossMiddleElements) { | |||||
| System.out.println("Diff response loss middle elements error!"); | |||||
| return DataSequenceErrorType.DATA_SEQUENCE_LOSS_MIDDLE_ELEMENT.CODE; | |||||
| } | |||||
| System.out.println("Diff response elements height normal!"); | |||||
| return DataSequenceErrorType.DATA_SEQUENCE_ELEMENT_HEIGHT_NORMAL.CODE; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public int updateDSInfo(DataSequenceInfo dsInfo, DataSequenceElement[] diffContents) { | |||||
| int result = 0; | |||||
| try { | |||||
| ArrayList<DataSequenceElement> dsUpdateElements = new ArrayList<DataSequenceElement>(); | |||||
| if (diffContents == null) { | |||||
| throw new IllegalArgumentException("Update diffContents is null!"); | |||||
| } | |||||
| //remove unexpected elements | |||||
| for (int i = 0 ; i < diffContents.length; i++) { | |||||
| if (diffContents[i].getId().equals(dsInfo.getId())) { | |||||
| dsUpdateElements.add(diffContents[i]); | |||||
| } | |||||
| } | |||||
| currHeight = dsInfo.getHeight(); | |||||
| // check element's height | |||||
| result = checkElementsHeight(currHeight, dsUpdateElements); | |||||
| // cann't exe update | |||||
| if (result == DataSequenceErrorType.DATA_SEQUENCE_LOSS_FIRST_ELEMENT.CODE) { | |||||
| return result; | |||||
| } | |||||
| // exe elements update | |||||
| else { | |||||
| System.out.println("Old data sequence state: "); | |||||
| System.out.println(" Current height = " + currDataSequence.getDataSequenceElements().getLast().getHeight()); | |||||
| currDataSequence.addElements(deceidedElements.toArray(new DataSequenceElement[deceidedElements.size()])); | |||||
| System.out.println("Update diffContents is completed!"); | |||||
| System.out.println("New data sequence state: "); | |||||
| System.out.println(" Current height = " + currDataSequence.getDataSequenceElements().getLast().getHeight()); | |||||
| return result; | |||||
| } | |||||
| } catch (Exception e) { | |||||
| System.out.println(e.getMessage()); | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return DataSequenceErrorType.DATA_SEQUENCE_ELEMENT_HEIGHT_NORMAL.CODE; | |||||
| } | |||||
| // @Override | |||||
| // public int updateDSInfo(DataSequenceInfo dsInfo, DataSequenceElement diffContent) { | |||||
| // currDataSequence.addElement(diffContent); | |||||
| // return DataSequenceErrorType.DATA_SEQUENCE_ELEMENT_HEIGHT_NORMAL.CODE; | |||||
| // } | |||||
| public enum DataSequenceErrorType { | |||||
| DATA_SEQUENCE_LOSS_FIRST_ELEMENT((byte) 0x1), | |||||
| DATA_SEQUENCE_LOSS_MIDDLE_ELEMENT((byte) 0x2), | |||||
| DATA_SEQUENCE_ELEMENT_HEIGHT_NORMAL((byte) 0x3), | |||||
| ; | |||||
| public final int CODE; | |||||
| private DataSequenceErrorType(byte code) { | |||||
| this.CODE = code; | |||||
| } | |||||
| public static DataSequenceErrorType valueOf(byte code) { | |||||
| for (DataSequenceErrorType errorType : DataSequenceErrorType.values()) { | |||||
| if (errorType.CODE == code) { | |||||
| return errorType; | |||||
| } | |||||
| } | |||||
| throw new IllegalArgumentException("Unsupported code[" + code + "] of errorType!"); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,32 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.comparator; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import java.util.Comparator; | |||||
| /** | |||||
| * 数据序列差异元素的高度比较器 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DataSequenceComparator implements Comparator<DataSequenceElement> { | |||||
| // sort by data sequence height | |||||
| /** | |||||
| * 对差异元素根据高度大小排序 | |||||
| * @param o1 差异元素1 | |||||
| * @param o2 差异元素2 | |||||
| * @return >0 or <0 | |||||
| */ | |||||
| @Override | |||||
| public int compare(DataSequenceElement o1, DataSequenceElement o2) { | |||||
| long height1; | |||||
| long height2; | |||||
| height1 = o1.getHeight(); | |||||
| height2 = o2.getHeight(); | |||||
| return (int) (height1 - height2); | |||||
| } | |||||
| } | |||||
| @@ -1,21 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.exception; | |||||
| /** | |||||
| * 数据序列异常处理 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DataSequenceException extends RuntimeException { | |||||
| private static final long serialVersionUID = -4090881296855827889L; | |||||
| public DataSequenceException(String message) { | |||||
| super(message); | |||||
| } | |||||
| public DataSequenceException(String message, Throwable cause) { | |||||
| super(message, cause); | |||||
| } | |||||
| } | |||||
| @@ -1,80 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.message; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceReader; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceWriter; | |||||
| import com.jd.blockchain.statetransfer.process.DSTransferProcess; | |||||
| import com.jd.blockchain.statetransfer.result.DSDiffRequestResult; | |||||
| import com.jd.blockchain.stp.communication.MessageExecutor; | |||||
| import com.jd.blockchain.stp.communication.RemoteSession; | |||||
| /** | |||||
| * 数据序列差异提供者使用,解析收到的差异请求消息并产生响应 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DSDefaultMessageExecutor implements MessageExecutor { | |||||
| DataSequenceReader dsReader; | |||||
| DataSequenceWriter dsWriter; | |||||
| public DSDefaultMessageExecutor(DataSequenceReader dsReader, DataSequenceWriter dsWriter) { | |||||
| this.dsReader = dsReader; | |||||
| this.dsWriter = dsWriter; | |||||
| } | |||||
| /** | |||||
| * 对状态机复制的差异请求进行响应 | |||||
| * @param key 请求消息的Key | |||||
| * @param data 需要解码的字节数组 | |||||
| * @param session 指定响应需要使用的目标结点会话 | |||||
| * @return 配置为自动响应时,返回值为响应的字节数组,配置为手动响应时,不需要关注返回值 | |||||
| */ | |||||
| @Override | |||||
| public byte[] receive(String key, byte[] data, RemoteSession session) { | |||||
| try { | |||||
| Object object = DSMsgResolverFactory.getDecoder(dsWriter, dsReader).decode(data); | |||||
| // 解析CMD_DSINFO_REQUEST 请求的情况 | |||||
| if (object instanceof String) { | |||||
| String id = (String)object; | |||||
| byte[] respLoadMsg = DSMsgResolverFactory.getEncoder(dsWriter, dsReader).encode(DSTransferProcess.DataSequenceMsgType.CMD_DSINFO_RESPONSE, id, 0, 0); | |||||
| session.reply(key, new DataSequenceLoadMessage(respLoadMsg)); | |||||
| } | |||||
| // 解析CMD_GETDSDIFF_REQUEST 请求的情况 | |||||
| else if (object instanceof DSDiffRequestResult) { | |||||
| DSDiffRequestResult requestResult = (DSDiffRequestResult)object; | |||||
| String id = requestResult.getId(); | |||||
| long fromHeight = requestResult.getFromHeight(); | |||||
| long toHeight = requestResult.getToHeight(); | |||||
| //每个高度的数据序列差异元素进行一次响应的情况 | |||||
| for (long i = fromHeight; i < toHeight + 1; i++) { | |||||
| byte[] respLoadMsg = DSMsgResolverFactory.getEncoder(dsWriter, dsReader).encode(DSTransferProcess.DataSequenceMsgType.CMD_GETDSDIFF_RESPONSE, id, i, i); | |||||
| session.reply(key, new DataSequenceLoadMessage(respLoadMsg)); | |||||
| } | |||||
| //所有差异进行一次响应的情况 | |||||
| } | |||||
| else { | |||||
| throw new IllegalArgumentException("Receive data exception, unknown message type!"); | |||||
| } | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| /** | |||||
| * 响应类型设置 | |||||
| * 分手动响应,自动响应两种类型 | |||||
| */ | |||||
| @Override | |||||
| public REPLY replyType() { | |||||
| return REPLY.MANUAL; | |||||
| } | |||||
| } | |||||
| @@ -1,34 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.message; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceReader; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceWriter; | |||||
| /** | |||||
| * 数据序列消息解析器工厂 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| * | |||||
| */ | |||||
| public class DSMsgResolverFactory { | |||||
| /** | |||||
| * 获得数据序列消息编码器实例 | |||||
| * @param dsWriter 差异请求者执行数据序列更新的执行器 | |||||
| * @param dsReader 差异响应者执行数据序列读取的执行器 | |||||
| * @return 消息编码器实例 | |||||
| */ | |||||
| public static DataSequenceMsgEncoder getEncoder(DataSequenceWriter dsWriter, DataSequenceReader dsReader) { | |||||
| return new DataSequenceMsgEncoder(dsWriter, dsReader); | |||||
| } | |||||
| /** | |||||
| * 获得数据序列消息解码器实例 | |||||
| * @param dsWriter 差异请求者执行数据序列更新的执行器 | |||||
| * @param dsReader 差异响应者执行数据序列读取的执行器 | |||||
| * @return 消息解码器实例 | |||||
| */ | |||||
| public static DataSequenceMsgDecoder getDecoder(DataSequenceWriter dsWriter, DataSequenceReader dsReader) { | |||||
| return new DataSequenceMsgDecoder(dsWriter, dsReader); | |||||
| } | |||||
| } | |||||
| @@ -1,28 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.message; | |||||
| import com.jd.blockchain.stp.communication.message.LoadMessage; | |||||
| /** | |||||
| * 数据序列复制的负载消息 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| * | |||||
| */ | |||||
| public class DataSequenceLoadMessage implements LoadMessage { | |||||
| byte[] bytes; | |||||
| public DataSequenceLoadMessage(byte[] bytes) { | |||||
| this.bytes = bytes; | |||||
| } | |||||
| public void setBytes(byte[] bytes) { | |||||
| this.bytes = bytes; | |||||
| } | |||||
| @Override | |||||
| public byte[] toBytes() { | |||||
| return bytes; | |||||
| } | |||||
| } | |||||
| @@ -1,107 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.message; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceInfo; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceReader; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceWriter; | |||||
| import com.jd.blockchain.statetransfer.process.DSTransferProcess; | |||||
| import com.jd.blockchain.statetransfer.result.DSDiffRequestResult; | |||||
| import com.jd.blockchain.utils.io.BytesUtils; | |||||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
| /** | |||||
| * 数据序列消息解码器 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DataSequenceMsgDecoder { | |||||
| private int heightSize = 8; | |||||
| private int msgTypeSize = 1; | |||||
| private long respHeight; | |||||
| private long fromHeight; | |||||
| private long toHeight; | |||||
| private int idSize; | |||||
| private byte[] idBytes; | |||||
| private String id; | |||||
| private int diffElemSize; | |||||
| private byte[] diffElem; | |||||
| DataSequenceElement dsElement; | |||||
| private DataSequenceWriter dsWriter; | |||||
| private DataSequenceReader dsReader; | |||||
| public DataSequenceMsgDecoder(DataSequenceWriter dsWriter, DataSequenceReader dsReader) { | |||||
| this.dsWriter = dsWriter; | |||||
| this.dsReader = dsReader; | |||||
| } | |||||
| /** | |||||
| * 对编过码的字节数组解码,还原成对象实例 | |||||
| * @param loadMessage 字节序列 | |||||
| * @return 解码后的对象 | |||||
| */ | |||||
| public Object decode(byte[] loadMessage) { | |||||
| try { | |||||
| if (loadMessage.length <= 5) { | |||||
| System.out.println("LoadMessage size is less than 5!"); | |||||
| throw new IllegalArgumentException(); | |||||
| } | |||||
| int dataLength = BytesUtils.toInt(loadMessage, 0, 4); | |||||
| byte msgCode = loadMessage[4]; | |||||
| // send by diff provider, diff requester decode | |||||
| if (msgCode == DSTransferProcess.DataSequenceMsgType.CMD_DSINFO_RESPONSE.CODE) { | |||||
| respHeight = BytesUtils.toLong(loadMessage, 4 + msgTypeSize); | |||||
| idSize = BytesUtils.toInt(loadMessage, 4 + msgTypeSize + heightSize, 4); | |||||
| idBytes = new byte[idSize]; | |||||
| System.arraycopy(loadMessage, 4 + msgTypeSize + heightSize + 4, idBytes, 0, idSize); | |||||
| id = new String(idBytes); | |||||
| return new DataSequenceInfo(id, respHeight); | |||||
| } | |||||
| // send by diff provider, diff requester decode | |||||
| else if (msgCode == DSTransferProcess.DataSequenceMsgType.CMD_GETDSDIFF_RESPONSE.CODE) { | |||||
| diffElemSize = BytesUtils.toInt(loadMessage, 4 + msgTypeSize, 4); | |||||
| diffElem = new byte[diffElemSize]; | |||||
| System.arraycopy(loadMessage, 4 + msgTypeSize + 4, diffElem, 0, diffElemSize); | |||||
| dsElement = BinarySerializeUtils.deserialize(diffElem); | |||||
| return dsElement; | |||||
| } | |||||
| // send by diff requester, diff provider decode | |||||
| else if (msgCode == DSTransferProcess.DataSequenceMsgType.CMD_DSINFO_REQUEST.CODE) { | |||||
| idSize = BytesUtils.toInt(loadMessage, 4 + msgTypeSize, 4); | |||||
| idBytes = new byte[idSize]; | |||||
| System.arraycopy(loadMessage, 4 + msgTypeSize + 4, idBytes, 0, idSize); | |||||
| id = new String(idBytes); | |||||
| return id; | |||||
| } | |||||
| // send by diff requester, diff provider decode | |||||
| else if (msgCode == DSTransferProcess.DataSequenceMsgType.CMD_GETDSDIFF_REQUEST.CODE) { | |||||
| fromHeight = BytesUtils.toLong(loadMessage, 4 + msgTypeSize); | |||||
| toHeight = BytesUtils.toLong(loadMessage, 4 + msgTypeSize + heightSize); | |||||
| idSize = BytesUtils.toInt(loadMessage, 4 + msgTypeSize + heightSize + heightSize, 4); | |||||
| idBytes = new byte[idSize]; | |||||
| System.arraycopy(loadMessage, 4 + msgTypeSize + heightSize + heightSize + 4, idBytes, 0, idSize); | |||||
| id = new String(idBytes); | |||||
| return new DSDiffRequestResult(id, fromHeight, toHeight); | |||||
| } | |||||
| else { | |||||
| System.out.println("Unknown message type!"); | |||||
| throw new IllegalArgumentException(); | |||||
| } | |||||
| } catch (Exception e) { | |||||
| System.out.println("Error to decode message: " + e.getMessage() + "!"); | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @@ -1,133 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.message; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceReader; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceWriter; | |||||
| import com.jd.blockchain.statetransfer.process.DSTransferProcess; | |||||
| import com.jd.blockchain.utils.io.BytesUtils; | |||||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
| /** | |||||
| * 数据序列消息编码器 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DataSequenceMsgEncoder { | |||||
| private int heightSize = 8; | |||||
| private int msgTypeSize = 1; | |||||
| private DataSequenceWriter dsWriter; | |||||
| private DataSequenceReader dsReader; | |||||
| public DataSequenceMsgEncoder(DataSequenceWriter dsWriter, DataSequenceReader dsReader) { | |||||
| this.dsWriter = dsWriter; | |||||
| this.dsReader = dsReader; | |||||
| } | |||||
| /** | |||||
| * 目前暂时考虑fromHeight与toHeight相同的情况,即每次只对一个高度的差异内容进行编码并响应 | |||||
| * 把消息编码成字节数组,再交给通信层传输 | |||||
| * @param msgType 数据序列状态复制消息类型 | |||||
| * @param id 数据序列唯一标识符 | |||||
| * @param fromHeight 差异元素起始高度 | |||||
| * @param toHeight 差异元素结束高度 | |||||
| */ | |||||
| public byte[] encode(DSTransferProcess.DataSequenceMsgType msgType, String id, long fromHeight, long toHeight) { | |||||
| try { | |||||
| int dataLength; | |||||
| int idSize = id.getBytes().length; | |||||
| byte[] loadMessage = null; | |||||
| // different encoding methods for different message types | |||||
| // send by diff requester, diff requester encode | |||||
| if (msgType == DSTransferProcess.DataSequenceMsgType.CMD_DSINFO_REQUEST) { | |||||
| // CMD_DSINFO_REQUEST Message parts : 4 bytes total message size, 1 byte message type coe, | |||||
| // 4 bytes id length, id content size bytes | |||||
| dataLength = 4 + msgTypeSize + 4 + idSize; | |||||
| loadMessage = new byte[dataLength]; | |||||
| System.arraycopy(BytesUtils.toBytes(dataLength), 0, loadMessage, 0, 4); | |||||
| loadMessage[4] = msgType.CODE; | |||||
| System.arraycopy(BytesUtils.toBytes(idSize), 0, loadMessage, 4 + msgTypeSize, 4); | |||||
| System.arraycopy(id.getBytes(), 0, loadMessage, 4 + msgTypeSize + 4, idSize); | |||||
| } | |||||
| // send by diff requester, diff requester encode | |||||
| else if (msgType == DSTransferProcess.DataSequenceMsgType.CMD_GETDSDIFF_REQUEST) { | |||||
| // CMD_GETDSDIFF_REQUEST Message parts : 4 bytes total message size, 1 byte message type coe, 8 bytes from height, | |||||
| // 8 bytes to height, 4 bytes id length, id content size bytes | |||||
| dataLength = 4 + msgTypeSize + heightSize + heightSize + 4 + idSize; | |||||
| loadMessage = new byte[dataLength]; | |||||
| System.arraycopy(BytesUtils.toBytes(dataLength), 0, loadMessage, 0, 4); | |||||
| loadMessage[4] = msgType.CODE; | |||||
| System.arraycopy(BytesUtils.toBytes(fromHeight), 0, loadMessage, 4 + msgTypeSize, heightSize); | |||||
| System.arraycopy(BytesUtils.toBytes(toHeight), 0, loadMessage, 4 + msgTypeSize + heightSize, heightSize); | |||||
| System.arraycopy(BytesUtils.toBytes(idSize), 0, loadMessage, 4 + msgTypeSize + heightSize + heightSize, 4); | |||||
| System.arraycopy(id.getBytes(), 0, loadMessage, 4 + msgTypeSize + heightSize + heightSize + 4, idSize); | |||||
| } | |||||
| // send by diff provider, diff provider encode | |||||
| else if (msgType == DSTransferProcess.DataSequenceMsgType.CMD_DSINFO_RESPONSE) { | |||||
| // CMD_DSINFO_RESPONSE Message parts : 4 bytes total message size, 1 byte message type coe, 8 bytes data sequence local height, | |||||
| // 4 bytes id length, id content size bytes | |||||
| dataLength = 4 + msgTypeSize + heightSize + 4 + idSize; | |||||
| loadMessage = new byte[dataLength]; | |||||
| System.arraycopy(BytesUtils.toBytes(dataLength), 0, loadMessage, 0, 4); | |||||
| loadMessage[4] = msgType.CODE; | |||||
| System.arraycopy(BytesUtils.toBytes(dsReader.getDSInfo(id).getHeight()), 0, loadMessage, 4 + msgTypeSize, heightSize); | |||||
| System.arraycopy(BytesUtils.toBytes(idSize), 0, loadMessage, 4 + msgTypeSize + heightSize, 4); | |||||
| System.arraycopy(id.getBytes(), 0, loadMessage, 4 + msgTypeSize + heightSize + 4, idSize); | |||||
| } | |||||
| // send by diff provider, diff provider encode | |||||
| else if (msgType == DSTransferProcess.DataSequenceMsgType.CMD_GETDSDIFF_RESPONSE) { | |||||
| if (fromHeight != toHeight) { | |||||
| throw new IllegalArgumentException("Height parameter error!"); | |||||
| } | |||||
| // CMD_DSINFO_RESPONSE Message parts : 4 bytes total message size, 1 byte message type coe, | |||||
| // 4 bytes diffElem size, diff content size; | |||||
| // 回调reader,获得这个高度上的所有差异的数据序列内容,并组织成DataSequenceElement结构 | |||||
| DataSequenceElement element = dsReader.getDSDiffContent(id, fromHeight); | |||||
| byte[] diffElem = BinarySerializeUtils.serialize(element); | |||||
| dataLength = 4 + msgTypeSize + 4 + diffElem.length; | |||||
| loadMessage = new byte[dataLength]; | |||||
| System.arraycopy(BytesUtils.toBytes(dataLength), 0, loadMessage, 0, 4); //total size | |||||
| loadMessage[4] = msgType.CODE; //msgType size | |||||
| System.arraycopy(BytesUtils.toBytes(diffElem.length), 0, loadMessage, 4 + msgTypeSize, 4); // diffElem size | |||||
| System.arraycopy(diffElem, 0, loadMessage, 4 + msgTypeSize + 4, diffElem.length); // diffElem bytes | |||||
| } | |||||
| else { | |||||
| System.out.println("Unknown message type!"); | |||||
| throw new IllegalArgumentException(); | |||||
| } | |||||
| return loadMessage; | |||||
| } catch (Exception e) { | |||||
| System.out.println("Error to encode message type : " + msgType + "!"); | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @@ -1,186 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.process; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceInfo; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceReader; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceWriter; | |||||
| import com.jd.blockchain.statetransfer.comparator.DataSequenceComparator; | |||||
| import com.jd.blockchain.statetransfer.message.DSDefaultMessageExecutor; | |||||
| import com.jd.blockchain.statetransfer.result.DSInfoResponseResult; | |||||
| import com.jd.blockchain.stp.communication.RemoteSession; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackBarrier; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackDataListener; | |||||
| import com.jd.blockchain.stp.communication.manager.RemoteSessionManager; | |||||
| import com.jd.blockchain.stp.communication.node.LocalNode; | |||||
| import com.jd.blockchain.stp.communication.node.RemoteNode; | |||||
| import java.net.InetSocketAddress; | |||||
| import java.util.*; | |||||
| import java.util.concurrent.*; | |||||
| /** | |||||
| * 数据序列状态复制过程管理器 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| * | |||||
| */ | |||||
| public class DSProcessManager { | |||||
| private static Map<String, DSTransferProcess> dSProcessMap = new ConcurrentHashMap<>(); | |||||
| private RemoteSession[] remoteSessions; | |||||
| private long dsInfoResponseTimeout = 20000; | |||||
| private ExecutorService writeExecutors = Executors.newFixedThreadPool(5); | |||||
| private int returnCode = 0; | |||||
| /** | |||||
| * 启动一个指定数据序列的状态复制过程 | |||||
| * @param dsInfo 数据序列当前状态信息 | |||||
| * @param listener 本地监听者 | |||||
| * @param targets 目标结点 | |||||
| * @param dsWriter 差异请求者执行数据序列更新的执行器 | |||||
| * @param dsReader 差异响应者执行数据序列读取的执行器 | |||||
| * @return returnCode 执行结果码 | |||||
| */ | |||||
| public int startDSProcess(DataSequenceInfo dsInfo, InetSocketAddress listener, InetSocketAddress[] targets, DataSequenceWriter dsWriter, DataSequenceReader dsReader) { | |||||
| // create remote sessions manager, add listener | |||||
| LocalNode listenNode = new LocalNode(listener.getHostName(), listener.getPort(), new DSDefaultMessageExecutor(dsReader, dsWriter)); | |||||
| RemoteSessionManager remoteSessionManager = new RemoteSessionManager(listenNode); | |||||
| // data sequence transfer process life cycle start | |||||
| DSTransferProcess dsTransferProcess = new DSTransferProcess(dsInfo, targets); | |||||
| dsTransferProcess.setDSReader(dsReader); | |||||
| dsTransferProcess.setDSWriter(dsWriter); | |||||
| dsTransferProcess.setRemoteSessionManager(remoteSessionManager); | |||||
| dSProcessMap.put(dsInfo.getId(), dsTransferProcess); | |||||
| try { | |||||
| //wait all listener nodes start | |||||
| Thread.sleep(2000); | |||||
| // start network connections with targets | |||||
| dsTransferProcess.start(); | |||||
| //get all target sessions | |||||
| remoteSessions = dsTransferProcess.getSessions(); | |||||
| // async message send process | |||||
| CallBackBarrier callBackBarrier = CallBackBarrier.newCallBackBarrier(remoteSessions.length, dsInfoResponseTimeout); | |||||
| // response message manage map | |||||
| LinkedList<CallBackDataListener> dsInfoResponses = new LinkedList<>(); | |||||
| System.out.println("Async send CMD_DSINFO_REQUEST msg to targets will start!"); | |||||
| // step1: send get dsInfo request, then hold | |||||
| for (RemoteSession remoteSession : remoteSessions) { | |||||
| CallBackDataListener dsInfoResponse = dsTransferProcess.send(DSTransferProcess.DataSequenceMsgType.CMD_DSINFO_REQUEST, remoteSession, 0, 0, callBackBarrier); | |||||
| dsInfoResponses.addLast(dsInfoResponse); | |||||
| } | |||||
| System.out.println("Wait CMD_DSINFO_RESPONSE msg from targets!"); | |||||
| // step2: collect get dsInfo response | |||||
| LinkedList<CallBackDataListener> receiveResponses = new LinkedList<>(); | |||||
| if (callBackBarrier.tryCall()) { | |||||
| Iterator<CallBackDataListener> iterator = dsInfoResponses.iterator(); | |||||
| while (iterator.hasNext()) { | |||||
| CallBackDataListener receiveResponse = iterator.next(); | |||||
| if (receiveResponse.isDone()) { | |||||
| receiveResponses.addLast(receiveResponse); | |||||
| } | |||||
| } | |||||
| } | |||||
| System.out.printf("%s:%d Compute diff info!\r\n", listener.getHostName(), listener.getPort()); | |||||
| // step3: process received responses | |||||
| DSInfoResponseResult diffResult = dsTransferProcess.computeDiffInfo(receiveResponses); | |||||
| System.out.printf("%s:%d Diff info result height = %x!\r\n", listener.getHostName(), listener.getPort(), diffResult.getMaxHeight()); | |||||
| // height diff | |||||
| long diff = dsInfo.getHeight() - diffResult.getMaxHeight(); | |||||
| if (diff == 0 || diff > 0) { | |||||
| System.out.printf("%s:%d No duplication is required!\r\n", listener.getHostName(), listener.getPort()); | |||||
| // no duplication is required, life cycle ends | |||||
| // dsTransferProcess.close(); | |||||
| dSProcessMap.remove(dsInfo.getId()); | |||||
| return returnCode; | |||||
| } | |||||
| else { | |||||
| System.out.printf("%s:%d Duplication is required!\r\n", listener.getHostName(), listener.getPort()); | |||||
| // step4: async send get data sequence diff request | |||||
| // single step get diff | |||||
| // async message send process | |||||
| CallBackBarrier callBackBarrierDiff = CallBackBarrier.newCallBackBarrier((int)(diffResult.getMaxHeight() - dsInfo.getHeight()), dsInfoResponseTimeout); | |||||
| LinkedList<CallBackDataListener> dsDiffResponses = new LinkedList<>(); | |||||
| RemoteSession responseSession = findResponseSession(diffResult.getMaxHeightRemoteNode(), remoteSessions); | |||||
| System.out.println("Async send CMD_GETDSDIFF_REQUEST msg to targets will start!"); | |||||
| // step5: collect get data sequence diff response | |||||
| for (long height = dsInfo.getHeight() + 1; height < diffResult.getMaxHeight() + 1; height++) { | |||||
| CallBackDataListener dsDiffResponse = dsTransferProcess.send(DSTransferProcess.DataSequenceMsgType.CMD_GETDSDIFF_REQUEST, responseSession, height, height, callBackBarrierDiff); | |||||
| dsDiffResponses.addLast(dsDiffResponse); | |||||
| } | |||||
| // 上述发送不合理,考虑一次性发送请求 | |||||
| System.out.println("Wait CMD_GETDSDIFF_RESPONSE msg from targets!"); | |||||
| LinkedList<byte[]> receiveDiffResponses = new LinkedList<>(); | |||||
| if (callBackBarrierDiff.tryCall()) { | |||||
| for (int i = 0; i < dsDiffResponses.size(); i++) { | |||||
| CallBackDataListener asyncFutureDiff = dsDiffResponses.get(i); | |||||
| if (asyncFutureDiff.isDone()) { | |||||
| receiveDiffResponses.addLast(asyncFutureDiff.getCallBackData()); | |||||
| } | |||||
| } | |||||
| } | |||||
| System.out.printf("%s:%d ReceiveDiffResponses size = %d !\r\n", listener.getHostName(), listener.getPort(), receiveDiffResponses.size()); | |||||
| // step6: process data sequence diff response, update local data sequence state | |||||
| System.out.println("Compute diff elements!"); | |||||
| ArrayList<DataSequenceElement> dataSequenceElements = dsTransferProcess.computeDiffElement(receiveDiffResponses.toArray(new byte[receiveDiffResponses.size()][])); | |||||
| System.out.println("Update local data sequence!"); | |||||
| Collections.sort(dataSequenceElements, new DataSequenceComparator()); | |||||
| returnCode = dsWriter.updateDSInfo(dsInfo, dataSequenceElements.toArray(new DataSequenceElement[dataSequenceElements.size()])); | |||||
| // data sequence transfer complete, close all sessions, end process life cycle | |||||
| System.out.println("Close all sessions"); | |||||
| // dsTransferProcess.close(); | |||||
| dSProcessMap.remove(dsInfo.getId()); | |||||
| } | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return returnCode; | |||||
| } | |||||
| /** | |||||
| * 根据远端结点找与远端结点建立的会话 | |||||
| * @param remoteNode 远端结点 | |||||
| * @param remoteSessions 本地维护的远端结点会话表 | |||||
| * @return 与远端结点对应的会话 | |||||
| */ | |||||
| RemoteSession findResponseSession(RemoteNode remoteNode, RemoteSession[] remoteSessions) { | |||||
| for (RemoteSession remoteSession : remoteSessions) { | |||||
| if (remoteSession.remoteNode().equals(remoteNode)) { | |||||
| return remoteSession; | |||||
| } | |||||
| } | |||||
| return null; | |||||
| } | |||||
| /** | |||||
| * | |||||
| * | |||||
| */ | |||||
| // void setDSReader(DataSequenceReader reader) { | |||||
| // | |||||
| // } | |||||
| } | |||||
| @@ -1,216 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.process; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceInfo; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceReader; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceWriter; | |||||
| import com.jd.blockchain.statetransfer.message.DSMsgResolverFactory; | |||||
| import com.jd.blockchain.statetransfer.message.DataSequenceLoadMessage; | |||||
| import com.jd.blockchain.statetransfer.result.DSInfoResponseResult; | |||||
| import com.jd.blockchain.stp.communication.RemoteSession; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackBarrier; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackDataListener; | |||||
| import com.jd.blockchain.stp.communication.manager.RemoteSessionManager; | |||||
| import com.jd.blockchain.stp.communication.node.RemoteNode; | |||||
| import com.jd.blockchain.utils.IllegalDataException; | |||||
| import java.net.InetSocketAddress; | |||||
| import java.util.ArrayList; | |||||
| import java.util.LinkedList; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * 数据序列状态复制过程 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DSTransferProcess { | |||||
| private InetSocketAddress[] targets; | |||||
| private DataSequenceWriter dsWriter; | |||||
| private DataSequenceReader dsReader; | |||||
| private DataSequenceInfo dsInfo; | |||||
| private RemoteSessionManager remoteSessionManager; | |||||
| private RemoteSession[] remoteSessions; | |||||
| private String id; | |||||
| /** | |||||
| * @param dsInfo 数据序列当前状态信息 | |||||
| * @param targets 目标结点 | |||||
| */ | |||||
| public DSTransferProcess(DataSequenceInfo dsInfo, InetSocketAddress[] targets) { | |||||
| this.dsInfo = dsInfo; | |||||
| this.targets = targets; | |||||
| this.id = dsInfo.getId(); | |||||
| } | |||||
| /** | |||||
| * @param dsWriter 差异请求者执行数据序列更新的执行器 | |||||
| * @return void | |||||
| */ | |||||
| public void setDSWriter(DataSequenceWriter dsWriter) { | |||||
| this.dsWriter = dsWriter; | |||||
| } | |||||
| /** | |||||
| * @param dsReader 差异响应者执行数据序列读取的执行器 | |||||
| * @return void | |||||
| */ | |||||
| public void setDSReader(DataSequenceReader dsReader) { | |||||
| this.dsReader = dsReader; | |||||
| } | |||||
| /** | |||||
| * @param remoteSessionManager 远端会话管理器 | |||||
| * @return void | |||||
| */ | |||||
| public void setRemoteSessionManager(RemoteSessionManager remoteSessionManager) { | |||||
| this.remoteSessionManager = remoteSessionManager; | |||||
| } | |||||
| /** | |||||
| * | |||||
| * @return 数据序列标识符 | |||||
| */ | |||||
| public String getId() { | |||||
| return id; | |||||
| } | |||||
| /** | |||||
| * @param msgType 数据序列差异请求消息类型 | |||||
| * @param remoteSession 目标结点对应的会话 | |||||
| * @param fromHeight 差异起始高度 | |||||
| * @param toHeight 差异结束高度 | |||||
| * @param callBackBarrier 异步回调 | |||||
| * @return 异步回调 | |||||
| */ | |||||
| CallBackDataListener send(DataSequenceMsgType msgType, RemoteSession remoteSession, long fromHeight, long toHeight, CallBackBarrier callBackBarrier) { | |||||
| byte[] loadMessage = DSMsgResolverFactory.getEncoder(dsWriter, dsReader).encode(msgType, id, fromHeight, toHeight); | |||||
| return remoteSession.asyncRequest(new DataSequenceLoadMessage(loadMessage), callBackBarrier); | |||||
| } | |||||
| /** | |||||
| * 计算数据序列差异元素数组 | |||||
| * @param diffArray 差异的字节数组 | |||||
| * @return 对差异字节数组的解码结果 | |||||
| */ | |||||
| public ArrayList<DataSequenceElement> computeDiffElement(byte[][] diffArray) { | |||||
| ArrayList<DataSequenceElement> dataSequenceElements = new ArrayList<>(); | |||||
| for (int i = 0 ; i < diffArray.length; i++) { | |||||
| Object object = DSMsgResolverFactory.getDecoder(dsWriter, dsReader).decode(diffArray[i]); | |||||
| if (object instanceof DataSequenceElement) { | |||||
| dataSequenceElements.add((DataSequenceElement) object); | |||||
| } | |||||
| else { | |||||
| throw new IllegalDataException("Unknown instance object!"); | |||||
| } | |||||
| } | |||||
| return dataSequenceElements; | |||||
| } | |||||
| /** | |||||
| * 根据差异提供者响应的数据序列状态信息找到拥有最大数据序列高度的远端结点 | |||||
| * @param receiveResponses 数据序列差异请求者收到的远端结点状态的响应信息 | |||||
| * @return 得到远端数据序列的最大高度以及拥有者结点 | |||||
| */ | |||||
| public DSInfoResponseResult computeDiffInfo(LinkedList<CallBackDataListener> receiveResponses) { | |||||
| long maxHeight = 0; | |||||
| RemoteNode maxHeightRemoteNode = null; | |||||
| System.out.println("ComputeDiffInfo receiveResponses size = "+ receiveResponses.size()); | |||||
| try { | |||||
| for (CallBackDataListener receiveResponse : receiveResponses) { | |||||
| Object object = DSMsgResolverFactory.getDecoder(dsWriter, dsReader).decode(receiveResponse.getCallBackData()); | |||||
| if (object instanceof DataSequenceInfo) { | |||||
| DataSequenceInfo dsInfo = (DataSequenceInfo) object; | |||||
| long height = dsInfo.getHeight(); | |||||
| // sava max height and its remote node | |||||
| if (maxHeight < height) { | |||||
| maxHeight = height; | |||||
| maxHeightRemoteNode = receiveResponse.remoteNode(); | |||||
| } | |||||
| } | |||||
| else { | |||||
| throw new IllegalDataException("Unknown instance object!"); | |||||
| } | |||||
| } | |||||
| } catch (Exception e) { | |||||
| System.out.println(e.getMessage()); | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return new DSInfoResponseResult(maxHeight, maxHeightRemoteNode); | |||||
| } | |||||
| /** | |||||
| * 获取本复制过程维护的远端会话表 | |||||
| * @param | |||||
| * @return 远端会话表数组 | |||||
| */ | |||||
| public RemoteSession[] getSessions() { | |||||
| return remoteSessions; | |||||
| } | |||||
| /** | |||||
| * 关闭本复制过程维护的所有远端会话 | |||||
| * @return void | |||||
| */ | |||||
| public void close() { | |||||
| for (RemoteSession session : remoteSessions) { | |||||
| session.closeAll(); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 建立与远端目标结点的连接,产生本地维护的远端会话表 | |||||
| * @return void | |||||
| */ | |||||
| public void start() { | |||||
| RemoteNode[] remoteNodes = new RemoteNode[targets.length]; | |||||
| for (int i = 0; i < remoteNodes.length; i++) { | |||||
| remoteNodes[i] = new RemoteNode(targets[i].getHostName(), targets[i].getPort()); | |||||
| } | |||||
| remoteSessions = remoteSessionManager.newSessions(remoteNodes); | |||||
| } | |||||
| /** | |||||
| * 数据序列状态传输使用的消息类型 | |||||
| * | |||||
| */ | |||||
| public enum DataSequenceMsgType { | |||||
| CMD_DSINFO_REQUEST((byte) 0x1), | |||||
| CMD_DSINFO_RESPONSE((byte) 0x2), | |||||
| CMD_GETDSDIFF_REQUEST((byte) 0x3), | |||||
| CMD_GETDSDIFF_RESPONSE((byte) 0x4), | |||||
| ; | |||||
| public final byte CODE; | |||||
| private DataSequenceMsgType(byte code) { | |||||
| this.CODE = code; | |||||
| } | |||||
| public static DataSequenceMsgType valueOf(byte code) { | |||||
| for (DataSequenceMsgType msgType : DataSequenceMsgType.values()) { | |||||
| if (msgType.CODE == code) { | |||||
| return msgType; | |||||
| } | |||||
| } | |||||
| throw new IllegalArgumentException("Unsupported code[" + code + "] of msgType!"); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,45 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.result; | |||||
| /** | |||||
| * 数据序列差异提供者解码请求者"CMD_GETDSDIFF_REQUEST"消息时得到的结果 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DSDiffRequestResult { | |||||
| String id; | |||||
| long fromHeight; | |||||
| long toHeight; | |||||
| public DSDiffRequestResult(String id ,long fromHeight, long toHeight) { | |||||
| this.id = id; | |||||
| this.fromHeight = fromHeight; | |||||
| this.toHeight = toHeight; | |||||
| } | |||||
| public String getId() { | |||||
| return id; | |||||
| } | |||||
| public long getFromHeight() { | |||||
| return fromHeight; | |||||
| } | |||||
| public long getToHeight() { | |||||
| return toHeight; | |||||
| } | |||||
| public void setId(String id) { | |||||
| this.id = id; | |||||
| } | |||||
| public void setFromHeight(long fromHeight) { | |||||
| this.fromHeight = fromHeight; | |||||
| } | |||||
| public void setToHeight(long toHeight) { | |||||
| this.toHeight = toHeight; | |||||
| } | |||||
| } | |||||
| @@ -1,37 +0,0 @@ | |||||
| package com.jd.blockchain.statetransfer.result; | |||||
| import com.jd.blockchain.stp.communication.node.RemoteNode; | |||||
| /** | |||||
| * 数据序列差异请求者解码提供者"CMD_DSINFO_RESPONSE"消息时得到的结果 | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DSInfoResponseResult { | |||||
| long maxHeight; | |||||
| RemoteNode maxHeightRemoteNode; | |||||
| public DSInfoResponseResult(long maxHeight, RemoteNode maxHeightRemoteNode) { | |||||
| this.maxHeight = maxHeight; | |||||
| this.maxHeightRemoteNode = maxHeightRemoteNode; | |||||
| } | |||||
| public long getMaxHeight() { | |||||
| return maxHeight; | |||||
| } | |||||
| public RemoteNode getMaxHeightRemoteNode() { | |||||
| return maxHeightRemoteNode; | |||||
| } | |||||
| public void setMaxHeight(long maxHeight) { | |||||
| this.maxHeight = maxHeight; | |||||
| } | |||||
| public void setMaxHeightRemoteNode(RemoteNode maxHeightRemoteNode) { | |||||
| this.maxHeightRemoteNode = maxHeightRemoteNode; | |||||
| } | |||||
| } | |||||
| @@ -1,155 +0,0 @@ | |||||
| package test.com.jd.blockchain.statetransfer; | |||||
| import com.jd.blockchain.statetransfer.DataSequence; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceElement; | |||||
| import com.jd.blockchain.statetransfer.DataSequenceInfo; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceReaderImpl; | |||||
| import com.jd.blockchain.statetransfer.callback.DataSequenceWriterImpl; | |||||
| import com.jd.blockchain.statetransfer.process.DSProcessManager; | |||||
| import com.jd.blockchain.utils.codec.Base58Utils; | |||||
| import org.junit.Before; | |||||
| import org.junit.Test; | |||||
| import java.net.InetSocketAddress; | |||||
| import java.util.LinkedList; | |||||
| import java.util.Random; | |||||
| import java.util.concurrent.CountDownLatch; | |||||
| import java.util.concurrent.ExecutorService; | |||||
| import java.util.concurrent.Executors; | |||||
| /** | |||||
| * @author zhangshuang | |||||
| * @create 2019/4/18 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class StateTransferLayerTest { | |||||
| private final int[] listenPorts = new int[]{9000, 9010, 9020, 9030}; | |||||
| private String localIp = "127.0.0.1"; | |||||
| private int DataSequenceNum = 1; | |||||
| private int nodesNum = 4; | |||||
| private byte[] idBytes = new byte[20]; | |||||
| private Random rand = new Random(); | |||||
| private String[] dataSequenceIds = new String[DataSequenceNum]; | |||||
| private InetSocketAddress[] remoteNodeIps = new InetSocketAddress[nodesNum]; | |||||
| private final ExecutorService threadPool = Executors.newFixedThreadPool(8); | |||||
| private static LinkedList<DataSequence> dataSequencesPerNode = new LinkedList<>(); | |||||
| // 假定每个数据序列元素里有四条记录数据 | |||||
| private byte[][] dsElementDatas = new byte[4][]; | |||||
| @Before | |||||
| public void init() { | |||||
| // 产生两个唯一的数据序列Id标识 | |||||
| for (int i = 0; i < DataSequenceNum; i++) { | |||||
| dataSequenceIds[i] = new String(); | |||||
| rand.nextBytes(idBytes); | |||||
| dataSequenceIds[i] = Base58Utils.encode(idBytes); | |||||
| } | |||||
| // 准备好所有的远端结点,包括监听者 | |||||
| for (int i = 0; i < nodesNum; i++) { | |||||
| remoteNodeIps[i] = new InetSocketAddress(localIp, listenPorts[i]); | |||||
| } | |||||
| // 为数据序列的每个高度准备好内容,为了方便测试,每个高度的内容设置为一致 | |||||
| for (int i = 0; i < dsElementDatas.length; i++) { | |||||
| rand.nextBytes(idBytes); | |||||
| dsElementDatas[i] = idBytes; | |||||
| } | |||||
| // 为结点准备数据序列 | |||||
| for (String id : dataSequenceIds) { | |||||
| for (int i = 0; i < remoteNodeIps.length; i++) { | |||||
| DataSequence dataSequence = new DataSequence(remoteNodeIps[i], id); | |||||
| // 为数据序列的0,1,2高度添加内容 | |||||
| for (int j = 0; j < 3; j++) { | |||||
| dataSequence.addElement(new DataSequenceElement(id, j, dsElementDatas)); | |||||
| } | |||||
| dataSequencesPerNode.addLast(dataSequence); | |||||
| } | |||||
| // 把其中一个结点的数据序列与其他结点区别开来 | |||||
| for (int i = 0; i < dataSequencesPerNode.size(); i++) { | |||||
| DataSequence dataSequence = dataSequencesPerNode.get(i); | |||||
| if (dataSequence.getAddress().getPort() != listenPorts[0]) { | |||||
| // 为数据序列的3,4高度添加内容 | |||||
| for (int j = 3; j < 5; j++) { | |||||
| dataSequence.addElement(new DataSequenceElement(id, j, dsElementDatas)); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| InetSocketAddress[] getTargetNodesIp(InetSocketAddress listenIp, InetSocketAddress[] remoteNodeIps) { | |||||
| // 获得除监听结点之外的其他远端结点 | |||||
| InetSocketAddress[] targets = new InetSocketAddress[remoteNodeIps.length - 1]; | |||||
| int j = 0; | |||||
| for (int i = 0; i < remoteNodeIps.length; i++) { | |||||
| if ((remoteNodeIps[i].getHostName().equals(listenIp.getHostName())) && (remoteNodeIps[i].getPort() == listenIp.getPort())) { | |||||
| continue; | |||||
| } | |||||
| targets[j++] = new InetSocketAddress(remoteNodeIps[i].getHostName(), remoteNodeIps[i].getPort()); | |||||
| } | |||||
| return targets; | |||||
| } | |||||
| DataSequence findDataSequence(String id, InetSocketAddress listenNodeAddr) { | |||||
| for (DataSequence dataSequence : dataSequencesPerNode) { | |||||
| if ((dataSequence.getAddress().getPort() == listenNodeAddr.getPort() && (dataSequence.getAddress().getHostName().equals(listenNodeAddr.getHostName())) | |||||
| && (dataSequence.getId().equals(id)))) { | |||||
| return dataSequence; | |||||
| } | |||||
| } | |||||
| return null; | |||||
| } | |||||
| @Test | |||||
| public void test() { | |||||
| CountDownLatch countDownLatch = new CountDownLatch(nodesNum); | |||||
| for (String id : dataSequenceIds) { | |||||
| for (int i = 0; i < nodesNum; i++) { | |||||
| InetSocketAddress listenNode = remoteNodeIps[i]; | |||||
| threadPool.execute(() -> { | |||||
| // 创建数据序列处理管理者实例 | |||||
| DSProcessManager dsProcessManager = new DSProcessManager(); | |||||
| DataSequence currDataSequence = findDataSequence(id, listenNode); | |||||
| DataSequenceInfo dsInfo = currDataSequence.getDSInfo(); | |||||
| InetSocketAddress[] targets = getTargetNodesIp(listenNode, remoteNodeIps); | |||||
| dsProcessManager.startDSProcess(dsInfo, listenNode, targets, new DataSequenceWriterImpl(currDataSequence), new DataSequenceReaderImpl(currDataSequence)); | |||||
| countDownLatch.countDown(); | |||||
| }); | |||||
| } | |||||
| } | |||||
| // 等待数据序列更新完成 | |||||
| try { | |||||
| Thread.sleep(60000); | |||||
| countDownLatch.await(); | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,42 +1,42 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>storage</artifactId> | |||||
| <version>1.1.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>storage-redis</artifactId> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>storage-service</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-autoconfigure</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-configuration-processor</artifactId> | |||||
| <optional>true</optional> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>redis.clients</groupId> | |||||
| <artifactId>jedis</artifactId> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>storage</artifactId> | |||||
| <version>1.1.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>storage-redis</artifactId> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>storage-service</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-autoconfigure</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-configuration-processor</artifactId> | |||||
| <optional>true</optional> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>redis.clients</groupId> | |||||
| <artifactId>jedis</artifactId> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| </project> | </project> | ||||
| @@ -31,6 +31,10 @@ | |||||
| <groupId>org.apache.commons</groupId> | <groupId>org.apache.commons</groupId> | ||||
| <artifactId>commons-collections4</artifactId> | <artifactId>commons-collections4</artifactId> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>junit</groupId> | |||||
| <artifactId>junit</artifactId> | |||||
| </dependency> | |||||
| <!-- <dependency> | <!-- <dependency> | ||||
| <groupId>org.rocksdb</groupId> | <groupId>org.rocksdb</groupId> | ||||
| <artifactId>rocksdbjni-osx</artifactId> | <artifactId>rocksdbjni-osx</artifactId> | ||||
| @@ -1,19 +1,19 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>storage</artifactId> | |||||
| <version>1.1.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>storage-service</artifactId> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>storage</artifactId> | |||||
| <version>1.1.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>storage-service</artifactId> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| </project> | </project> | ||||
| @@ -10,37 +10,6 @@ import com.jd.blockchain.utils.Bytes; | |||||
| */ | */ | ||||
| public interface ExPolicyKVStorage extends BatchStorageService{ | public interface ExPolicyKVStorage extends BatchStorageService{ | ||||
| // /** | |||||
| // * 返回“键”对应的“值”;<br> | |||||
| // * 如果“键”不存在,则返回 null; | |||||
| // * | |||||
| // * @param key | |||||
| // * @return | |||||
| // */ | |||||
| // byte[] get(String key); | |||||
| // | |||||
| // /** | |||||
| // * 如果满足指定的存在性策略,则创建/更新指定的“键-值”; | |||||
| // * | |||||
| // * @param key | |||||
| // * 键; | |||||
| // * @param value | |||||
| // * 值; | |||||
| // * @param ex | |||||
| // * 如果指定 {@link ExPolicy#EXISTING} ,则只有键存在时才更新; <br> | |||||
| // * 如果指定 {@link ExPolicy#NOT_EXISTING} ,则只有键不存在时才更新/创建; | |||||
| // * @return 如果符合存在性策略,并执行了创建/更新操作,则返回 true,否则返回 false; | |||||
| // */ | |||||
| // boolean set(String key, byte[] value, ExPolicy ex); | |||||
| // | |||||
| // /** | |||||
| // * 指定的 key 是否存在; | |||||
| // * | |||||
| // * @param key | |||||
| // * @return | |||||
| // */ | |||||
| // boolean exist(String key); | |||||
| /** | /** | ||||
| * 返回“键”对应的“值”;<br> | * 返回“键”对应的“值”;<br> | ||||
| * 如果“键”不存在,则返回 null; | * 如果“键”不存在,则返回 null; | ||||
| @@ -19,65 +19,6 @@ import com.jd.blockchain.utils.Bytes; | |||||
| */ | */ | ||||
| public interface VersioningKVStorage extends BatchStorageService { | public interface VersioningKVStorage extends BatchStorageService { | ||||
| // /** | |||||
| // * Return the latest version entry associated the specified key; | |||||
| // * | |||||
| // * If the key doesn't exist, then return -1; | |||||
| // * | |||||
| // * @param key | |||||
| // * @return | |||||
| // */ | |||||
| // long getVersion(String key); | |||||
| // | |||||
| // /** | |||||
| // * Return the specified verson's entry;<br> | |||||
| // * | |||||
| // * It will return the latest one if the version is -1; <br> | |||||
| // * | |||||
| // * It will return null if the key or version not exist. | |||||
| // * | |||||
| // * @param key | |||||
| // * @param version | |||||
| // * @return | |||||
| // */ | |||||
| // VersioningKVEntry getEntry(String key, long version); | |||||
| // | |||||
| // /** | |||||
| // * Return the specified verson's value; <br> | |||||
| // * | |||||
| // * If the specified version of key doesn't exist, then return null;<br> | |||||
| // * | |||||
| // * If the version is specified to -1, then return the latest version's | |||||
| // * value;<br> | |||||
| // * | |||||
| // * @param key | |||||
| // * @param version | |||||
| // * @return | |||||
| // */ | |||||
| // byte[] get(String key, long version); | |||||
| // | |||||
| // /** | |||||
| // * Update the value of the key;<br> | |||||
| // * | |||||
| // * If key exist, and the specified version equals to latest , then the value is | |||||
| // * updated and version is increased by 1;<br> | |||||
| // * If key not exist, and the specified version is -1, then the value will be | |||||
| // * created and initialized it's version by 0; <br> | |||||
| // * | |||||
| // * @param key | |||||
| // * the key; | |||||
| // * @param value | |||||
| // * the new value to update if expected version match the actual | |||||
| // * version; | |||||
| // * @param version | |||||
| // * the latest version expected; | |||||
| // * @return The latest version entry after setting. <br> | |||||
| // * If the version checking fail, or concurrent confliction occur, then | |||||
| // * return -1 as indication. <br> | |||||
| // */ | |||||
| // long set(String key, byte[] value, long version); | |||||
| // | |||||
| /** | /** | ||||
| * Return the latest version entry associated the specified key; | * Return the latest version entry associated the specified key; | ||||
| * | * | ||||
| @@ -118,8 +59,8 @@ public interface VersioningKVStorage extends BatchStorageService { | |||||
| /** | /** | ||||
| * Update the value of the key;<br> | * Update the value of the key;<br> | ||||
| * | * | ||||
| * If key exist, and the specified version equals to latest , then the value is | |||||
| * updated and version is increased by 1;<br> | |||||
| * If key exist, and the specified version equals to it's latest version, then the value will be | |||||
| * updated and version will be increased by 1;<br> | |||||
| * If key not exist, and the specified version is -1, then the value will be | * If key not exist, and the specified version is -1, then the value will be | ||||
| * created and initialized it's version by 0; <br> | * created and initialized it's version by 0; <br> | ||||
| * | * | ||||
| @@ -1,9 +1,9 @@ | |||||
| package test.com.jd.blockchain.storage.service.utils; | package test.com.jd.blockchain.storage.service.utils; | ||||
| import static org.junit.Assert.*; | |||||
| import static org.junit.Assert.assertEquals; | |||||
| import static org.junit.Assert.assertTrue; | |||||
| import static org.mockito.Matchers.any; | import static org.mockito.Matchers.any; | ||||
| import static org.mockito.Matchers.anyLong; | import static org.mockito.Matchers.anyLong; | ||||
| import static org.mockito.Matchers.anyString; | |||||
| import static org.mockito.Matchers.eq; | import static org.mockito.Matchers.eq; | ||||
| import static org.mockito.Mockito.times; | import static org.mockito.Mockito.times; | ||||
| import static org.mockito.Mockito.verify; | import static org.mockito.Mockito.verify; | ||||
| @@ -15,14 +15,11 @@ import org.junit.Test; | |||||
| import org.mockito.Mockito; | import org.mockito.Mockito; | ||||
| import org.mockito.invocation.InvocationOnMock; | import org.mockito.invocation.InvocationOnMock; | ||||
| import org.mockito.stubbing.Answer; | import org.mockito.stubbing.Answer; | ||||
| import org.mockito.verification.VerificationMode; | |||||
| import com.jd.blockchain.storage.service.ExPolicyKVStorage; | import com.jd.blockchain.storage.service.ExPolicyKVStorage; | ||||
| import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; | import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; | ||||
| import com.jd.blockchain.storage.service.VersioningKVStorage; | import com.jd.blockchain.storage.service.VersioningKVStorage; | ||||
| import com.jd.blockchain.storage.service.utils.BufferedKVStorage; | import com.jd.blockchain.storage.service.utils.BufferedKVStorage; | ||||
| import com.jd.blockchain.storage.service.utils.ExistancePolicyKVStorageMap; | |||||
| import com.jd.blockchain.storage.service.utils.VersioningKVStorageMap; | |||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| public class BufferedKVStorageTest { | public class BufferedKVStorageTest { | ||||
| @@ -77,82 +74,4 @@ public class BufferedKVStorageTest { | |||||
| verify(exStorage, times(1)).set(eq(Bytes.fromString("C")), any(), eq(ExPolicy.NOT_EXISTING)); | verify(exStorage, times(1)).set(eq(Bytes.fromString("C")), any(), eq(ExPolicy.NOT_EXISTING)); | ||||
| verify(exStorage, times(1)).set(eq(Bytes.fromString("D")), any(), eq(ExPolicy.NOT_EXISTING)); | verify(exStorage, times(1)).set(eq(Bytes.fromString("D")), any(), eq(ExPolicy.NOT_EXISTING)); | ||||
| } | } | ||||
| // 改变了存储结构,此测试用例不再适合; | |||||
| // @Test | |||||
| // public void testDataSet() { | |||||
| // | |||||
| // ExistancePolicyKVStorageMap memoryExStorage = new | |||||
| // ExistancePolicyKVStorageMap(); | |||||
| // | |||||
| // VersioningKVStorageMap memoryVerStorage = new VersioningKVStorageMap(); | |||||
| // | |||||
| // long v = memoryVerStorage.set("A", "A1".getBytes(), -1); | |||||
| // assertEquals(0, v); | |||||
| // v = memoryVerStorage.set("A", "A2".getBytes(), 0); | |||||
| // assertEquals(1, v); | |||||
| // v = memoryVerStorage.set("A", "A3".getBytes(), 1); | |||||
| // assertEquals(2, v); | |||||
| // v = memoryVerStorage.set("B", "B1".getBytes(), -1); | |||||
| // assertEquals(0, v); | |||||
| // | |||||
| // BufferedKVStorage bufferedStorage = new BufferedKVStorage(memoryExStorage, | |||||
| // memoryVerStorage, false); | |||||
| // | |||||
| // // test versioning get; | |||||
| // byte[] value = bufferedStorage.get("A", 0); | |||||
| // assertEquals("A1", new String(value)); | |||||
| // | |||||
| // value = bufferedStorage.get("A", -1); | |||||
| // assertEquals("A3", new String(value)); | |||||
| // | |||||
| // value = bufferedStorage.get("A", 2); | |||||
| // assertEquals("A3", new String(value)); | |||||
| // | |||||
| // value = bufferedStorage.get("B", 0); | |||||
| // assertEquals("B1", new String(value)); | |||||
| // | |||||
| // | |||||
| // // test versioning buffered set; | |||||
| // v = bufferedStorage.set("C", "C1".getBytes(), -1); | |||||
| // assertEquals(v, 0); | |||||
| // | |||||
| // v = bufferedStorage.set("C", "C2".getBytes(), 0); | |||||
| // assertEquals(v, 1); | |||||
| // | |||||
| // v = bufferedStorage.set("D", "D1".getBytes(), -1); | |||||
| // assertEquals(v, 0); | |||||
| // | |||||
| // v = bufferedStorage.set("E", "E1".getBytes(), 0); | |||||
| // assertEquals(v, -1); | |||||
| // | |||||
| // | |||||
| // value = bufferedStorage.get("C", 0); | |||||
| // assertEquals("C1", new String(value)); | |||||
| // value = bufferedStorage.get("C", 1); | |||||
| // assertEquals("C2", new String(value)); | |||||
| // value = bufferedStorage.get("D", 0); | |||||
| // assertEquals("D1", new String(value)); | |||||
| // value = bufferedStorage.get("E", 0); | |||||
| // assertNull(value); | |||||
| // | |||||
| // value = memoryVerStorage.get("C", 0); | |||||
| // assertNull(value); | |||||
| // value = memoryVerStorage.get("D", 0); | |||||
| // assertNull(value); | |||||
| // value = memoryVerStorage.get("E", 0); | |||||
| // assertNull(value); | |||||
| // | |||||
| // bufferedStorage.flush(); | |||||
| // | |||||
| // value = memoryVerStorage.get("C", 0); | |||||
| // assertEquals("C1", new String(value)); | |||||
| // value = memoryVerStorage.get("C", 1); | |||||
| // assertEquals("C2", new String(value)); | |||||
| // value = memoryVerStorage.get("D", 0); | |||||
| // assertEquals("D1", new String(value)); | |||||
| // value = memoryVerStorage.get("E", 0); | |||||
| // assertNull(value); | |||||
| // } | |||||
| } | } | ||||
| @@ -1,21 +0,0 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <parent> | |||||
| <artifactId>jdchain-root</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <version>0.9.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <artifactId>stp</artifactId> | |||||
| <packaging>pom</packaging> | |||||
| <modules> | |||||
| <module>stp-communication</module> | |||||
| </modules> | |||||
| <properties> | |||||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
| </properties> | |||||
| </project> | |||||
| @@ -1,50 +0,0 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <parent> | |||||
| <artifactId>stp</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <version>0.9.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <artifactId>stp-communication</artifactId> | |||||
| <name>stp-communication</name> | |||||
| <properties> | |||||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
| <maven.compiler.source>1.8</maven.compiler.source> | |||||
| <maven.compiler.target>1.8</maven.compiler.target> | |||||
| </properties> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>junit</groupId> | |||||
| <artifactId>junit</artifactId> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>commons-codec</groupId> | |||||
| <artifactId>commons-codec</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.alibaba</groupId> | |||||
| <artifactId>fastjson</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>io.netty</groupId> | |||||
| <artifactId>netty-all</artifactId> | |||||
| <version>4.1.29.Final</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.google.guava</groupId> | |||||
| <artifactId>guava</artifactId> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| </project> | |||||
| @@ -1,56 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.stp.communication.MessageExecutor | |||||
| * Author: shaozhuguang | |||||
| * Department: Jingdong Digits Technology | |||||
| * Date: 2019/4/11 上午10:59 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.stp.communication; | |||||
| /** | |||||
| * 消息执行器 | |||||
| * 该执行器由其他应用实现 | |||||
| * @author shaozhuguang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| * @date 2019-04-18 15:29 | |||||
| */ | |||||
| public interface MessageExecutor { | |||||
| /** | |||||
| * 接收到receive消息如何处理 | |||||
| * | |||||
| * @param key | |||||
| * 请求消息的Key,调用者需要在应答时通过该Key应答远端 | |||||
| * @param data | |||||
| * 请求消息的内容 | |||||
| * @param session | |||||
| * 远端Session,描述该消息是从哪发送来的 | |||||
| * @return | |||||
| * 应答结果 | |||||
| */ | |||||
| byte[] receive(String key, byte[] data, RemoteSession session); | |||||
| /** | |||||
| * 应答方式 | |||||
| * | |||||
| * @return | |||||
| * 参考:{@link REPLY} | |||||
| */ | |||||
| REPLY replyType(); | |||||
| // 应答方式 | |||||
| enum REPLY { | |||||
| // 手动应答:Receiver不会自动发送应答请求,需要调用 | |||||
| // session.reply(String key, LoadMessage loadMessage) 或 | |||||
| // asyncReply(String key, LoadMessage loadMessage) | |||||
| MANUAL, | |||||
| // 自动应答:Receiver会根据receive方法的应答结果自动调用应答 | |||||
| // 使用者不能重新调用 | |||||
| AUTO, | |||||
| ; | |||||
| } | |||||
| } | |||||
| @@ -1,206 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.stp.communication.RemoteSession | |||||
| * Author: shaozhuguang | |||||
| * Department: Jingdong Digits Technology | |||||
| * Date: 2019/4/11 上午11:15 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.stp.communication; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackBarrier; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackDataListener; | |||||
| import com.jd.blockchain.stp.communication.connection.Connection; | |||||
| import com.jd.blockchain.stp.communication.message.LoadMessage; | |||||
| import com.jd.blockchain.stp.communication.node.RemoteNode; | |||||
| import org.apache.commons.codec.binary.Hex; | |||||
| import java.util.concurrent.TimeUnit; | |||||
| /** | |||||
| * 远端Session | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class RemoteSession { | |||||
| /** | |||||
| * 本地节点ID | |||||
| */ | |||||
| private String localId; | |||||
| /** | |||||
| * 远端节点 | |||||
| */ | |||||
| private RemoteNode remoteNode; | |||||
| /** | |||||
| * 远端连接 | |||||
| */ | |||||
| private Connection connection; | |||||
| /** | |||||
| * 对应远端节点消息的处理器 | |||||
| * 该处理器若为NULL,则使用当前节点默认处理器 | |||||
| */ | |||||
| private MessageExecutor messageExecutor; | |||||
| /** | |||||
| * 构造器 | |||||
| * @param localId | |||||
| * 本地节点ID | |||||
| * @param connection | |||||
| * 对应连接 | |||||
| */ | |||||
| public RemoteSession(String localId, Connection connection) { | |||||
| this(localId, connection, null); | |||||
| } | |||||
| /** | |||||
| * 构造器 | |||||
| * @param localId | |||||
| * 本地节点ID | |||||
| * @param connection | |||||
| * 对应连接 | |||||
| * @param messageExecutor | |||||
| * 对应远端消息处理器 | |||||
| */ | |||||
| public RemoteSession(String localId, Connection connection, MessageExecutor messageExecutor) { | |||||
| this.localId = localId; | |||||
| this.connection = connection; | |||||
| this.messageExecutor = messageExecutor; | |||||
| this.remoteNode = connection.remoteNode(); | |||||
| } | |||||
| public void init() { | |||||
| connection.initSession(this); | |||||
| } | |||||
| public void initExecutor(MessageExecutor messageExecutor) { | |||||
| this.messageExecutor = messageExecutor; | |||||
| } | |||||
| /** | |||||
| * 同步请求 | |||||
| * 该请求会阻塞原线程 | |||||
| * | |||||
| * @param loadMessage | |||||
| * 要请求的负载消息 | |||||
| * @return | |||||
| * 应答,直到有消息应答或出现异常 | |||||
| * @throws Exception | |||||
| */ | |||||
| public byte[] request(LoadMessage loadMessage) throws Exception { | |||||
| return this.connection.request(this.localId, loadMessage, null).getCallBackData(); | |||||
| } | |||||
| /** | |||||
| * 同步请求 | |||||
| * 该请求会阻塞原线程 | |||||
| * | |||||
| * @param loadMessage | |||||
| * 要请求的负载消息 | |||||
| * @param time | |||||
| * 请求的最长等待时间 | |||||
| * @param timeUnit | |||||
| * 请求的最长等待单位 | |||||
| * @return | |||||
| * 应答,直到有消息或时间截止或出现异常 | |||||
| * @throws Exception | |||||
| */ | |||||
| public byte[] request(LoadMessage loadMessage, long time, TimeUnit timeUnit) throws Exception { | |||||
| return this.connection.request(this.localId, loadMessage, null).getCallBackData(time, timeUnit); | |||||
| } | |||||
| /** | |||||
| * 异步请求 | |||||
| * 不会阻塞调用线程 | |||||
| * | |||||
| * @param loadMessage | |||||
| * 要发送的负载消息 | |||||
| * @return | |||||
| * 应答,需要调用者从Listener中获取结果 | |||||
| */ | |||||
| public CallBackDataListener asyncRequest(LoadMessage loadMessage) { | |||||
| return asyncRequest(loadMessage, null); | |||||
| } | |||||
| /** | |||||
| * 异步请求 | |||||
| * 不会阻塞调用线程 | |||||
| * | |||||
| * @param loadMessage | |||||
| * 要请求的负载消息 | |||||
| * @param callBackBarrier | |||||
| * 回调栅栏(用于多个请求时进行统一阻拦) | |||||
| * @return | |||||
| * 应答,需要调用者从Listener中获取结果 | |||||
| */ | |||||
| public CallBackDataListener asyncRequest(LoadMessage loadMessage, CallBackBarrier callBackBarrier) { | |||||
| return this.connection.request(this.localId, loadMessage, callBackBarrier); | |||||
| } | |||||
| /** | |||||
| * 应答 | |||||
| * | |||||
| * @param key | |||||
| * 请求消息的Key | |||||
| * @param loadMessage | |||||
| * 需要应答的负载消息 | |||||
| */ | |||||
| public void reply(String key, LoadMessage loadMessage) { | |||||
| this.connection.reply(this.localId, key, loadMessage); | |||||
| } | |||||
| public void closeAll() { | |||||
| this.connection.closeAll(); | |||||
| } | |||||
| public void closeReceiver() { | |||||
| this.connection.closeReceiver(); | |||||
| } | |||||
| public void closeSender() { | |||||
| this.connection.closeSender(); | |||||
| } | |||||
| /** | |||||
| * 返回本地节点ID | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| public String localId() { | |||||
| return localId; | |||||
| } | |||||
| /** | |||||
| * 返回远端对应的SessionID | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| public String remoteSessionId() { | |||||
| return Hex.encodeHexString(remoteNode.toString().getBytes()); | |||||
| } | |||||
| /** | |||||
| * 返回远端对应执行器 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| public MessageExecutor messageExecutor() { | |||||
| return this.messageExecutor; | |||||
| } | |||||
| /** | |||||
| * 返回对应远端节点 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| public RemoteNode remoteNode() { | |||||
| return remoteNode; | |||||
| } | |||||
| } | |||||
| @@ -1,74 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.stp.communication.CallBackBarrier | |||||
| * Author: shaozhuguang | |||||
| * Department: Jingdong Digits Technology | |||||
| * Date: 2019/4/12 上午10:22 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.stp.communication.callback; | |||||
| import java.util.concurrent.CountDownLatch; | |||||
| import java.util.concurrent.TimeUnit; | |||||
| /** | |||||
| * 回调栅栏 | |||||
| * 用于对批量请求的应答回调处理 | |||||
| * @author shaozhuguang | |||||
| * @create 2019/4/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class CallBackBarrier { | |||||
| private CountDownLatch countDownLatch; | |||||
| /** | |||||
| * 默认最大尝试调用时间(单位:毫秒) | |||||
| */ | |||||
| private long maxTryCallMillSeconds = 2000; | |||||
| /** | |||||
| * 静态构造器 | |||||
| * @param barrierLength | |||||
| * 请求的远端数量 | |||||
| * @return | |||||
| */ | |||||
| public static final CallBackBarrier newCallBackBarrier(int barrierLength) { | |||||
| return new CallBackBarrier(barrierLength); | |||||
| } | |||||
| /** | |||||
| * 静态构造器 | |||||
| * @param barrierLength | |||||
| * 请求的远端数量 | |||||
| * @param maxTryCallMillSeconds | |||||
| * 最大尝试的时间,单位:毫秒 | |||||
| * @return | |||||
| */ | |||||
| public static final CallBackBarrier newCallBackBarrier(int barrierLength, long maxTryCallMillSeconds) { | |||||
| return new CallBackBarrier(barrierLength, maxTryCallMillSeconds); | |||||
| } | |||||
| private CallBackBarrier(int barrierLength) { | |||||
| this.countDownLatch = new CountDownLatch(barrierLength); | |||||
| } | |||||
| private CallBackBarrier(int barrierLength, long maxTryCallMillSeconds) { | |||||
| this.countDownLatch = new CountDownLatch(barrierLength); | |||||
| this.maxTryCallMillSeconds = maxTryCallMillSeconds; | |||||
| } | |||||
| public void release() { | |||||
| countDownLatch.countDown(); | |||||
| } | |||||
| public boolean tryCall() throws InterruptedException { | |||||
| return countDownLatch.await(maxTryCallMillSeconds, TimeUnit.MILLISECONDS); | |||||
| } | |||||
| public boolean tryCall(long timeout, TimeUnit unit) throws InterruptedException { | |||||
| return countDownLatch.await(timeout, unit); | |||||
| } | |||||
| } | |||||
| @@ -1,113 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.stp.communication.callback.CallBackDataListener | |||||
| * Author: shaozhuguang | |||||
| * Department: Jingdong Digits Technology | |||||
| * Date: 2019/4/15 下午4:40 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.stp.communication.callback; | |||||
| import com.jd.blockchain.stp.communication.node.RemoteNode; | |||||
| import java.util.concurrent.CompletableFuture; | |||||
| import java.util.concurrent.ExecutionException; | |||||
| import java.util.concurrent.TimeUnit; | |||||
| import java.util.concurrent.TimeoutException; | |||||
| import java.util.concurrent.locks.Lock; | |||||
| import java.util.concurrent.locks.ReentrantLock; | |||||
| /** | |||||
| * 数据回调监听器 | |||||
| * @author shaozhuguang | |||||
| * @create 2019/4/15 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class CallBackDataListener { | |||||
| /** | |||||
| * Future | |||||
| */ | |||||
| private CompletableFuture<byte[]> future = new CompletableFuture<>(); | |||||
| /** | |||||
| * 远端节点 | |||||
| */ | |||||
| private RemoteNode remoteNode; | |||||
| private boolean isFill = false; | |||||
| private Lock lock = new ReentrantLock(); | |||||
| /** | |||||
| * 构造器 | |||||
| * @param remoteNode | |||||
| * 远端节点信息 | |||||
| */ | |||||
| public CallBackDataListener(RemoteNode remoteNode) { | |||||
| this.remoteNode = remoteNode; | |||||
| } | |||||
| /** | |||||
| * 获取返回的数据 | |||||
| * 调用该方法会阻塞当前线程,直到有数据返回或出现异常 | |||||
| * @return | |||||
| * 应答结果 | |||||
| * @throws InterruptedException | |||||
| * @throws ExecutionException | |||||
| */ | |||||
| public byte[] getCallBackData() throws InterruptedException, ExecutionException { | |||||
| return future.get(); | |||||
| } | |||||
| /** | |||||
| * 指定时间内获取返回的数据 | |||||
| * 调用该方法会阻塞当前线程,直到时间到达或有数据返回或出现异常 | |||||
| * @param time | |||||
| * 超时时间 | |||||
| * @param timeUnit | |||||
| * 超时单位 | |||||
| * @return | |||||
| * 应答结果,若指定时间内没有数据,则返回null | |||||
| * @throws InterruptedException | |||||
| * @throws ExecutionException | |||||
| * @throws TimeoutException | |||||
| */ | |||||
| public byte[] getCallBackData(long time, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException { | |||||
| return future.get(time, timeUnit); | |||||
| } | |||||
| /** | |||||
| * 设置返回的数据 | |||||
| * @param data | |||||
| */ | |||||
| public void setCallBackData(byte[] data) { | |||||
| // 防止数据多次设置 | |||||
| if (!isFill) { | |||||
| try { | |||||
| lock.lock(); | |||||
| // Double Check | |||||
| if (!isFill) { | |||||
| future.complete(data); | |||||
| isFill = true; | |||||
| } | |||||
| } finally { | |||||
| lock.unlock(); | |||||
| } | |||||
| } | |||||
| } | |||||
| public RemoteNode remoteNode() { | |||||
| return this.remoteNode; | |||||
| } | |||||
| /** | |||||
| * 判断是否异步操作完成 | |||||
| * @return | |||||
| */ | |||||
| public boolean isDone() { | |||||
| return future.isDone(); | |||||
| } | |||||
| } | |||||
| @@ -1,80 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.stp.communication.callback.CallBackLauncher | |||||
| * Author: shaozhuguang | |||||
| * Department: Jingdong Digits Technology | |||||
| * Date: 2019/4/17 下午6:27 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.stp.communication.callback; | |||||
| import java.util.concurrent.Semaphore; | |||||
| /** | |||||
| * 启动器回调 | |||||
| * @author shaozhuguang | |||||
| * @create 2019/4/17 | |||||
| * @since 1.0.0 | |||||
| * @date 2019-04-19 09:53 | |||||
| */ | |||||
| public class CallBackLauncher { | |||||
| /** | |||||
| * 是否启动成功 | |||||
| */ | |||||
| private boolean isBootSuccess = false; | |||||
| /** | |||||
| * 信号量 | |||||
| */ | |||||
| private Semaphore isBooted = new Semaphore(0, true); | |||||
| /** | |||||
| * 异常 | |||||
| */ | |||||
| private Exception exception; | |||||
| /** | |||||
| * 标识当前启动成功 | |||||
| */ | |||||
| public void bootSuccess() { | |||||
| isBootSuccess = true; | |||||
| release(); | |||||
| } | |||||
| /** | |||||
| * 标识当前启动失败 | |||||
| * @param e | |||||
| * 导致失败的异常信息 | |||||
| */ | |||||
| public void bootFail(Exception e) { | |||||
| this.exception = e; | |||||
| isBootSuccess = false; | |||||
| release(); | |||||
| } | |||||
| /** | |||||
| * 等待启动完成 | |||||
| * 调用该方法会阻塞当前线程,知道启动完成或发生异常 | |||||
| * @return | |||||
| * 当前对象 | |||||
| * @throws InterruptedException | |||||
| */ | |||||
| public CallBackLauncher waitingBooted() throws InterruptedException { | |||||
| this.isBooted.acquire(); | |||||
| return this; | |||||
| } | |||||
| public boolean isBootSuccess() { | |||||
| return isBootSuccess; | |||||
| } | |||||
| public Exception exception() { | |||||
| return exception; | |||||
| } | |||||
| private void release() { | |||||
| this.isBooted.release(); | |||||
| } | |||||
| } | |||||
| @@ -1,71 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.stp.communication.connection.AbstractAsyncExecutor | |||||
| * Author: shaozhuguang | |||||
| * Department: Jingdong Digits Technology | |||||
| * Date: 2019/4/17 上午11:16 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.stp.communication.connection; | |||||
| import com.google.common.util.concurrent.ThreadFactoryBuilder; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackLauncher; | |||||
| import java.util.concurrent.*; | |||||
| /** | |||||
| * 抽象异步执行器 | |||||
| * @author shaozhuguang | |||||
| * @create 2019/4/17 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public abstract class AbstractAsyncExecutor implements AsyncExecutor{ | |||||
| /** | |||||
| * 线程池可处理队列的容量 | |||||
| */ | |||||
| private static final int QUEUE_CAPACITY = 1024; | |||||
| /** | |||||
| * 回调执行器 | |||||
| */ | |||||
| protected final CallBackLauncher callBackLauncher = new CallBackLauncher(); | |||||
| /** | |||||
| * 默认提供的初始化活跃线程调度器 | |||||
| * @return | |||||
| */ | |||||
| @Override | |||||
| public ThreadPoolExecutor initRunThread() { | |||||
| ThreadFactory timerFactory = new ThreadFactoryBuilder() | |||||
| .setNameFormat(threadNameFormat()).build(); | |||||
| ThreadPoolExecutor runThread = new ThreadPoolExecutor(1, 1, | |||||
| 60, TimeUnit.SECONDS, | |||||
| new LinkedBlockingQueue<>(QUEUE_CAPACITY), | |||||
| timerFactory, | |||||
| new ThreadPoolExecutor.AbortPolicy()); | |||||
| return runThread; | |||||
| } | |||||
| /** | |||||
| * 启动完成后回调 | |||||
| * 该调用会阻塞当前线程,直到启动完成,无论是成功或失败 | |||||
| * @return | |||||
| * 回调执行器 | |||||
| * 成功或失败会在回调执行器中有所体现 | |||||
| * @throws InterruptedException | |||||
| */ | |||||
| @Override | |||||
| public CallBackLauncher waitBooted() throws InterruptedException { | |||||
| return callBackLauncher.waitingBooted(); | |||||
| } | |||||
| /** | |||||
| * 线程池中的线程命名格式 | |||||
| * @return | |||||
| */ | |||||
| public abstract String threadNameFormat(); | |||||
| } | |||||
| @@ -1,36 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.stp.communication.connection.AsyncExecutor | |||||
| * Author: shaozhuguang | |||||
| * Department: Jingdong Digits Technology | |||||
| * Date: 2019/4/17 上午11:14 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.stp.communication.connection; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackLauncher; | |||||
| import java.util.concurrent.ThreadPoolExecutor; | |||||
| /** | |||||
| * 异步执行器接口 | |||||
| * @author shaozhuguang | |||||
| * @create 2019/4/17 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public interface AsyncExecutor { | |||||
| /** | |||||
| * 初始化运行线程 | |||||
| * @return | |||||
| */ | |||||
| ThreadPoolExecutor initRunThread(); | |||||
| /** | |||||
| * 启动完成后返回调度执行器 | |||||
| * @return | |||||
| * @throws InterruptedException | |||||
| */ | |||||
| CallBackLauncher waitBooted() throws InterruptedException; | |||||
| } | |||||
| @@ -1,220 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.stp.communication.connection.Connection | |||||
| * Author: shaozhuguang | |||||
| * Department: Jingdong Digits Technology | |||||
| * Date: 2019/4/11 下午5:39 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.stp.communication.connection; | |||||
| import com.jd.blockchain.stp.communication.RemoteSession; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackBarrier; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackDataListener; | |||||
| import com.jd.blockchain.stp.communication.callback.CallBackLauncher; | |||||
| import com.jd.blockchain.stp.communication.connection.listener.ReplyListener; | |||||
| import com.jd.blockchain.stp.communication.message.LoadMessage; | |||||
| import com.jd.blockchain.stp.communication.message.SessionMessage; | |||||
| import com.jd.blockchain.stp.communication.message.TransferMessage; | |||||
| import com.jd.blockchain.stp.communication.node.LocalNode; | |||||
| import com.jd.blockchain.stp.communication.node.RemoteNode; | |||||
| import org.apache.commons.codec.binary.Base64; | |||||
| import org.apache.commons.codec.digest.DigestUtils; | |||||
| import java.util.Random; | |||||
| /** | |||||
| * 统一连接对象 | |||||
| * 该对象中有两个对象Receiver和Sender | |||||
| * Receiver为复用对象(每个端口监听产生的Receiver只有一个) | |||||
| * @author shaozhuguang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| * @date 2019-04-18 14:49 | |||||
| */ | |||||
| public class Connection { | |||||
| /** | |||||
| * 远端节点 | |||||
| */ | |||||
| private RemoteNode remoteNode; | |||||
| /** | |||||
| * 接收器 | |||||
| */ | |||||
| private Receiver receiver; | |||||
| /** | |||||
| * 发送器 | |||||
| */ | |||||
| private Sender sender; | |||||
| /** | |||||
| * 构造器 | |||||
| * | |||||
| * @param receiver | |||||
| */ | |||||
| public Connection(Receiver receiver) { | |||||
| this.receiver = receiver; | |||||
| } | |||||
| /** | |||||
| * 初始化RemoteSession | |||||
| * | |||||
| * @param remoteSession | |||||
| */ | |||||
| public void initSession(RemoteSession remoteSession) { | |||||
| this.receiver.initRemoteSession(remoteSession.remoteSessionId(), remoteSession); | |||||
| } | |||||
| /** | |||||
| * 连接远端 | |||||
| * | |||||
| * @param remoteNode | |||||
| * 远端节点 | |||||
| * @param messageExecutorClass | |||||
| * 希望远端节点处理本地节点消息时的消息处理器 | |||||
| * @return | |||||
| * 回调执行器 | |||||
| * @throws InterruptedException | |||||
| */ | |||||
| public CallBackLauncher connect(RemoteNode remoteNode, String messageExecutorClass) throws InterruptedException { | |||||
| this.remoteNode = remoteNode; | |||||
| this.sender = new Sender(this.receiver.localNode(), this.remoteNode, sessionMessage(messageExecutorClass)); | |||||
| this.sender.connect(); | |||||
| return this.sender.waitBooted(); | |||||
| } | |||||
| /** | |||||
| * 发送请求 | |||||
| * | |||||
| * 处理过程简述如下: | |||||
| * 1、生成底层消息(TransferMessage),其中消息类型为请求,用于描述本次发送的消息是用于请求应答; | |||||
| * 2、根据消息的唯一Key,生成listenKey,并生成应答监听器 | |||||
| * 3、将应答监听器添加到Receiver中(Receiver中是以Map存储) | |||||
| * 4、调用Sender发送消息至对端节点 | |||||
| * 5、返回应答监听器的回调数据监听对象 | |||||
| * | |||||
| * @param sessionId | |||||
| * 当前SessionId | |||||
| * @param loadMessage | |||||
| * 载体消息 | |||||
| * @param callBackBarrier | |||||
| * 回调栅栏 | |||||
| * @return | |||||
| */ | |||||
| public CallBackDataListener request(String sessionId, LoadMessage loadMessage, CallBackBarrier callBackBarrier) { | |||||
| TransferMessage transferMessage = transferMessage(sessionId, null, loadMessage, TransferMessage.MESSAGE_TYPE.TYPE_REQUEST); | |||||
| // 监听器的Key | |||||
| String listenKey = transferMessage.toListenKey(); | |||||
| // 创建监听器 | |||||
| ReplyListener replyListener = new ReplyListener(listenKey, this.remoteNode, callBackBarrier); | |||||
| // 添加监听器至Receiver | |||||
| this.receiver.addListener(replyListener); | |||||
| // 发送请求 | |||||
| this.sender.send(transferMessage); | |||||
| return replyListener.callBackDataListener(); | |||||
| } | |||||
| /** | |||||
| * 发送应答 | |||||
| * | |||||
| * @param sessionId | |||||
| * 当前SessionID | |||||
| * @param key | |||||
| * 请求消息的Key,用于描述对应的请求 | |||||
| * @param loadMessage | |||||
| * 应答的载体消息 | |||||
| */ | |||||
| public void reply(String sessionId, String key, LoadMessage loadMessage) { | |||||
| TransferMessage transferMessage = transferMessage(sessionId, key, loadMessage, TransferMessage.MESSAGE_TYPE.TYPE_RESPONSE); | |||||
| // 通过Sender发送数据 | |||||
| this.sender.send(transferMessage); | |||||
| } | |||||
| /** | |||||
| * 生成载体消息的Key | |||||
| * | |||||
| * @param loadMessage | |||||
| * @return | |||||
| */ | |||||
| private String loadKey(LoadMessage loadMessage) { | |||||
| // key每次不能一致,因此增加随机数 | |||||
| byte[] randomBytes = new byte[8]; | |||||
| new Random().nextBytes(randomBytes); | |||||
| byte[] loadBytes = loadMessage.toBytes(); | |||||
| byte[] keyBytes = new byte[loadBytes.length + randomBytes.length]; | |||||
| System.arraycopy(randomBytes, 0, keyBytes, 0, randomBytes.length); | |||||
| System.arraycopy(loadBytes, 0, keyBytes, randomBytes.length, loadBytes.length); | |||||
| // 使用Sha256求Hash | |||||
| byte[] sha256Bytes = DigestUtils.sha256(keyBytes); | |||||
| // 使用base64作为Key | |||||
| return Base64.encodeBase64String(sha256Bytes); | |||||
| } | |||||
| /** | |||||
| * 生成TransferMessage | |||||
| * | |||||
| * @param sessionId | |||||
| * 节点ID | |||||
| * @param key | |||||
| * 消息Key | |||||
| * @param loadMessage | |||||
| * 载体消息 | |||||
| * @param messageType | |||||
| * 消息类型 | |||||
| * @return | |||||
| */ | |||||
| private TransferMessage transferMessage(String sessionId, String key, LoadMessage loadMessage, TransferMessage.MESSAGE_TYPE messageType) { | |||||
| if (key == null || key.length() == 0) { | |||||
| key = loadKey(loadMessage); | |||||
| } | |||||
| TransferMessage transferMessage = new TransferMessage( | |||||
| sessionId, messageType.code(), key, loadMessage.toBytes()); | |||||
| return transferMessage; | |||||
| } | |||||
| /** | |||||
| * 生成SessionMessage | |||||
| * | |||||
| * @param messageExecutorClass | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| private SessionMessage sessionMessage(String messageExecutorClass) { | |||||
| LocalNode localNode = this.receiver.localNode(); | |||||
| SessionMessage sessionMessage = new SessionMessage( | |||||
| localNode.getHostName(), localNode.getPort(), messageExecutorClass); | |||||
| return sessionMessage; | |||||
| } | |||||
| public void closeAll() { | |||||
| closeReceiver(); | |||||
| closeSender(); | |||||
| } | |||||
| public RemoteNode remoteNode() { | |||||
| return remoteNode; | |||||
| } | |||||
| public void closeReceiver() { | |||||
| this.receiver.close(); | |||||
| } | |||||
| public void closeSender() { | |||||
| this.sender.close(); | |||||
| } | |||||
| } | |||||
| @@ -1,161 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.stp.communication.inner.Receiver | |||||
| * Author: shaozhuguang | |||||
| * Department: Jingdong Digits Technology | |||||
| * Date: 2019/4/11 上午10:59 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.stp.communication.connection; | |||||
| import com.jd.blockchain.stp.communication.RemoteSession; | |||||
| import com.jd.blockchain.stp.communication.connection.handler.HeartBeatReceiverHandler; | |||||
| import com.jd.blockchain.stp.communication.connection.handler.HeartBeatReceiverTrigger; | |||||
| import com.jd.blockchain.stp.communication.connection.handler.ReceiverHandler; | |||||
| import com.jd.blockchain.stp.communication.connection.listener.ReplyListener; | |||||
| import com.jd.blockchain.stp.communication.manager.ConnectionManager; | |||||
| import com.jd.blockchain.stp.communication.node.LocalNode; | |||||
| import io.netty.bootstrap.ServerBootstrap; | |||||
| import io.netty.channel.ChannelFuture; | |||||
| import io.netty.channel.ChannelInitializer; | |||||
| import io.netty.channel.ChannelOption; | |||||
| import io.netty.channel.EventLoopGroup; | |||||
| import io.netty.channel.nio.NioEventLoopGroup; | |||||
| import io.netty.channel.socket.SocketChannel; | |||||
| import io.netty.channel.socket.nio.NioServerSocketChannel; | |||||
| import io.netty.handler.codec.LineBasedFrameDecoder; | |||||
| import io.netty.handler.codec.string.StringDecoder; | |||||
| import io.netty.handler.timeout.IdleStateHandler; | |||||
| import java.io.Closeable; | |||||
| import java.net.InetSocketAddress; | |||||
| import java.util.concurrent.*; | |||||
| /** | |||||
| * 接收器 | |||||
| * @author shaozhuguang | |||||
| * @create 2019/4/11 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class Receiver extends AbstractAsyncExecutor implements Closeable { | |||||
| /** | |||||
| * Netty中的BOSS线程 | |||||
| */ | |||||
| private final EventLoopGroup bossGroup = new NioEventLoopGroup(); | |||||
| /** | |||||
| * Netty中的Worker线程 | |||||
| */ | |||||
| private final EventLoopGroup workerGroup = new NioEventLoopGroup(); | |||||
| /** | |||||
| * 本地节点 | |||||
| */ | |||||
| private LocalNode localNode; | |||||
| /** | |||||
| * 消息接收Handler | |||||
| */ | |||||
| private ReceiverHandler receiverHandler; | |||||
| public Receiver(LocalNode localNode) { | |||||
| this.localNode = localNode; | |||||
| } | |||||
| /** | |||||
| * 启动监听 | |||||
| */ | |||||
| public void startListen() { | |||||
| ServerBootstrap bootstrap = new ServerBootstrap(); | |||||
| bootstrap.group(bossGroup, workerGroup) | |||||
| .channel(NioServerSocketChannel.class) | |||||
| .option(ChannelOption.SO_BACKLOG, 1024) | |||||
| .childOption(ChannelOption.SO_KEEPALIVE, true) | |||||
| .localAddress(new InetSocketAddress(this.localNode.getPort())) | |||||
| .childHandler(new ChannelInitializer<SocketChannel>() { | |||||
| @Override | |||||
| protected void initChannel(SocketChannel socketChannel) throws Exception { | |||||
| socketChannel.pipeline() | |||||
| // .addLast(new LoggingHandler(LogLevel.ERROR)) | |||||
| .addLast(new IdleStateHandler(8, 0, 0, TimeUnit.SECONDS)) | |||||
| .addLast(new LineBasedFrameDecoder(1024)) | |||||
| .addLast(new StringDecoder()) | |||||
| .addLast(new HeartBeatReceiverTrigger()) | |||||
| .addLast(new HeartBeatReceiverHandler()) | |||||
| .addLast(receiverHandler); | |||||
| } | |||||
| }); | |||||
| // 由单独的线程启动,防止外部调用线程阻塞 | |||||
| ThreadPoolExecutor runThread = initRunThread(); | |||||
| runThread.execute(() -> { | |||||
| try { | |||||
| ChannelFuture f = bootstrap.bind().sync(); | |||||
| boolean isStartSuccess = f.isSuccess(); | |||||
| if (isStartSuccess) { | |||||
| super.callBackLauncher.bootSuccess(); | |||||
| // 启动成功 | |||||
| f.channel().closeFuture().sync(); | |||||
| } else { | |||||
| // 启动失败 | |||||
| throw new Exception("Receiver start fail :" + f.cause().getMessage() + " !!!"); | |||||
| } | |||||
| } catch (Exception e) { | |||||
| super.callBackLauncher.bootFail(e); | |||||
| } finally { | |||||
| close(); | |||||
| } | |||||
| }); | |||||
| } | |||||
| @Override | |||||
| public String threadNameFormat() { | |||||
| return "receiver-pool-%d"; | |||||
| } | |||||
| /** | |||||
| * 初始化ReceiverHandler | |||||
| * | |||||
| * @param connectionManager | |||||
| * 连接管理器 | |||||
| * @param messageExecutorClass | |||||
| * 当前节点的消息处理Class | |||||
| */ | |||||
| public void initReceiverHandler(ConnectionManager connectionManager, String messageExecutorClass) { | |||||
| receiverHandler = new ReceiverHandler(connectionManager, messageExecutorClass, this.localNode); | |||||
| } | |||||
| /** | |||||
| * 初始化远端Session | |||||
| * | |||||
| * @param sessionId | |||||
| * | |||||
| * @param remoteSession | |||||
| */ | |||||
| public void initRemoteSession(String sessionId, RemoteSession remoteSession) { | |||||
| receiverHandler.putRemoteSession(sessionId, remoteSession); | |||||
| } | |||||
| /** | |||||
| * 添加监听器 | |||||
| * | |||||
| * @param replyListener | |||||
| */ | |||||
| public void addListener(ReplyListener replyListener) { | |||||
| receiverHandler.addListener(replyListener); | |||||
| } | |||||
| @Override | |||||
| public void close() { | |||||
| receiverHandler.close(); | |||||
| bossGroup.shutdownGracefully(); | |||||
| workerGroup.shutdownGracefully(); | |||||
| } | |||||
| public LocalNode localNode() { | |||||
| return this.localNode; | |||||
| } | |||||
| } | |||||